]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libarchive/test/main.c
This commit was generated by cvs2svn to compensate for changes in r178382,
[FreeBSD/FreeBSD.git] / lib / libarchive / test / main.c
1 /*
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 /*
27  * Various utility routines useful for test programs.
28  * Each test program is linked against this file.
29  */
30 #include <errno.h>
31 #include <locale.h>
32 #include <stdarg.h>
33 #include <time.h>
34
35 #include "test.h"
36
37 /*
38  * This same file is used pretty much verbatim for all test harnesses.
39  *
40  * The next few lines are the only differences.
41  */
42 #undef  PROGRAM              /* Testing a library, not a program. */
43 #define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */
44 #define EXTRA_DUMP(x)   archive_error_string((struct archive *)(x))
45 #define EXTRA_VERSION   archive_version()
46 __FBSDID("$FreeBSD$");
47
48 /*
49  * "list.h" is simply created by "grep DEFINE_TEST"; it has
50  * a line like
51  *      DEFINE_TEST(test_function)
52  * for each test.
53  * Include it here with a suitable DEFINE_TEST to declare all of the
54  * test functions.
55  */
56 #undef DEFINE_TEST
57 #define DEFINE_TEST(name) void name(void);
58 #include "list.h"
59
60 /* Interix doesn't define these in a standard header. */
61 #if __INTERIX__
62 extern char *optarg;
63 extern int optind;
64 #endif
65
66 /* Default is to crash and try to force a core dump on failure. */
67 static int dump_on_failure = 1;
68 /* Default is to print some basic information about each test. */
69 static int quiet_flag = 0;
70 /* Cumulative count of component failures. */
71 static int failures = 0;
72 /* Cumulative count of skipped component tests. */
73 static int skips = 0;
74 /* Cumulative count of assertions. */
75 static int assertions = 0;
76
77 /* Directory where uuencoded reference files can be found. */
78 static char *refdir;
79
80 /*
81  * My own implementation of the standard assert() macro emits the
82  * message in the same format as GCC (file:line: message).
83  * It also includes some additional useful information.
84  * This makes it a lot easier to skim through test failures in
85  * Emacs.  ;-)
86  *
87  * It also supports a few special features specifically to simplify
88  * test harnesses:
89  *    failure(fmt, args) -- Stores a text string that gets
90  *          printed if the following assertion fails, good for
91  *          explaining subtle tests.
92  */
93 static char msg[4096];
94
95 /*
96  * For each test source file, we remember how many times each
97  * failure was reported.
98  */
99 static const char *failed_filename = NULL;
100 static struct line {
101         int line;
102         int count;
103 }  failed_lines[1000];
104
105 /*
106  * Count this failure; return the number of previous failures.
107  */
108 static int
109 previous_failures(const char *filename, int line)
110 {
111         unsigned int i;
112         int count;
113
114         if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
115                 memset(failed_lines, 0, sizeof(failed_lines));
116         failed_filename = filename;
117
118         for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
119                 if (failed_lines[i].line == line) {
120                         count = failed_lines[i].count;
121                         failed_lines[i].count++;
122                         return (count);
123                 }
124                 if (failed_lines[i].line == 0) {
125                         failed_lines[i].line = line;
126                         failed_lines[i].count = 1;
127                         return (0);
128                 }
129         }
130         return (0);
131 }
132
133 /*
134  * Copy arguments into file-local variables.
135  */
136 static const char *test_filename;
137 static int test_line;
138 static void *test_extra;
139 void test_setup(const char *filename, int line)
140 {
141         test_filename = filename;
142         test_line = line;
143 }
144
145 /*
146  * Inform user that we're skipping a test.
147  */
148 void
149 test_skipping(const char *fmt, ...)
150 {
151         va_list ap;
152
153         if (previous_failures(test_filename, test_line))
154                 return;
155
156         va_start(ap, fmt);
157         fprintf(stderr, " *** SKIPPING: ");
158         vfprintf(stderr, fmt, ap);
159         fprintf(stderr, "\n");
160         va_end(ap);
161         ++skips;
162 }
163
164 /* Common handling of failed tests. */
165 static void
166 report_failure(void *extra)
167 {
168         if (msg[0] != '\0') {
169                 fprintf(stderr, "   Description: %s\n", msg);
170                 msg[0] = '\0';
171         }
172
173 #ifdef EXTRA_DUMP
174         if (extra != NULL)
175                 fprintf(stderr, "   detail: %s\n", EXTRA_DUMP(extra));
176 #else
177         (void)extra; /* UNUSED */
178 #endif
179
180         if (dump_on_failure) {
181                 fprintf(stderr,
182                     " *** forcing core dump so failure can be debugged ***\n");
183                 *(char *)(NULL) = 0;
184                 exit(1);
185         }
186 }
187
188 /*
189  * Summarize repeated failures in the just-completed test file.
190  * The reports above suppress multiple failures from the same source
191  * line; this reports on any tests that did fail multiple times.
192  */
193 static int
194 summarize_comparator(const void *a0, const void *b0)
195 {
196         const struct line *a = a0, *b = b0;
197         if (a->line == 0 && b->line == 0)
198                 return (0);
199         if (a->line == 0)
200                 return (1);
201         if (b->line == 0)
202                 return (-1);
203         return (a->line - b->line);
204 }
205
206 static void
207 summarize(void)
208 {
209         unsigned int i;
210
211         qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
212             sizeof(failed_lines[0]), summarize_comparator);
213         for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
214                 if (failed_lines[i].line == 0)
215                         break;
216                 if (failed_lines[i].count > 1)
217                         fprintf(stderr, "%s:%d: Failed %d times\n",
218                             failed_filename, failed_lines[i].line,
219                             failed_lines[i].count);
220         }
221         /* Clear the failure history for the next file. */
222         memset(failed_lines, 0, sizeof(failed_lines));
223 }
224
225 /* Set up a message to display only after a test fails. */
226 void
227 failure(const char *fmt, ...)
228 {
229         va_list ap;
230         va_start(ap, fmt);
231         vsprintf(msg, fmt, ap);
232         va_end(ap);
233 }
234
235 /* Generic assert() just displays the failed condition. */
236 int
237 test_assert(const char *file, int line, int value, const char *condition, void *extra)
238 {
239         ++assertions;
240         if (value) {
241                 msg[0] = '\0';
242                 return (value);
243         }
244         failures ++;
245         if (previous_failures(file, line))
246                 return (value);
247         fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
248         fprintf(stderr, "   Condition: %s\n", condition);
249         report_failure(extra);
250         return (value);
251 }
252
253 /* assertEqualInt() displays the values of the two integers. */
254 int
255 test_assert_equal_int(const char *file, int line,
256     int v1, const char *e1, int v2, const char *e2, void *extra)
257 {
258         ++assertions;
259         if (v1 == v2) {
260                 msg[0] = '\0';
261                 return (1);
262         }
263         failures ++;
264         if (previous_failures(file, line))
265                 return (0);
266         fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
267             file, line);
268         fprintf(stderr, "      %s=%d\n", e1, v1);
269         fprintf(stderr, "      %s=%d\n", e2, v2);
270         report_failure(extra);
271         return (0);
272 }
273
274 /* assertEqualString() displays the values of the two strings. */
275 int
276 test_assert_equal_string(const char *file, int line,
277     const char *v1, const char *e1,
278     const char *v2, const char *e2,
279     void *extra)
280 {
281         ++assertions;
282         if (v1 == NULL || v2 == NULL) {
283                 if (v1 == v2) {
284                         msg[0] = '\0';
285                         return (1);
286                 }
287         } else if (strcmp(v1, v2) == 0) {
288                 msg[0] = '\0';
289                 return (1);
290         }
291         failures ++;
292         if (previous_failures(file, line))
293                 return (0);
294         fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
295             file, line);
296         fprintf(stderr, "      %s = \"%s\"\n", e1, v1);
297         fprintf(stderr, "      %s = \"%s\"\n", e2, v2);
298         report_failure(extra);
299         return (0);
300 }
301
302 /* assertEqualWString() displays the values of the two strings. */
303 int
304 test_assert_equal_wstring(const char *file, int line,
305     const wchar_t *v1, const char *e1,
306     const wchar_t *v2, const char *e2,
307     void *extra)
308 {
309         ++assertions;
310         if (wcscmp(v1, v2) == 0) {
311                 msg[0] = '\0';
312                 return (1);
313         }
314         failures ++;
315         if (previous_failures(file, line))
316                 return (0);
317         fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
318             file, line);
319         fwprintf(stderr, L"      %s = \"%ls\"\n", e1, v1);
320         fwprintf(stderr, L"      %s = \"%ls\"\n", e2, v2);
321         report_failure(extra);
322         return (0);
323 }
324
325 /*
326  * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
327  * any bytes in p that differ from ref will be highlighted with '_'
328  * before and after the hex value.
329  */
330 static void
331 hexdump(const char *p, const char *ref, size_t l, size_t offset)
332 {
333         size_t i, j;
334         char sep;
335
336         for(i=0; i < l; i+=16) {
337                 fprintf(stderr, "%04x", i + offset);
338                 sep = ' ';
339                 for (j = 0; j < 16 && i + j < l; j++) {
340                         if (ref != NULL && p[i + j] != ref[i + j])
341                                 sep = '_';
342                         fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]);
343                         if (ref != NULL && p[i + j] == ref[i + j])
344                                 sep = ' ';
345                 }
346                 for (; j < 16; j++) {
347                         fprintf(stderr, "%c  ", sep);
348                         sep = ' ';
349                 }
350                 fprintf(stderr, "%c", sep);
351                 for (j=0; j < 16 && i + j < l; j++) {
352                         int c = p[i + j];
353                         if (c >= ' ' && c <= 126)
354                                 fprintf(stderr, "%c", c);
355                         else
356                                 fprintf(stderr, ".");
357                 }
358                 fprintf(stderr, "\n");
359         }
360 }
361
362 /* assertEqualMem() displays the values of the two memory blocks. */
363 /* TODO: For long blocks, hexdump the first bytes that actually differ. */
364 int
365 test_assert_equal_mem(const char *file, int line,
366     const char *v1, const char *e1,
367     const char *v2, const char *e2,
368     size_t l, const char *ld, void *extra)
369 {
370         ++assertions;
371         if (v1 == NULL || v2 == NULL) {
372                 if (v1 == v2) {
373                         msg[0] = '\0';
374                         return (1);
375                 }
376         } else if (memcmp(v1, v2, l) == 0) {
377                 msg[0] = '\0';
378                 return (1);
379         }
380         failures ++;
381         if (previous_failures(file, line))
382                 return (0);
383         fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n",
384             file, line);
385         fprintf(stderr, "      size %s = %d\n", ld, (int)l);
386         fprintf(stderr, "      Dump of %s\n", e1);
387         hexdump(v1, v2, l < 32 ? l : 32, 0);
388         fprintf(stderr, "      Dump of %s\n", e2);
389         hexdump(v2, v1, l < 32 ? l : 32, 0);
390         fprintf(stderr, "\n");
391         report_failure(extra);
392         return (0);
393 }
394
395 int
396 test_assert_empty_file(const char *f1fmt, ...)
397 {
398         char buff[1024];
399         char f1[1024];
400         struct stat st;
401         va_list ap;
402         ssize_t s;
403         int fd;
404
405
406         va_start(ap, f1fmt);
407         vsprintf(f1, f1fmt, ap);
408         va_end(ap);
409
410         if (stat(f1, &st) != 0) {
411                 fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1);
412                 report_failure(NULL);
413         }
414         if (st.st_size == 0)
415                 return (1);
416
417         failures ++;
418         if (previous_failures(test_filename, test_line))
419                 return (0);
420
421         fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1);
422         fprintf(stderr, "    File size: %d\n", (int)st.st_size);
423         fprintf(stderr, "    Contents:\n");
424         fd = open(f1, O_RDONLY);
425         if (fd < 0) {
426                 fprintf(stderr, "    Unable to open %s\n", f1);
427         } else {
428                 s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size;
429                 s = read(fd, buff, s);
430                 hexdump(buff, NULL, s, 0);
431         }
432         report_failure(NULL);
433         return (0);
434 }
435
436 /* assertEqualFile() asserts that two files have the same contents. */
437 /* TODO: hexdump the first bytes that actually differ. */
438 int
439 test_assert_equal_file(const char *f1, const char *f2pattern, ...)
440 {
441         char f2[1024];
442         va_list ap;
443         char buff1[1024];
444         char buff2[1024];
445         int fd1, fd2;
446         int n1, n2;
447
448         va_start(ap, f2pattern);
449         vsprintf(f2, f2pattern, ap);
450         va_end(ap);
451
452         fd1 = open(f1, O_RDONLY);
453         fd2 = open(f2, O_RDONLY);
454         for (;;) {
455                 n1 = read(fd1, buff1, sizeof(buff1));
456                 n2 = read(fd2, buff2, sizeof(buff2));
457                 if (n1 != n2)
458                         break;
459                 if (n1 == 0 && n2 == 0)
460                         return (1);
461                 if (memcmp(buff1, buff2, n1) != 0)
462                         break;
463         }
464         failures ++;
465         if (previous_failures(test_filename, test_line))
466                 return (0);
467         fprintf(stderr, "%s:%d: Files are not identical\n",
468             test_filename, test_line);
469         fprintf(stderr, "  file1=\"%s\"\n", f1);
470         fprintf(stderr, "  file2=\"%s\"\n", f2);
471         report_failure(test_extra);
472         return (0);
473 }
474
475 /* assertFileContents() asserts the contents of a file. */
476 int
477 test_assert_file_contents(const void *buff, int s, const char *fpattern, ...)
478 {
479         char f[1024];
480         va_list ap;
481         char *contents;
482         int fd;
483         int n;
484
485         va_start(ap, fpattern);
486         vsprintf(f, fpattern, ap);
487         va_end(ap);
488
489         fd = open(f, O_RDONLY);
490         contents = malloc(s * 2);
491         n = read(fd, contents, s * 2);
492         if (n == s && memcmp(buff, contents, s) == 0) {
493                 free(contents);
494                 return (1);
495         }
496         failures ++;
497         if (!previous_failures(test_filename, test_line)) {
498                 fprintf(stderr, "%s:%d: File contents don't match\n",
499                     test_filename, test_line);
500                 fprintf(stderr, "  file=\"%s\"\n", f);
501                 if (n > 0)
502                         hexdump(contents, buff, n, 0);
503                 else {
504                         fprintf(stderr, "  File empty, contents should be:\n");
505                         hexdump(buff, NULL, s, 0);
506                 }
507                 report_failure(test_extra);
508         }
509         free(contents);
510         return (0);
511 }
512
513 /*
514  * Call standard system() call, but build up the command line using
515  * sprintf() conventions.
516  */
517 int
518 systemf(const char *fmt, ...)
519 {
520         char buff[8192];
521         va_list ap;
522         int r;
523
524         va_start(ap, fmt);
525         vsprintf(buff, fmt, ap);
526         r = system(buff);
527         va_end(ap);
528         return (r);
529 }
530
531 /*
532  * Slurp a file into memory for ease of comparison and testing.
533  * Returns size of file in 'sizep' if non-NULL, null-terminates
534  * data in memory for ease of use.
535  */
536 char *
537 slurpfile(size_t * sizep, const char *fmt, ...)
538 {
539         char filename[8192];
540         struct stat st;
541         va_list ap;
542         char *p;
543         ssize_t bytes_read;
544         int fd;
545         int r;
546
547         va_start(ap, fmt);
548         vsprintf(filename, fmt, ap);
549         va_end(ap);
550
551         fd = open(filename, O_RDONLY);
552         if (fd < 0) {
553                 /* Note: No error; non-existent file is okay here. */
554                 return (NULL);
555         }
556         r = fstat(fd, &st);
557         if (r != 0) {
558                 fprintf(stderr, "Can't stat file %s\n", filename);
559                 close(fd);
560                 return (NULL);
561         }
562         p = malloc(st.st_size + 1);
563         if (p == NULL) {
564                 fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename);
565                 close(fd);
566                 return (NULL);
567         }
568         bytes_read = read(fd, p, st.st_size);
569         if (bytes_read < st.st_size) {
570                 fprintf(stderr, "Can't read file %s\n", filename);
571                 close(fd);
572                 free(p);
573                 return (NULL);
574         }
575         p[st.st_size] = '\0';
576         if (sizep != NULL)
577                 *sizep = (size_t)st.st_size;
578         close(fd);
579         return (p);
580 }
581
582 /*
583  * "list.h" is automatically generated; it just has a lot of lines like:
584  *      DEFINE_TEST(function_name)
585  * It's used above to declare all of the test functions.
586  * We reuse it here to define a list of all tests (functions and names).
587  */
588 #undef DEFINE_TEST
589 #define DEFINE_TEST(n) { n, #n },
590 struct { void (*func)(void); const char *name; } tests[] = {
591         #include "list.h"
592 };
593
594 /*
595  * Each test is run in a private work dir.  Those work dirs
596  * do have consistent and predictable names, in case a group
597  * of tests need to collaborate.  However, there is no provision
598  * for requiring that tests run in a certain order.
599  */
600 static int test_run(int i, const char *tmpdir)
601 {
602         int failures_before = failures;
603
604         if (!quiet_flag)
605                 printf("%d: %s\n", i, tests[i].name);
606         /*
607          * Always explicitly chdir() in case the last test moved us to
608          * a strange place.
609          */
610         if (chdir(tmpdir)) {
611                 fprintf(stderr,
612                     "ERROR: Couldn't chdir to temp dir %s\n",
613                     tmpdir);
614                 exit(1);
615         }
616         /* Create a temp directory for this specific test. */
617         if (mkdir(tests[i].name, 0755)) {
618                 fprintf(stderr,
619                     "ERROR: Couldn't create temp dir ``%s''\n",
620                     tests[i].name);
621                 exit(1);
622         }
623         /* Chdir() to that work directory. */
624         if (chdir(tests[i].name)) {
625                 fprintf(stderr,
626                     "ERROR: Couldn't chdir to temp dir ``%s''\n",
627                     tests[i].name);
628                 exit(1);
629         }
630         /* Explicitly reset the locale before each test. */
631         setlocale(LC_ALL, "C");
632         /* Run the actual test. */
633         (*tests[i].func)();
634         /* Summarize the results of this test. */
635         summarize();
636         /* Return appropriate status. */
637         return (failures == failures_before ? 0 : 1);
638 }
639
640 static void usage(const char *program)
641 {
642         static const int limit = sizeof(tests) / sizeof(tests[0]);
643         int i;
644
645         printf("Usage: %s [options] <test> <test> ...\n", program);
646         printf("Default is to run all tests.\n");
647         printf("Otherwise, specify the numbers of the tests you wish to run.\n");
648         printf("Options:\n");
649         printf("  -k  Keep running after failures.\n");
650         printf("      Default: Core dump after any failure.\n");
651 #ifdef PROGRAM
652         printf("  -p <path>  Path to executable to be tested.\n");
653         printf("      Default: path taken from " ENVBASE " environment variable.\n");
654 #endif
655         printf("  -q  Quiet.\n");
656         printf("  -r <dir>   Path to dir containing reference files.\n");
657         printf("      Default: Current directory.\n");
658         printf("Available tests:\n");
659         for (i = 0; i < limit; i++)
660                 printf("  %d: %s\n", i, tests[i].name);
661         exit(1);
662 }
663
664 #define UUDECODE(c) (((c) - 0x20) & 0x3f)
665
666 void
667 extract_reference_file(const char *name)
668 {
669         char buff[1024];
670         FILE *in, *out;
671
672         sprintf(buff, "%s/%s.uu", refdir, name);
673         in = fopen(buff, "r");
674         failure("Couldn't open reference file %s", buff);
675         assert(in != NULL);
676         if (in == NULL)
677                 return;
678         /* Read up to and including the 'begin' line. */
679         for (;;) {
680                 if (fgets(buff, sizeof(buff), in) == NULL) {
681                         /* TODO: This is a failure. */
682                         return;
683                 }
684                 if (memcmp(buff, "begin ", 6) == 0)
685                         break;
686         }
687         /* Now, decode the rest and write it. */
688         /* Not a lot of error checking here; the input better be right. */
689         out = fopen(name, "w");
690         while (fgets(buff, sizeof(buff), in) != NULL) {
691                 char *p = buff;
692                 int bytes;
693
694                 if (memcmp(buff, "end", 3) == 0)
695                         break;
696
697                 bytes = UUDECODE(*p++);
698                 while (bytes > 0) {
699                         int n = 0;
700                         /* Write out 1-3 bytes from that. */
701                         if (bytes > 0) {
702                                 n = UUDECODE(*p++) << 18;
703                                 n |= UUDECODE(*p++) << 12;
704                                 fputc(n >> 16, out);
705                                 --bytes;
706                         }
707                         if (bytes > 0) {
708                                 n |= UUDECODE(*p++) << 6;
709                                 fputc((n >> 8) & 0xFF, out);
710                                 --bytes;
711                         }
712                         if (bytes > 0) {
713                                 n |= UUDECODE(*p++);
714                                 fputc(n & 0xFF, out);
715                                 --bytes;
716                         }
717                 }
718         }
719         fclose(out);
720         fclose(in);
721 }
722
723
724 int main(int argc, char **argv)
725 {
726         static const int limit = sizeof(tests) / sizeof(tests[0]);
727         int i, tests_run = 0, tests_failed = 0, opt;
728         time_t now;
729         char *refdir_alloc = NULL;
730         char *progname, *p;
731         char tmpdir[256];
732         char tmpdir_timestamp[256];
733
734         /*
735          * Name of this program, used to build root of our temp directory
736          * tree.
737          */
738         progname = p = argv[0];
739         while (*p != '\0') {
740                 if (*p == '/')
741                         progname = p + 1;
742                 ++p;
743         }
744
745 #ifdef PROGRAM
746         /* Get the target program from environment, if available. */
747         testprog = getenv(ENVBASE);
748 #endif
749
750         /* Allow -k to be controlled through the environment. */
751         if (getenv(ENVBASE "_KEEP_GOING") != NULL)
752                 dump_on_failure = 0;
753
754         /* Get the directory holding test files from environment. */
755         refdir = getenv(ENVBASE "_TEST_FILES");
756
757         /*
758          * Parse options.
759          */
760         while ((opt = getopt(argc, argv, "kp:qr:")) != -1) {
761                 switch (opt) {
762                 case 'k':
763                         dump_on_failure = 0;
764                         break;
765                 case 'p':
766 #ifdef PROGRAM
767                         testprog = optarg;
768 #else
769                         usage(progname);
770 #endif
771                         break;
772                 case 'q':
773                         quiet_flag++;
774                         break;
775                 case 'r':
776                         refdir = optarg;
777                         break;
778                 case '?':
779                 default:
780                         usage(progname);
781                 }
782         }
783         argc -= optind;
784         argv += optind;
785
786         /*
787          * Sanity-check that our options make sense.
788          */
789 #ifdef PROGRAM
790         if (testprog == NULL)
791                 usage(progname);
792 #endif
793
794         /*
795          * Create a temp directory for the following tests.
796          * Include the time the tests started as part of the name,
797          * to make it easier to track the results of multiple tests.
798          */
799         now = time(NULL);
800         for (i = 0; i < 1000; i++) {
801                 strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
802                     "%Y-%m-%dT%H.%M.%S",
803                     localtime(&now));
804                 sprintf(tmpdir, "/tmp/%s.%s-%03d", progname, tmpdir_timestamp, i);
805                 if (mkdir(tmpdir,0755) == 0)
806                         break;
807                 if (errno == EEXIST)
808                         continue;
809                 fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
810                     tmpdir);
811                 exit(1);
812         }
813
814         /*
815          * If the user didn't specify a directory for locating
816          * reference files, use the current directory for that.
817          */
818         if (refdir == NULL) {
819                 systemf("/bin/pwd > %s/refdir", tmpdir);
820                 refdir = refdir_alloc = slurpfile(NULL, "%s/refdir", tmpdir);
821                 p = refdir + strlen(refdir);
822                 while (p[-1] == '\n') {
823                         --p;
824                         *p = '\0';
825                 }
826         }
827
828         /*
829          * Banner with basic information.
830          */
831         if (!quiet_flag) {
832                 printf("Running tests in: %s\n", tmpdir);
833                 printf("Reference files will be read from: %s\n", refdir);
834 #ifdef PROGRAM
835                 printf("Running tests on: %s\n", testprog);
836 #endif
837                 printf("Exercising: ");
838                 fflush(stdout);
839                 printf("%s\n", EXTRA_VERSION);
840         }
841
842         /*
843          * Run some or all of the individual tests.
844          */
845         if (argc == 0) {
846                 /* Default: Run all tests. */
847                 for (i = 0; i < limit; i++) {
848                         if (test_run(i, tmpdir))
849                                 tests_failed++;
850                         tests_run++;
851                 }
852         } else {
853                 while (*(argv) != NULL) {
854                         i = atoi(*argv);
855                         if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
856                                 printf("*** INVALID Test %s\n", *argv);
857                                 usage(progname);
858                         } else {
859                                 if (test_run(i, tmpdir))
860                                         tests_failed++;
861                                 tests_run++;
862                         }
863                         argv++;
864                 }
865         }
866
867         /*
868          * Report summary statistics.
869          */
870         if (!quiet_flag) {
871                 printf("\n");
872                 printf("%d of %d tests reported failures\n",
873                     tests_failed, tests_run);
874                 printf(" Total of %d assertions checked.\n", assertions);
875                 printf(" Total of %d assertions failed.\n", failures);
876                 printf(" Total of %d assertions skipped.\n", skips);
877         }
878
879         free(refdir_alloc);
880
881         return (tests_failed);
882 }