diff options
author | Russ Allbery <eagle@eyrie.org> | 2016-01-17 19:43:10 -0800 |
---|---|---|
committer | Russ Allbery <eagle@eyrie.org> | 2016-01-17 19:43:10 -0800 |
commit | 4b3f858ef567c0d12511e7fea2a56f08f2729635 (patch) | |
tree | e1cad1c445669045b47264c8957878352c7adc03 /tests/runtests.c | |
parent | 7856dc7cc5e16140c0084474fe54338f293bf77e (diff) | |
parent | 76f93739a8a933d98b87db9496861dae7de0ae1a (diff) |
Imported Upstream version 1.3upstream/1.3
Diffstat (limited to 'tests/runtests.c')
-rw-r--r-- | tests/runtests.c | 204 |
1 files changed, 144 insertions, 60 deletions
diff --git a/tests/runtests.c b/tests/runtests.c index a9d2373..42a73ea 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -3,15 +3,19 @@ * * Usage: * - * runtests [-b <build-dir>] [-s <source-dir>] <test-list> - * runtests -o [-b <build-dir>] [-s <source-dir>] <test> + * runtests [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list> + * runtests [-hv] [-b <build-dir>] [-s <source-dir>] <test> [<test> ...] + * runtests -o [-h] [-b <build-dir>] [-s <source-dir>] <test> * * In the first case, expects a list of executables located in the given file, * one line per executable. For each one, runs it as part of a test suite, - * reporting results. Test output should start with a line containing the - * number of tests (numbered from 1 to this number), optionally preceded by - * "1..", although that line may be given anywhere in the output. Each - * additional line should be in the following format: + * reporting results. In the second case, use the same infrastructure, but + * run only the tests listed on the command line. + * + * Test output should start with a line containing the number of tests + * (numbered from 1 to this number), optionally preceded by "1..", although + * that line may be given anywhere in the output. Each additional line should + * be in the following format: * * ok <number> * not ok <number> @@ -50,12 +54,16 @@ * directories. These paths can also be set with the -b and -s command-line * options, which will override anything set at build time. * + * If the -v option is given, or the C_TAP_VERBOSE environment variable is set, + * display the full output of each test as it runs rather than showing a + * summary of the results of each test. + * * Any bug reports, bug fixes, and improvements are very much welcome and * should be sent to the e-mail address below. This program is part of C TAP * Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>. * * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, - * 2014 Russ Allbery <eagle@eyrie.org> + * 2014, 2015 Russ Allbery <eagle@eyrie.org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -146,6 +154,12 @@ enum test_status { TEST_INVALID }; +/* Really, just a boolean, but this is more self-documenting. */ +enum test_verbose { + CONCISE = 0, + VERBOSE = 1 +}; + /* Indicates the state of our plan. */ enum plan_status { PLAN_INIT, /* Nothing seen yet. */ @@ -192,16 +206,18 @@ struct testlist { * split into variables to satisfy the pedantic ISO C90 limit on strings. */ static const char usage_message[] = "\ -Usage: %s [-b <build-dir>] [-s <source-dir>] <test> ...\n\ - %s [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\ - %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\ -\n%s"; -static const char usage_extra[] = "\ +Usage: %s [-hv] [-b <build-dir>] [-s <source-dir>] <test> ...\n\ + %s [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\ + %s -o [-h] [-b <build-dir>] [-s <source-dir>] <test>\n\ +\n\ Options:\n\ -b <build-dir> Set the build directory to <build-dir>\n\ +%s"; +static const char usage_extra[] = "\ -l <list> Take the list of tests to run from <test-list>\n\ -o Run a single test rather than a list of tests\n\ -s <source-dir> Set the source directory to <source-dir>\n\ + -v Show the full output of each test\n\ \n\ runtests normally runs each test listed on the command line. With the -l\n\ option, it instead runs every test listed in a file. With the -o option,\n\ @@ -246,8 +262,10 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ * variadic macro support. */ #if !defined(__attribute__) && !defined(__alloc_size__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) -# define __alloc_size__(spec, args...) /* empty */ +# if defined(__GNUC__) && !defined(__clang__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif # endif #endif @@ -591,13 +609,28 @@ resize_results(struct testset *ts, unsigned long n) /* + * Report an invalid test number and set the appropriate flags. Pulled into a + * separate function since we do this in several places. + */ +static void +invalid_test_number(struct testset *ts, long n, enum test_verbose verbose) +{ + if (!verbose) + test_backspace(ts); + printf("ABORTED (invalid test number %ld)\n", n); + ts->aborted = 1; + ts->reported = 1; +} + + +/* * Read the plan line of test output, which should contain the range of test * numbers. We may initialize the testset structure here if we haven't yet * seen a test. Return true if initialization succeeded and the test should * continue, false otherwise. */ static int -test_plan(const char *line, struct testset *ts) +test_plan(const char *line, struct testset *ts, enum test_verbose verbose) { long n; @@ -654,10 +687,7 @@ test_plan(const char *line, struct testset *ts) * range. */ if (ts->plan == PLAN_PENDING && (unsigned long) n < ts->count) { - test_backspace(ts); - printf("ABORTED (invalid test number %lu)\n", ts->count); - ts->aborted = 1; - ts->reported = 1; + invalid_test_number(ts, (long) ts->count, verbose); return 0; } @@ -665,8 +695,8 @@ test_plan(const char *line, struct testset *ts) * Otherwise, allocated or resize the results if needed and update count, * and then record that we've seen a plan. */ - resize_results(ts, n); - ts->count = n; + resize_results(ts, (unsigned long) n); + ts->count = (unsigned long) n; if (ts->plan == PLAN_INIT) ts->plan = PLAN_FIRST; else if (ts->plan == PLAN_PENDING) @@ -682,7 +712,8 @@ test_plan(const char *line, struct testset *ts) * reported status. */ static void -test_checkline(const char *line, struct testset *ts) +test_checkline(const char *line, struct testset *ts, + enum test_verbose verbose) { enum test_status status = TEST_PASS; const char *bail; @@ -701,7 +732,8 @@ test_checkline(const char *line, struct testset *ts) length = strlen(bail); if (bail[length - 1] == '\n') length--; - test_backspace(ts); + if (!verbose) + test_backspace(ts); printf("ABORTED (%.*s)\n", (int) length, bail); ts->reported = 1; } @@ -722,14 +754,15 @@ test_checkline(const char *line, struct testset *ts) /* If we haven't yet seen a plan, look for one. */ if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) { - if (!test_plan(line, ts)) + if (!test_plan(line, ts, verbose)) return; } else if (strncmp(line, "1..", 3) == 0) { if (ts->plan == PLAN_PENDING) { - if (!test_plan(line, ts)) + if (!test_plan(line, ts, verbose)) return; } else { - test_backspace(ts); + if (!verbose) + test_backspace(ts); puts("ABORTED (multiple plans)"); ts->aborted = 1; ts->reported = 1; @@ -748,13 +781,14 @@ test_checkline(const char *line, struct testset *ts) errno = 0; number = strtol(line, &end, 10); if (errno != 0 || end == line) - number = ts->current + 1; - current = number; - if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) { - test_backspace(ts); - printf("ABORTED (invalid test number %lu)\n", current); - ts->aborted = 1; - ts->reported = 1; + current = ts->current + 1; + else if (number <= 0) { + invalid_test_number(ts, number, verbose); + return; + } else + current = (unsigned long) number; + if (current > ts->count && ts->plan == PLAN_FIRST) { + invalid_test_number(ts, (long) current, verbose); return; } @@ -783,7 +817,8 @@ test_checkline(const char *line, struct testset *ts) /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { - test_backspace(ts); + if (!verbose) + test_backspace(ts); printf("ABORTED (duplicate test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; @@ -799,13 +834,13 @@ test_checkline(const char *line, struct testset *ts) } ts->current = current; ts->results[current - 1] = status; - if (isatty(STDOUT_FILENO)) { + if (!verbose && isatty(STDOUT_FILENO)) { test_backspace(ts); if (ts->plan == PLAN_PENDING) outlen = printf("%lu/?", current); else outlen = printf("%lu/%lu", current, ts->count); - ts->length = (outlen >= 0) ? outlen : 0; + ts->length = (outlen >= 0) ? (unsigned int) outlen : 0; fflush(stdout); } } @@ -821,7 +856,7 @@ test_checkline(const char *line, struct testset *ts) * disable this). */ static unsigned int -test_print_range(unsigned long first, unsigned long last, unsigned int chars, +test_print_range(unsigned long first, unsigned long last, unsigned long chars, unsigned int limit) { unsigned int needed = 0; @@ -991,7 +1026,7 @@ test_analyze(struct testset *ts) * false otherwise. */ static int -test_run(struct testset *ts) +test_run(struct testset *ts, enum test_verbose verbose) { pid_t testpid, child; int outfd, status; @@ -1008,12 +1043,19 @@ test_run(struct testset *ts) sysdie("fdopen failed"); } - /* Pass each line of output to test_checkline(). */ - while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) - test_checkline(buffer, ts); + /* + * Pass each line of output to test_checkline(), and print the line if + * verbosity is requested. + */ + while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) { + if (verbose) + printf("%s", buffer); + test_checkline(buffer, ts, verbose); + } if (ferror(output) || ts->plan == PLAN_INIT) ts->aborted = 1; - test_backspace(ts); + if (!verbose) + test_backspace(ts); /* * Consume the rest of the test output, close the output descriptor, @@ -1021,7 +1063,8 @@ test_run(struct testset *ts) * for eventual output. */ while (fgets(buffer, sizeof(buffer), output)) - ; + if (verbose) + printf("%s", buffer); fclose(output); child = waitpid(testpid, &ts->status, 0); if (child == (pid_t) -1) { @@ -1129,7 +1172,7 @@ is_valid_test(const char *path) static char * find_test(const char *name, const char *source, const char *build) { - char *path; + char *path = NULL; const char *bases[3], *suffix, *base; unsigned int i, j; const char *suffixes[3] = { "-t", ".t", "" }; @@ -1161,7 +1204,8 @@ find_test(const char *name, const char *source, const char *build) /* * Read a list of tests from a file, returning the list of tests as a struct - * testlist. Reports an error to standard error and exits if the list of + * testlist, or NULL if there were no tests (such as a file containing only + * comments). Reports an error to standard error and exits if the list of * tests cannot be read. */ static struct testlist * @@ -1171,6 +1215,7 @@ read_test_list(const char *filename) unsigned int line; size_t length; char buffer[BUFSIZ]; + const char *testname; struct testlist *listhead, *current; /* Create the initial container list that will hold our results. */ @@ -1193,6 +1238,15 @@ read_test_list(const char *filename) exit(1); } buffer[length] = '\0'; + + /* Skip comments, leading spaces, and blank lines. */ + testname = skip_whitespace(buffer); + if (strlen(testname) == 0) + continue; + if (testname[0] == '#') + continue; + + /* Allocate the new testset structure. */ if (current == NULL) current = listhead; else { @@ -1201,10 +1255,16 @@ read_test_list(const char *filename) } current->ts = xcalloc(1, sizeof(struct testset)); current->ts->plan = PLAN_INIT; - current->ts->file = xstrdup(buffer); + current->ts->file = xstrdup(testname); } fclose(file); + /* If there were no tests, current is still NULL. */ + if (current == NULL) { + free(listhead); + return NULL; + } + /* Return the results. */ return listhead; } @@ -1213,7 +1273,8 @@ read_test_list(const char *filename) /* * Build a list of tests from command line arguments. Takes the argv and argc * representing the command line arguments and returns a newly allocated test - * list. The caller is responsible for freeing. + * list, or NULL if there were no tests. The caller is responsible for + * freeing. */ static struct testlist * build_test_list(char *argv[], int argc) @@ -1238,6 +1299,12 @@ build_test_list(char *argv[], int argc) current->ts->file = xstrdup(argv[i]); } + /* If there were no tests, current is still NULL. */ + if (current == NULL) { + free(listhead); + return NULL; + } + /* Return the results. */ return listhead; } @@ -1263,11 +1330,11 @@ free_testset(struct testset *ts) * frees the test list that's passed in. */ static int -test_batch(struct testlist *tests, const char *source, const char *build) +test_batch(struct testlist *tests, const char *source, const char *build, + enum test_verbose verbose) { - size_t length; - unsigned int i; - unsigned int longest = 0; + size_t length, i; + size_t longest = 0; unsigned int count = 0; struct testset *ts; struct timeval start, end; @@ -1306,15 +1373,20 @@ test_batch(struct testlist *tests, const char *source, const char *build) /* Print out the name of the test file. */ fputs(ts->file, stdout); - for (i = strlen(ts->file); i < longest; i++) - putchar('.'); + if (verbose) + fputs("\n\n", stdout); + else + for (i = strlen(ts->file); i < longest; i++) + putchar('.'); if (isatty(STDOUT_FILENO)) fflush(stdout); /* Run the test. */ ts->path = find_test(ts->file, source, build); - succeeded = test_run(ts); + succeeded = test_run(ts, verbose); fflush(stdout); + if (verbose) + putchar('\n'); /* Record cumulative statistics. */ aborted += ts->aborted; @@ -1416,23 +1488,25 @@ main(int argc, char *argv[]) int option; int status = 0; int single = 0; + enum test_verbose verbose = CONCISE; char *source_env = NULL; char *build_env = NULL; + const char *program; const char *shortlist; const char *list = NULL; const char *source = SOURCE; const char *build = BUILD; struct testlist *tests; - while ((option = getopt(argc, argv, "b:hl:os:")) != EOF) { + program = argv[0]; + while ((option = getopt(argc, argv, "b:hl:os:v")) != EOF) { switch (option) { case 'b': build = optarg; break; case 'h': - printf(usage_message, argv[0], argv[0], argv[0], usage_extra); + printf(usage_message, program, program, program, usage_extra); exit(0); - break; case 'l': list = optarg; break; @@ -1442,6 +1516,9 @@ main(int argc, char *argv[]) case 's': source = optarg; break; + case 'v': + verbose = VERBOSE; + break; default: exit(1); } @@ -1449,10 +1526,17 @@ main(int argc, char *argv[]) argv += optind; argc -= optind; if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) { - fprintf(stderr, usage_message, argv[0], argv[0], argv[0], usage_extra); + fprintf(stderr, usage_message, program, program, program, usage_extra); exit(1); } + /* + * If C_TAP_VERBOSE is set in the environment, that also turns on verbose + * mode. + */ + if (getenv("C_TAP_VERBOSE") != NULL) + verbose = VERBOSE; + /* Set SOURCE and BUILD environment variables. */ if (source != NULL) { source_env = concat("SOURCE=", source, (const char *) 0); @@ -1476,10 +1560,10 @@ main(int argc, char *argv[]) shortlist++; printf(banner, shortlist); tests = read_test_list(list); - status = test_batch(tests, source, build) ? 0 : 1; + status = test_batch(tests, source, build, verbose) ? 0 : 1; } else { tests = build_test_list(argv, argc); - status = test_batch(tests, source, build) ? 0 : 1; + status = test_batch(tests, source, build, verbose) ? 0 : 1; } /* For valgrind cleanliness, free all our memory. */ |