Greetings all.
Attached is a patch plus a pair of new files (to be placed in the util
subdirectory) to create a display subsystem for the exec utility. This
is a preparatory patch for other changes related to displaying the results.
It may make sense to rename the existing output subsystem (as parser?)
and call the new subsystem 'output'.
--Andrew Black
Log:
* GNUmakefile.bin: Add display.o to link objects for exec utility
* display.h: Define display subsystem
* display.cpp: Implement initial display subsystem, equivalent to
existing output.
* output.h (parse_output): Alter signature to support display subsystem
* output.cpp: Include display.h, Update comments.
(check_test, check_compat_test, check_example, parse_output): Alter
signatures to support display subsystem, replace I/O calls with updates
to target_status structure.
* runall.cpp: Same as above.
(check_target_ok, process_results): Same as above.
(run_target): Add storage structure for display subsystem, calls into
display subsystem.
(main): Add calls into display subsystem.
/************************************************************************
*
* 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 <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"
enum display_modes output_fmt = DISPLAY_PLAIN;
void set_header (const char* format);
void print_header ()
{
puts ("NAME STATUS ASSERTS FAILED PERCNT");
}
void begin_row ()
{
printf ("%-25.25s ", target_name);
fflush (stdout);
}
void set_progress (int time);
#if 0
{
if (DISPLAY_XTERM == output_fmt) {
if (0 <= time)
/* print time */;
else
/* print get_signame (-time) */;
}
}
#endif
void end_row (const struct target_status* status)
{
if (status->status) /* if status (message) is set, print it */
printf ("%6s\n", status->status);
else if (status->exit) /* if exit (status) is non-zero, print it */
printf ("%6d\n", status->exit);
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 ()
{
switch (output_fmt) {
case DISPLAY_HTML:
break;
default:
break;
}
}
/************************************************************************
*
* exec.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.
*/
extern enum display_modes {
DISPLAY_PLAIN = 0, /**< plain text output, intended for files. */
DISPLAY_XTERM, /**< xterm colored output. */
DISPLAY_INDEP, /**< Non-targeted formated output. */
DISPLAY_HTML /**< HTML formated output. */
} output_fmt;
/**
Structure encapsulating the results extracted from a run.
*/
struct target_status {
int exit; /**< exit code for process */
const char* status; /**< Name for process status */
unsigned warn; /**< Number of (test) warnings */
unsigned assert; /**< Number of (test) assertions */
unsigned failed; /**< Number of failed (test) assertions */
};
/**
Sets the column order and widths (if applicable) for output.
@todo describe format string format
@param format custom format string for output.
*/
void set_header (const char* format);
/**
Prints the table preamble formatting, followed by the formatted header row.
*/
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
*/
void begin_row ();
/**
Updates the display of a (non-final) status indicator
This function is only useful when output_fmt == DISPLAY_XTERM.
@param time 0 > time indicates -signal number, otherwise time remaining
*/
void set_progress (int time);
/**
Prints the formatted results for a target row.
@param status describes the results of the run
*/
void end_row (const struct target_status* status);
/**
Prints the closing formatting for the table.
*/
void print_footer ();
#endif // RW_DISPLAY_H
Index: etc/config/GNUmakefile.bin
===================================================================
--- etc/config/GNUmakefile.bin (revision 433677)
+++ 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 436991)
+++ util/output.h (working copy)
@@ -28,6 +28,6 @@
#define OUTPUT_H
void
-parse_output (const char* target);
+parse_output (const char* target, struct target_status* status);
#endif // OUTPUT_H
Index: util/output.cpp
===================================================================
--- util/output.cpp (revision 436991)
+++ 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"
@@ -49,27 +50,23 @@
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.
+ If a result summary is found, it then parses it and fills the status
+ structure with the results of the parsing. Otherwise, status->status is
+ set to the error state.
- Output messages produced:
- - OUTPUT\n
- Output file couldn't be opened
+ Error states:
- 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;
@@ -110,10 +107,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 = "OFLOW";
return;
}
}
@@ -122,16 +119,8 @@
}
}
- 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 = "FORMAT";
}
/**
@@ -145,9 +134,8 @@
@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;
@@ -179,19 +167,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 = "FORMAT";
}
- else {
- puts ("FORMAT");
- }
}
/**
@@ -205,31 +186,31 @@
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:
+ Error states:
- 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)
+ - BADREF\n
+ Error accessing reference file
@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;
@@ -246,7 +227,7 @@
if (ENOENT != errno) {
warn ("stat (%s) error: %s\n", exe_name, ref_name,
strerror (errno));
- puts ("BADREF");
+ status->status = "BADREF";
free (ref_name);
return;
}
@@ -260,10 +241,10 @@
if (ENOENT != errno) {
warn ("stat (%s) error: %s\n", exe_name, ref_name,
strerror (errno));
- puts ("BADREF");
+ status->status = "BADREF";
}
else
- puts (" NOREF");
+ status->status = "NOREF";
free (ref_name);
return;
@@ -276,7 +257,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 = "BADREF";
}
else {
int match = 1; /* do the two files match? */
@@ -317,10 +298,8 @@
}
}
- if (match)
- puts (" 0");
- else
- puts ("OUTPUT");
+ if (!match)
+ status->status = "OUTPUT";
fclose (fref);
}
@@ -328,13 +307,17 @@
}
/**
- 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,
+ status->status is set to an error state of "OUTPUT".
+
@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 (const char* target, struct target_status* status)
{
char* out_name;
FILE* data;
@@ -347,20 +330,20 @@
if (0 == data) {
if (ENOENT != errno)
warn ("Error opening %s: %s\n", out_name, strerror (errno));
- puts ("OUTPUT");
+ status->status = "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 436991)
+++ 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(), ... */
@@ -40,6 +39,7 @@
#endif
#include "cmdopt.h"
+#include "display.h"
#include "exec.h"
#include "output.h"
#include "util.h"
@@ -195,10 +195,12 @@
This method checks to see if target exists and is theoretically executable.
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. If there are problems, or the target is a .o file, status->status
+ is set with an error state and 0 is returned.
- Output messages produced:
+ Error states:
+ - ERROR\n
+ System error (during stat operation)
- COMP\n
Target failed to compile
- LINK\n
@@ -209,10 +211,11 @@
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 (const char* target, struct target_status* status)
{
struct stat file_info;
int exists = 1;
@@ -220,7 +223,7 @@
if (0 > stat (target, &file_info)) {
if (ENOENT != errno) {
warn ("Error stating %s: %s\n", target, strerror (errno));
- puts ("ERROR");
+ status->status = "ERROR";
return 0;
}
file_info.st_mode = 0; /* force mode on non-existant file to 0 */
@@ -236,17 +239,15 @@
#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 (!exists)
+ status->status = "COMP";
return 0;
}
#endif
/* If the target exists, it doesn't have valid permissions */
if (exists) {
- puts (" XPERM");
+ status->status = "XPERM";
return 0;
}
@@ -277,13 +278,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 = "ERROR";
}
else
- puts (" COMP");
+ status->status = "COMP";
}
else {
- puts (" LINK");
+ status->status = "LINK";
}
free (tmp_name);
@@ -298,67 +299,78 @@
This method examines the content of result, dispatching the processing
to check_test () or check_example () 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 sets status->status or status->exit to the error state.
- Output messages produced:
+ Unix Error states:
- 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
+ - UNKN \n
+ Child process subsystem returned in an unknown state.
+ Windows Error states:
+ - I/O\n
+ System error during child process execution.
+ - KILLED\n
+ Child process killed by the exec utility.
+ - SEGV\n
+ Child process terminated in a segfault
+
@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
+ @param status status object to record results in.
@see check_test ()
@see check_example ()
*/
static void
-process_results (const char* target, const struct exec_attrs* result)
+process_results (const char* target, const struct exec_attrs* result,
+ struct target_status* status)
{
if (0 == result->status) {
- parse_output (target);
+ parse_output (target, 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 = "EXIST";
break;
case 127:
- puts (" EXEC");
+ status->status = "EXEC";
break;
default:
- printf ("%6d\n", retcode);
+ status->exit = retcode;
}
}
else if (WIFSIGNALED (result->status)) {
- printf ("%6s\n", get_signame (WTERMSIG (result->status)));
+ status->status = get_signame (WTERMSIG (result->status));
}
else if (WIFSTOPPED (result->status)) {
- printf ("%6s\n", get_signame (WSTOPSIG (result->status)));
+ status->status = get_signame (WSTOPSIG (result->status));
}
else if (-1 == result->status && -1 == result->killed) {
- puts (" NKILL");
+ status->status = "NKILL";
}
else {
- printf ("(%d|%d)\n", result->status, result->killed);
+ warn ("Unknown result status: %d, %d\n", result->status,
+ result->killed);
+ status->status = "UNKN";
}
#else
else if (-1 == result->status)
- puts (" I/O");
+ status->status = "I/O";
else if (result->error)
- puts ("KILLED");
+ status->status = "KILLED";
else if (STATUS_ACCESS_VIOLATION == result->status)
- puts (" SEGV");
+ status->status = "SEGV";
else
- printf ("%6d\n", result->status);
+ status->exit = result->status;
#endif /* _WIN{32,64} */
}
@@ -410,6 +422,7 @@
run_target (char* target, char** argv)
{
char** childargv;
+ struct target_status results;
assert (0 != target);
assert (0 != argv);
@@ -421,14 +434,16 @@
target_name = rw_basename (childargv [0]);
- printf ("%-25.25s ", target_name);
- fflush (stdout);
+ begin_row ();
+ memset (&results, 0, sizeof results);
- if (check_target_ok (childargv [0])) {
+ if (check_target_ok (childargv [0], &results)) {
struct exec_attrs status = exec_file (childargv);
- process_results (childargv [0], &status);
+ process_results (childargv [0], &status, &results);
}
+ end_row (&results);
+
free (childargv [0]);
free (childargv);
}
@@ -468,12 +483,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);