"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.8.3/tests/tap/runtests.c" (16 Jul 2019, 43818 Bytes) of package /linux/misc/dns/knot-2.8.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * Run a set of tests, reporting results.
    3  *
    4  * Usage:
    5  *
    6  *      runtests [-b <build-dir>] [-s <source-dir>] <test-list>
    7  *      runtests -o [-b <build-dir>] [-s <source-dir>] <test>
    8  *
    9  * In the first case, expects a list of executables located in the given file,
   10  * one line per executable.  For each one, runs it as part of a test suite,
   11  * reporting results.  Test output should start with a line containing the
   12  * number of tests (numbered from 1 to this number), optionally preceded by
   13  * "1..", although that line may be given anywhere in the output.  Each
   14  * additional line should be in the following format:
   15  *
   16  *      ok <number>
   17  *      not ok <number>
   18  *      ok <number> # skip
   19  *      not ok <number> # todo
   20  *
   21  * where <number> is the number of the test.  An optional comment is permitted
   22  * after the number if preceded by whitespace.  ok indicates success, not ok
   23  * indicates failure.  "# skip" and "# todo" are a special cases of a comment,
   24  * and must start with exactly that formatting.  They indicate the test was
   25  * skipped for some reason (maybe because it doesn't apply to this platform)
   26  * or is testing something known to currently fail.  The text following either
   27  * "# skip" or "# todo" and whitespace is the reason.
   28  *
   29  * As a special case, the first line of the output may be in the form:
   30  *
   31  *      1..0 # skip some reason
   32  *
   33  * which indicates that this entire test case should be skipped and gives a
   34  * reason.
   35  *
   36  * Any other lines are ignored, although for compliance with the TAP protocol
   37  * all lines other than the ones in the above format should be sent to
   38  * standard error rather than standard output and start with #.
   39  *
   40  * This is a subset of TAP as documented in Test::Harness::TAP or
   41  * TAP::Parser::Grammar, which comes with Perl.
   42  *
   43  * If the -o option is given, instead run a single test and display all of its
   44  * output.  This is intended for use with failing tests so that the person
   45  * running the test suite can get more details about what failed.
   46  *
   47  * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP
   48  * Harness will export those values in the environment so that tests can find
   49  * the source and build directory and will look for tests under both
   50  * directories.  These paths can also be set with the -b and -s command-line
   51  * options, which will override anything set at build time.
   52  *
   53  * Any bug reports, bug fixes, and improvements are very much welcome and
   54  * should be sent to the e-mail address below.  This program is part of C TAP
   55  * Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
   56  *
   57  * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011
   58  *     Russ Allbery <rra@stanford.edu>
   59  *
   60  * Permission is hereby granted, free of charge, to any person obtaining a
   61  * copy of this software and associated documentation files (the "Software"),
   62  * to deal in the Software without restriction, including without limitation
   63  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   64  * and/or sell copies of the Software, and to permit persons to whom the
   65  * Software is furnished to do so, subject to the following conditions:
   66  *
   67  * The above copyright notice and this permission notice shall be included in
   68  * all copies or substantial portions of the Software.
   69  *
   70  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   71  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   72  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   73  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   74  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   75  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   76  * DEALINGS IN THE SOFTWARE.
   77 */
   78 
   79 /* Required for fdopen(), getopt(), and putenv(). */
   80 #if defined(__STRICT_ANSI__) || defined(PEDANTIC)
   81 # ifndef _XOPEN_SOURCE
   82 #  define _XOPEN_SOURCE 500
   83 # endif
   84 #endif
   85 
   86 #include <ctype.h>
   87 #include <errno.h>
   88 #include <fcntl.h>
   89 #include <stdarg.h>
   90 #include <stddef.h>
   91 #include <stdio.h>
   92 #include <stdlib.h>
   93 #include <string.h>
   94 #include <strings.h>
   95 #include <sys/stat.h>
   96 #include <sys/time.h>
   97 #include <sys/types.h>
   98 #include <sys/wait.h>
   99 #include <time.h>
  100 #include <unistd.h>
  101 
  102 /* sys/time.h must be included before sys/resource.h on some platforms. */
  103 #include <sys/resource.h>
  104 
  105 /* AIX doesn't have WCOREDUMP. */
  106 #ifndef WCOREDUMP
  107 # define WCOREDUMP(status) ((unsigned)(status) & 0x80)
  108 #endif
  109 
  110 /*
  111  * Used for iterating through arrays.  Returns the number of elements in the
  112  * array (useful for a < upper bound in a for loop).
  113  */
  114 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
  115 
  116 /*
  117  * The source and build versions of the tests directory.  This is used to set
  118  * the SOURCE and BUILD environment variables and find test programs, if set.
  119  * Normally, this should be set as part of the build process to the test
  120  * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively.
  121  */
  122 #ifndef SOURCE
  123 # define SOURCE NULL
  124 #endif
  125 #ifndef BUILD
  126 # define BUILD NULL
  127 #endif
  128 
  129 /* Test status codes. */
  130 enum test_status {
  131     TEST_FAIL,
  132     TEST_PASS,
  133     TEST_SKIP,
  134     TEST_INVALID
  135 };
  136 
  137 /* Indicates the state of our plan. */
  138 enum plan_status {
  139     PLAN_INIT,                  /* Nothing seen yet. */
  140     PLAN_FIRST,                 /* Plan seen before any tests. */
  141     PLAN_PENDING,               /* Test seen and no plan yet. */
  142     PLAN_FINAL                  /* Plan seen after some tests. */
  143 };
  144 
  145 /* Error exit statuses for test processes. */
  146 #define CHILDERR_DUP    100     /* Couldn't redirect stderr or stdout. */
  147 #define CHILDERR_EXEC   101     /* Couldn't exec child process. */
  148 #define CHILDERR_STDERR 102     /* Couldn't open stderr file. */
  149 
  150 /* Structure to hold data for a set of tests. */
  151 struct testset {
  152     char *file;                 /* The file name of the test. */
  153     char *path;                 /* The path to the test program. */
  154     enum plan_status plan;      /* The status of our plan. */
  155     unsigned long count;        /* Expected count of tests. */
  156     unsigned long current;      /* The last seen test number. */
  157     unsigned int length;        /* The length of the last status message. */
  158     unsigned long passed;       /* Count of passing tests. */
  159     unsigned long failed;       /* Count of failing lists. */
  160     unsigned long skipped;      /* Count of skipped tests (passed). */
  161     unsigned long allocated;    /* The size of the results table. */
  162     enum test_status *results;  /* Table of results by test number. */
  163     unsigned int aborted;       /* Whether the set was aborted. */
  164     int reported;               /* Whether the results were reported. */
  165     int status;                 /* The exit status of the test. */
  166     unsigned int all_skipped;   /* Whether all tests were skipped. */
  167     char *reason;               /* Why all tests were skipped. */
  168 };
  169 
  170 /* Structure to hold a linked list of test sets. */
  171 struct testlist {
  172     struct testset *ts;
  173     struct testlist *next;
  174 };
  175 
  176 /*
  177  * Usage message.  Should be used as a printf format with four arguments: the
  178  * path to runtests, given three times, and the usage_description.  This is
  179  * split into variables to satisfy the pedantic ISO C90 limit on strings.
  180  */
  181 static const char usage_message[] = "\
  182 Usage: %s [-b <build-dir>] [-s <source-dir>] <test> ...\n\
  183        %s [-b <build-dir>] [-s <source-dir>] -l <test-list> -L <log-file>\n\
  184        %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\
  185 \n%s";
  186 static const char usage_extra[] = "\
  187 Options:\n\
  188     -b <build-dir>      Set the build directory to <build-dir>\n\
  189     -l <list>           Take the list of tests to run from <test-list>\n\
  190     -o                  Run a single test rather than a list of tests\n\
  191     -s <source-dir>     Set the source directory to <source-dir>\n\
  192     -L <log-file>       Set log file for a list of tests\n\
  193 \n\
  194 runtests normally runs each test listed on the command line.  With the -l\n\
  195 option, it instead runs every test listed in a file.  With the -o option,\n\
  196 it instead runs a single test and shows its complete output.\n";
  197 
  198 /*
  199  * Header used for test output.  %s is replaced by the file name of the list
  200  * of tests.
  201  */
  202 static const char banner[] = "\n\
  203 Running all tests listed in %s.  If any tests fail, run the failing\n\
  204 test program with runtests -o to see more details.\n\n";
  205 
  206 /* Header for reports of failed tests. */
  207 static const char header[] = "\n\
  208 Failed Set                 Fail/Total (%) Skip Stat  Failing Tests\n\
  209 -------------------------- -------------- ---- ----  ------------------------";
  210 
  211 /* Include the file name and line number in malloc failures. */
  212 #define xcalloc(n, size)  x_calloc((n), (size), __FILE__, __LINE__)
  213 #define xmalloc(size)     x_malloc((size), __FILE__, __LINE__)
  214 #define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
  215 #define xstrdup(p)        x_strdup((p), __FILE__, __LINE__)
  216 
  217 /*
  218  * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
  219  * could you use the __format__ form of the attributes, which is what we use
  220  * (to avoid confusion with other macros).
  221  */
  222 #ifndef __attribute__
  223 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
  224 #  define __attribute__(spec)   /* empty */
  225 # endif
  226 #endif
  227 
  228 /*
  229  * We use __alloc_size__, but it was only available in fairly recent versions
  230  * of GCC.  Suppress warnings about the unknown attribute if GCC is too old.
  231  * We know that we're GCC at this point, so we can use the GCC variadic macro
  232  * extension, which will still work with versions of GCC too old to have C99
  233  * variadic macro support.
  234  */
  235 #if !defined(__attribute__) && !defined(__alloc_size__)
  236 # if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
  237 #  define __alloc_size__(spec, args...) /* empty */
  238 # endif
  239 #endif
  240 
  241 /*
  242  * LLVM and Clang pretend to be GCC but don't support all of the __attribute__
  243  * settings that GCC does.  For them, suppress warnings about unknown
  244  * attributes on declarations.  This unfortunately will affect the entire
  245  * compilation context, but there's no push and pop available.
  246  */
  247 #if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
  248 # pragma GCC diagnostic ignored "-Wattributes"
  249 #endif
  250 
  251 /* Declare internal functions that benefit from compiler attributes. */
  252 static void sysdie(const char *, ...)
  253     __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
  254 static void *x_calloc(size_t, size_t, const char *, int)
  255     __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__));
  256 static void *x_malloc(size_t, const char *, int)
  257     __attribute__((__alloc_size__(1), __malloc__, __nonnull__));
  258 static void *x_realloc(void *, size_t, const char *, int)
  259     __attribute__((__alloc_size__(2), __malloc__, __nonnull__(3)));
  260 static char *x_strdup(const char *, const char *, int)
  261     __attribute__((__malloc__, __nonnull__));
  262 
  263 /*
  264  * Report a fatal error, including the results of strerror, and exit.
  265  */
  266 static void
  267 sysdie(const char *format, ...)
  268 {
  269     int oerrno;
  270     va_list args;
  271 
  272     oerrno = errno;
  273     fflush(stdout);
  274     fprintf(stderr, "runtests: ");
  275     va_start(args, format);
  276     vfprintf(stderr, format, args);
  277     va_end(args);
  278     fprintf(stderr, ": %s\n", strerror(oerrno));
  279     exit(1);
  280 }
  281 
  282 /*
  283  * Allocate zeroed memory, reporting a fatal error and exiting on failure.
  284  */
  285 static void *
  286 x_calloc(size_t n, size_t size, const char *file, int line)
  287 {
  288     void *p;
  289 
  290     n = (n > 0) ? n : 1;
  291     size = (size > 0) ? size : 1;
  292     p = calloc(n, size);
  293     if (p == NULL)
  294         sysdie("failed to calloc %lu bytes at %s line %d",
  295                (unsigned long) size, file, line);
  296     return p;
  297 }
  298 
  299 /*
  300  * Allocate memory, reporting a fatal error and exiting on failure.
  301  */
  302 static void *
  303 x_malloc(size_t size, const char *file, int line)
  304 {
  305     void *p;
  306 
  307     p = malloc(size);
  308     if (p == NULL)
  309         sysdie("failed to malloc %lu bytes at %s line %d",
  310                (unsigned long) size, file, line);
  311     return p;
  312 }
  313 
  314 /*
  315  * Reallocate memory, reporting a fatal error and exiting on failure.
  316  */
  317 static void *
  318 x_realloc(void *p, size_t size, const char *file, int line)
  319 {
  320     p = realloc(p, size);
  321     if (p == NULL)
  322         sysdie("failed to realloc %lu bytes at %s line %d",
  323                (unsigned long) size, file, line);
  324     return p;
  325 }
  326 
  327 /*
  328  * Copy a string, reporting a fatal error and exiting on failure.
  329  */
  330 static char *
  331 x_strdup(const char *s, const char *file, int line)
  332 {
  333     char *p;
  334     size_t len;
  335 
  336     len = strlen(s) + 1;
  337     p = malloc(len);
  338     if (p == NULL)
  339         sysdie("failed to strdup %lu bytes at %s line %d",
  340                (unsigned long) len, file, line);
  341     memcpy(p, s, len);
  342     return p;
  343 }
  344 
  345 /*
  346  * Given a struct timeval, return the number of seconds it represents as a
  347  * double.  Use difftime() to convert a time_t to a double.
  348  */
  349 static double
  350 tv_seconds(const struct timeval *tv)
  351 {
  352     return difftime(tv->tv_sec, 0) + tv->tv_usec * 1e-6;
  353 }
  354 
  355 /*
  356  * Given two struct timevals, return the difference in seconds.
  357  */
  358 static double
  359 tv_diff(const struct timeval *tv1, const struct timeval *tv0)
  360 {
  361     return tv_seconds(tv1) - tv_seconds(tv0);
  362 }
  363 
  364 /*
  365  * Given two struct timevals, return the sum in seconds as a double.
  366  */
  367 static double
  368 tv_sum(const struct timeval *tv1, const struct timeval *tv2)
  369 {
  370     return tv_seconds(tv1) + tv_seconds(tv2);
  371 }
  372 
  373 /*
  374  * Given a pointer to a string, skip any leading whitespace and return a
  375  * pointer to the first non-whitespace character.
  376  */
  377 static const char *
  378 skip_whitespace(const char *p)
  379 {
  380     while (isspace((unsigned char)(*p)))
  381         p++;
  382     return p;
  383 }
  384 
  385 /*
  386  * Start a program, connecting its stdout to a pipe on our end and its stderr
  387  * to /dev/null, and storing the file descriptor to read from in the two
  388  * argument.  Returns the PID of the new process.  Errors are fatal.
  389  */
  390 static pid_t
  391 test_start(const char *path, int *fd)
  392 {
  393     int fds[2], errfd;
  394     pid_t child;
  395 
  396     if (pipe(fds) == -1) {
  397         puts("ABORTED");
  398         fflush(stdout);
  399         sysdie("can't create pipe");
  400     }
  401     child = fork();
  402     if (child == (pid_t) -1) {
  403         puts("ABORTED");
  404         fflush(stdout);
  405         sysdie("can't fork");
  406     } else if (child == 0) {
  407         /* In child.  Set up our stdout and stderr. */
  408         errfd = open("/dev/null", O_WRONLY);
  409         if (errfd < 0)
  410             _exit(CHILDERR_STDERR);
  411         if (dup2(errfd, 2) == -1)
  412             _exit(CHILDERR_DUP);
  413         close(fds[0]);
  414         if (dup2(fds[1], 1) == -1)
  415             _exit(CHILDERR_DUP);
  416 
  417         /* Now, exec our process. */
  418         if (execl(path, path, (char *) 0) == -1)
  419             _exit(CHILDERR_EXEC);
  420     } else {
  421         /* In parent.  Close the extra file descriptor. */
  422         close(fds[1]);
  423     }
  424     *fd = fds[0];
  425     return child;
  426 }
  427 
  428 /*
  429  * Back up over the output saying what test we were executing.
  430  */
  431 static void
  432 test_backspace(struct testset *ts)
  433 {
  434     unsigned int i;
  435 
  436     if (!isatty(STDOUT_FILENO))
  437         return;
  438     for (i = 0; i < ts->length; i++)
  439         putchar('\b');
  440     for (i = 0; i < ts->length; i++)
  441         putchar(' ');
  442     for (i = 0; i < ts->length; i++)
  443         putchar('\b');
  444     ts->length = 0;
  445 }
  446 
  447 /*
  448  * Read the plan line of test output, which should contain the range of test
  449  * numbers.  We may initialize the testset structure here if we haven't yet
  450  * seen a test.  Return true if initialization succeeded and the test should
  451  * continue, false otherwise.
  452  */
  453 static int
  454 test_plan(const char *line, struct testset *ts)
  455 {
  456     unsigned long i;
  457     long n;
  458 
  459     /*
  460      * Accept a plan without the leading 1.. for compatibility with older
  461      * versions of runtests.  This will only be allowed if we've not yet seen
  462      * a test result.
  463      */
  464     line = skip_whitespace(line);
  465     if (strncmp(line, "1..", 3) == 0)
  466         line += 3;
  467 
  468     /*
  469      * Get the count, check it for validity, and initialize the struct.  If we
  470      * have something of the form "1..0 # skip foo", the whole file was
  471      * skipped; record that.  If we do skip the whole file, zero out all of
  472      * our statistics, since they're no longer relevant.  strtol is called
  473      * with a second argument to advance the line pointer past the count to
  474      * make it simpler to detect the # skip case.
  475      */
  476     n = strtol(line, (char **) &line, 10);
  477     if (n == 0) {
  478         line = skip_whitespace(line);
  479         if (*line == '#') {
  480             line = skip_whitespace(line + 1);
  481             if (strncasecmp(line, "skip", 4) == 0) {
  482                 line = skip_whitespace(line + 4);
  483                 if (*line != '\0') {
  484                     ts->reason = xstrdup(line);
  485                     ts->reason[strlen(ts->reason) - 1] = '\0';
  486                 }
  487                 ts->all_skipped = 1;
  488                 ts->aborted = 1;
  489                 ts->count = 0;
  490                 ts->passed = 0;
  491                 ts->skipped = 0;
  492                 ts->failed = 0;
  493                 return 0;
  494             }
  495         }
  496     }
  497     if (n <= 0) {
  498         puts("ABORTED (invalid test count)");
  499         ts->aborted = 1;
  500         ts->reported = 1;
  501         return 0;
  502     }
  503     if (ts->plan == PLAN_INIT && ts->allocated == 0) {
  504         ts->count = n;
  505         ts->allocated = n;
  506         ts->plan = PLAN_FIRST;
  507         ts->results = xmalloc(ts->count * sizeof(enum test_status));
  508         for (i = 0; i < ts->count; i++)
  509             ts->results[i] = TEST_INVALID;
  510     } else if (ts->plan == PLAN_PENDING) {
  511         if ((unsigned long) n < ts->count) {
  512             test_backspace(ts);
  513             printf("ABORTED (invalid test number %lu)\n", ts->count);
  514             ts->aborted = 1;
  515             ts->reported = 1;
  516             return 0;
  517         }
  518         ts->count = n;
  519         if ((unsigned long) n > ts->allocated) {
  520             ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
  521             for (i = ts->allocated; i < ts->count; i++)
  522                 ts->results[i] = TEST_INVALID;
  523             ts->allocated = n;
  524         }
  525         ts->plan = PLAN_FINAL;
  526     }
  527     return 1;
  528 }
  529 
  530 /*
  531  * Given a single line of output from a test, parse it and return the success
  532  * status of that test.  Anything printed to stdout not matching the form
  533  * /^(not )?ok \d+/ is ignored.  Sets ts->current to the test number that just
  534  * reported status.
  535  */
  536 static void
  537 test_checkline(const char *line, struct testset *ts)
  538 {
  539     enum test_status status = TEST_PASS;
  540     const char *bail;
  541     char *end;
  542     long number;
  543     unsigned long i, current;
  544     int outlen;
  545 
  546     /* Before anything, check for a test abort. */
  547     bail = strstr(line, "Bail out!");
  548     if (bail != NULL) {
  549         bail = skip_whitespace(bail + strlen("Bail out!"));
  550         if (*bail != '\0') {
  551             size_t length;
  552 
  553             length = strlen(bail);
  554             if (bail[length - 1] == '\n')
  555                 length--;
  556             test_backspace(ts);
  557             printf("ABORTED (%.*s)\n", (int) length, bail);
  558             ts->reported = 1;
  559         }
  560         ts->aborted = 1;
  561         return;
  562     }
  563 
  564     /*
  565      * If the given line isn't newline-terminated, it was too big for an
  566      * fgets(), which means ignore it.
  567      */
  568     if (line[strlen(line) - 1] != '\n')
  569         return;
  570 
  571     /* If the line begins with a hash mark, ignore it. */
  572     if (line[0] == '#')
  573         return;
  574 
  575     /* If we haven't yet seen a plan, look for one. */
  576     if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) {
  577         if (!test_plan(line, ts))
  578             return;
  579     } else if (strncmp(line, "1..", 3) == 0) {
  580         if (ts->plan == PLAN_PENDING) {
  581             if (!test_plan(line, ts))
  582                 return;
  583         } else {
  584             test_backspace(ts);
  585             puts("ABORTED (multiple plans)");
  586             ts->aborted = 1;
  587             ts->reported = 1;
  588             return;
  589         }
  590     }
  591 
  592     /* Parse the line, ignoring something we can't parse. */
  593     if (strncmp(line, "not ", 4) == 0) {
  594         status = TEST_FAIL;
  595         line += 4;
  596     }
  597     if (strncmp(line, "ok", 2) != 0)
  598         return;
  599     line = skip_whitespace(line + 2);
  600     errno = 0;
  601     number = strtol(line, &end, 10);
  602     if (errno != 0 || end == line)
  603         number = ts->current + 1;
  604     current = number;
  605     if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) {
  606         test_backspace(ts);
  607         printf("ABORTED (invalid test number %lu)\n", current);
  608         ts->aborted = 1;
  609         ts->reported = 1;
  610         return;
  611     }
  612 
  613     /* We have a valid test result.  Tweak the results array if needed. */
  614     if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) {
  615         ts->plan = PLAN_PENDING;
  616         if (current > ts->count)
  617             ts->count = current;
  618         if (current > ts->allocated) {
  619             unsigned long n;
  620 
  621             n = (ts->allocated == 0) ? 32 : ts->allocated * 2;
  622             if (n < current)
  623                 n = current;
  624             ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
  625             for (i = ts->allocated; i < n; i++)
  626                 ts->results[i] = TEST_INVALID;
  627             ts->allocated = n;
  628         }
  629     }
  630 
  631     /*
  632      * Handle directives.  We should probably do something more interesting
  633      * with unexpected passes of todo tests.
  634      */
  635     while (isdigit((unsigned char)(*line)))
  636         line++;
  637     line = skip_whitespace(line);
  638     if (*line == '#') {
  639         line = skip_whitespace(line + 1);
  640         if (strncasecmp(line, "skip", 4) == 0)
  641             status = TEST_SKIP;
  642         if (strncasecmp(line, "todo", 4) == 0)
  643             status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL;
  644     }
  645 
  646     /* Make sure that the test number is in range and not a duplicate. */
  647     if (ts->results[current - 1] != TEST_INVALID) {
  648         test_backspace(ts);
  649         printf("ABORTED (duplicate test number %lu)\n", current);
  650         ts->aborted = 1;
  651         ts->reported = 1;
  652         return;
  653     }
  654 
  655     /* Good results.  Increment our various counters. */
  656     switch (status) {
  657         case TEST_PASS: ts->passed++;   break;
  658         case TEST_FAIL: ts->failed++;   break;
  659         case TEST_SKIP: ts->skipped++;  break;
  660         case TEST_INVALID:              break;
  661     }
  662     ts->current = current;
  663     ts->results[current - 1] = status;
  664     if (isatty(STDOUT_FILENO)) {
  665         test_backspace(ts);
  666         if (ts->plan == PLAN_PENDING)
  667             outlen = printf("%lu/?", current);
  668         else
  669             outlen = printf("%lu/%lu", current, ts->count);
  670         ts->length = (outlen >= 0) ? outlen : 0;
  671         fflush(stdout);
  672     }
  673 }
  674 
  675 /*
  676  * Print out a range of test numbers, returning the number of characters it
  677  * took up.  Takes the first number, the last number, the number of characters
  678  * already printed on the line, and the limit of number of characters the line
  679  * can hold.  Add a comma and a space before the range if chars indicates that
  680  * something has already been printed on the line, and print ... instead if
  681  * chars plus the space needed would go over the limit (use a limit of 0 to
  682  * disable this).
  683  */
  684 static unsigned int
  685 test_print_range(unsigned long first, unsigned long last, unsigned int chars,
  686                  unsigned int limit)
  687 {
  688     unsigned int needed = 0;
  689     unsigned long n;
  690 
  691     for (n = first; n > 0; n /= 10)
  692         needed++;
  693     if (last > first) {
  694         for (n = last; n > 0; n /= 10)
  695             needed++;
  696         needed++;
  697     }
  698     if (chars > 0)
  699         needed += 2;
  700     if (limit > 0 && chars + needed > limit) {
  701         needed = 0;
  702         if (chars <= limit) {
  703             if (chars > 0) {
  704                 printf(", ");
  705                 needed += 2;
  706             }
  707             printf("...");
  708             needed += 3;
  709         }
  710     } else {
  711         if (chars > 0)
  712             printf(", ");
  713         if (last > first)
  714             printf("%lu-", first);
  715         printf("%lu", last);
  716     }
  717     return needed;
  718 }
  719 
  720 /*
  721  * Summarize a single test set.  The second argument is 0 if the set exited
  722  * cleanly, a positive integer representing the exit status if it exited
  723  * with a non-zero status, and a negative integer representing the signal
  724  * that terminated it if it was killed by a signal.
  725  */
  726 static void
  727 test_summarize(struct testset *ts, int status)
  728 {
  729     unsigned long i;
  730     unsigned long missing = 0;
  731     unsigned long failed = 0;
  732     unsigned long first = 0;
  733     unsigned long last = 0;
  734 
  735     if (ts->aborted) {
  736         fputs("ABORTED", stdout);
  737         if (ts->count > 0)
  738             printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped);
  739     } else {
  740         for (i = 0; i < ts->count; i++) {
  741             if (ts->results[i] == TEST_INVALID) {
  742                 if (missing == 0)
  743                     fputs("MISSED ", stdout);
  744                 if (first && i == last)
  745                     last = i + 1;
  746                 else {
  747                     if (first)
  748                         test_print_range(first, last, missing - 1, 0);
  749                     missing++;
  750                     first = i + 1;
  751                     last = i + 1;
  752                 }
  753             }
  754         }
  755         if (first)
  756             test_print_range(first, last, missing - 1, 0);
  757         first = 0;
  758         last = 0;
  759         for (i = 0; i < ts->count; i++) {
  760             if (ts->results[i] == TEST_FAIL) {
  761                 if (missing && !failed)
  762                     fputs("; ", stdout);
  763                 if (failed == 0)
  764                     fputs("FAILED ", stdout);
  765                 if (first && i == last)
  766                     last = i + 1;
  767                 else {
  768                     if (first)
  769                         test_print_range(first, last, failed - 1, 0);
  770                     failed++;
  771                     first = i + 1;
  772                     last = i + 1;
  773                 }
  774             }
  775         }
  776         if (first)
  777             test_print_range(first, last, failed - 1, 0);
  778         if (!missing && !failed) {
  779             fputs(!status ? "ok" : "dubious", stdout);
  780             if (ts->skipped > 0) {
  781                 if (ts->skipped == 1)
  782                     printf(" (skipped %lu test)", ts->skipped);
  783                 else
  784                     printf(" (skipped %lu tests)", ts->skipped);
  785             }
  786         }
  787     }
  788     if (status > 0)
  789         printf(" (exit status %d)", status);
  790     else if (status < 0)
  791         printf(" (killed by signal %d%s)", -status,
  792                WCOREDUMP(ts->status) ? ", core dumped" : "");
  793     putchar('\n');
  794 }
  795 
  796 /*
  797  * Given a test set, analyze the results, classify the exit status, handle a
  798  * few special error messages, and then pass it along to test_summarize() for
  799  * the regular output.  Returns true if the test set ran successfully and all
  800  * tests passed or were skipped, false otherwise.
  801  */
  802 static int
  803 test_analyze(struct testset *ts)
  804 {
  805     if (ts->reported)
  806         return 0;
  807     if (ts->all_skipped) {
  808         if (ts->reason == NULL)
  809             puts("skipped");
  810         else
  811             printf("skipped (%s)\n", ts->reason);
  812         return 1;
  813     } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) {
  814         switch (WEXITSTATUS(ts->status)) {
  815         case CHILDERR_DUP:
  816             if (!ts->reported)
  817                 puts("ABORTED (can't dup file descriptors)");
  818             break;
  819         case CHILDERR_EXEC:
  820             if (!ts->reported)
  821                 puts("ABORTED (execution failed -- not found?)");
  822             break;
  823         case CHILDERR_STDERR:
  824             if (!ts->reported)
  825                 puts("ABORTED (can't open /dev/null)");
  826             break;
  827         default:
  828             test_summarize(ts, WEXITSTATUS(ts->status));
  829             break;
  830         }
  831         return 0;
  832     } else if (WIFSIGNALED(ts->status)) {
  833         test_summarize(ts, -WTERMSIG(ts->status));
  834         return 0;
  835     } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) {
  836         puts("ABORTED (no valid test plan)");
  837         ts->aborted = 1;
  838         return 0;
  839     } else {
  840         test_summarize(ts, 0);
  841         return (ts->failed == 0);
  842     }
  843 }
  844 
  845 static void
  846 cond_fputs(const char *buffer, FILE *stream)
  847 {
  848     if (!stream) {
  849         return;
  850     }
  851 
  852     fputs(buffer, stream);
  853 }
  854 
  855 /*
  856  * Runs a single test set, accumulating and then reporting the results.
  857  * Returns true if the test set was successfully run and all tests passed,
  858  * false otherwise.
  859  */
  860 static int
  861 test_run(struct testset *ts, FILE *logfile)
  862 {
  863     pid_t testpid, child;
  864     int outfd, status;
  865     unsigned long i;
  866     FILE *output;
  867     char buffer[BUFSIZ];
  868 
  869     /* Run the test program. */
  870     testpid = test_start(ts->path, &outfd);
  871     output = fdopen(outfd, "r");
  872     if (!output) {
  873         puts("ABORTED");
  874         fflush(stdout);
  875         sysdie("fdopen failed");
  876     }
  877 
  878     /* Pass each line of output to test_checkline(). */
  879     while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) {
  880         cond_fputs(buffer, logfile);
  881         test_checkline(buffer, ts);
  882     }
  883     if (ferror(output) || ts->plan == PLAN_INIT)
  884         ts->aborted = 1;
  885     test_backspace(ts);
  886 
  887     /*
  888      * Consume the rest of the test output, close the output descriptor,
  889      * retrieve the exit status, and pass that information to test_analyze()
  890      * for eventual output.
  891      */
  892     while (fgets(buffer, sizeof(buffer), output))
  893         ;
  894     fclose(output);
  895     child = waitpid(testpid, &ts->status, 0);
  896     if (child == (pid_t) -1) {
  897         if (!ts->reported) {
  898             puts("ABORTED");
  899             fflush(stdout);
  900         }
  901         sysdie("waitpid for %u failed", (unsigned int) testpid);
  902     }
  903     if (ts->all_skipped)
  904         ts->aborted = 0;
  905     if (WEXITSTATUS(ts->status) > 0)
  906         ts->failed++;
  907     status = test_analyze(ts);
  908 
  909     /* Convert missing tests to failed tests. */
  910     for (i = 0; i < ts->count; i++) {
  911         if (ts->results[i] == TEST_INVALID) {
  912             ts->failed++;
  913             ts->results[i] = TEST_FAIL;
  914             status = 0;
  915         }
  916     }
  917     return status;
  918 }
  919 
  920 /* Summarize a list of test failures. */
  921 static void
  922 test_fail_summary(const struct testlist *fails)
  923 {
  924     struct testset *ts;
  925     unsigned int chars;
  926     unsigned long i, first, last, total;
  927 
  928     puts(header);
  929 
  930     /* Failed Set                 Fail/Total (%) Skip Stat  Failing (25)
  931        -------------------------- -------------- ---- ----  -------------- */
  932     for (; fails; fails = fails->next) {
  933         ts = fails->ts;
  934         total = ts->count - ts->skipped;
  935         printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed,
  936                total, total ? (ts->failed * 100.0) / total : 0,
  937                ts->skipped);
  938         if (WIFEXITED(ts->status))
  939             printf("%4d  ", WEXITSTATUS(ts->status));
  940         else
  941             printf("  --  ");
  942         if (ts->aborted) {
  943             puts("aborted");
  944             continue;
  945         }
  946         chars = 0;
  947         first = 0;
  948         last = 0;
  949         for (i = 0; i < ts->count; i++) {
  950             if (ts->results[i] == TEST_FAIL) {
  951                 if (first != 0 && i == last)
  952                     last = i + 1;
  953                 else {
  954                     if (first != 0)
  955                         chars += test_print_range(first, last, chars, 19);
  956                     first = i + 1;
  957                     last = i + 1;
  958                 }
  959             }
  960         }
  961         if (first != 0)
  962             test_print_range(first, last, chars, 19);
  963         putchar('\n');
  964     }
  965 }
  966 
  967 /*
  968  * Check whether a given file path is a valid test.  Currently, this checks
  969  * whether it is executable and is a regular file.  Returns true or false.
  970  */
  971 static int
  972 is_valid_test(const char *path)
  973 {
  974     struct stat st;
  975 
  976     if (access(path, X_OK) < 0)
  977         return 0;
  978     if (stat(path, &st) < 0)
  979         return 0;
  980     if (!S_ISREG(st.st_mode))
  981         return 0;
  982     return 1;
  983 }
  984 
  985 /*
  986  * Given the name of a test, a pointer to the testset struct, and the source
  987  * and build directories, find the test.  We try first relative to the current
  988  * directory, then in the build directory (if not NULL), then in the source
  989  * directory.  In each of those directories, we first try a "-t" extension and
  990  * then a ".t" extension.  When we find an executable program, we return the
  991  * path to that program.  If none of those paths are executable, just fill in
  992  * the name of the test as is.
  993  *
  994  * The caller is responsible for freeing the path member of the testset
  995  * struct.
  996  */
  997 static char *
  998 find_test(const char *name, const char *source, const char *build)
  999 {
 1000     char *path;
 1001     const char *bases[3], *suffix, *base;
 1002     unsigned int i, j;
 1003     const char *suffixes[3] = { "-t", ".t", "" };
 1004 
 1005     /* Possible base directories. */
 1006     bases[0] = ".";
 1007     bases[1] = build;
 1008     bases[2] = source;
 1009 
 1010     /* Try each suffix with each base. */
 1011     for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
 1012         suffix = suffixes[i];
 1013         for (j = 0; j < ARRAY_SIZE(bases); j++) {
 1014             unsigned int path_len;
 1015 
 1016             base = bases[j];
 1017             if (base == NULL)
 1018                 continue;
 1019             path_len = strlen(base) + strlen(name) + strlen(suffix) + 2;
 1020             path = xmalloc(path_len);
 1021             snprintf(path, path_len, "%s/%s%s", base, name, suffix);
 1022             if (is_valid_test(path))
 1023                 return path;
 1024             free(path);
 1025             path = NULL;
 1026         }
 1027     }
 1028     if (path == NULL)
 1029         path = xstrdup(name);
 1030     return path;
 1031 }
 1032 
 1033 /*
 1034  * Read a list of tests from a file, returning the list of tests as a struct
 1035  * testlist.  Reports an error to standard error and exits if the list of
 1036  * tests cannot be read.
 1037  */
 1038 static struct testlist *
 1039 read_test_list(const char *filename)
 1040 {
 1041     FILE *file;
 1042     unsigned int line;
 1043     size_t length;
 1044     char buffer[BUFSIZ];
 1045     struct testlist *listhead, *current;
 1046 
 1047     /* Create the initial container list that will hold our results. */
 1048     listhead = xmalloc(sizeof(struct testlist));
 1049     listhead->ts = NULL;
 1050     listhead->next = NULL;
 1051     current = NULL;
 1052 
 1053     /*
 1054      * Open our file of tests to run and read it line by line, creating a new
 1055      * struct testlist and struct testset for each line.
 1056      */
 1057     file = fopen(filename, "r");
 1058     if (file == NULL)
 1059         sysdie("can't open %s", filename);
 1060     line = 0;
 1061     while (fgets(buffer, sizeof(buffer), file)) {
 1062         line++;
 1063         length = strlen(buffer) - 1;
 1064         if (buffer[length] != '\n') {
 1065             fprintf(stderr, "%s:%u: line too long\n", filename, line);
 1066             exit(1);
 1067         }
 1068         buffer[length] = '\0';
 1069         if (current == NULL)
 1070             current = listhead;
 1071         else {
 1072             current->next = xmalloc(sizeof(struct testlist));
 1073             current = current->next;
 1074             current->next = NULL;
 1075         }
 1076         current->ts = xcalloc(1, sizeof(struct testset));
 1077         current->ts->plan = PLAN_INIT;
 1078         current->ts->file = xstrdup(buffer);
 1079         current->ts->reason = NULL;
 1080     }
 1081     fclose(file);
 1082 
 1083     /* Return the results. */
 1084     return listhead;
 1085 }
 1086 
 1087 /*
 1088  * Build a list of tests from command line arguments.  Takes the argv and argc
 1089  * representing the command line arguments and returns a newly allocated test
 1090  * list.  The caller is responsible for freeing.
 1091  */
 1092 static struct testlist *
 1093 build_test_list(char *argv[], int argc)
 1094 {
 1095     int i;
 1096     struct testlist *listhead, *current;
 1097 
 1098     /* Create the initial container list that will hold our results. */
 1099     listhead = xmalloc(sizeof(struct testlist));
 1100     listhead->ts = NULL;
 1101     listhead->next = NULL;
 1102     current = NULL;
 1103 
 1104     /* Walk the list of arguments and create test sets for them. */
 1105     for (i = 0; i < argc; i++) {
 1106         if (current == NULL)
 1107             current = listhead;
 1108         else {
 1109             current->next = xmalloc(sizeof(struct testlist));
 1110             current = current->next;
 1111             current->next = NULL;
 1112         }
 1113         current->ts = xcalloc(1, sizeof(struct testset));
 1114         current->ts->plan = PLAN_INIT;
 1115         current->ts->file = xstrdup(argv[i]);
 1116         current->ts->reason = NULL;
 1117     }
 1118 
 1119     /* Return the results. */
 1120     return listhead;
 1121 }
 1122 
 1123 /* Free a struct testset. */
 1124 static void
 1125 free_testset(struct testset *ts)
 1126 {
 1127     free(ts->file);
 1128     free(ts->path);
 1129     free(ts->results);
 1130     if (ts->reason != NULL)
 1131         free(ts->reason);
 1132     free(ts);
 1133 }
 1134 
 1135 /*
 1136  * Run a batch of tests.  Takes two additional parameters: the root of the
 1137  * source directory and the root of the build directory.  Test programs will
 1138  * be first searched for in the current directory, then the build directory,
 1139  * then the source directory.  Returns true iff all tests passed, and always
 1140  * frees the test list that's passed in.
 1141  */
 1142 static int
 1143 test_batch(struct testlist *tests, const char *source, const char *build,
 1144            const char *logfile_name)
 1145 {
 1146     size_t length;
 1147     unsigned int i;
 1148     unsigned int longest = 0;
 1149     unsigned int count = 0;
 1150     struct testset *ts;
 1151     struct timeval start, end;
 1152     struct rusage stats;
 1153     struct testlist *failhead = NULL;
 1154     struct testlist *failtail = NULL;
 1155     struct testlist *current, *next;
 1156     int succeeded;
 1157     FILE *logfile = NULL;
 1158     unsigned long total = 0;
 1159     unsigned long passed = 0;
 1160     unsigned long skipped = 0;
 1161     unsigned long failed = 0;
 1162     unsigned long aborted = 0;
 1163 
 1164     /* Walk the list of tests to find the longest name. */
 1165     for (current = tests; current != NULL; current = current->next) {
 1166         length = strlen(current->ts->file);
 1167         if (length > longest)
 1168             longest = length;
 1169     }
 1170 
 1171     /*
 1172      * Add two to longest and round up to the nearest tab stop.  This is how
 1173      * wide the column for printing the current test name will be.
 1174      */
 1175     longest += 2;
 1176     if (longest % 8)
 1177         longest += 8 - (longest % 8);
 1178 
 1179     /* Start the wall clock timer. */
 1180     gettimeofday(&start, NULL);
 1181 
 1182     /* Open the log (soft error). */
 1183     if (logfile_name != NULL) {
 1184         logfile = fopen(logfile_name, "w+");
 1185         if (!logfile) {
 1186             fprintf(stderr, "Could not open the log file.\n");
 1187         }
 1188     }
 1189 
 1190     /* Now, plow through our tests again, running each one. */
 1191     for (current = tests; current != NULL; current = current->next) {
 1192         ts = current->ts;
 1193 
 1194         /* Print out the name of the test file. */
 1195         fputs(ts->file, stdout);
 1196         for (i = strlen(ts->file); i < longest; i++)
 1197             putchar('.');
 1198         if (isatty(STDOUT_FILENO))
 1199             fflush(stdout);
 1200 
 1201         /* Run the test. */
 1202         ts->path = find_test(ts->file, source, build);
 1203         succeeded = test_run(ts, logfile);
 1204         fflush(stdout);
 1205 
 1206         /* Record cumulative statistics. */
 1207         aborted += ts->aborted;
 1208         total += ts->count + ts->all_skipped;
 1209         passed += ts->passed;
 1210         skipped += ts->skipped + ts->all_skipped;
 1211         failed += ts->failed;
 1212         count++;
 1213 
 1214         /* If the test fails, we shuffle it over to the fail list. */
 1215         if (!succeeded) {
 1216             if (failhead == NULL) {
 1217                 failhead = xmalloc(sizeof(struct testset));
 1218                 failtail = failhead;
 1219             } else {
 1220                 failtail->next = xmalloc(sizeof(struct testset));
 1221                 failtail = failtail->next;
 1222             }
 1223             failtail->ts = ts;
 1224             failtail->next = NULL;
 1225         }
 1226     }
 1227     total -= skipped;
 1228 
 1229     /* Close the log. */
 1230     if (logfile) {
 1231         fclose(logfile);
 1232     }
 1233 
 1234     /* Stop the timer and get our child resource statistics. */
 1235     gettimeofday(&end, NULL);
 1236     getrusage(RUSAGE_CHILDREN, &stats);
 1237 
 1238     /* Summarize the failures and free the failure list. */
 1239     if (failhead != NULL) {
 1240         test_fail_summary(failhead);
 1241         while (failhead != NULL) {
 1242             next = failhead->next;
 1243             free(failhead);
 1244             failhead = next;
 1245         }
 1246     }
 1247 
 1248     /* Free the memory used by the test lists. */
 1249     while (tests != NULL) {
 1250         next = tests->next;
 1251         free_testset(tests->ts);
 1252         free(tests);
 1253         tests = next;
 1254     }
 1255 
 1256     /* Print out the final test summary. */
 1257     putchar('\n');
 1258     if (aborted != 0) {
 1259         if (aborted == 1)
 1260             printf("Aborted %lu test set", aborted);
 1261         else
 1262             printf("Aborted %lu test sets", aborted);
 1263         printf(", passed %lu/%lu tests", passed, total);
 1264     }
 1265     else if (failed == 0)
 1266         fputs("All tests successful", stdout);
 1267     else
 1268         printf("Failed %lu/%lu tests, %.2f%% okay", failed, total,
 1269                (total - failed) * 100.0 / total);
 1270     if (skipped != 0) {
 1271         if (skipped == 1)
 1272             printf(", %lu test skipped", skipped);
 1273         else
 1274             printf(", %lu tests skipped", skipped);
 1275     }
 1276     puts(".");
 1277     printf("Files=%u,  Tests=%lu", count, total);
 1278     printf(",  %.2f seconds", tv_diff(&end, &start));
 1279     printf(" (%.2f usr + %.2f sys = %.2f CPU)\n",
 1280            tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime),
 1281            tv_sum(&stats.ru_utime, &stats.ru_stime));
 1282     return (failed == 0 && aborted == 0);
 1283 }
 1284 
 1285 /*
 1286  * Run a single test case.  This involves just running the test program after
 1287  * having done the environment setup and finding the test program.
 1288  */
 1289 static void
 1290 test_single(const char *program, const char *source, const char *build)
 1291 {
 1292     char *path;
 1293 
 1294     path = find_test(program, source, build);
 1295     if (execl(path, path, (char *) 0) == -1)
 1296         sysdie("cannot exec %s", path);
 1297 }
 1298 
 1299 /*
 1300  * Main routine.  Set the SOURCE and BUILD environment variables and then,
 1301  * given a file listing tests, run each test listed.
 1302  */
 1303 int
 1304 main(int argc, char *argv[])
 1305 {
 1306     int option;
 1307     int status = 0;
 1308     int single = 0;
 1309     char *source_env = NULL;
 1310     char *build_env = NULL;
 1311     const char *shortlist;
 1312     const char *list = NULL;
 1313     const char *source = SOURCE;
 1314     const char *build = BUILD;
 1315     const char *logfile = NULL;
 1316     struct testlist *tests;
 1317 
 1318     while ((option = getopt(argc, argv, "b:hl:os:L:")) != EOF) {
 1319         switch (option) {
 1320         case 'b':
 1321             build = optarg;
 1322             break;
 1323         case 'h':
 1324             printf(usage_message, argv[0], argv[0], argv[0], usage_extra);
 1325             exit(0);
 1326             break;
 1327         case 'l':
 1328             list = optarg;
 1329             break;
 1330         case 'o':
 1331             single = 1;
 1332             break;
 1333         case 's':
 1334             source = optarg;
 1335             break;
 1336         case 'L':
 1337             logfile = optarg;
 1338             break;
 1339         default:
 1340             exit(1);
 1341         }
 1342     }
 1343     argv += optind;
 1344     argc -= optind;
 1345     if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) {
 1346         fprintf(stderr, usage_message, argv[0], argv[0], argv[0], usage_extra);
 1347         exit(1);
 1348     }
 1349 
 1350     /* Set SOURCE and BUILD environment variables. */
 1351     if (source != NULL) {
 1352         unsigned int len = strlen("SOURCE=") + strlen(source) + 1;
 1353         source_env = xmalloc(len);
 1354         snprintf(source_env, len, "SOURCE=%s", source);
 1355         if (putenv(source_env) != 0)
 1356             sysdie("cannot set SOURCE in the environment");
 1357     }
 1358     if (build != NULL) {
 1359         unsigned int len = strlen("BUILD=") + strlen(build) + 1;
 1360         build_env = xmalloc(len);
 1361         snprintf(build_env, len, "BUILD=%s", build);
 1362         if (putenv(build_env) != 0)
 1363             sysdie("cannot set BUILD in the environment");
 1364     }
 1365 
 1366     /* Run the tests as instructed. */
 1367     if (single)
 1368         test_single(argv[0], source, build);
 1369     else if (list != NULL) {
 1370         shortlist = strrchr(list, '/');
 1371         if (shortlist == NULL)
 1372             shortlist = list;
 1373         else
 1374             shortlist++;
 1375         printf(banner, shortlist);
 1376         tests = read_test_list(list);
 1377         status = test_batch(tests, source, build, logfile) ? 0 : 1;
 1378     } else {
 1379         tests = build_test_list(argv, argc);
 1380         status = test_batch(tests, source, build, logfile) ? 0 : 1;
 1381     }
 1382 
 1383     /* For valgrind cleanliness, free all our memory. */
 1384     if (source_env != NULL) {
 1385         putenv((char *) "SOURCE=");
 1386         free(source_env);
 1387     }
 1388     if (build_env != NULL) {
 1389         putenv((char *) "BUILD=");
 1390         free(build_env);
 1391     }
 1392     exit(status);
 1393 }