Greetings all.
Take 2 on this patch. I believe the same change log can be used,
despite the number of implementation changes.
--Andrew Black
Index: etc/config/GNUmakefile.bin
===================================================================
--- etc/config/GNUmakefile.bin (revision 437752)
+++ etc/config/GNUmakefile.bin (working copy)
@@ -54,7 +54,7 @@
@$(MAKE) -C $(LIBDIR)
# link the run utility
-exec: runall.o cmdopt.o output.o util.o exec.o
+exec: runall.o cmdopt.o output.o util.o exec.o display.o
@echo "$(LD) $^ -o $@ $(LDFLAGS) $(LDLIBS)" >> $(LOGFILE)
$(LD) $^ -o $@ $(LDFLAGS) $(LDLIBS) $(TEEOPTS)
Index: util/output.h
===================================================================
--- util/output.h (revision 437752)
+++ util/output.h (working copy)
@@ -28,6 +28,6 @@
#define OUTPUT_H
void
-parse_output (const char* target);
+parse_output (struct target_status* status);
#endif // OUTPUT_H
Index: util/output.cpp
===================================================================
--- util/output.cpp (revision 437752)
+++ util/output.cpp (working copy)
@@ -35,6 +35,7 @@
#include <sys/stat.h>
#include "cmdopt.h"
+#include "display.h"
#include "util.h"
#include "output.h"
@@ -45,37 +46,26 @@
/**
- Parses output file out_name for test target_name.
+ Parses contents of the open file handle data for test target_name.
- This method tries to open out_name. If this succedes, it then searches
- the file for a result summary as produced by the rwtest driver.
- If a result summary is found, it then parses the result and displays the
- result of the parsing. Otherwise, and appropriate error messages is
- displayed.
+ This method reads the data file handle, looking for the output summary from
+ the test driver. The results of this search (and the parsing of this
+ summary, if found) are stored in the status structure.
- Output messages produced:
- - OUTPUT\n
- Output file couldn't be opened
- - OFLOW\n
- Assertions counted overflows the (internal)storage variable
- - FORMAT\n
- Result summary can't be found in output file
- - 0 [assertion count] [failed assertion count] [pass rate]\n
- Parsed results
-
@param data pointer to file structure for output file being parsed
+ @param status status object to record results in.
*/
static void
-check_test (FILE* data)
+check_test (FILE* data, struct target_status* status)
{
unsigned r_lvl = 0, r_active = 0, r_total = 0;
- unsigned asserts = 0, failed = 0;
int fmt_ok = 0;
unsigned fsm = 0;
char tok;
assert (0 != target_name);
assert (0 != data);
+ assert (0 != status);
for (tok = fgetc (data); fsm < 6 && !feof (data); tok = fgetc (data)) {
switch (tok) {
@@ -110,10 +100,10 @@
expected to be active, so invert the total */
if (8 == r_lvl && 0 == strcmp (target_name, "0.new"))
r_active = r_total-r_active;
- failed += r_active;
- asserts += r_total;
- if (failed < r_active || asserts < r_total) {
- puts (" OFLOW");
+ status->failed += r_active;
+ status->assert += r_total;
+ if (status->failed < r_active || status->assert < r_total) {
+ status->status = ST_OVERFLOW;
return;
}
}
@@ -122,20 +112,12 @@
}
}
- if (fmt_ok) {
- unsigned pcnt = 0;
- if (asserts) {
- pcnt = 100 * (asserts - failed) / asserts;
- }
- printf (" 0 %7d %6d %5d%%\n", asserts, failed, pcnt);
- }
- else {
- puts ("FORMAT");
- }
+ if (!fmt_ok)
+ status->status = ST_FORMAT;
}
/**
- Parses output file out_name for test target_name.
+ Parses contents of the open file handle data for test target_name.
This method is a reimplementation of check_test (). The difference between
this method and check_test () is how it parses the results. This version
@@ -145,14 +127,14 @@
@see check_test ()
*/
static void
-check_compat_test (FILE* data)
+check_compat_test (FILE* data, struct target_status* status)
{
- unsigned asserts = 0, failed = 0;
int read = 0;
unsigned fsm = 0;
char tok;
assert (0 != data);
+ assert (0 != status);
fseek (data, -64, SEEK_END); /* Seek near the end of the file */
@@ -179,19 +161,12 @@
if (!feof (data)) { /* leading "## A" eaten above */
read = fscanf (data, "ssertions = %u\n## FailedAssertions = %u",
- &asserts, &failed);
+ &status->assert, &status->failed);
}
- if (2 == read) {
- unsigned pcnt = 0;
- if (asserts) {
- pcnt = 100 * (asserts - failed) / asserts;
- }
- printf (" 0 %7d %6d %5d%%\n", asserts, failed, pcnt);
+ if (2 != read) {
+ status->status = ST_FORMAT;
}
- else {
- puts ("FORMAT");
- }
}
/**
@@ -205,31 +180,23 @@
Parses output file out_name for the example target_name.
This method tries to compare out_name against a reference output file and
- displays a result code. The reference file is determined by target_name and
- the in_root global variable.
- This method relies on the POSIX diff utility at this time. This dependancy
- needs to be removed, though doing so will require a rewrite of the method.
+ reports the result of the comparison. If the comparison fails to result in
+ a match, status->status is set to the appropriate error state. The
+ reference file name is generated by the reference_name function.
Reference file locations:
- [in_root]/manual/out/[target_name].out
- [in_root]/tutorial/out/[target_name].out
- Output messages produced:
- - NOREF\n
- No reference file could be found
- - OUTPUT\n
- Output file differs from the reference file
- - 0\n
- Output file matches the reference file (check passes)
-
@todo add logic to handle differing line endings (CR vs LF vs CRLF)
- @param output_name the name of the output file
- @param data pointer to file structure for output file being parsed
- @see in_root
+ @param out_name the name of the output file
+ @param fout pointer to file structure for output file being parsed
+ @param status status object to record results in.
+ @see reference_name
*/
static void
-check_example (char* const out_name, FILE* fout)
+check_example (char* const out_name, FILE* fout, struct target_status* status)
{
struct stat file_info;
char* ref_name;
@@ -238,6 +205,7 @@
assert (0 != out_name);
assert (0 != fout);
+ assert (0 != status);
/* Try in_root/manual/out/target_name.out */
ref_name = reference_name ("manual", "out");
@@ -246,7 +214,7 @@
if (ENOENT != errno) {
warn ("stat (%s) error: %s\n", exe_name, ref_name,
strerror (errno));
- puts ("BADREF");
+ status->status = ST_BAD_REF;
free (ref_name);
return;
}
@@ -260,10 +228,10 @@
if (ENOENT != errno) {
warn ("stat (%s) error: %s\n", exe_name, ref_name,
strerror (errno));
- puts ("BADREF");
+ status->status = ST_BAD_REF;
}
else
- puts (" NOREF");
+ status->status = ST_NO_REF;
free (ref_name);
return;
@@ -276,7 +244,7 @@
int cache = errno; /* caching errno, as puts could alter it */
if (ENOENT != cache)
warn ("Error opening %s: %s\n", ref_name, strerror (cache));
- puts ("BADREF");
+ status->status = ST_BAD_REF;
}
else {
int match = 1; /* do the two files match? */
@@ -317,10 +285,8 @@
}
}
- if (match)
- puts (" 0");
- else
- puts ("OUTPUT");
+ if (!match)
+ status->status = ST_BAD_OUTPUT;
fclose (fref);
}
@@ -328,39 +294,46 @@
}
/**
- Umbrella (dispatch) function to analyse the (saved) output of target
+ Umbrella (dispatch) function to analyse the (saved) output of target.
+ This file opens the saved output for parsing. If this fails,
+ the status structure is updated to reflect this. Otherwise, check_test (),
+ check_compat_test (), or check_example () is called to parse the output
+ file.
+
@param target the path to the executable that generated the output file
being parsed.
+ @param status status object to record results in.
*/
void
-parse_output (const char* target)
+parse_output (struct target_status* status)
{
char* out_name;
FILE* data;
- assert (0 != target);
- out_name = output_name (target);
+ assert (0 != status);
+ assert (0 != status->argv [0]);
+ out_name = output_name (status->argv [0]);
data = fopen (out_name, "r");
if (0 == data) {
if (ENOENT != errno)
warn ("Error opening %s: %s\n", out_name, strerror (errno));
- puts ("OUTPUT");
+ status->status = ST_NO_OUTPUT;
}
else {
if (!strlen (in_root)) {
/* If there is not an input directory, look at the assertion tags */
if (!compat)
- check_test (data);
+ check_test (data, status);
else
- check_compat_test (data);
+ check_compat_test (data, status);
}
else {
/* Otherwise, diff against the output file */
- check_example (out_name, data);
+ check_example (out_name, data, status);
}
fclose (data);
}
Index: util/runall.cpp
===================================================================
--- util/runall.cpp (revision 437752)
+++ util/runall.cpp (working copy)
@@ -26,7 +26,6 @@
#include <assert.h> /* for assert() */
#include <errno.h> /* for errno */
-#include <stdio.h> /* for fflush(), printf(), puts(), ... */
#include <stdlib.h> /* for exit(), free() */
#include <string.h> /* for memcpy(), ... */
@@ -36,10 +35,12 @@
#if !defined (_WIN32) && !defined (_WIN64)
# include <sys/wait.h> /* for WIFEXITED(), ... */
#else
+# include <signal.h> /* for SIGSEGV */
# include <windows.h> /* for STATUS_ACCESS_VIOLATION */
#endif
#include "cmdopt.h"
+#include "display.h"
#include "exec.h"
#include "output.h"
#include "util.h"
@@ -193,34 +194,31 @@
Preflight check to ensure that target is something that should be run.
This method checks to see if target exists and is theoretically executable.
+ If a problem is detected, the condition is recorded in the status structure
+ and 0 is returned.
+
A special case is the situation where the target name ends in .o,
indicating that the target only needed to compile, and doesn't need to
- be run. If there are problems, or the target is a .o file, a result is
- displayed and 0 is returned.
+ be run. Processing for this case is currently disabled as it is unused.
- Output messages produced:
- - COMP\n
- Target failed to compile
- - LINK\n
- Target failed to link
- - XPERM\n
- Target lacks execute permissions
- - 0\n
- Target .o file exists (check passes)
-
@param target the path to the executable to check
+ @param status status object to record results in.
@return 1 if valid target to run, 0 otherwise.
*/
static int
-check_target_ok (const char* target)
+check_target_ok (struct target_status* status)
{
struct stat file_info;
int exists = 1;
- if (0 > stat (target, &file_info)) {
+ assert (0 != status);
+ assert (0 != status->argv);
+ assert (0 != status->argv[0]);
+
+ if (0 > stat (status->argv [0], &file_info)) {
if (ENOENT != errno) {
- warn ("Error stating %s: %s\n", target, strerror (errno));
- puts ("ERROR");
+ warn ("Error stating %s: %s\n", status->argv [0], strerror (errno));
+ status->status = ST_SYSTEM_ERROR;
return 0;
}
file_info.st_mode = 0; /* force mode on non-existant file to 0 */
@@ -230,30 +228,28 @@
/* This is roughly equivlent to the -x bash operator.
It checks if the file can be run, /not/ if we can run it
*/
- const size_t path_len = strlen (target);
+ const size_t path_len = strlen (status->argv [0]);
char* tmp_name;
#if 0 /* Disable .o target check as unused */
/* If target is a .o file, check if it exists */
- if ('.' == target [path_len-1] && 'o' == target [path_len]) {
- if (exists)
- puts (" 0");
- else
- puts (" COMP");
+ if ('.' == status->argv [0] [path_len-1] && 'o' == status->argv [0] [path_len]) {
+ if (!exists)
+ status->status = ST_COMPILE;
return 0;
}
#endif
/* If the target exists, it doesn't have valid permissions */
if (exists) {
- puts (" XPERM");
+ status->status = ST_EXECUTE_FLAG;
return 0;
}
#if !defined (_WIN32) && !defined (_WIN64)
/* Otherwise, check for the .o file on non-windows systems */
tmp_name = (char*)RW_MALLOC (path_len + 3);
- memcpy (tmp_name, target, path_len + 1);
+ memcpy (tmp_name, status->argv [0], path_len + 1);
strcat (tmp_name,".o");
#else
/* Or the target\target.obj file on windows systems*/
@@ -264,7 +260,7 @@
adding 2 characters (\ directory seperator and trailing
null) */
tmp_name = (char*)RW_MALLOC (tmp_len);
- memcpy (tmp_name, target, path_len - 4);
+ memcpy (tmp_name, status->argv [0], path_len - 4);
tmp_name [path_len - 4] = default_path_sep;
memcpy (tmp_name + path_len - 3, target_name, target_len);
tmp_name [tmp_len - 4] = 'o';
@@ -277,13 +273,13 @@
if (0 > stat (tmp_name, &file_info)) {
if (ENOENT != errno) {
warn ("Error stating %s: %s\n", tmp_name, strerror (errno));
- puts ("ERROR");
+ status->status = ST_SYSTEM_ERROR;
}
else
- puts (" COMP");
+ status->status = ST_COMPILE;
}
else {
- puts (" LINK");
+ status->status = ST_LINK;
}
free (tmp_name);
@@ -296,69 +292,62 @@
Post target execution result analysis.
This method examines the content of result, dispatching the processing
- to check_test () or check_example () if target had a return code of 0.
+ to pares_output () if target had a return code of 0.
Otherwise, this method determines why target terminated, based on the
- return code and outputs that reason.
+ return code and fills the result structure with these results.
- Output messages produced:
- - EXIST\n
- Return code 126 (target doesn't exist?)
- - EXEC\n
- Return code 127 (target couldn't be run?)
- - NKILL\n
- Couldn't kill the child executable
- - n\n
- Child exited with the non-zero return code n
- - name\n
- Child terminated upon recieving the signal SIGname
-
@param target the path to the executable that was run
- @param exec_name the short name of the executable
@param result the return code data from running the target
- @see check_test ()
- @see check_example ()
+ @param status status object to record results in.
+ @see parse_output ()
*/
static void
-process_results (const char* target, const struct exec_attrs* result)
+process_results (const struct exec_attrs* result,
+ struct target_status* status)
{
+ assert (0 != result);
+ assert (0 != status);
+
if (0 == result->status) {
- parse_output (target);
+ parse_output (status);
}
#if !defined (_WIN32) && !defined (_WIN64)
else if (WIFEXITED (result->status)) {
const int retcode = WEXITSTATUS (result->status);
switch (retcode) {
case 126:
- puts (" EXIST");
+ status->status = ST_EXIST;
break;
case 127:
- puts (" EXEC");
+ status->status = ST_EXECUTE;
break;
default:
- printf ("%6d\n", retcode);
+ status->exit = retcode;
}
}
else if (WIFSIGNALED (result->status)) {
- printf ("%6s\n", get_signame (WTERMSIG (result->status)));
+ status->signal = WTERMSIG (result->status);
}
else if (WIFSTOPPED (result->status)) {
- printf ("%6s\n", get_signame (WSTOPSIG (result->status)));
+ status->signal = WSTOPSIG (result->status);
}
else if (-1 == result->status && -1 == result->killed) {
- puts (" NKILL");
+ status->status = ST_NOT_KILLED;
}
else {
- printf ("(%d|%d)\n", result->status, result->killed);
+ warn ("Unknown result status: %d, %d\n", result->status,
+ result->killed);
+ status->status = ST_SYSTEM_ERROR;
}
#else
else if (-1 == result->status)
- puts (" I/O");
+ status->status = ST_SYSTEM_ERROR;
else if (result->error)
- puts ("KILLED");
+ status->status = ST_KILLED;
else if (STATUS_ACCESS_VIOLATION == result->status)
- puts (" SEGV");
+ status->signal = SIGSEGV;
else
- printf ("%6d\n", result->status);
+ status->exit = result->status;
#endif /* _WIN{32,64} */
}
@@ -409,28 +398,31 @@
static void
run_target (char* target, char** argv)
{
- char** childargv;
+ struct target_status results;
assert (0 != target);
assert (0 != argv);
- childargv = merge_argv (target, argv);
+ memset (&results, 0, sizeof results);
- assert (0 != childargv);
- assert (0 != childargv [0]);
+ results.argv = merge_argv (target, argv);
- target_name = rw_basename (childargv [0]);
+ assert (0 != results.argv);
+ assert (0 != results.argv [0]);
- printf ("%-25.25s ", target_name);
- fflush (stdout);
+ target_name = rw_basename (results.argv [0]);
- if (check_target_ok (childargv [0])) {
- struct exec_attrs status = exec_file (childargv);
- process_results (childargv [0], &status);
+ print_target (&results);
+
+ if (check_target_ok (&results)) {
+ struct exec_attrs status = exec_file (results.argv);
+ process_results (&status, &results);
}
- free (childargv [0]);
- free (childargv);
+ print_status (&results);
+
+ free (results.argv [0]);
+ free (results.argv);
}
/**
@@ -468,12 +460,15 @@
char** childargv = split_opt_string (exe_opts);
assert (0 != childargv);
- puts ("NAME STATUS ASSERTS FAILED PERCNT");
+ print_header ();
+
for (i = 0; i < argc; ++i) {
run_target (argv [i], childargv);
}
+ print_footer ();
+
if (childargv [0])
free (childargv [0]);
free (childargv);
/************************************************************************
*
* display.cpp - Definitions of the result display subsystem
*
* $Id$
*
************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
**************************************************************************/
#include <assert.h>
#include <stdio.h> /* for fflush(), printf(), puts(), ... */
#include "cmdopt.h" /* for target_name -should this be moved? */
#include "exec.h" /* for get_signame */
#include "display.h"
void print_header_plain ()
{
puts ("NAME STATUS ASSERTS FAILED PERCNT");
}
void print_target_plain (const struct target_status*)
{
printf ("%-25.25s ", target_name);
fflush (stdout);
}
void print_status_plain (const struct target_status* status)
{
assert (ST_OK <= status->status && ST_LAST > status->status);
if (status->status) /* if status is set, print it */
printf ("%6s\n", short_st_name [status->status]);
else if (status->exit) /* if exit code is non-zero, print it */
printf ("%6d\n", status->exit);
else if (status->signal) /* if exit signal is non-zero, print it */
printf ("%6s\n", get_signame (status->signal));
else if (!status->assert) /* if the assertion count is 0, it's an example */
puts (" 0");
else {
unsigned pcnt = 0;
if (status->assert) {
pcnt = 100 * (status->assert - status->failed) / status->assert;
}
printf (" 0 %7u %6u %5d%%\n", status->assert, status->failed, pcnt);
}
}
void print_footer_plain () {}
const char* short_st_name [ST_LAST] = {
"OK", /*ST_OK*/
"COMP", /*ST_COMPILE*/
"LINK", /*ST_LINK*/
"EXIST", /*ST_EXIST*/
"XPERM", /*ST_EXECUTE_FLAG*/
"EXEC", /*ST_EXECUTE*/
"NOUT", /*ST_NO_OUTPUT*/
"NREF", /*ST_NO_REF*/
"BREF", /*ST_BAD_REF*/
"OUTPUT", /*ST_BAD_OUTPUT*/
"FORMAT", /*ST_FORMAT*/
"OFLOW", /*ST_OVERFLOW*/
"ERROR", /*ST_SYSTEM_ERROR*/
"KILLED", /*ST_KILLED*/
"NKILL" /*ST_NOT_KILLED*/
};
void (*print_header) () = print_header_plain;
void (*print_target) (const struct target_status*) = print_target_plain;
void (*print_status) (const struct target_status* status) = print_status_plain;
void (*print_footer) () = print_footer_plain;
/************************************************************************
*
* display.h - Interface declaration for the result display subsystem
*
* $Id$
*
************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
**************************************************************************/
#ifndef RW_DISPLAY_H
#define RW_DISPLAY_H
/**
Output format mode enumeration.
Used to determine the mode used in producing output.
*/
enum OutputFmt {
FMT_PLAIN = 0, /**< plain text output. */
FMT_XTERM, /**< xterm colored output. */
FMT_VERBOSE, /**< verbose text output. */
FMT_INDEP, /**< Non-targeted formated output. */
FMT_HTML /**< HTML formated output. */
};
/**
Status codes to denote result of analysis.
*/
enum ProcessStatus {
ST_OK = 0, /**< Default (OK) status */
ST_COMPILE, /**< Target failed to compile */
ST_LINK, /**< Target failed to link */
ST_EXIST, /**< Target doesn't exist? */
ST_EXECUTE_FLAG, /**< Target lacks X flag */
ST_EXECUTE, /**< Target failed to execute? */
ST_NO_OUTPUT, /**< Target produced no output */
ST_NO_REF, /**< No reference file found */
ST_BAD_REF, /**< Invalid reference file found */
ST_BAD_OUTPUT, /**< Incorrect output found */
ST_FORMAT, /**< Incorrectly formatted (test) output found */
ST_OVERFLOW, /**< Assertion counter overflow */
ST_SYSTEM_ERROR, /**< System error during file system operation */
ST_KILLED, /**< Target killed by exec utility */
ST_NOT_KILLED, /** Target not killed by exec utility */
ST_LAST /**< Array terminator */
};
/**
ProcessStatus enum lookup table for 'short' (6 character) strings.
*/
extern const char* short_st_name [ST_LAST];
/**
Structure encapsulating the results extracted from a run.
*/
struct target_status {
char** argv; /**< Target argv array. */
int exit; /**< exit code for process. */
int signal; /**< Termination signal for process. */
enum ProcessStatus status; /**< Textual process status. */
unsigned warn; /**< Number of (test) warnings. */
unsigned assert; /**< Number of (test) assertions. */
unsigned failed; /**< Number of failed (test) assertions. */
};
/**
Sets the output functions referenced.
*/
void set_output_format (enum OutputFmt format);
/**
Sets the column order and widths (if applicable) for output.
@param format custom format string for output.
*/
extern void (*set_header) (const char* format);
/**
Prints the table preamble formatting, followed by the formatted header row.
*/
extern void (*print_header) ();
/**
Prints the formatted header column for a target row.
This method uses the target_name global (defined in cmdopt.h), and flushes
stdout after printing.
@see target_name
*/
extern void (*print_target) (const struct target_status* status);
/**
Updates the display of a (non-final) status indicator
This function is only useful for FMT_XTERM.
@param time 0 > time indicates -signal number, otherwise time remaining
*/
extern void (*set_progress) (int time);
/**
Prints the formatted results for a target row.
@param status describes the results of the run
*/
extern void (*print_status) (const struct target_status* status);
/**
Prints the closing formatting for the table.
*/
extern void (*print_footer) ();
#endif // RW_DISPLAY_H