xref: /freebsd/contrib/pam-krb5/tests/runtests.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert  * Run a set of tests, reporting results.
3*bf6873c5SCy Schubert  *
4*bf6873c5SCy Schubert  * Test suite driver that runs a set of tests implementing a subset of the
5*bf6873c5SCy Schubert  * Test Anything Protocol (TAP) and reports the results.
6*bf6873c5SCy Schubert  *
7*bf6873c5SCy Schubert  * Any bug reports, bug fixes, and improvements are very much welcome and
8*bf6873c5SCy Schubert  * should be sent to the e-mail address below.  This program is part of C TAP
9*bf6873c5SCy Schubert  * Harness <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
10*bf6873c5SCy Schubert  *
11*bf6873c5SCy Schubert  * Copyright 2000-2001, 2004, 2006-2019 Russ Allbery <eagle@eyrie.org>
12*bf6873c5SCy Schubert  *
13*bf6873c5SCy Schubert  * Permission is hereby granted, free of charge, to any person obtaining a
14*bf6873c5SCy Schubert  * copy of this software and associated documentation files (the "Software"),
15*bf6873c5SCy Schubert  * to deal in the Software without restriction, including without limitation
16*bf6873c5SCy Schubert  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17*bf6873c5SCy Schubert  * and/or sell copies of the Software, and to permit persons to whom the
18*bf6873c5SCy Schubert  * Software is furnished to do so, subject to the following conditions:
19*bf6873c5SCy Schubert  *
20*bf6873c5SCy Schubert  * The above copyright notice and this permission notice shall be included in
21*bf6873c5SCy Schubert  * all copies or substantial portions of the Software.
22*bf6873c5SCy Schubert  *
23*bf6873c5SCy Schubert  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24*bf6873c5SCy Schubert  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25*bf6873c5SCy Schubert  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
26*bf6873c5SCy Schubert  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27*bf6873c5SCy Schubert  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28*bf6873c5SCy Schubert  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29*bf6873c5SCy Schubert  * DEALINGS IN THE SOFTWARE.
30*bf6873c5SCy Schubert  *
31*bf6873c5SCy Schubert  * SPDX-License-Identifier: MIT
32*bf6873c5SCy Schubert  */
33*bf6873c5SCy Schubert 
34*bf6873c5SCy Schubert /*
35*bf6873c5SCy Schubert  * Usage:
36*bf6873c5SCy Schubert  *
37*bf6873c5SCy Schubert  *      runtests [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list>
38*bf6873c5SCy Schubert  *      runtests [-hv] [-b <build-dir>] [-s <source-dir>] <test> [<test> ...]
39*bf6873c5SCy Schubert  *      runtests -o [-h] [-b <build-dir>] [-s <source-dir>] <test>
40*bf6873c5SCy Schubert  *
41*bf6873c5SCy Schubert  * In the first case, expects a list of executables located in the given file,
42*bf6873c5SCy Schubert  * one line per executable, possibly followed by a space-separated list of
43*bf6873c5SCy Schubert  * options.  For each one, runs it as part of a test suite, reporting results.
44*bf6873c5SCy Schubert  * In the second case, use the same infrastructure, but run only the tests
45*bf6873c5SCy Schubert  * listed on the command line.
46*bf6873c5SCy Schubert  *
47*bf6873c5SCy Schubert  * Test output should start with a line containing the number of tests
48*bf6873c5SCy Schubert  * (numbered from 1 to this number), optionally preceded by "1..", although
49*bf6873c5SCy Schubert  * that line may be given anywhere in the output.  Each additional line should
50*bf6873c5SCy Schubert  * be in the following format:
51*bf6873c5SCy Schubert  *
52*bf6873c5SCy Schubert  *      ok <number>
53*bf6873c5SCy Schubert  *      not ok <number>
54*bf6873c5SCy Schubert  *      ok <number> # skip
55*bf6873c5SCy Schubert  *      not ok <number> # todo
56*bf6873c5SCy Schubert  *
57*bf6873c5SCy Schubert  * where <number> is the number of the test.  An optional comment is permitted
58*bf6873c5SCy Schubert  * after the number if preceded by whitespace.  ok indicates success, not ok
59*bf6873c5SCy Schubert  * indicates failure.  "# skip" and "# todo" are a special cases of a comment,
60*bf6873c5SCy Schubert  * and must start with exactly that formatting.  They indicate the test was
61*bf6873c5SCy Schubert  * skipped for some reason (maybe because it doesn't apply to this platform)
62*bf6873c5SCy Schubert  * or is testing something known to currently fail.  The text following either
63*bf6873c5SCy Schubert  * "# skip" or "# todo" and whitespace is the reason.
64*bf6873c5SCy Schubert  *
65*bf6873c5SCy Schubert  * As a special case, the first line of the output may be in the form:
66*bf6873c5SCy Schubert  *
67*bf6873c5SCy Schubert  *      1..0 # skip some reason
68*bf6873c5SCy Schubert  *
69*bf6873c5SCy Schubert  * which indicates that this entire test case should be skipped and gives a
70*bf6873c5SCy Schubert  * reason.
71*bf6873c5SCy Schubert  *
72*bf6873c5SCy Schubert  * Any other lines are ignored, although for compliance with the TAP protocol
73*bf6873c5SCy Schubert  * all lines other than the ones in the above format should be sent to
74*bf6873c5SCy Schubert  * standard error rather than standard output and start with #.
75*bf6873c5SCy Schubert  *
76*bf6873c5SCy Schubert  * This is a subset of TAP as documented in Test::Harness::TAP or
77*bf6873c5SCy Schubert  * TAP::Parser::Grammar, which comes with Perl.
78*bf6873c5SCy Schubert  *
79*bf6873c5SCy Schubert  * If the -o option is given, instead run a single test and display all of its
80*bf6873c5SCy Schubert  * output.  This is intended for use with failing tests so that the person
81*bf6873c5SCy Schubert  * running the test suite can get more details about what failed.
82*bf6873c5SCy Schubert  *
83*bf6873c5SCy Schubert  * If built with the C preprocessor symbols C_TAP_SOURCE and C_TAP_BUILD
84*bf6873c5SCy Schubert  * defined, C TAP Harness will export those values in the environment so that
85*bf6873c5SCy Schubert  * tests can find the source and build directory and will look for tests under
86*bf6873c5SCy Schubert  * both directories.  These paths can also be set with the -b and -s
87*bf6873c5SCy Schubert  * command-line options, which will override anything set at build time.
88*bf6873c5SCy Schubert  *
89*bf6873c5SCy Schubert  * If the -v option is given, or the C_TAP_VERBOSE environment variable is set,
90*bf6873c5SCy Schubert  * display the full output of each test as it runs rather than showing a
91*bf6873c5SCy Schubert  * summary of the results of each test.
92*bf6873c5SCy Schubert  */
93*bf6873c5SCy Schubert 
94*bf6873c5SCy Schubert /* Required for fdopen(), getopt(), and putenv(). */
95*bf6873c5SCy Schubert #if defined(__STRICT_ANSI__) || defined(PEDANTIC)
96*bf6873c5SCy Schubert #    ifndef _XOPEN_SOURCE
97*bf6873c5SCy Schubert #        define _XOPEN_SOURCE 500
98*bf6873c5SCy Schubert #    endif
99*bf6873c5SCy Schubert #endif
100*bf6873c5SCy Schubert 
101*bf6873c5SCy Schubert #include <ctype.h>
102*bf6873c5SCy Schubert #include <errno.h>
103*bf6873c5SCy Schubert #include <fcntl.h>
104*bf6873c5SCy Schubert #include <limits.h>
105*bf6873c5SCy Schubert #include <stdarg.h>
106*bf6873c5SCy Schubert #include <stddef.h>
107*bf6873c5SCy Schubert #include <stdio.h>
108*bf6873c5SCy Schubert #include <stdlib.h>
109*bf6873c5SCy Schubert #include <string.h>
110*bf6873c5SCy Schubert #include <strings.h>
111*bf6873c5SCy Schubert #include <sys/stat.h>
112*bf6873c5SCy Schubert #include <sys/time.h>
113*bf6873c5SCy Schubert #include <sys/types.h>
114*bf6873c5SCy Schubert #include <sys/wait.h>
115*bf6873c5SCy Schubert #include <time.h>
116*bf6873c5SCy Schubert #include <unistd.h>
117*bf6873c5SCy Schubert 
118*bf6873c5SCy Schubert /* sys/time.h must be included before sys/resource.h on some platforms. */
119*bf6873c5SCy Schubert #include <sys/resource.h>
120*bf6873c5SCy Schubert 
121*bf6873c5SCy Schubert /* AIX 6.1 (and possibly later) doesn't have WCOREDUMP. */
122*bf6873c5SCy Schubert #ifndef WCOREDUMP
123*bf6873c5SCy Schubert #    define WCOREDUMP(status) ((unsigned) (status) &0x80)
124*bf6873c5SCy Schubert #endif
125*bf6873c5SCy Schubert 
126*bf6873c5SCy Schubert /*
127*bf6873c5SCy Schubert  * POSIX requires that these be defined in <unistd.h>, but they're not always
128*bf6873c5SCy Schubert  * available.  If one of them has been defined, all the rest almost certainly
129*bf6873c5SCy Schubert  * have.
130*bf6873c5SCy Schubert  */
131*bf6873c5SCy Schubert #ifndef STDIN_FILENO
132*bf6873c5SCy Schubert #    define STDIN_FILENO  0
133*bf6873c5SCy Schubert #    define STDOUT_FILENO 1
134*bf6873c5SCy Schubert #    define STDERR_FILENO 2
135*bf6873c5SCy Schubert #endif
136*bf6873c5SCy Schubert 
137*bf6873c5SCy Schubert /*
138*bf6873c5SCy Schubert  * Used for iterating through arrays.  Returns the number of elements in the
139*bf6873c5SCy Schubert  * array (useful for a < upper bound in a for loop).
140*bf6873c5SCy Schubert  */
141*bf6873c5SCy Schubert #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
142*bf6873c5SCy Schubert 
143*bf6873c5SCy Schubert /*
144*bf6873c5SCy Schubert  * The source and build versions of the tests directory.  This is used to set
145*bf6873c5SCy Schubert  * the C_TAP_SOURCE and C_TAP_BUILD environment variables (and the SOURCE and
146*bf6873c5SCy Schubert  * BUILD environment variables set for backward compatibility) and find test
147*bf6873c5SCy Schubert  * programs, if set.  Normally, this should be set as part of the build
148*bf6873c5SCy Schubert  * process to the test subdirectories of $(abs_top_srcdir) and
149*bf6873c5SCy Schubert  * $(abs_top_builddir) respectively.
150*bf6873c5SCy Schubert  */
151*bf6873c5SCy Schubert #ifndef C_TAP_SOURCE
152*bf6873c5SCy Schubert #    define C_TAP_SOURCE NULL
153*bf6873c5SCy Schubert #endif
154*bf6873c5SCy Schubert #ifndef C_TAP_BUILD
155*bf6873c5SCy Schubert #    define C_TAP_BUILD NULL
156*bf6873c5SCy Schubert #endif
157*bf6873c5SCy Schubert 
158*bf6873c5SCy Schubert /* Test status codes. */
159*bf6873c5SCy Schubert enum test_status
160*bf6873c5SCy Schubert {
161*bf6873c5SCy Schubert     TEST_FAIL,
162*bf6873c5SCy Schubert     TEST_PASS,
163*bf6873c5SCy Schubert     TEST_SKIP,
164*bf6873c5SCy Schubert     TEST_INVALID
165*bf6873c5SCy Schubert };
166*bf6873c5SCy Schubert 
167*bf6873c5SCy Schubert /* Really, just a boolean, but this is more self-documenting. */
168*bf6873c5SCy Schubert enum test_verbose
169*bf6873c5SCy Schubert {
170*bf6873c5SCy Schubert     CONCISE = 0,
171*bf6873c5SCy Schubert     VERBOSE = 1
172*bf6873c5SCy Schubert };
173*bf6873c5SCy Schubert 
174*bf6873c5SCy Schubert /* Indicates the state of our plan. */
175*bf6873c5SCy Schubert enum plan_status
176*bf6873c5SCy Schubert {
177*bf6873c5SCy Schubert     PLAN_INIT,    /* Nothing seen yet. */
178*bf6873c5SCy Schubert     PLAN_FIRST,   /* Plan seen before any tests. */
179*bf6873c5SCy Schubert     PLAN_PENDING, /* Test seen and no plan yet. */
180*bf6873c5SCy Schubert     PLAN_FINAL    /* Plan seen after some tests. */
181*bf6873c5SCy Schubert };
182*bf6873c5SCy Schubert 
183*bf6873c5SCy Schubert /* Error exit statuses for test processes. */
184*bf6873c5SCy Schubert #define CHILDERR_DUP    100 /* Couldn't redirect stderr or stdout. */
185*bf6873c5SCy Schubert #define CHILDERR_EXEC   101 /* Couldn't exec child process. */
186*bf6873c5SCy Schubert #define CHILDERR_STDIN  102 /* Couldn't open stdin file. */
187*bf6873c5SCy Schubert #define CHILDERR_STDERR 103 /* Couldn't open stderr file. */
188*bf6873c5SCy Schubert 
189*bf6873c5SCy Schubert /* Structure to hold data for a set of tests. */
190*bf6873c5SCy Schubert struct testset {
191*bf6873c5SCy Schubert     char *file;                /* The file name of the test. */
192*bf6873c5SCy Schubert     char **command;            /* The argv vector to run the command. */
193*bf6873c5SCy Schubert     enum plan_status plan;     /* The status of our plan. */
194*bf6873c5SCy Schubert     unsigned long count;       /* Expected count of tests. */
195*bf6873c5SCy Schubert     unsigned long current;     /* The last seen test number. */
196*bf6873c5SCy Schubert     unsigned int length;       /* The length of the last status message. */
197*bf6873c5SCy Schubert     unsigned long passed;      /* Count of passing tests. */
198*bf6873c5SCy Schubert     unsigned long failed;      /* Count of failing lists. */
199*bf6873c5SCy Schubert     unsigned long skipped;     /* Count of skipped tests (passed). */
200*bf6873c5SCy Schubert     unsigned long allocated;   /* The size of the results table. */
201*bf6873c5SCy Schubert     enum test_status *results; /* Table of results by test number. */
202*bf6873c5SCy Schubert     unsigned int aborted;      /* Whether the set was aborted. */
203*bf6873c5SCy Schubert     unsigned int reported;     /* Whether the results were reported. */
204*bf6873c5SCy Schubert     int status;                /* The exit status of the test. */
205*bf6873c5SCy Schubert     unsigned int all_skipped;  /* Whether all tests were skipped. */
206*bf6873c5SCy Schubert     char *reason;              /* Why all tests were skipped. */
207*bf6873c5SCy Schubert };
208*bf6873c5SCy Schubert 
209*bf6873c5SCy Schubert /* Structure to hold a linked list of test sets. */
210*bf6873c5SCy Schubert struct testlist {
211*bf6873c5SCy Schubert     struct testset *ts;
212*bf6873c5SCy Schubert     struct testlist *next;
213*bf6873c5SCy Schubert };
214*bf6873c5SCy Schubert 
215*bf6873c5SCy Schubert /*
216*bf6873c5SCy Schubert  * Usage message.  Should be used as a printf format with four arguments: the
217*bf6873c5SCy Schubert  * path to runtests, given three times, and the usage_description.  This is
218*bf6873c5SCy Schubert  * split into variables to satisfy the pedantic ISO C90 limit on strings.
219*bf6873c5SCy Schubert  */
220*bf6873c5SCy Schubert static const char usage_message[] = "\
221*bf6873c5SCy Schubert Usage: %s [-hv] [-b <build-dir>] [-s <source-dir>] <test> ...\n\
222*bf6873c5SCy Schubert        %s [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\
223*bf6873c5SCy Schubert        %s -o [-h] [-b <build-dir>] [-s <source-dir>] <test>\n\
224*bf6873c5SCy Schubert \n\
225*bf6873c5SCy Schubert Options:\n\
226*bf6873c5SCy Schubert     -b <build-dir>      Set the build directory to <build-dir>\n\
227*bf6873c5SCy Schubert %s";
228*bf6873c5SCy Schubert static const char usage_extra[] = "\
229*bf6873c5SCy Schubert     -l <list>           Take the list of tests to run from <test-list>\n\
230*bf6873c5SCy Schubert     -o                  Run a single test rather than a list of tests\n\
231*bf6873c5SCy Schubert     -s <source-dir>     Set the source directory to <source-dir>\n\
232*bf6873c5SCy Schubert     -v                  Show the full output of each test\n\
233*bf6873c5SCy Schubert \n\
234*bf6873c5SCy Schubert runtests normally runs each test listed on the command line.  With the -l\n\
235*bf6873c5SCy Schubert option, it instead runs every test listed in a file.  With the -o option,\n\
236*bf6873c5SCy Schubert it instead runs a single test and shows its complete output.\n";
237*bf6873c5SCy Schubert 
238*bf6873c5SCy Schubert /*
239*bf6873c5SCy Schubert  * Header used for test output.  %s is replaced by the file name of the list
240*bf6873c5SCy Schubert  * of tests.
241*bf6873c5SCy Schubert  */
242*bf6873c5SCy Schubert static const char banner[] = "\n\
243*bf6873c5SCy Schubert Running all tests listed in %s.  If any tests fail, run the failing\n\
244*bf6873c5SCy Schubert test program with runtests -o to see more details.\n\n";
245*bf6873c5SCy Schubert 
246*bf6873c5SCy Schubert /* Header for reports of failed tests. */
247*bf6873c5SCy Schubert static const char header[] = "\n\
248*bf6873c5SCy Schubert Failed Set                 Fail/Total (%) Skip Stat  Failing Tests\n\
249*bf6873c5SCy Schubert -------------------------- -------------- ---- ----  ------------------------";
250*bf6873c5SCy Schubert 
251*bf6873c5SCy Schubert /* Include the file name and line number in malloc failures. */
252*bf6873c5SCy Schubert #define xcalloc(n, type) \
253*bf6873c5SCy Schubert     ((type *) x_calloc((n), sizeof(type), __FILE__, __LINE__))
254*bf6873c5SCy Schubert #define xmalloc(size)     ((char *) x_malloc((size), __FILE__, __LINE__))
255*bf6873c5SCy Schubert #define xstrdup(p)        x_strdup((p), __FILE__, __LINE__)
256*bf6873c5SCy Schubert #define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__)
257*bf6873c5SCy Schubert #define xreallocarray(p, n, type) \
258*bf6873c5SCy Schubert     ((type *) x_reallocarray((p), (n), sizeof(type), __FILE__, __LINE__))
259*bf6873c5SCy Schubert 
260*bf6873c5SCy Schubert /*
261*bf6873c5SCy Schubert  * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
262*bf6873c5SCy Schubert  * could you use the __format__ form of the attributes, which is what we use
263*bf6873c5SCy Schubert  * (to avoid confusion with other macros).
264*bf6873c5SCy Schubert  */
265*bf6873c5SCy Schubert #ifndef __attribute__
266*bf6873c5SCy Schubert #    if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
267*bf6873c5SCy Schubert #        define __attribute__(spec) /* empty */
268*bf6873c5SCy Schubert #    endif
269*bf6873c5SCy Schubert #endif
270*bf6873c5SCy Schubert 
271*bf6873c5SCy Schubert /*
272*bf6873c5SCy Schubert  * We use __alloc_size__, but it was only available in fairly recent versions
273*bf6873c5SCy Schubert  * of GCC.  Suppress warnings about the unknown attribute if GCC is too old.
274*bf6873c5SCy Schubert  * We know that we're GCC at this point, so we can use the GCC variadic macro
275*bf6873c5SCy Schubert  * extension, which will still work with versions of GCC too old to have C99
276*bf6873c5SCy Schubert  * variadic macro support.
277*bf6873c5SCy Schubert  */
278*bf6873c5SCy Schubert #if !defined(__attribute__) && !defined(__alloc_size__)
279*bf6873c5SCy Schubert #    if defined(__GNUC__) && !defined(__clang__)
280*bf6873c5SCy Schubert #        if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
281*bf6873c5SCy Schubert #            define __alloc_size__(spec, args...) /* empty */
282*bf6873c5SCy Schubert #        endif
283*bf6873c5SCy Schubert #    endif
284*bf6873c5SCy Schubert #endif
285*bf6873c5SCy Schubert 
286*bf6873c5SCy Schubert /*
287*bf6873c5SCy Schubert  * LLVM and Clang pretend to be GCC but don't support all of the __attribute__
288*bf6873c5SCy Schubert  * settings that GCC does.  For them, suppress warnings about unknown
289*bf6873c5SCy Schubert  * attributes on declarations.  This unfortunately will affect the entire
290*bf6873c5SCy Schubert  * compilation context, but there's no push and pop available.
291*bf6873c5SCy Schubert  */
292*bf6873c5SCy Schubert #if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
293*bf6873c5SCy Schubert #    pragma GCC diagnostic ignored "-Wattributes"
294*bf6873c5SCy Schubert #endif
295*bf6873c5SCy Schubert 
296*bf6873c5SCy Schubert /* Declare internal functions that benefit from compiler attributes. */
297*bf6873c5SCy Schubert static void die(const char *, ...)
298*bf6873c5SCy Schubert     __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
299*bf6873c5SCy Schubert static void sysdie(const char *, ...)
300*bf6873c5SCy Schubert     __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
301*bf6873c5SCy Schubert static void *x_calloc(size_t, size_t, const char *, int)
302*bf6873c5SCy Schubert     __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__));
303*bf6873c5SCy Schubert static void *x_malloc(size_t, const char *, int)
304*bf6873c5SCy Schubert     __attribute__((__alloc_size__(1), __malloc__, __nonnull__));
305*bf6873c5SCy Schubert static void *x_reallocarray(void *, size_t, size_t, const char *, int)
306*bf6873c5SCy Schubert     __attribute__((__alloc_size__(2, 3), __malloc__, __nonnull__(4)));
307*bf6873c5SCy Schubert static char *x_strdup(const char *, const char *, int)
308*bf6873c5SCy Schubert     __attribute__((__malloc__, __nonnull__));
309*bf6873c5SCy Schubert static char *x_strndup(const char *, size_t, const char *, int)
310*bf6873c5SCy Schubert     __attribute__((__malloc__, __nonnull__));
311*bf6873c5SCy Schubert 
312*bf6873c5SCy Schubert 
313*bf6873c5SCy Schubert /*
314*bf6873c5SCy Schubert  * Report a fatal error and exit.
315*bf6873c5SCy Schubert  */
316*bf6873c5SCy Schubert static void
die(const char * format,...)317*bf6873c5SCy Schubert die(const char *format, ...)
318*bf6873c5SCy Schubert {
319*bf6873c5SCy Schubert     va_list args;
320*bf6873c5SCy Schubert 
321*bf6873c5SCy Schubert     fflush(stdout);
322*bf6873c5SCy Schubert     fprintf(stderr, "runtests: ");
323*bf6873c5SCy Schubert     va_start(args, format);
324*bf6873c5SCy Schubert     vfprintf(stderr, format, args);
325*bf6873c5SCy Schubert     va_end(args);
326*bf6873c5SCy Schubert     fprintf(stderr, "\n");
327*bf6873c5SCy Schubert     exit(1);
328*bf6873c5SCy Schubert }
329*bf6873c5SCy Schubert 
330*bf6873c5SCy Schubert 
331*bf6873c5SCy Schubert /*
332*bf6873c5SCy Schubert  * Report a fatal error, including the results of strerror, and exit.
333*bf6873c5SCy Schubert  */
334*bf6873c5SCy Schubert static void
sysdie(const char * format,...)335*bf6873c5SCy Schubert sysdie(const char *format, ...)
336*bf6873c5SCy Schubert {
337*bf6873c5SCy Schubert     int oerrno;
338*bf6873c5SCy Schubert     va_list args;
339*bf6873c5SCy Schubert 
340*bf6873c5SCy Schubert     oerrno = errno;
341*bf6873c5SCy Schubert     fflush(stdout);
342*bf6873c5SCy Schubert     fprintf(stderr, "runtests: ");
343*bf6873c5SCy Schubert     va_start(args, format);
344*bf6873c5SCy Schubert     vfprintf(stderr, format, args);
345*bf6873c5SCy Schubert     va_end(args);
346*bf6873c5SCy Schubert     fprintf(stderr, ": %s\n", strerror(oerrno));
347*bf6873c5SCy Schubert     exit(1);
348*bf6873c5SCy Schubert }
349*bf6873c5SCy Schubert 
350*bf6873c5SCy Schubert 
351*bf6873c5SCy Schubert /*
352*bf6873c5SCy Schubert  * Allocate zeroed memory, reporting a fatal error and exiting on failure.
353*bf6873c5SCy Schubert  */
354*bf6873c5SCy Schubert static void *
x_calloc(size_t n,size_t size,const char * file,int line)355*bf6873c5SCy Schubert x_calloc(size_t n, size_t size, const char *file, int line)
356*bf6873c5SCy Schubert {
357*bf6873c5SCy Schubert     void *p;
358*bf6873c5SCy Schubert 
359*bf6873c5SCy Schubert     n = (n > 0) ? n : 1;
360*bf6873c5SCy Schubert     size = (size > 0) ? size : 1;
361*bf6873c5SCy Schubert     p = calloc(n, size);
362*bf6873c5SCy Schubert     if (p == NULL)
363*bf6873c5SCy Schubert         sysdie("failed to calloc %lu bytes at %s line %d",
364*bf6873c5SCy Schubert                (unsigned long) size, file, line);
365*bf6873c5SCy Schubert     return p;
366*bf6873c5SCy Schubert }
367*bf6873c5SCy Schubert 
368*bf6873c5SCy Schubert 
369*bf6873c5SCy Schubert /*
370*bf6873c5SCy Schubert  * Allocate memory, reporting a fatal error and exiting on failure.
371*bf6873c5SCy Schubert  */
372*bf6873c5SCy Schubert static void *
x_malloc(size_t size,const char * file,int line)373*bf6873c5SCy Schubert x_malloc(size_t size, const char *file, int line)
374*bf6873c5SCy Schubert {
375*bf6873c5SCy Schubert     void *p;
376*bf6873c5SCy Schubert 
377*bf6873c5SCy Schubert     p = malloc(size);
378*bf6873c5SCy Schubert     if (p == NULL)
379*bf6873c5SCy Schubert         sysdie("failed to malloc %lu bytes at %s line %d",
380*bf6873c5SCy Schubert                (unsigned long) size, file, line);
381*bf6873c5SCy Schubert     return p;
382*bf6873c5SCy Schubert }
383*bf6873c5SCy Schubert 
384*bf6873c5SCy Schubert 
385*bf6873c5SCy Schubert /*
386*bf6873c5SCy Schubert  * Reallocate memory, reporting a fatal error and exiting on failure.
387*bf6873c5SCy Schubert  *
388*bf6873c5SCy Schubert  * We should technically use SIZE_MAX here for the overflow check, but
389*bf6873c5SCy Schubert  * SIZE_MAX is C99 and we're only assuming C89 + SUSv3, which does not
390*bf6873c5SCy Schubert  * guarantee that it exists.  They do guarantee that UINT_MAX exists, and we
391*bf6873c5SCy Schubert  * can assume that UINT_MAX <= SIZE_MAX.  And we should not be allocating
392*bf6873c5SCy Schubert  * anything anywhere near that large.
393*bf6873c5SCy Schubert  *
394*bf6873c5SCy Schubert  * (In theory, C89 and C99 permit size_t to be smaller than unsigned int, but
395*bf6873c5SCy Schubert  * I disbelieve in the existence of such systems and they will have to cope
396*bf6873c5SCy Schubert  * without overflow checks.)
397*bf6873c5SCy Schubert  */
398*bf6873c5SCy Schubert static void *
x_reallocarray(void * p,size_t n,size_t size,const char * file,int line)399*bf6873c5SCy Schubert x_reallocarray(void *p, size_t n, size_t size, const char *file, int line)
400*bf6873c5SCy Schubert {
401*bf6873c5SCy Schubert     n = (n > 0) ? n : 1;
402*bf6873c5SCy Schubert     size = (size > 0) ? size : 1;
403*bf6873c5SCy Schubert 
404*bf6873c5SCy Schubert     if (n > 0 && UINT_MAX / n <= size)
405*bf6873c5SCy Schubert         sysdie("realloc too large at %s line %d", file, line);
406*bf6873c5SCy Schubert     p = realloc(p, n * size);
407*bf6873c5SCy Schubert     if (p == NULL)
408*bf6873c5SCy Schubert         sysdie("failed to realloc %lu bytes at %s line %d",
409*bf6873c5SCy Schubert                (unsigned long) (n * size), file, line);
410*bf6873c5SCy Schubert     return p;
411*bf6873c5SCy Schubert }
412*bf6873c5SCy Schubert 
413*bf6873c5SCy Schubert 
414*bf6873c5SCy Schubert /*
415*bf6873c5SCy Schubert  * Copy a string, reporting a fatal error and exiting on failure.
416*bf6873c5SCy Schubert  */
417*bf6873c5SCy Schubert static char *
x_strdup(const char * s,const char * file,int line)418*bf6873c5SCy Schubert x_strdup(const char *s, const char *file, int line)
419*bf6873c5SCy Schubert {
420*bf6873c5SCy Schubert     char *p;
421*bf6873c5SCy Schubert     size_t len;
422*bf6873c5SCy Schubert 
423*bf6873c5SCy Schubert     len = strlen(s) + 1;
424*bf6873c5SCy Schubert     p = (char *) malloc(len);
425*bf6873c5SCy Schubert     if (p == NULL)
426*bf6873c5SCy Schubert         sysdie("failed to strdup %lu bytes at %s line %d", (unsigned long) len,
427*bf6873c5SCy Schubert                file, line);
428*bf6873c5SCy Schubert     memcpy(p, s, len);
429*bf6873c5SCy Schubert     return p;
430*bf6873c5SCy Schubert }
431*bf6873c5SCy Schubert 
432*bf6873c5SCy Schubert 
433*bf6873c5SCy Schubert /*
434*bf6873c5SCy Schubert  * Copy the first n characters of a string, reporting a fatal error and
435*bf6873c5SCy Schubert  * existing on failure.
436*bf6873c5SCy Schubert  *
437*bf6873c5SCy Schubert  * Avoid using the system strndup function since it may not exist (on Mac OS
438*bf6873c5SCy Schubert  * X, for example), and there's no need to introduce another portability
439*bf6873c5SCy Schubert  * requirement.
440*bf6873c5SCy Schubert  */
441*bf6873c5SCy Schubert char *
x_strndup(const char * s,size_t size,const char * file,int line)442*bf6873c5SCy Schubert x_strndup(const char *s, size_t size, const char *file, int line)
443*bf6873c5SCy Schubert {
444*bf6873c5SCy Schubert     const char *p;
445*bf6873c5SCy Schubert     size_t len;
446*bf6873c5SCy Schubert     char *copy;
447*bf6873c5SCy Schubert 
448*bf6873c5SCy Schubert     /* Don't assume that the source string is nul-terminated. */
449*bf6873c5SCy Schubert     for (p = s; (size_t)(p - s) < size && *p != '\0'; p++)
450*bf6873c5SCy Schubert         ;
451*bf6873c5SCy Schubert     len = (size_t)(p - s);
452*bf6873c5SCy Schubert     copy = (char *) malloc(len + 1);
453*bf6873c5SCy Schubert     if (copy == NULL)
454*bf6873c5SCy Schubert         sysdie("failed to strndup %lu bytes at %s line %d",
455*bf6873c5SCy Schubert                (unsigned long) len, file, line);
456*bf6873c5SCy Schubert     memcpy(copy, s, len);
457*bf6873c5SCy Schubert     copy[len] = '\0';
458*bf6873c5SCy Schubert     return copy;
459*bf6873c5SCy Schubert }
460*bf6873c5SCy Schubert 
461*bf6873c5SCy Schubert 
462*bf6873c5SCy Schubert /*
463*bf6873c5SCy Schubert  * Form a new string by concatenating multiple strings.  The arguments must be
464*bf6873c5SCy Schubert  * terminated by (const char *) 0.
465*bf6873c5SCy Schubert  *
466*bf6873c5SCy Schubert  * This function only exists because we can't assume asprintf.  We can't
467*bf6873c5SCy Schubert  * simulate asprintf with snprintf because we're only assuming SUSv3, which
468*bf6873c5SCy Schubert  * does not require that snprintf with a NULL buffer return the required
469*bf6873c5SCy Schubert  * length.  When those constraints are relaxed, this should be ripped out and
470*bf6873c5SCy Schubert  * replaced with asprintf or a more trivial replacement with snprintf.
471*bf6873c5SCy Schubert  */
472*bf6873c5SCy Schubert static char *
concat(const char * first,...)473*bf6873c5SCy Schubert concat(const char *first, ...)
474*bf6873c5SCy Schubert {
475*bf6873c5SCy Schubert     va_list args;
476*bf6873c5SCy Schubert     char *result;
477*bf6873c5SCy Schubert     const char *string;
478*bf6873c5SCy Schubert     size_t offset;
479*bf6873c5SCy Schubert     size_t length = 0;
480*bf6873c5SCy Schubert 
481*bf6873c5SCy Schubert     /*
482*bf6873c5SCy Schubert      * Find the total memory required.  Ensure we don't overflow length.  We
483*bf6873c5SCy Schubert      * aren't guaranteed to have SIZE_MAX, so use UINT_MAX as an acceptable
484*bf6873c5SCy Schubert      * substitute (see the x_nrealloc comments).
485*bf6873c5SCy Schubert      */
486*bf6873c5SCy Schubert     va_start(args, first);
487*bf6873c5SCy Schubert     for (string = first; string != NULL; string = va_arg(args, const char *)) {
488*bf6873c5SCy Schubert         if (length >= UINT_MAX - strlen(string)) {
489*bf6873c5SCy Schubert             errno = EINVAL;
490*bf6873c5SCy Schubert             sysdie("strings too long in concat");
491*bf6873c5SCy Schubert         }
492*bf6873c5SCy Schubert         length += strlen(string);
493*bf6873c5SCy Schubert     }
494*bf6873c5SCy Schubert     va_end(args);
495*bf6873c5SCy Schubert     length++;
496*bf6873c5SCy Schubert 
497*bf6873c5SCy Schubert     /* Create the string. */
498*bf6873c5SCy Schubert     result = xmalloc(length);
499*bf6873c5SCy Schubert     va_start(args, first);
500*bf6873c5SCy Schubert     offset = 0;
501*bf6873c5SCy Schubert     for (string = first; string != NULL; string = va_arg(args, const char *)) {
502*bf6873c5SCy Schubert         memcpy(result + offset, string, strlen(string));
503*bf6873c5SCy Schubert         offset += strlen(string);
504*bf6873c5SCy Schubert     }
505*bf6873c5SCy Schubert     va_end(args);
506*bf6873c5SCy Schubert     result[offset] = '\0';
507*bf6873c5SCy Schubert     return result;
508*bf6873c5SCy Schubert }
509*bf6873c5SCy Schubert 
510*bf6873c5SCy Schubert 
511*bf6873c5SCy Schubert /*
512*bf6873c5SCy Schubert  * Given a struct timeval, return the number of seconds it represents as a
513*bf6873c5SCy Schubert  * double.  Use difftime() to convert a time_t to a double.
514*bf6873c5SCy Schubert  */
515*bf6873c5SCy Schubert static double
tv_seconds(const struct timeval * tv)516*bf6873c5SCy Schubert tv_seconds(const struct timeval *tv)
517*bf6873c5SCy Schubert {
518*bf6873c5SCy Schubert     return difftime(tv->tv_sec, 0) + (double) tv->tv_usec * 1e-6;
519*bf6873c5SCy Schubert }
520*bf6873c5SCy Schubert 
521*bf6873c5SCy Schubert 
522*bf6873c5SCy Schubert /*
523*bf6873c5SCy Schubert  * Given two struct timevals, return the difference in seconds.
524*bf6873c5SCy Schubert  */
525*bf6873c5SCy Schubert static double
tv_diff(const struct timeval * tv1,const struct timeval * tv0)526*bf6873c5SCy Schubert tv_diff(const struct timeval *tv1, const struct timeval *tv0)
527*bf6873c5SCy Schubert {
528*bf6873c5SCy Schubert     return tv_seconds(tv1) - tv_seconds(tv0);
529*bf6873c5SCy Schubert }
530*bf6873c5SCy Schubert 
531*bf6873c5SCy Schubert 
532*bf6873c5SCy Schubert /*
533*bf6873c5SCy Schubert  * Given two struct timevals, return the sum in seconds as a double.
534*bf6873c5SCy Schubert  */
535*bf6873c5SCy Schubert static double
tv_sum(const struct timeval * tv1,const struct timeval * tv2)536*bf6873c5SCy Schubert tv_sum(const struct timeval *tv1, const struct timeval *tv2)
537*bf6873c5SCy Schubert {
538*bf6873c5SCy Schubert     return tv_seconds(tv1) + tv_seconds(tv2);
539*bf6873c5SCy Schubert }
540*bf6873c5SCy Schubert 
541*bf6873c5SCy Schubert 
542*bf6873c5SCy Schubert /*
543*bf6873c5SCy Schubert  * Given a pointer to a string, skip any leading whitespace and return a
544*bf6873c5SCy Schubert  * pointer to the first non-whitespace character.
545*bf6873c5SCy Schubert  */
546*bf6873c5SCy Schubert static const char *
skip_whitespace(const char * p)547*bf6873c5SCy Schubert skip_whitespace(const char *p)
548*bf6873c5SCy Schubert {
549*bf6873c5SCy Schubert     while (isspace((unsigned char) (*p)))
550*bf6873c5SCy Schubert         p++;
551*bf6873c5SCy Schubert     return p;
552*bf6873c5SCy Schubert }
553*bf6873c5SCy Schubert 
554*bf6873c5SCy Schubert 
555*bf6873c5SCy Schubert /*
556*bf6873c5SCy Schubert  * Given a pointer to a string, skip any non-whitespace characters and return
557*bf6873c5SCy Schubert  * a pointer to the first whitespace character, or to the end of the string.
558*bf6873c5SCy Schubert  */
559*bf6873c5SCy Schubert static const char *
skip_non_whitespace(const char * p)560*bf6873c5SCy Schubert skip_non_whitespace(const char *p)
561*bf6873c5SCy Schubert {
562*bf6873c5SCy Schubert     while (*p != '\0' && !isspace((unsigned char) (*p)))
563*bf6873c5SCy Schubert         p++;
564*bf6873c5SCy Schubert     return p;
565*bf6873c5SCy Schubert }
566*bf6873c5SCy Schubert 
567*bf6873c5SCy Schubert 
568*bf6873c5SCy Schubert /*
569*bf6873c5SCy Schubert  * Start a program, connecting its stdout to a pipe on our end and its stderr
570*bf6873c5SCy Schubert  * to /dev/null, and storing the file descriptor to read from in the two
571*bf6873c5SCy Schubert  * argument.  Returns the PID of the new process.  Errors are fatal.
572*bf6873c5SCy Schubert  */
573*bf6873c5SCy Schubert static pid_t
test_start(char * const * command,int * fd)574*bf6873c5SCy Schubert test_start(char *const *command, int *fd)
575*bf6873c5SCy Schubert {
576*bf6873c5SCy Schubert     int fds[2], infd, errfd;
577*bf6873c5SCy Schubert     pid_t child;
578*bf6873c5SCy Schubert 
579*bf6873c5SCy Schubert     /* Create a pipe used to capture the output from the test program. */
580*bf6873c5SCy Schubert     if (pipe(fds) == -1) {
581*bf6873c5SCy Schubert         puts("ABORTED");
582*bf6873c5SCy Schubert         fflush(stdout);
583*bf6873c5SCy Schubert         sysdie("can't create pipe");
584*bf6873c5SCy Schubert     }
585*bf6873c5SCy Schubert 
586*bf6873c5SCy Schubert     /* Fork a child process, massage the file descriptors, and exec. */
587*bf6873c5SCy Schubert     child = fork();
588*bf6873c5SCy Schubert     switch (child) {
589*bf6873c5SCy Schubert     case -1:
590*bf6873c5SCy Schubert         puts("ABORTED");
591*bf6873c5SCy Schubert         fflush(stdout);
592*bf6873c5SCy Schubert         sysdie("can't fork");
593*bf6873c5SCy Schubert 
594*bf6873c5SCy Schubert     /* In the child.  Set up our standard output. */
595*bf6873c5SCy Schubert     case 0:
596*bf6873c5SCy Schubert         close(fds[0]);
597*bf6873c5SCy Schubert         close(STDOUT_FILENO);
598*bf6873c5SCy Schubert         if (dup2(fds[1], STDOUT_FILENO) < 0)
599*bf6873c5SCy Schubert             _exit(CHILDERR_DUP);
600*bf6873c5SCy Schubert         close(fds[1]);
601*bf6873c5SCy Schubert 
602*bf6873c5SCy Schubert         /* Point standard input at /dev/null. */
603*bf6873c5SCy Schubert         close(STDIN_FILENO);
604*bf6873c5SCy Schubert         infd = open("/dev/null", O_RDONLY);
605*bf6873c5SCy Schubert         if (infd < 0)
606*bf6873c5SCy Schubert             _exit(CHILDERR_STDIN);
607*bf6873c5SCy Schubert         if (infd != STDIN_FILENO) {
608*bf6873c5SCy Schubert             if (dup2(infd, STDIN_FILENO) < 0)
609*bf6873c5SCy Schubert                 _exit(CHILDERR_DUP);
610*bf6873c5SCy Schubert             close(infd);
611*bf6873c5SCy Schubert         }
612*bf6873c5SCy Schubert 
613*bf6873c5SCy Schubert         /* Point standard error at /dev/null. */
614*bf6873c5SCy Schubert         close(STDERR_FILENO);
615*bf6873c5SCy Schubert         errfd = open("/dev/null", O_WRONLY);
616*bf6873c5SCy Schubert         if (errfd < 0)
617*bf6873c5SCy Schubert             _exit(CHILDERR_STDERR);
618*bf6873c5SCy Schubert         if (errfd != STDERR_FILENO) {
619*bf6873c5SCy Schubert             if (dup2(errfd, STDERR_FILENO) < 0)
620*bf6873c5SCy Schubert                 _exit(CHILDERR_DUP);
621*bf6873c5SCy Schubert             close(errfd);
622*bf6873c5SCy Schubert         }
623*bf6873c5SCy Schubert 
624*bf6873c5SCy Schubert         /* Now, exec our process. */
625*bf6873c5SCy Schubert         if (execv(command[0], command) == -1)
626*bf6873c5SCy Schubert             _exit(CHILDERR_EXEC);
627*bf6873c5SCy Schubert         break;
628*bf6873c5SCy Schubert 
629*bf6873c5SCy Schubert     /* In parent.  Close the extra file descriptor. */
630*bf6873c5SCy Schubert     default:
631*bf6873c5SCy Schubert         close(fds[1]);
632*bf6873c5SCy Schubert         break;
633*bf6873c5SCy Schubert     }
634*bf6873c5SCy Schubert     *fd = fds[0];
635*bf6873c5SCy Schubert     return child;
636*bf6873c5SCy Schubert }
637*bf6873c5SCy Schubert 
638*bf6873c5SCy Schubert 
639*bf6873c5SCy Schubert /*
640*bf6873c5SCy Schubert  * Back up over the output saying what test we were executing.
641*bf6873c5SCy Schubert  */
642*bf6873c5SCy Schubert static void
test_backspace(struct testset * ts)643*bf6873c5SCy Schubert test_backspace(struct testset *ts)
644*bf6873c5SCy Schubert {
645*bf6873c5SCy Schubert     unsigned int i;
646*bf6873c5SCy Schubert 
647*bf6873c5SCy Schubert     if (!isatty(STDOUT_FILENO))
648*bf6873c5SCy Schubert         return;
649*bf6873c5SCy Schubert     for (i = 0; i < ts->length; i++)
650*bf6873c5SCy Schubert         putchar('\b');
651*bf6873c5SCy Schubert     for (i = 0; i < ts->length; i++)
652*bf6873c5SCy Schubert         putchar(' ');
653*bf6873c5SCy Schubert     for (i = 0; i < ts->length; i++)
654*bf6873c5SCy Schubert         putchar('\b');
655*bf6873c5SCy Schubert     ts->length = 0;
656*bf6873c5SCy Schubert }
657*bf6873c5SCy Schubert 
658*bf6873c5SCy Schubert 
659*bf6873c5SCy Schubert /*
660*bf6873c5SCy Schubert  * Allocate or resize the array of test results to be large enough to contain
661*bf6873c5SCy Schubert  * the test number in.
662*bf6873c5SCy Schubert  */
663*bf6873c5SCy Schubert static void
resize_results(struct testset * ts,unsigned long n)664*bf6873c5SCy Schubert resize_results(struct testset *ts, unsigned long n)
665*bf6873c5SCy Schubert {
666*bf6873c5SCy Schubert     unsigned long i;
667*bf6873c5SCy Schubert     size_t s;
668*bf6873c5SCy Schubert 
669*bf6873c5SCy Schubert     /* If there's already enough space, return quickly. */
670*bf6873c5SCy Schubert     if (n <= ts->allocated)
671*bf6873c5SCy Schubert         return;
672*bf6873c5SCy Schubert 
673*bf6873c5SCy Schubert     /*
674*bf6873c5SCy Schubert      * If no space has been allocated, do the initial allocation.  Otherwise,
675*bf6873c5SCy Schubert      * resize.  Start with 32 test cases and then add 1024 with each resize to
676*bf6873c5SCy Schubert      * try to reduce the number of reallocations.
677*bf6873c5SCy Schubert      */
678*bf6873c5SCy Schubert     if (ts->allocated == 0) {
679*bf6873c5SCy Schubert         s = (n > 32) ? n : 32;
680*bf6873c5SCy Schubert         ts->results = xcalloc(s, enum test_status);
681*bf6873c5SCy Schubert     } else {
682*bf6873c5SCy Schubert         s = (n > ts->allocated + 1024) ? n : ts->allocated + 1024;
683*bf6873c5SCy Schubert         ts->results = xreallocarray(ts->results, s, enum test_status);
684*bf6873c5SCy Schubert     }
685*bf6873c5SCy Schubert 
686*bf6873c5SCy Schubert     /* Set the results for the newly-allocated test array. */
687*bf6873c5SCy Schubert     for (i = ts->allocated; i < s; i++)
688*bf6873c5SCy Schubert         ts->results[i] = TEST_INVALID;
689*bf6873c5SCy Schubert     ts->allocated = s;
690*bf6873c5SCy Schubert }
691*bf6873c5SCy Schubert 
692*bf6873c5SCy Schubert 
693*bf6873c5SCy Schubert /*
694*bf6873c5SCy Schubert  * Report an invalid test number and set the appropriate flags.  Pulled into a
695*bf6873c5SCy Schubert  * separate function since we do this in several places.
696*bf6873c5SCy Schubert  */
697*bf6873c5SCy Schubert static void
invalid_test_number(struct testset * ts,long n,enum test_verbose verbose)698*bf6873c5SCy Schubert invalid_test_number(struct testset *ts, long n, enum test_verbose verbose)
699*bf6873c5SCy Schubert {
700*bf6873c5SCy Schubert     if (!verbose)
701*bf6873c5SCy Schubert         test_backspace(ts);
702*bf6873c5SCy Schubert     printf("ABORTED (invalid test number %ld)\n", n);
703*bf6873c5SCy Schubert     ts->aborted = 1;
704*bf6873c5SCy Schubert     ts->reported = 1;
705*bf6873c5SCy Schubert }
706*bf6873c5SCy Schubert 
707*bf6873c5SCy Schubert 
708*bf6873c5SCy Schubert /*
709*bf6873c5SCy Schubert  * Read the plan line of test output, which should contain the range of test
710*bf6873c5SCy Schubert  * numbers.  We may initialize the testset structure here if we haven't yet
711*bf6873c5SCy Schubert  * seen a test.  Return true if initialization succeeded and the test should
712*bf6873c5SCy Schubert  * continue, false otherwise.
713*bf6873c5SCy Schubert  */
714*bf6873c5SCy Schubert static int
test_plan(const char * line,struct testset * ts,enum test_verbose verbose)715*bf6873c5SCy Schubert test_plan(const char *line, struct testset *ts, enum test_verbose verbose)
716*bf6873c5SCy Schubert {
717*bf6873c5SCy Schubert     long n;
718*bf6873c5SCy Schubert 
719*bf6873c5SCy Schubert     /*
720*bf6873c5SCy Schubert      * Accept a plan without the leading 1.. for compatibility with older
721*bf6873c5SCy Schubert      * versions of runtests.  This will only be allowed if we've not yet seen
722*bf6873c5SCy Schubert      * a test result.
723*bf6873c5SCy Schubert      */
724*bf6873c5SCy Schubert     line = skip_whitespace(line);
725*bf6873c5SCy Schubert     if (strncmp(line, "1..", 3) == 0)
726*bf6873c5SCy Schubert         line += 3;
727*bf6873c5SCy Schubert 
728*bf6873c5SCy Schubert     /*
729*bf6873c5SCy Schubert      * Get the count and check it for validity.
730*bf6873c5SCy Schubert      *
731*bf6873c5SCy Schubert      * If we have something of the form "1..0 # skip foo", the whole file was
732*bf6873c5SCy Schubert      * skipped; record that.  If we do skip the whole file, zero out all of
733*bf6873c5SCy Schubert      * our statistics, since they're no longer relevant.
734*bf6873c5SCy Schubert      *
735*bf6873c5SCy Schubert      * strtol is called with a second argument to advance the line pointer
736*bf6873c5SCy Schubert      * past the count to make it simpler to detect the # skip case.
737*bf6873c5SCy Schubert      */
738*bf6873c5SCy Schubert     n = strtol(line, (char **) &line, 10);
739*bf6873c5SCy Schubert     if (n == 0) {
740*bf6873c5SCy Schubert         line = skip_whitespace(line);
741*bf6873c5SCy Schubert         if (*line == '#') {
742*bf6873c5SCy Schubert             line = skip_whitespace(line + 1);
743*bf6873c5SCy Schubert             if (strncasecmp(line, "skip", 4) == 0) {
744*bf6873c5SCy Schubert                 line = skip_whitespace(line + 4);
745*bf6873c5SCy Schubert                 if (*line != '\0') {
746*bf6873c5SCy Schubert                     ts->reason = xstrdup(line);
747*bf6873c5SCy Schubert                     ts->reason[strlen(ts->reason) - 1] = '\0';
748*bf6873c5SCy Schubert                 }
749*bf6873c5SCy Schubert                 ts->all_skipped = 1;
750*bf6873c5SCy Schubert                 ts->aborted = 1;
751*bf6873c5SCy Schubert                 ts->count = 0;
752*bf6873c5SCy Schubert                 ts->passed = 0;
753*bf6873c5SCy Schubert                 ts->skipped = 0;
754*bf6873c5SCy Schubert                 ts->failed = 0;
755*bf6873c5SCy Schubert                 return 0;
756*bf6873c5SCy Schubert             }
757*bf6873c5SCy Schubert         }
758*bf6873c5SCy Schubert     }
759*bf6873c5SCy Schubert     if (n <= 0) {
760*bf6873c5SCy Schubert         puts("ABORTED (invalid test count)");
761*bf6873c5SCy Schubert         ts->aborted = 1;
762*bf6873c5SCy Schubert         ts->reported = 1;
763*bf6873c5SCy Schubert         return 0;
764*bf6873c5SCy Schubert     }
765*bf6873c5SCy Schubert 
766*bf6873c5SCy Schubert     /*
767*bf6873c5SCy Schubert      * If we are doing lazy planning, check the plan against the largest test
768*bf6873c5SCy Schubert      * number that we saw and fail now if we saw a check outside the plan
769*bf6873c5SCy Schubert      * range.
770*bf6873c5SCy Schubert      */
771*bf6873c5SCy Schubert     if (ts->plan == PLAN_PENDING && (unsigned long) n < ts->count) {
772*bf6873c5SCy Schubert         invalid_test_number(ts, (long) ts->count, verbose);
773*bf6873c5SCy Schubert         return 0;
774*bf6873c5SCy Schubert     }
775*bf6873c5SCy Schubert 
776*bf6873c5SCy Schubert     /*
777*bf6873c5SCy Schubert      * Otherwise, allocated or resize the results if needed and update count,
778*bf6873c5SCy Schubert      * and then record that we've seen a plan.
779*bf6873c5SCy Schubert      */
780*bf6873c5SCy Schubert     resize_results(ts, (unsigned long) n);
781*bf6873c5SCy Schubert     ts->count = (unsigned long) n;
782*bf6873c5SCy Schubert     if (ts->plan == PLAN_INIT)
783*bf6873c5SCy Schubert         ts->plan = PLAN_FIRST;
784*bf6873c5SCy Schubert     else if (ts->plan == PLAN_PENDING)
785*bf6873c5SCy Schubert         ts->plan = PLAN_FINAL;
786*bf6873c5SCy Schubert     return 1;
787*bf6873c5SCy Schubert }
788*bf6873c5SCy Schubert 
789*bf6873c5SCy Schubert 
790*bf6873c5SCy Schubert /*
791*bf6873c5SCy Schubert  * Given a single line of output from a test, parse it and return the success
792*bf6873c5SCy Schubert  * status of that test.  Anything printed to stdout not matching the form
793*bf6873c5SCy Schubert  * /^(not )?ok \d+/ is ignored.  Sets ts->current to the test number that just
794*bf6873c5SCy Schubert  * reported status.
795*bf6873c5SCy Schubert  */
796*bf6873c5SCy Schubert static void
test_checkline(const char * line,struct testset * ts,enum test_verbose verbose)797*bf6873c5SCy Schubert test_checkline(const char *line, struct testset *ts, enum test_verbose verbose)
798*bf6873c5SCy Schubert {
799*bf6873c5SCy Schubert     enum test_status status = TEST_PASS;
800*bf6873c5SCy Schubert     const char *bail;
801*bf6873c5SCy Schubert     char *end;
802*bf6873c5SCy Schubert     long number;
803*bf6873c5SCy Schubert     unsigned long current;
804*bf6873c5SCy Schubert     int outlen;
805*bf6873c5SCy Schubert 
806*bf6873c5SCy Schubert     /* Before anything, check for a test abort. */
807*bf6873c5SCy Schubert     bail = strstr(line, "Bail out!");
808*bf6873c5SCy Schubert     if (bail != NULL) {
809*bf6873c5SCy Schubert         bail = skip_whitespace(bail + strlen("Bail out!"));
810*bf6873c5SCy Schubert         if (*bail != '\0') {
811*bf6873c5SCy Schubert             size_t length;
812*bf6873c5SCy Schubert 
813*bf6873c5SCy Schubert             length = strlen(bail);
814*bf6873c5SCy Schubert             if (bail[length - 1] == '\n')
815*bf6873c5SCy Schubert                 length--;
816*bf6873c5SCy Schubert             if (!verbose)
817*bf6873c5SCy Schubert                 test_backspace(ts);
818*bf6873c5SCy Schubert             printf("ABORTED (%.*s)\n", (int) length, bail);
819*bf6873c5SCy Schubert             ts->reported = 1;
820*bf6873c5SCy Schubert         }
821*bf6873c5SCy Schubert         ts->aborted = 1;
822*bf6873c5SCy Schubert         return;
823*bf6873c5SCy Schubert     }
824*bf6873c5SCy Schubert 
825*bf6873c5SCy Schubert     /*
826*bf6873c5SCy Schubert      * If the given line isn't newline-terminated, it was too big for an
827*bf6873c5SCy Schubert      * fgets(), which means ignore it.
828*bf6873c5SCy Schubert      */
829*bf6873c5SCy Schubert     if (line[strlen(line) - 1] != '\n')
830*bf6873c5SCy Schubert         return;
831*bf6873c5SCy Schubert 
832*bf6873c5SCy Schubert     /* If the line begins with a hash mark, ignore it. */
833*bf6873c5SCy Schubert     if (line[0] == '#')
834*bf6873c5SCy Schubert         return;
835*bf6873c5SCy Schubert 
836*bf6873c5SCy Schubert     /* If we haven't yet seen a plan, look for one. */
837*bf6873c5SCy Schubert     if (ts->plan == PLAN_INIT && isdigit((unsigned char) (*line))) {
838*bf6873c5SCy Schubert         if (!test_plan(line, ts, verbose))
839*bf6873c5SCy Schubert             return;
840*bf6873c5SCy Schubert     } else if (strncmp(line, "1..", 3) == 0) {
841*bf6873c5SCy Schubert         if (ts->plan == PLAN_PENDING) {
842*bf6873c5SCy Schubert             if (!test_plan(line, ts, verbose))
843*bf6873c5SCy Schubert                 return;
844*bf6873c5SCy Schubert         } else {
845*bf6873c5SCy Schubert             if (!verbose)
846*bf6873c5SCy Schubert                 test_backspace(ts);
847*bf6873c5SCy Schubert             puts("ABORTED (multiple plans)");
848*bf6873c5SCy Schubert             ts->aborted = 1;
849*bf6873c5SCy Schubert             ts->reported = 1;
850*bf6873c5SCy Schubert             return;
851*bf6873c5SCy Schubert         }
852*bf6873c5SCy Schubert     }
853*bf6873c5SCy Schubert 
854*bf6873c5SCy Schubert     /* Parse the line, ignoring something we can't parse. */
855*bf6873c5SCy Schubert     if (strncmp(line, "not ", 4) == 0) {
856*bf6873c5SCy Schubert         status = TEST_FAIL;
857*bf6873c5SCy Schubert         line += 4;
858*bf6873c5SCy Schubert     }
859*bf6873c5SCy Schubert     if (strncmp(line, "ok", 2) != 0)
860*bf6873c5SCy Schubert         return;
861*bf6873c5SCy Schubert     line = skip_whitespace(line + 2);
862*bf6873c5SCy Schubert     errno = 0;
863*bf6873c5SCy Schubert     number = strtol(line, &end, 10);
864*bf6873c5SCy Schubert     if (errno != 0 || end == line)
865*bf6873c5SCy Schubert         current = ts->current + 1;
866*bf6873c5SCy Schubert     else if (number <= 0) {
867*bf6873c5SCy Schubert         invalid_test_number(ts, number, verbose);
868*bf6873c5SCy Schubert         return;
869*bf6873c5SCy Schubert     } else
870*bf6873c5SCy Schubert         current = (unsigned long) number;
871*bf6873c5SCy Schubert     if (current > ts->count && ts->plan == PLAN_FIRST) {
872*bf6873c5SCy Schubert         invalid_test_number(ts, (long) current, verbose);
873*bf6873c5SCy Schubert         return;
874*bf6873c5SCy Schubert     }
875*bf6873c5SCy Schubert 
876*bf6873c5SCy Schubert     /* We have a valid test result.  Tweak the results array if needed. */
877*bf6873c5SCy Schubert     if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) {
878*bf6873c5SCy Schubert         ts->plan = PLAN_PENDING;
879*bf6873c5SCy Schubert         resize_results(ts, current);
880*bf6873c5SCy Schubert         if (current > ts->count)
881*bf6873c5SCy Schubert             ts->count = current;
882*bf6873c5SCy Schubert     }
883*bf6873c5SCy Schubert 
884*bf6873c5SCy Schubert     /*
885*bf6873c5SCy Schubert      * Handle directives.  We should probably do something more interesting
886*bf6873c5SCy Schubert      * with unexpected passes of todo tests.
887*bf6873c5SCy Schubert      */
888*bf6873c5SCy Schubert     while (isdigit((unsigned char) (*line)))
889*bf6873c5SCy Schubert         line++;
890*bf6873c5SCy Schubert     line = skip_whitespace(line);
891*bf6873c5SCy Schubert     if (*line == '#') {
892*bf6873c5SCy Schubert         line = skip_whitespace(line + 1);
893*bf6873c5SCy Schubert         if (strncasecmp(line, "skip", 4) == 0)
894*bf6873c5SCy Schubert             status = TEST_SKIP;
895*bf6873c5SCy Schubert         if (strncasecmp(line, "todo", 4) == 0)
896*bf6873c5SCy Schubert             status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL;
897*bf6873c5SCy Schubert     }
898*bf6873c5SCy Schubert 
899*bf6873c5SCy Schubert     /* Make sure that the test number is in range and not a duplicate. */
900*bf6873c5SCy Schubert     if (ts->results[current - 1] != TEST_INVALID) {
901*bf6873c5SCy Schubert         if (!verbose)
902*bf6873c5SCy Schubert             test_backspace(ts);
903*bf6873c5SCy Schubert         printf("ABORTED (duplicate test number %lu)\n", current);
904*bf6873c5SCy Schubert         ts->aborted = 1;
905*bf6873c5SCy Schubert         ts->reported = 1;
906*bf6873c5SCy Schubert         return;
907*bf6873c5SCy Schubert     }
908*bf6873c5SCy Schubert 
909*bf6873c5SCy Schubert     /* Good results.  Increment our various counters. */
910*bf6873c5SCy Schubert     switch (status) {
911*bf6873c5SCy Schubert     case TEST_PASS:
912*bf6873c5SCy Schubert         ts->passed++;
913*bf6873c5SCy Schubert         break;
914*bf6873c5SCy Schubert     case TEST_FAIL:
915*bf6873c5SCy Schubert         ts->failed++;
916*bf6873c5SCy Schubert         break;
917*bf6873c5SCy Schubert     case TEST_SKIP:
918*bf6873c5SCy Schubert         ts->skipped++;
919*bf6873c5SCy Schubert         break;
920*bf6873c5SCy Schubert     case TEST_INVALID:
921*bf6873c5SCy Schubert         break;
922*bf6873c5SCy Schubert     }
923*bf6873c5SCy Schubert     ts->current = current;
924*bf6873c5SCy Schubert     ts->results[current - 1] = status;
925*bf6873c5SCy Schubert     if (!verbose && isatty(STDOUT_FILENO)) {
926*bf6873c5SCy Schubert         test_backspace(ts);
927*bf6873c5SCy Schubert         if (ts->plan == PLAN_PENDING)
928*bf6873c5SCy Schubert             outlen = printf("%lu/?", current);
929*bf6873c5SCy Schubert         else
930*bf6873c5SCy Schubert             outlen = printf("%lu/%lu", current, ts->count);
931*bf6873c5SCy Schubert         ts->length = (outlen >= 0) ? (unsigned int) outlen : 0;
932*bf6873c5SCy Schubert         fflush(stdout);
933*bf6873c5SCy Schubert     }
934*bf6873c5SCy Schubert }
935*bf6873c5SCy Schubert 
936*bf6873c5SCy Schubert 
937*bf6873c5SCy Schubert /*
938*bf6873c5SCy Schubert  * Print out a range of test numbers, returning the number of characters it
939*bf6873c5SCy Schubert  * took up.  Takes the first number, the last number, the number of characters
940*bf6873c5SCy Schubert  * already printed on the line, and the limit of number of characters the line
941*bf6873c5SCy Schubert  * can hold.  Add a comma and a space before the range if chars indicates that
942*bf6873c5SCy Schubert  * something has already been printed on the line, and print ... instead if
943*bf6873c5SCy Schubert  * chars plus the space needed would go over the limit (use a limit of 0 to
944*bf6873c5SCy Schubert  * disable this).
945*bf6873c5SCy Schubert  */
946*bf6873c5SCy Schubert static unsigned int
test_print_range(unsigned long first,unsigned long last,unsigned long chars,unsigned int limit)947*bf6873c5SCy Schubert test_print_range(unsigned long first, unsigned long last, unsigned long chars,
948*bf6873c5SCy Schubert                  unsigned int limit)
949*bf6873c5SCy Schubert {
950*bf6873c5SCy Schubert     unsigned int needed = 0;
951*bf6873c5SCy Schubert     unsigned long n;
952*bf6873c5SCy Schubert 
953*bf6873c5SCy Schubert     for (n = first; n > 0; n /= 10)
954*bf6873c5SCy Schubert         needed++;
955*bf6873c5SCy Schubert     if (last > first) {
956*bf6873c5SCy Schubert         for (n = last; n > 0; n /= 10)
957*bf6873c5SCy Schubert             needed++;
958*bf6873c5SCy Schubert         needed++;
959*bf6873c5SCy Schubert     }
960*bf6873c5SCy Schubert     if (chars > 0)
961*bf6873c5SCy Schubert         needed += 2;
962*bf6873c5SCy Schubert     if (limit > 0 && chars + needed > limit) {
963*bf6873c5SCy Schubert         needed = 0;
964*bf6873c5SCy Schubert         if (chars <= limit) {
965*bf6873c5SCy Schubert             if (chars > 0) {
966*bf6873c5SCy Schubert                 printf(", ");
967*bf6873c5SCy Schubert                 needed += 2;
968*bf6873c5SCy Schubert             }
969*bf6873c5SCy Schubert             printf("...");
970*bf6873c5SCy Schubert             needed += 3;
971*bf6873c5SCy Schubert         }
972*bf6873c5SCy Schubert     } else {
973*bf6873c5SCy Schubert         if (chars > 0)
974*bf6873c5SCy Schubert             printf(", ");
975*bf6873c5SCy Schubert         if (last > first)
976*bf6873c5SCy Schubert             printf("%lu-", first);
977*bf6873c5SCy Schubert         printf("%lu", last);
978*bf6873c5SCy Schubert     }
979*bf6873c5SCy Schubert     return needed;
980*bf6873c5SCy Schubert }
981*bf6873c5SCy Schubert 
982*bf6873c5SCy Schubert 
983*bf6873c5SCy Schubert /*
984*bf6873c5SCy Schubert  * Summarize a single test set.  The second argument is 0 if the set exited
985*bf6873c5SCy Schubert  * cleanly, a positive integer representing the exit status if it exited
986*bf6873c5SCy Schubert  * with a non-zero status, and a negative integer representing the signal
987*bf6873c5SCy Schubert  * that terminated it if it was killed by a signal.
988*bf6873c5SCy Schubert  */
989*bf6873c5SCy Schubert static void
test_summarize(struct testset * ts,int status)990*bf6873c5SCy Schubert test_summarize(struct testset *ts, int status)
991*bf6873c5SCy Schubert {
992*bf6873c5SCy Schubert     unsigned long i;
993*bf6873c5SCy Schubert     unsigned long missing = 0;
994*bf6873c5SCy Schubert     unsigned long failed = 0;
995*bf6873c5SCy Schubert     unsigned long first = 0;
996*bf6873c5SCy Schubert     unsigned long last = 0;
997*bf6873c5SCy Schubert 
998*bf6873c5SCy Schubert     if (ts->aborted) {
999*bf6873c5SCy Schubert         fputs("ABORTED", stdout);
1000*bf6873c5SCy Schubert         if (ts->count > 0)
1001*bf6873c5SCy Schubert             printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped);
1002*bf6873c5SCy Schubert     } else {
1003*bf6873c5SCy Schubert         for (i = 0; i < ts->count; i++) {
1004*bf6873c5SCy Schubert             if (ts->results[i] == TEST_INVALID) {
1005*bf6873c5SCy Schubert                 if (missing == 0)
1006*bf6873c5SCy Schubert                     fputs("MISSED ", stdout);
1007*bf6873c5SCy Schubert                 if (first && i == last)
1008*bf6873c5SCy Schubert                     last = i + 1;
1009*bf6873c5SCy Schubert                 else {
1010*bf6873c5SCy Schubert                     if (first)
1011*bf6873c5SCy Schubert                         test_print_range(first, last, missing - 1, 0);
1012*bf6873c5SCy Schubert                     missing++;
1013*bf6873c5SCy Schubert                     first = i + 1;
1014*bf6873c5SCy Schubert                     last = i + 1;
1015*bf6873c5SCy Schubert                 }
1016*bf6873c5SCy Schubert             }
1017*bf6873c5SCy Schubert         }
1018*bf6873c5SCy Schubert         if (first)
1019*bf6873c5SCy Schubert             test_print_range(first, last, missing - 1, 0);
1020*bf6873c5SCy Schubert         first = 0;
1021*bf6873c5SCy Schubert         last = 0;
1022*bf6873c5SCy Schubert         for (i = 0; i < ts->count; i++) {
1023*bf6873c5SCy Schubert             if (ts->results[i] == TEST_FAIL) {
1024*bf6873c5SCy Schubert                 if (missing && !failed)
1025*bf6873c5SCy Schubert                     fputs("; ", stdout);
1026*bf6873c5SCy Schubert                 if (failed == 0)
1027*bf6873c5SCy Schubert                     fputs("FAILED ", stdout);
1028*bf6873c5SCy Schubert                 if (first && i == last)
1029*bf6873c5SCy Schubert                     last = i + 1;
1030*bf6873c5SCy Schubert                 else {
1031*bf6873c5SCy Schubert                     if (first)
1032*bf6873c5SCy Schubert                         test_print_range(first, last, failed - 1, 0);
1033*bf6873c5SCy Schubert                     failed++;
1034*bf6873c5SCy Schubert                     first = i + 1;
1035*bf6873c5SCy Schubert                     last = i + 1;
1036*bf6873c5SCy Schubert                 }
1037*bf6873c5SCy Schubert             }
1038*bf6873c5SCy Schubert         }
1039*bf6873c5SCy Schubert         if (first)
1040*bf6873c5SCy Schubert             test_print_range(first, last, failed - 1, 0);
1041*bf6873c5SCy Schubert         if (!missing && !failed) {
1042*bf6873c5SCy Schubert             fputs(!status ? "ok" : "dubious", stdout);
1043*bf6873c5SCy Schubert             if (ts->skipped > 0) {
1044*bf6873c5SCy Schubert                 if (ts->skipped == 1)
1045*bf6873c5SCy Schubert                     printf(" (skipped %lu test)", ts->skipped);
1046*bf6873c5SCy Schubert                 else
1047*bf6873c5SCy Schubert                     printf(" (skipped %lu tests)", ts->skipped);
1048*bf6873c5SCy Schubert             }
1049*bf6873c5SCy Schubert         }
1050*bf6873c5SCy Schubert     }
1051*bf6873c5SCy Schubert     if (status > 0)
1052*bf6873c5SCy Schubert         printf(" (exit status %d)", status);
1053*bf6873c5SCy Schubert     else if (status < 0)
1054*bf6873c5SCy Schubert         printf(" (killed by signal %d%s)", -status,
1055*bf6873c5SCy Schubert                WCOREDUMP(ts->status) ? ", core dumped" : "");
1056*bf6873c5SCy Schubert     putchar('\n');
1057*bf6873c5SCy Schubert }
1058*bf6873c5SCy Schubert 
1059*bf6873c5SCy Schubert 
1060*bf6873c5SCy Schubert /*
1061*bf6873c5SCy Schubert  * Given a test set, analyze the results, classify the exit status, handle a
1062*bf6873c5SCy Schubert  * few special error messages, and then pass it along to test_summarize() for
1063*bf6873c5SCy Schubert  * the regular output.  Returns true if the test set ran successfully and all
1064*bf6873c5SCy Schubert  * tests passed or were skipped, false otherwise.
1065*bf6873c5SCy Schubert  */
1066*bf6873c5SCy Schubert static int
test_analyze(struct testset * ts)1067*bf6873c5SCy Schubert test_analyze(struct testset *ts)
1068*bf6873c5SCy Schubert {
1069*bf6873c5SCy Schubert     if (ts->reported)
1070*bf6873c5SCy Schubert         return 0;
1071*bf6873c5SCy Schubert     if (ts->all_skipped) {
1072*bf6873c5SCy Schubert         if (ts->reason == NULL)
1073*bf6873c5SCy Schubert             puts("skipped");
1074*bf6873c5SCy Schubert         else
1075*bf6873c5SCy Schubert             printf("skipped (%s)\n", ts->reason);
1076*bf6873c5SCy Schubert         return 1;
1077*bf6873c5SCy Schubert     } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) {
1078*bf6873c5SCy Schubert         switch (WEXITSTATUS(ts->status)) {
1079*bf6873c5SCy Schubert         case CHILDERR_DUP:
1080*bf6873c5SCy Schubert             if (!ts->reported)
1081*bf6873c5SCy Schubert                 puts("ABORTED (can't dup file descriptors)");
1082*bf6873c5SCy Schubert             break;
1083*bf6873c5SCy Schubert         case CHILDERR_EXEC:
1084*bf6873c5SCy Schubert             if (!ts->reported)
1085*bf6873c5SCy Schubert                 puts("ABORTED (execution failed -- not found?)");
1086*bf6873c5SCy Schubert             break;
1087*bf6873c5SCy Schubert         case CHILDERR_STDIN:
1088*bf6873c5SCy Schubert         case CHILDERR_STDERR:
1089*bf6873c5SCy Schubert             if (!ts->reported)
1090*bf6873c5SCy Schubert                 puts("ABORTED (can't open /dev/null)");
1091*bf6873c5SCy Schubert             break;
1092*bf6873c5SCy Schubert         default:
1093*bf6873c5SCy Schubert             test_summarize(ts, WEXITSTATUS(ts->status));
1094*bf6873c5SCy Schubert             break;
1095*bf6873c5SCy Schubert         }
1096*bf6873c5SCy Schubert         return 0;
1097*bf6873c5SCy Schubert     } else if (WIFSIGNALED(ts->status)) {
1098*bf6873c5SCy Schubert         test_summarize(ts, -WTERMSIG(ts->status));
1099*bf6873c5SCy Schubert         return 0;
1100*bf6873c5SCy Schubert     } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) {
1101*bf6873c5SCy Schubert         puts("ABORTED (no valid test plan)");
1102*bf6873c5SCy Schubert         ts->aborted = 1;
1103*bf6873c5SCy Schubert         return 0;
1104*bf6873c5SCy Schubert     } else {
1105*bf6873c5SCy Schubert         test_summarize(ts, 0);
1106*bf6873c5SCy Schubert         return (ts->failed == 0);
1107*bf6873c5SCy Schubert     }
1108*bf6873c5SCy Schubert }
1109*bf6873c5SCy Schubert 
1110*bf6873c5SCy Schubert 
1111*bf6873c5SCy Schubert /*
1112*bf6873c5SCy Schubert  * Runs a single test set, accumulating and then reporting the results.
1113*bf6873c5SCy Schubert  * Returns true if the test set was successfully run and all tests passed,
1114*bf6873c5SCy Schubert  * false otherwise.
1115*bf6873c5SCy Schubert  */
1116*bf6873c5SCy Schubert static int
test_run(struct testset * ts,enum test_verbose verbose)1117*bf6873c5SCy Schubert test_run(struct testset *ts, enum test_verbose verbose)
1118*bf6873c5SCy Schubert {
1119*bf6873c5SCy Schubert     pid_t testpid, child;
1120*bf6873c5SCy Schubert     int outfd, status;
1121*bf6873c5SCy Schubert     unsigned long i;
1122*bf6873c5SCy Schubert     FILE *output;
1123*bf6873c5SCy Schubert     char buffer[BUFSIZ];
1124*bf6873c5SCy Schubert 
1125*bf6873c5SCy Schubert     /* Run the test program. */
1126*bf6873c5SCy Schubert     testpid = test_start(ts->command, &outfd);
1127*bf6873c5SCy Schubert     output = fdopen(outfd, "r");
1128*bf6873c5SCy Schubert     if (!output) {
1129*bf6873c5SCy Schubert         puts("ABORTED");
1130*bf6873c5SCy Schubert         fflush(stdout);
1131*bf6873c5SCy Schubert         sysdie("fdopen failed");
1132*bf6873c5SCy Schubert     }
1133*bf6873c5SCy Schubert 
1134*bf6873c5SCy Schubert     /*
1135*bf6873c5SCy Schubert      * Pass each line of output to test_checkline(), and print the line if
1136*bf6873c5SCy Schubert      * verbosity is requested.
1137*bf6873c5SCy Schubert      */
1138*bf6873c5SCy Schubert     while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) {
1139*bf6873c5SCy Schubert         if (verbose)
1140*bf6873c5SCy Schubert             printf("%s", buffer);
1141*bf6873c5SCy Schubert         test_checkline(buffer, ts, verbose);
1142*bf6873c5SCy Schubert     }
1143*bf6873c5SCy Schubert     if (ferror(output) || ts->plan == PLAN_INIT)
1144*bf6873c5SCy Schubert         ts->aborted = 1;
1145*bf6873c5SCy Schubert     if (!verbose)
1146*bf6873c5SCy Schubert         test_backspace(ts);
1147*bf6873c5SCy Schubert 
1148*bf6873c5SCy Schubert     /*
1149*bf6873c5SCy Schubert      * Consume the rest of the test output, close the output descriptor,
1150*bf6873c5SCy Schubert      * retrieve the exit status, and pass that information to test_analyze()
1151*bf6873c5SCy Schubert      * for eventual output.
1152*bf6873c5SCy Schubert      */
1153*bf6873c5SCy Schubert     while (fgets(buffer, sizeof(buffer), output))
1154*bf6873c5SCy Schubert         if (verbose)
1155*bf6873c5SCy Schubert             printf("%s", buffer);
1156*bf6873c5SCy Schubert     fclose(output);
1157*bf6873c5SCy Schubert     child = waitpid(testpid, &ts->status, 0);
1158*bf6873c5SCy Schubert     if (child == (pid_t) -1) {
1159*bf6873c5SCy Schubert         if (!ts->reported) {
1160*bf6873c5SCy Schubert             puts("ABORTED");
1161*bf6873c5SCy Schubert             fflush(stdout);
1162*bf6873c5SCy Schubert         }
1163*bf6873c5SCy Schubert         sysdie("waitpid for %u failed", (unsigned int) testpid);
1164*bf6873c5SCy Schubert     }
1165*bf6873c5SCy Schubert     if (ts->all_skipped)
1166*bf6873c5SCy Schubert         ts->aborted = 0;
1167*bf6873c5SCy Schubert     status = test_analyze(ts);
1168*bf6873c5SCy Schubert 
1169*bf6873c5SCy Schubert     /* Convert missing tests to failed tests. */
1170*bf6873c5SCy Schubert     for (i = 0; i < ts->count; i++) {
1171*bf6873c5SCy Schubert         if (ts->results[i] == TEST_INVALID) {
1172*bf6873c5SCy Schubert             ts->failed++;
1173*bf6873c5SCy Schubert             ts->results[i] = TEST_FAIL;
1174*bf6873c5SCy Schubert             status = 0;
1175*bf6873c5SCy Schubert         }
1176*bf6873c5SCy Schubert     }
1177*bf6873c5SCy Schubert     return status;
1178*bf6873c5SCy Schubert }
1179*bf6873c5SCy Schubert 
1180*bf6873c5SCy Schubert 
1181*bf6873c5SCy Schubert /* Summarize a list of test failures. */
1182*bf6873c5SCy Schubert static void
test_fail_summary(const struct testlist * fails)1183*bf6873c5SCy Schubert test_fail_summary(const struct testlist *fails)
1184*bf6873c5SCy Schubert {
1185*bf6873c5SCy Schubert     struct testset *ts;
1186*bf6873c5SCy Schubert     unsigned int chars;
1187*bf6873c5SCy Schubert     unsigned long i, first, last, total;
1188*bf6873c5SCy Schubert     double failed;
1189*bf6873c5SCy Schubert 
1190*bf6873c5SCy Schubert     puts(header);
1191*bf6873c5SCy Schubert 
1192*bf6873c5SCy Schubert     /* Failed Set                 Fail/Total (%) Skip Stat  Failing (25)
1193*bf6873c5SCy Schubert        -------------------------- -------------- ---- ----  -------------- */
1194*bf6873c5SCy Schubert     for (; fails; fails = fails->next) {
1195*bf6873c5SCy Schubert         ts = fails->ts;
1196*bf6873c5SCy Schubert         total = ts->count - ts->skipped;
1197*bf6873c5SCy Schubert         failed = (double) ts->failed;
1198*bf6873c5SCy Schubert         printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed,
1199*bf6873c5SCy Schubert                total, total ? (failed * 100.0) / (double) total : 0,
1200*bf6873c5SCy Schubert                ts->skipped);
1201*bf6873c5SCy Schubert         if (WIFEXITED(ts->status))
1202*bf6873c5SCy Schubert             printf("%4d  ", WEXITSTATUS(ts->status));
1203*bf6873c5SCy Schubert         else
1204*bf6873c5SCy Schubert             printf("  --  ");
1205*bf6873c5SCy Schubert         if (ts->aborted) {
1206*bf6873c5SCy Schubert             puts("aborted");
1207*bf6873c5SCy Schubert             continue;
1208*bf6873c5SCy Schubert         }
1209*bf6873c5SCy Schubert         chars = 0;
1210*bf6873c5SCy Schubert         first = 0;
1211*bf6873c5SCy Schubert         last = 0;
1212*bf6873c5SCy Schubert         for (i = 0; i < ts->count; i++) {
1213*bf6873c5SCy Schubert             if (ts->results[i] == TEST_FAIL) {
1214*bf6873c5SCy Schubert                 if (first != 0 && i == last)
1215*bf6873c5SCy Schubert                     last = i + 1;
1216*bf6873c5SCy Schubert                 else {
1217*bf6873c5SCy Schubert                     if (first != 0)
1218*bf6873c5SCy Schubert                         chars += test_print_range(first, last, chars, 19);
1219*bf6873c5SCy Schubert                     first = i + 1;
1220*bf6873c5SCy Schubert                     last = i + 1;
1221*bf6873c5SCy Schubert                 }
1222*bf6873c5SCy Schubert             }
1223*bf6873c5SCy Schubert         }
1224*bf6873c5SCy Schubert         if (first != 0)
1225*bf6873c5SCy Schubert             test_print_range(first, last, chars, 19);
1226*bf6873c5SCy Schubert         putchar('\n');
1227*bf6873c5SCy Schubert     }
1228*bf6873c5SCy Schubert }
1229*bf6873c5SCy Schubert 
1230*bf6873c5SCy Schubert 
1231*bf6873c5SCy Schubert /*
1232*bf6873c5SCy Schubert  * Check whether a given file path is a valid test.  Currently, this checks
1233*bf6873c5SCy Schubert  * whether it is executable and is a regular file.  Returns true or false.
1234*bf6873c5SCy Schubert  */
1235*bf6873c5SCy Schubert static int
is_valid_test(const char * path)1236*bf6873c5SCy Schubert is_valid_test(const char *path)
1237*bf6873c5SCy Schubert {
1238*bf6873c5SCy Schubert     struct stat st;
1239*bf6873c5SCy Schubert 
1240*bf6873c5SCy Schubert     if (access(path, X_OK) < 0)
1241*bf6873c5SCy Schubert         return 0;
1242*bf6873c5SCy Schubert     if (stat(path, &st) < 0)
1243*bf6873c5SCy Schubert         return 0;
1244*bf6873c5SCy Schubert     if (!S_ISREG(st.st_mode))
1245*bf6873c5SCy Schubert         return 0;
1246*bf6873c5SCy Schubert     return 1;
1247*bf6873c5SCy Schubert }
1248*bf6873c5SCy Schubert 
1249*bf6873c5SCy Schubert 
1250*bf6873c5SCy Schubert /*
1251*bf6873c5SCy Schubert  * Given the name of a test, a pointer to the testset struct, and the source
1252*bf6873c5SCy Schubert  * and build directories, find the test.  We try first relative to the current
1253*bf6873c5SCy Schubert  * directory, then in the build directory (if not NULL), then in the source
1254*bf6873c5SCy Schubert  * directory.  In each of those directories, we first try a "-t" extension and
1255*bf6873c5SCy Schubert  * then a ".t" extension.  When we find an executable program, we return the
1256*bf6873c5SCy Schubert  * path to that program.  If none of those paths are executable, just fill in
1257*bf6873c5SCy Schubert  * the name of the test as is.
1258*bf6873c5SCy Schubert  *
1259*bf6873c5SCy Schubert  * The caller is responsible for freeing the path member of the testset
1260*bf6873c5SCy Schubert  * struct.
1261*bf6873c5SCy Schubert  */
1262*bf6873c5SCy Schubert static char *
find_test(const char * name,const char * source,const char * build)1263*bf6873c5SCy Schubert find_test(const char *name, const char *source, const char *build)
1264*bf6873c5SCy Schubert {
1265*bf6873c5SCy Schubert     char *path = NULL;
1266*bf6873c5SCy Schubert     const char *bases[3], *suffix, *base;
1267*bf6873c5SCy Schubert     unsigned int i, j;
1268*bf6873c5SCy Schubert     const char *suffixes[3] = {"-t", ".t", ""};
1269*bf6873c5SCy Schubert 
1270*bf6873c5SCy Schubert     /* Possible base directories. */
1271*bf6873c5SCy Schubert     bases[0] = ".";
1272*bf6873c5SCy Schubert     bases[1] = build;
1273*bf6873c5SCy Schubert     bases[2] = source;
1274*bf6873c5SCy Schubert 
1275*bf6873c5SCy Schubert     /* Try each suffix with each base. */
1276*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
1277*bf6873c5SCy Schubert         suffix = suffixes[i];
1278*bf6873c5SCy Schubert         for (j = 0; j < ARRAY_SIZE(bases); j++) {
1279*bf6873c5SCy Schubert             base = bases[j];
1280*bf6873c5SCy Schubert             if (base == NULL)
1281*bf6873c5SCy Schubert                 continue;
1282*bf6873c5SCy Schubert             path = concat(base, "/", name, suffix, (const char *) 0);
1283*bf6873c5SCy Schubert             if (is_valid_test(path))
1284*bf6873c5SCy Schubert                 return path;
1285*bf6873c5SCy Schubert             free(path);
1286*bf6873c5SCy Schubert             path = NULL;
1287*bf6873c5SCy Schubert         }
1288*bf6873c5SCy Schubert     }
1289*bf6873c5SCy Schubert     if (path == NULL)
1290*bf6873c5SCy Schubert         path = xstrdup(name);
1291*bf6873c5SCy Schubert     return path;
1292*bf6873c5SCy Schubert }
1293*bf6873c5SCy Schubert 
1294*bf6873c5SCy Schubert 
1295*bf6873c5SCy Schubert /*
1296*bf6873c5SCy Schubert  * Parse a single line of a test list and store the test name and command to
1297*bf6873c5SCy Schubert  * execute it in the given testset struct.
1298*bf6873c5SCy Schubert  *
1299*bf6873c5SCy Schubert  * Normally, each line is just the name of the test, which is located in the
1300*bf6873c5SCy Schubert  * test directory and turned into a command to run.  However, each line may
1301*bf6873c5SCy Schubert  * have whitespace-separated options, which change the command that's run.
1302*bf6873c5SCy Schubert  * Current supported options are:
1303*bf6873c5SCy Schubert  *
1304*bf6873c5SCy Schubert  * valgrind
1305*bf6873c5SCy Schubert  *     Run the test under valgrind if C_TAP_VALGRIND is set.  The contents
1306*bf6873c5SCy Schubert  *     of that environment variable are taken as the valgrind command (with
1307*bf6873c5SCy Schubert  *     options) to run.  The command is parsed with a simple split on
1308*bf6873c5SCy Schubert  *     whitespace and no quoting is supported.
1309*bf6873c5SCy Schubert  *
1310*bf6873c5SCy Schubert  * libtool
1311*bf6873c5SCy Schubert  *     If running under valgrind, use libtool to invoke valgrind.  This avoids
1312*bf6873c5SCy Schubert  *     running valgrind on the wrapper shell script generated by libtool.  If
1313*bf6873c5SCy Schubert  *     set, C_TAP_LIBTOOL must be set to the full path to the libtool program
1314*bf6873c5SCy Schubert  *     to use to run valgrind and thus the test.  Ignored if the test isn't
1315*bf6873c5SCy Schubert  *     being run under valgrind.
1316*bf6873c5SCy Schubert  */
1317*bf6873c5SCy Schubert static void
parse_test_list_line(const char * line,struct testset * ts,const char * source,const char * build)1318*bf6873c5SCy Schubert parse_test_list_line(const char *line, struct testset *ts, const char *source,
1319*bf6873c5SCy Schubert                      const char *build)
1320*bf6873c5SCy Schubert {
1321*bf6873c5SCy Schubert     const char *p, *end, *option, *libtool;
1322*bf6873c5SCy Schubert     const char *valgrind = NULL;
1323*bf6873c5SCy Schubert     unsigned int use_libtool = 0;
1324*bf6873c5SCy Schubert     unsigned int use_valgrind = 0;
1325*bf6873c5SCy Schubert     size_t len, i;
1326*bf6873c5SCy Schubert 
1327*bf6873c5SCy Schubert     /* Determine the name of the test. */
1328*bf6873c5SCy Schubert     p = skip_non_whitespace(line);
1329*bf6873c5SCy Schubert     ts->file = xstrndup(line, p - line);
1330*bf6873c5SCy Schubert 
1331*bf6873c5SCy Schubert     /* Check if any test options are set. */
1332*bf6873c5SCy Schubert     p = skip_whitespace(p);
1333*bf6873c5SCy Schubert     while (*p != '\0') {
1334*bf6873c5SCy Schubert         end = skip_non_whitespace(p);
1335*bf6873c5SCy Schubert         if (strncmp(p, "libtool", end - p) == 0) {
1336*bf6873c5SCy Schubert             use_libtool = 1;
1337*bf6873c5SCy Schubert         } else if (strncmp(p, "valgrind", end - p) == 0) {
1338*bf6873c5SCy Schubert             valgrind = getenv("C_TAP_VALGRIND");
1339*bf6873c5SCy Schubert             use_valgrind = (valgrind != NULL);
1340*bf6873c5SCy Schubert         } else {
1341*bf6873c5SCy Schubert             option = xstrndup(p, end - p);
1342*bf6873c5SCy Schubert             die("unknown test list option %s", option);
1343*bf6873c5SCy Schubert         }
1344*bf6873c5SCy Schubert         p = skip_whitespace(end);
1345*bf6873c5SCy Schubert     }
1346*bf6873c5SCy Schubert 
1347*bf6873c5SCy Schubert     /* Construct the argv to run the test.  First, find the length. */
1348*bf6873c5SCy Schubert     len = 1;
1349*bf6873c5SCy Schubert     if (use_valgrind && valgrind != NULL) {
1350*bf6873c5SCy Schubert         p = skip_whitespace(valgrind);
1351*bf6873c5SCy Schubert         while (*p != '\0') {
1352*bf6873c5SCy Schubert             len++;
1353*bf6873c5SCy Schubert             p = skip_whitespace(skip_non_whitespace(p));
1354*bf6873c5SCy Schubert         }
1355*bf6873c5SCy Schubert         if (use_libtool)
1356*bf6873c5SCy Schubert             len += 2;
1357*bf6873c5SCy Schubert     }
1358*bf6873c5SCy Schubert 
1359*bf6873c5SCy Schubert     /* Now, build the command. */
1360*bf6873c5SCy Schubert     ts->command = xcalloc(len + 1, char *);
1361*bf6873c5SCy Schubert     i = 0;
1362*bf6873c5SCy Schubert     if (use_valgrind && valgrind != NULL) {
1363*bf6873c5SCy Schubert         if (use_libtool) {
1364*bf6873c5SCy Schubert             libtool = getenv("C_TAP_LIBTOOL");
1365*bf6873c5SCy Schubert             if (libtool == NULL)
1366*bf6873c5SCy Schubert                 die("valgrind with libtool requested, but C_TAP_LIBTOOL is not"
1367*bf6873c5SCy Schubert                     " set");
1368*bf6873c5SCy Schubert             ts->command[i++] = xstrdup(libtool);
1369*bf6873c5SCy Schubert             ts->command[i++] = xstrdup("--mode=execute");
1370*bf6873c5SCy Schubert         }
1371*bf6873c5SCy Schubert         p = skip_whitespace(valgrind);
1372*bf6873c5SCy Schubert         while (*p != '\0') {
1373*bf6873c5SCy Schubert             end = skip_non_whitespace(p);
1374*bf6873c5SCy Schubert             ts->command[i++] = xstrndup(p, end - p);
1375*bf6873c5SCy Schubert             p = skip_whitespace(end);
1376*bf6873c5SCy Schubert         }
1377*bf6873c5SCy Schubert     }
1378*bf6873c5SCy Schubert     if (i != len - 1)
1379*bf6873c5SCy Schubert         die("internal error while constructing command line");
1380*bf6873c5SCy Schubert     ts->command[i++] = find_test(ts->file, source, build);
1381*bf6873c5SCy Schubert     ts->command[i] = NULL;
1382*bf6873c5SCy Schubert }
1383*bf6873c5SCy Schubert 
1384*bf6873c5SCy Schubert 
1385*bf6873c5SCy Schubert /*
1386*bf6873c5SCy Schubert  * Read a list of tests from a file, returning the list of tests as a struct
1387*bf6873c5SCy Schubert  * testlist, or NULL if there were no tests (such as a file containing only
1388*bf6873c5SCy Schubert  * comments).  Reports an error to standard error and exits if the list of
1389*bf6873c5SCy Schubert  * tests cannot be read.
1390*bf6873c5SCy Schubert  */
1391*bf6873c5SCy Schubert static struct testlist *
read_test_list(const char * filename,const char * source,const char * build)1392*bf6873c5SCy Schubert read_test_list(const char *filename, const char *source, const char *build)
1393*bf6873c5SCy Schubert {
1394*bf6873c5SCy Schubert     FILE *file;
1395*bf6873c5SCy Schubert     unsigned int line;
1396*bf6873c5SCy Schubert     size_t length;
1397*bf6873c5SCy Schubert     char buffer[BUFSIZ];
1398*bf6873c5SCy Schubert     const char *start;
1399*bf6873c5SCy Schubert     struct testlist *listhead, *current;
1400*bf6873c5SCy Schubert 
1401*bf6873c5SCy Schubert     /* Create the initial container list that will hold our results. */
1402*bf6873c5SCy Schubert     listhead = xcalloc(1, struct testlist);
1403*bf6873c5SCy Schubert     current = NULL;
1404*bf6873c5SCy Schubert 
1405*bf6873c5SCy Schubert     /*
1406*bf6873c5SCy Schubert      * Open our file of tests to run and read it line by line, creating a new
1407*bf6873c5SCy Schubert      * struct testlist and struct testset for each line.
1408*bf6873c5SCy Schubert      */
1409*bf6873c5SCy Schubert     file = fopen(filename, "r");
1410*bf6873c5SCy Schubert     if (file == NULL)
1411*bf6873c5SCy Schubert         sysdie("can't open %s", filename);
1412*bf6873c5SCy Schubert     line = 0;
1413*bf6873c5SCy Schubert     while (fgets(buffer, sizeof(buffer), file)) {
1414*bf6873c5SCy Schubert         line++;
1415*bf6873c5SCy Schubert         length = strlen(buffer) - 1;
1416*bf6873c5SCy Schubert         if (buffer[length] != '\n') {
1417*bf6873c5SCy Schubert             fprintf(stderr, "%s:%u: line too long\n", filename, line);
1418*bf6873c5SCy Schubert             exit(1);
1419*bf6873c5SCy Schubert         }
1420*bf6873c5SCy Schubert         buffer[length] = '\0';
1421*bf6873c5SCy Schubert 
1422*bf6873c5SCy Schubert         /* Skip comments, leading spaces, and blank lines. */
1423*bf6873c5SCy Schubert         start = skip_whitespace(buffer);
1424*bf6873c5SCy Schubert         if (strlen(start) == 0)
1425*bf6873c5SCy Schubert             continue;
1426*bf6873c5SCy Schubert         if (start[0] == '#')
1427*bf6873c5SCy Schubert             continue;
1428*bf6873c5SCy Schubert 
1429*bf6873c5SCy Schubert         /* Allocate the new testset structure. */
1430*bf6873c5SCy Schubert         if (current == NULL)
1431*bf6873c5SCy Schubert             current = listhead;
1432*bf6873c5SCy Schubert         else {
1433*bf6873c5SCy Schubert             current->next = xcalloc(1, struct testlist);
1434*bf6873c5SCy Schubert             current = current->next;
1435*bf6873c5SCy Schubert         }
1436*bf6873c5SCy Schubert         current->ts = xcalloc(1, struct testset);
1437*bf6873c5SCy Schubert         current->ts->plan = PLAN_INIT;
1438*bf6873c5SCy Schubert 
1439*bf6873c5SCy Schubert         /* Parse the line and store the results in the testset struct. */
1440*bf6873c5SCy Schubert         parse_test_list_line(start, current->ts, source, build);
1441*bf6873c5SCy Schubert     }
1442*bf6873c5SCy Schubert     fclose(file);
1443*bf6873c5SCy Schubert 
1444*bf6873c5SCy Schubert     /* If there were no tests, current is still NULL. */
1445*bf6873c5SCy Schubert     if (current == NULL) {
1446*bf6873c5SCy Schubert         free(listhead);
1447*bf6873c5SCy Schubert         return NULL;
1448*bf6873c5SCy Schubert     }
1449*bf6873c5SCy Schubert 
1450*bf6873c5SCy Schubert     /* Return the results. */
1451*bf6873c5SCy Schubert     return listhead;
1452*bf6873c5SCy Schubert }
1453*bf6873c5SCy Schubert 
1454*bf6873c5SCy Schubert 
1455*bf6873c5SCy Schubert /*
1456*bf6873c5SCy Schubert  * Build a list of tests from command line arguments.  Takes the argv and argc
1457*bf6873c5SCy Schubert  * representing the command line arguments and returns a newly allocated test
1458*bf6873c5SCy Schubert  * list, or NULL if there were no tests.  The caller is responsible for
1459*bf6873c5SCy Schubert  * freeing.
1460*bf6873c5SCy Schubert  */
1461*bf6873c5SCy Schubert static struct testlist *
build_test_list(char * argv[],int argc,const char * source,const char * build)1462*bf6873c5SCy Schubert build_test_list(char *argv[], int argc, const char *source, const char *build)
1463*bf6873c5SCy Schubert {
1464*bf6873c5SCy Schubert     int i;
1465*bf6873c5SCy Schubert     struct testlist *listhead, *current;
1466*bf6873c5SCy Schubert 
1467*bf6873c5SCy Schubert     /* Create the initial container list that will hold our results. */
1468*bf6873c5SCy Schubert     listhead = xcalloc(1, struct testlist);
1469*bf6873c5SCy Schubert     current = NULL;
1470*bf6873c5SCy Schubert 
1471*bf6873c5SCy Schubert     /* Walk the list of arguments and create test sets for them. */
1472*bf6873c5SCy Schubert     for (i = 0; i < argc; i++) {
1473*bf6873c5SCy Schubert         if (current == NULL)
1474*bf6873c5SCy Schubert             current = listhead;
1475*bf6873c5SCy Schubert         else {
1476*bf6873c5SCy Schubert             current->next = xcalloc(1, struct testlist);
1477*bf6873c5SCy Schubert             current = current->next;
1478*bf6873c5SCy Schubert         }
1479*bf6873c5SCy Schubert         current->ts = xcalloc(1, struct testset);
1480*bf6873c5SCy Schubert         current->ts->plan = PLAN_INIT;
1481*bf6873c5SCy Schubert         current->ts->file = xstrdup(argv[i]);
1482*bf6873c5SCy Schubert         current->ts->command = xcalloc(2, char *);
1483*bf6873c5SCy Schubert         current->ts->command[0] = find_test(current->ts->file, source, build);
1484*bf6873c5SCy Schubert         current->ts->command[1] = NULL;
1485*bf6873c5SCy Schubert     }
1486*bf6873c5SCy Schubert 
1487*bf6873c5SCy Schubert     /* If there were no tests, current is still NULL. */
1488*bf6873c5SCy Schubert     if (current == NULL) {
1489*bf6873c5SCy Schubert         free(listhead);
1490*bf6873c5SCy Schubert         return NULL;
1491*bf6873c5SCy Schubert     }
1492*bf6873c5SCy Schubert 
1493*bf6873c5SCy Schubert     /* Return the results. */
1494*bf6873c5SCy Schubert     return listhead;
1495*bf6873c5SCy Schubert }
1496*bf6873c5SCy Schubert 
1497*bf6873c5SCy Schubert 
1498*bf6873c5SCy Schubert /* Free a struct testset. */
1499*bf6873c5SCy Schubert static void
free_testset(struct testset * ts)1500*bf6873c5SCy Schubert free_testset(struct testset *ts)
1501*bf6873c5SCy Schubert {
1502*bf6873c5SCy Schubert     size_t i;
1503*bf6873c5SCy Schubert 
1504*bf6873c5SCy Schubert     free(ts->file);
1505*bf6873c5SCy Schubert     for (i = 0; ts->command[i] != NULL; i++)
1506*bf6873c5SCy Schubert         free(ts->command[i]);
1507*bf6873c5SCy Schubert     free(ts->command);
1508*bf6873c5SCy Schubert     free(ts->results);
1509*bf6873c5SCy Schubert     free(ts->reason);
1510*bf6873c5SCy Schubert     free(ts);
1511*bf6873c5SCy Schubert }
1512*bf6873c5SCy Schubert 
1513*bf6873c5SCy Schubert 
1514*bf6873c5SCy Schubert /*
1515*bf6873c5SCy Schubert  * Run a batch of tests.  Takes two additional parameters: the root of the
1516*bf6873c5SCy Schubert  * source directory and the root of the build directory.  Test programs will
1517*bf6873c5SCy Schubert  * be first searched for in the current directory, then the build directory,
1518*bf6873c5SCy Schubert  * then the source directory.  Returns true iff all tests passed, and always
1519*bf6873c5SCy Schubert  * frees the test list that's passed in.
1520*bf6873c5SCy Schubert  */
1521*bf6873c5SCy Schubert static int
test_batch(struct testlist * tests,enum test_verbose verbose)1522*bf6873c5SCy Schubert test_batch(struct testlist *tests, enum test_verbose verbose)
1523*bf6873c5SCy Schubert {
1524*bf6873c5SCy Schubert     size_t length, i;
1525*bf6873c5SCy Schubert     size_t longest = 0;
1526*bf6873c5SCy Schubert     unsigned int count = 0;
1527*bf6873c5SCy Schubert     struct testset *ts;
1528*bf6873c5SCy Schubert     struct timeval start, end;
1529*bf6873c5SCy Schubert     struct rusage stats;
1530*bf6873c5SCy Schubert     struct testlist *failhead = NULL;
1531*bf6873c5SCy Schubert     struct testlist *failtail = NULL;
1532*bf6873c5SCy Schubert     struct testlist *current, *next;
1533*bf6873c5SCy Schubert     int succeeded;
1534*bf6873c5SCy Schubert     unsigned long total = 0;
1535*bf6873c5SCy Schubert     unsigned long passed = 0;
1536*bf6873c5SCy Schubert     unsigned long skipped = 0;
1537*bf6873c5SCy Schubert     unsigned long failed = 0;
1538*bf6873c5SCy Schubert     unsigned long aborted = 0;
1539*bf6873c5SCy Schubert 
1540*bf6873c5SCy Schubert     /* Walk the list of tests to find the longest name. */
1541*bf6873c5SCy Schubert     for (current = tests; current != NULL; current = current->next) {
1542*bf6873c5SCy Schubert         length = strlen(current->ts->file);
1543*bf6873c5SCy Schubert         if (length > longest)
1544*bf6873c5SCy Schubert             longest = length;
1545*bf6873c5SCy Schubert     }
1546*bf6873c5SCy Schubert 
1547*bf6873c5SCy Schubert     /*
1548*bf6873c5SCy Schubert      * Add two to longest and round up to the nearest tab stop.  This is how
1549*bf6873c5SCy Schubert      * wide the column for printing the current test name will be.
1550*bf6873c5SCy Schubert      */
1551*bf6873c5SCy Schubert     longest += 2;
1552*bf6873c5SCy Schubert     if (longest % 8)
1553*bf6873c5SCy Schubert         longest += 8 - (longest % 8);
1554*bf6873c5SCy Schubert 
1555*bf6873c5SCy Schubert     /* Start the wall clock timer. */
1556*bf6873c5SCy Schubert     gettimeofday(&start, NULL);
1557*bf6873c5SCy Schubert 
1558*bf6873c5SCy Schubert     /* Now, plow through our tests again, running each one. */
1559*bf6873c5SCy Schubert     for (current = tests; current != NULL; current = current->next) {
1560*bf6873c5SCy Schubert         ts = current->ts;
1561*bf6873c5SCy Schubert 
1562*bf6873c5SCy Schubert         /* Print out the name of the test file. */
1563*bf6873c5SCy Schubert         fputs(ts->file, stdout);
1564*bf6873c5SCy Schubert         if (verbose)
1565*bf6873c5SCy Schubert             fputs("\n\n", stdout);
1566*bf6873c5SCy Schubert         else
1567*bf6873c5SCy Schubert             for (i = strlen(ts->file); i < longest; i++)
1568*bf6873c5SCy Schubert                 putchar('.');
1569*bf6873c5SCy Schubert         if (isatty(STDOUT_FILENO))
1570*bf6873c5SCy Schubert             fflush(stdout);
1571*bf6873c5SCy Schubert 
1572*bf6873c5SCy Schubert         /* Run the test. */
1573*bf6873c5SCy Schubert         succeeded = test_run(ts, verbose);
1574*bf6873c5SCy Schubert         fflush(stdout);
1575*bf6873c5SCy Schubert         if (verbose)
1576*bf6873c5SCy Schubert             putchar('\n');
1577*bf6873c5SCy Schubert 
1578*bf6873c5SCy Schubert         /* Record cumulative statistics. */
1579*bf6873c5SCy Schubert         aborted += ts->aborted;
1580*bf6873c5SCy Schubert         total += ts->count + ts->all_skipped;
1581*bf6873c5SCy Schubert         passed += ts->passed;
1582*bf6873c5SCy Schubert         skipped += ts->skipped + ts->all_skipped;
1583*bf6873c5SCy Schubert         failed += ts->failed;
1584*bf6873c5SCy Schubert         count++;
1585*bf6873c5SCy Schubert 
1586*bf6873c5SCy Schubert         /* If the test fails, we shuffle it over to the fail list. */
1587*bf6873c5SCy Schubert         if (!succeeded) {
1588*bf6873c5SCy Schubert             if (failhead == NULL) {
1589*bf6873c5SCy Schubert                 failhead = xcalloc(1, struct testlist);
1590*bf6873c5SCy Schubert                 failtail = failhead;
1591*bf6873c5SCy Schubert             } else {
1592*bf6873c5SCy Schubert                 failtail->next = xcalloc(1, struct testlist);
1593*bf6873c5SCy Schubert                 failtail = failtail->next;
1594*bf6873c5SCy Schubert             }
1595*bf6873c5SCy Schubert             failtail->ts = ts;
1596*bf6873c5SCy Schubert             failtail->next = NULL;
1597*bf6873c5SCy Schubert         }
1598*bf6873c5SCy Schubert     }
1599*bf6873c5SCy Schubert     total -= skipped;
1600*bf6873c5SCy Schubert 
1601*bf6873c5SCy Schubert     /* Stop the timer and get our child resource statistics. */
1602*bf6873c5SCy Schubert     gettimeofday(&end, NULL);
1603*bf6873c5SCy Schubert     getrusage(RUSAGE_CHILDREN, &stats);
1604*bf6873c5SCy Schubert 
1605*bf6873c5SCy Schubert     /* Summarize the failures and free the failure list. */
1606*bf6873c5SCy Schubert     if (failhead != NULL) {
1607*bf6873c5SCy Schubert         test_fail_summary(failhead);
1608*bf6873c5SCy Schubert         while (failhead != NULL) {
1609*bf6873c5SCy Schubert             next = failhead->next;
1610*bf6873c5SCy Schubert             free(failhead);
1611*bf6873c5SCy Schubert             failhead = next;
1612*bf6873c5SCy Schubert         }
1613*bf6873c5SCy Schubert     }
1614*bf6873c5SCy Schubert 
1615*bf6873c5SCy Schubert     /* Free the memory used by the test lists. */
1616*bf6873c5SCy Schubert     while (tests != NULL) {
1617*bf6873c5SCy Schubert         next = tests->next;
1618*bf6873c5SCy Schubert         free_testset(tests->ts);
1619*bf6873c5SCy Schubert         free(tests);
1620*bf6873c5SCy Schubert         tests = next;
1621*bf6873c5SCy Schubert     }
1622*bf6873c5SCy Schubert 
1623*bf6873c5SCy Schubert     /* Print out the final test summary. */
1624*bf6873c5SCy Schubert     putchar('\n');
1625*bf6873c5SCy Schubert     if (aborted != 0) {
1626*bf6873c5SCy Schubert         if (aborted == 1)
1627*bf6873c5SCy Schubert             printf("Aborted %lu test set", aborted);
1628*bf6873c5SCy Schubert         else
1629*bf6873c5SCy Schubert             printf("Aborted %lu test sets", aborted);
1630*bf6873c5SCy Schubert         printf(", passed %lu/%lu tests", passed, total);
1631*bf6873c5SCy Schubert     } else if (failed == 0)
1632*bf6873c5SCy Schubert         fputs("All tests successful", stdout);
1633*bf6873c5SCy Schubert     else
1634*bf6873c5SCy Schubert         printf("Failed %lu/%lu tests, %.2f%% okay", failed, total,
1635*bf6873c5SCy Schubert                (double) (total - failed) * 100.0 / (double) total);
1636*bf6873c5SCy Schubert     if (skipped != 0) {
1637*bf6873c5SCy Schubert         if (skipped == 1)
1638*bf6873c5SCy Schubert             printf(", %lu test skipped", skipped);
1639*bf6873c5SCy Schubert         else
1640*bf6873c5SCy Schubert             printf(", %lu tests skipped", skipped);
1641*bf6873c5SCy Schubert     }
1642*bf6873c5SCy Schubert     puts(".");
1643*bf6873c5SCy Schubert     printf("Files=%u,  Tests=%lu", count, total);
1644*bf6873c5SCy Schubert     printf(",  %.2f seconds", tv_diff(&end, &start));
1645*bf6873c5SCy Schubert     printf(" (%.2f usr + %.2f sys = %.2f CPU)\n", tv_seconds(&stats.ru_utime),
1646*bf6873c5SCy Schubert            tv_seconds(&stats.ru_stime),
1647*bf6873c5SCy Schubert            tv_sum(&stats.ru_utime, &stats.ru_stime));
1648*bf6873c5SCy Schubert     return (failed == 0 && aborted == 0);
1649*bf6873c5SCy Schubert }
1650*bf6873c5SCy Schubert 
1651*bf6873c5SCy Schubert 
1652*bf6873c5SCy Schubert /*
1653*bf6873c5SCy Schubert  * Run a single test case.  This involves just running the test program after
1654*bf6873c5SCy Schubert  * having done the environment setup and finding the test program.
1655*bf6873c5SCy Schubert  */
1656*bf6873c5SCy Schubert static void
test_single(const char * program,const char * source,const char * build)1657*bf6873c5SCy Schubert test_single(const char *program, const char *source, const char *build)
1658*bf6873c5SCy Schubert {
1659*bf6873c5SCy Schubert     char *path;
1660*bf6873c5SCy Schubert 
1661*bf6873c5SCy Schubert     path = find_test(program, source, build);
1662*bf6873c5SCy Schubert     if (execl(path, path, (char *) 0) == -1)
1663*bf6873c5SCy Schubert         sysdie("cannot exec %s", path);
1664*bf6873c5SCy Schubert }
1665*bf6873c5SCy Schubert 
1666*bf6873c5SCy Schubert 
1667*bf6873c5SCy Schubert /*
1668*bf6873c5SCy Schubert  * Main routine.  Set the C_TAP_SOURCE, C_TAP_BUILD, SOURCE, and BUILD
1669*bf6873c5SCy Schubert  * environment variables and then, given a file listing tests, run each test
1670*bf6873c5SCy Schubert  * listed.
1671*bf6873c5SCy Schubert  */
1672*bf6873c5SCy Schubert int
main(int argc,char * argv[])1673*bf6873c5SCy Schubert main(int argc, char *argv[])
1674*bf6873c5SCy Schubert {
1675*bf6873c5SCy Schubert     int option;
1676*bf6873c5SCy Schubert     int status = 0;
1677*bf6873c5SCy Schubert     int single = 0;
1678*bf6873c5SCy Schubert     enum test_verbose verbose = CONCISE;
1679*bf6873c5SCy Schubert     char *c_tap_source_env = NULL;
1680*bf6873c5SCy Schubert     char *c_tap_build_env = NULL;
1681*bf6873c5SCy Schubert     char *source_env = NULL;
1682*bf6873c5SCy Schubert     char *build_env = NULL;
1683*bf6873c5SCy Schubert     const char *program;
1684*bf6873c5SCy Schubert     const char *shortlist;
1685*bf6873c5SCy Schubert     const char *list = NULL;
1686*bf6873c5SCy Schubert     const char *source = C_TAP_SOURCE;
1687*bf6873c5SCy Schubert     const char *build = C_TAP_BUILD;
1688*bf6873c5SCy Schubert     struct testlist *tests;
1689*bf6873c5SCy Schubert 
1690*bf6873c5SCy Schubert     program = argv[0];
1691*bf6873c5SCy Schubert     while ((option = getopt(argc, argv, "b:hl:os:v")) != EOF) {
1692*bf6873c5SCy Schubert         switch (option) {
1693*bf6873c5SCy Schubert         case 'b':
1694*bf6873c5SCy Schubert             build = optarg;
1695*bf6873c5SCy Schubert             break;
1696*bf6873c5SCy Schubert         case 'h':
1697*bf6873c5SCy Schubert             printf(usage_message, program, program, program, usage_extra);
1698*bf6873c5SCy Schubert             exit(0);
1699*bf6873c5SCy Schubert         case 'l':
1700*bf6873c5SCy Schubert             list = optarg;
1701*bf6873c5SCy Schubert             break;
1702*bf6873c5SCy Schubert         case 'o':
1703*bf6873c5SCy Schubert             single = 1;
1704*bf6873c5SCy Schubert             break;
1705*bf6873c5SCy Schubert         case 's':
1706*bf6873c5SCy Schubert             source = optarg;
1707*bf6873c5SCy Schubert             break;
1708*bf6873c5SCy Schubert         case 'v':
1709*bf6873c5SCy Schubert             verbose = VERBOSE;
1710*bf6873c5SCy Schubert             break;
1711*bf6873c5SCy Schubert         default:
1712*bf6873c5SCy Schubert             exit(1);
1713*bf6873c5SCy Schubert         }
1714*bf6873c5SCy Schubert     }
1715*bf6873c5SCy Schubert     argv += optind;
1716*bf6873c5SCy Schubert     argc -= optind;
1717*bf6873c5SCy Schubert     if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) {
1718*bf6873c5SCy Schubert         fprintf(stderr, usage_message, program, program, program, usage_extra);
1719*bf6873c5SCy Schubert         exit(1);
1720*bf6873c5SCy Schubert     }
1721*bf6873c5SCy Schubert 
1722*bf6873c5SCy Schubert     /*
1723*bf6873c5SCy Schubert      * If C_TAP_VERBOSE is set in the environment, that also turns on verbose
1724*bf6873c5SCy Schubert      * mode.
1725*bf6873c5SCy Schubert      */
1726*bf6873c5SCy Schubert     if (getenv("C_TAP_VERBOSE") != NULL)
1727*bf6873c5SCy Schubert         verbose = VERBOSE;
1728*bf6873c5SCy Schubert 
1729*bf6873c5SCy Schubert     /*
1730*bf6873c5SCy Schubert      * Set C_TAP_SOURCE and C_TAP_BUILD environment variables.  Also set
1731*bf6873c5SCy Schubert      * SOURCE and BUILD for backward compatibility, although we're trying to
1732*bf6873c5SCy Schubert      * migrate to the ones with a C_TAP_* prefix.
1733*bf6873c5SCy Schubert      */
1734*bf6873c5SCy Schubert     if (source != NULL) {
1735*bf6873c5SCy Schubert         c_tap_source_env = concat("C_TAP_SOURCE=", source, (const char *) 0);
1736*bf6873c5SCy Schubert         if (putenv(c_tap_source_env) != 0)
1737*bf6873c5SCy Schubert             sysdie("cannot set C_TAP_SOURCE in the environment");
1738*bf6873c5SCy Schubert         source_env = concat("SOURCE=", source, (const char *) 0);
1739*bf6873c5SCy Schubert         if (putenv(source_env) != 0)
1740*bf6873c5SCy Schubert             sysdie("cannot set SOURCE in the environment");
1741*bf6873c5SCy Schubert     }
1742*bf6873c5SCy Schubert     if (build != NULL) {
1743*bf6873c5SCy Schubert         c_tap_build_env = concat("C_TAP_BUILD=", build, (const char *) 0);
1744*bf6873c5SCy Schubert         if (putenv(c_tap_build_env) != 0)
1745*bf6873c5SCy Schubert             sysdie("cannot set C_TAP_BUILD in the environment");
1746*bf6873c5SCy Schubert         build_env = concat("BUILD=", build, (const char *) 0);
1747*bf6873c5SCy Schubert         if (putenv(build_env) != 0)
1748*bf6873c5SCy Schubert             sysdie("cannot set BUILD in the environment");
1749*bf6873c5SCy Schubert     }
1750*bf6873c5SCy Schubert 
1751*bf6873c5SCy Schubert     /* Run the tests as instructed. */
1752*bf6873c5SCy Schubert     if (single)
1753*bf6873c5SCy Schubert         test_single(argv[0], source, build);
1754*bf6873c5SCy Schubert     else if (list != NULL) {
1755*bf6873c5SCy Schubert         shortlist = strrchr(list, '/');
1756*bf6873c5SCy Schubert         if (shortlist == NULL)
1757*bf6873c5SCy Schubert             shortlist = list;
1758*bf6873c5SCy Schubert         else
1759*bf6873c5SCy Schubert             shortlist++;
1760*bf6873c5SCy Schubert         printf(banner, shortlist);
1761*bf6873c5SCy Schubert         tests = read_test_list(list, source, build);
1762*bf6873c5SCy Schubert         status = test_batch(tests, verbose) ? 0 : 1;
1763*bf6873c5SCy Schubert     } else {
1764*bf6873c5SCy Schubert         tests = build_test_list(argv, argc, source, build);
1765*bf6873c5SCy Schubert         status = test_batch(tests, verbose) ? 0 : 1;
1766*bf6873c5SCy Schubert     }
1767*bf6873c5SCy Schubert 
1768*bf6873c5SCy Schubert     /* For valgrind cleanliness, free all our memory. */
1769*bf6873c5SCy Schubert     if (source_env != NULL) {
1770*bf6873c5SCy Schubert         putenv((char *) "C_TAP_SOURCE=");
1771*bf6873c5SCy Schubert         putenv((char *) "SOURCE=");
1772*bf6873c5SCy Schubert         free(c_tap_source_env);
1773*bf6873c5SCy Schubert         free(source_env);
1774*bf6873c5SCy Schubert     }
1775*bf6873c5SCy Schubert     if (build_env != NULL) {
1776*bf6873c5SCy Schubert         putenv((char *) "C_TAP_BUILD=");
1777*bf6873c5SCy Schubert         putenv((char *) "BUILD=");
1778*bf6873c5SCy Schubert         free(c_tap_build_env);
1779*bf6873c5SCy Schubert         free(build_env);
1780*bf6873c5SCy Schubert     }
1781*bf6873c5SCy Schubert     exit(status);
1782*bf6873c5SCy Schubert }
1783