]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libarchive/test/main.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 "test.h"
31
32 #include <errno.h>
33 #include <locale.h>
34 #include <stdarg.h>
35 #include <time.h>
36 #if defined(_WIN32) && !defined(__CYGWIN__)
37 #include <crtdbg.h>
38 #include <windows.h>
39 #include <winbase.h>
40 #endif
41
42 /*
43  * This same file is used pretty much verbatim for all test harnesses.
44  *
45  * The next few lines are the only differences.
46  */
47 #undef  PROGRAM              /* Testing a library, not a program. */
48 #define LIBRARY "libarchive"
49 #define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */
50 #define EXTRA_DUMP(x)   archive_error_string((struct archive *)(x))
51 #define EXTRA_VERSION   archive_version()
52 #define KNOWNREF        "test_compat_gtar_1.tar.uu"
53 __FBSDID("$FreeBSD$");
54
55 /*
56  * "list.h" is simply created by "grep DEFINE_TEST"; it has
57  * a line like
58  *      DEFINE_TEST(test_function)
59  * for each test.
60  * Include it here with a suitable DEFINE_TEST to declare all of the
61  * test functions.
62  */
63 #undef DEFINE_TEST
64 #define DEFINE_TEST(name) void name(void);
65 #include "list.h"
66
67 /* Interix doesn't define these in a standard header. */
68 #if __INTERIX__
69 extern char *optarg;
70 extern int optind;
71 #endif
72
73 /* Enable core dump on failure. */
74 static int dump_on_failure = 0;
75 /* Default is to remove temp dirs for successful tests. */
76 static int keep_temp_files = 0;
77 /* Default is to print some basic information about each test. */
78 static int quiet_flag = 0;
79 /* Default is to summarize repeated failures. */
80 static int verbose = 0;
81 /* Cumulative count of component failures. */
82 static int failures = 0;
83 /* Cumulative count of skipped component tests. */
84 static int skips = 0;
85 /* Cumulative count of assertions. */
86 static int assertions = 0;
87
88 /* Directory where uuencoded reference files can be found. */
89 static const char *refdir;
90
91
92 #if defined(_WIN32) && !defined(__CYGWIN__)
93
94 static void
95 invalid_parameter_handler(const wchar_t * expression,
96     const wchar_t * function, const wchar_t * file,
97     unsigned int line, uintptr_t pReserved)
98 {
99         /* nop */
100 }
101
102 #endif
103
104 /*
105  * My own implementation of the standard assert() macro emits the
106  * message in the same format as GCC (file:line: message).
107  * It also includes some additional useful information.
108  * This makes it a lot easier to skim through test failures in
109  * Emacs.  ;-)
110  *
111  * It also supports a few special features specifically to simplify
112  * test harnesses:
113  *    failure(fmt, args) -- Stores a text string that gets
114  *          printed if the following assertion fails, good for
115  *          explaining subtle tests.
116  */
117 static char msg[4096];
118
119 /*
120  * For each test source file, we remember how many times each
121  * failure was reported.
122  */
123 static const char *failed_filename = NULL;
124 static struct line {
125         int line;
126         int count;
127         int critical;
128 }  failed_lines[1000];
129
130 /*
131  * Called at the beginning of each assert() function.
132  */
133 static void
134 count_assertion(const char *file, int line)
135 {
136         (void)file; /* UNUSED */
137         (void)line; /* UNUSED */
138         ++assertions;
139         /* Uncomment to print file:line after every assertion.
140          * Verbose, but occasionally useful in tracking down crashes. */
141         /* printf("Checked %s:%d\n", file, line); */
142 }
143
144 /*
145  * Count this failure; return the number of previous failures.
146  */
147 static int
148 previous_failures(const char *filename, int line, int critical)
149 {
150         unsigned int i;
151         int count;
152
153         if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
154                 memset(failed_lines, 0, sizeof(failed_lines));
155         failed_filename = filename;
156
157         for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
158                 if (failed_lines[i].line == line) {
159                         count = failed_lines[i].count;
160                         failed_lines[i].count++;
161                         return (count);
162                 }
163                 if (failed_lines[i].line == 0) {
164                         failed_lines[i].line = line;
165                         failed_lines[i].count = 1;
166                         failed_lines[i].critical = critical;
167                         return (0);
168                 }
169         }
170         return (0);
171 }
172
173 /*
174  * Copy arguments into file-local variables.
175  */
176 static const char *test_filename;
177 static int test_line;
178 static void *test_extra;
179 void test_setup(const char *filename, int line)
180 {
181         test_filename = filename;
182         test_line = line;
183 }
184
185 /*
186  * Inform user that we're skipping a test.
187  */
188 void
189 test_skipping(const char *fmt, ...)
190 {
191         va_list ap;
192
193         if (previous_failures(test_filename, test_line, 0))
194                 return;
195
196         va_start(ap, fmt);
197         fprintf(stderr, " *** SKIPPING: ");
198         vfprintf(stderr, fmt, ap);
199         fprintf(stderr, "\n");
200         va_end(ap);
201         ++skips;
202 }
203
204 /* Common handling of failed tests. */
205 static void
206 report_failure(void *extra)
207 {
208         if (msg[0] != '\0') {
209                 fprintf(stderr, "   Description: %s\n", msg);
210                 msg[0] = '\0';
211         }
212
213 #ifdef EXTRA_DUMP
214         if (extra != NULL)
215                 fprintf(stderr, "   detail: %s\n", EXTRA_DUMP(extra));
216 #else
217         (void)extra; /* UNUSED */
218 #endif
219
220         if (dump_on_failure) {
221                 fprintf(stderr,
222                     " *** forcing core dump so failure can be debugged ***\n");
223                 *(char *)(NULL) = 0;
224                 exit(1);
225         }
226 }
227
228 /*
229  * Summarize repeated failures in the just-completed test file.
230  * The reports above suppress multiple failures from the same source
231  * line; this reports on any tests that did fail multiple times.
232  */
233 static int
234 summarize_comparator(const void *a0, const void *b0)
235 {
236         const struct line *a = a0, *b = b0;
237         if (a->line == 0 && b->line == 0)
238                 return (0);
239         if (a->line == 0)
240                 return (1);
241         if (b->line == 0)
242                 return (-1);
243         return (a->line - b->line);
244 }
245
246 static void
247 summarize(void)
248 {
249         unsigned int i;
250
251         qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
252             sizeof(failed_lines[0]), summarize_comparator);
253         for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
254                 if (failed_lines[i].line == 0)
255                         break;
256                 if (failed_lines[i].count > 1 && failed_lines[i].critical)
257                         fprintf(stderr, "%s:%d: Failed %d times\n",
258                             failed_filename, failed_lines[i].line,
259                             failed_lines[i].count);
260         }
261         /* Clear the failure history for the next file. */
262         memset(failed_lines, 0, sizeof(failed_lines));
263 }
264
265 /* Set up a message to display only after a test fails. */
266 void
267 failure(const char *fmt, ...)
268 {
269         va_list ap;
270         va_start(ap, fmt);
271         vsprintf(msg, fmt, ap);
272         va_end(ap);
273 }
274
275 /* Generic assert() just displays the failed condition. */
276 int
277 test_assert(const char *file, int line, int value, const char *condition, void *extra)
278 {
279         count_assertion(file, line);
280         if (value) {
281                 msg[0] = '\0';
282                 return (value);
283         }
284         failures ++;
285         if (!verbose && previous_failures(file, line, 1))
286                 return (value);
287         fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
288         fprintf(stderr, "   Condition: %s\n", condition);
289         report_failure(extra);
290         return (value);
291 }
292
293 /* assertEqualInt() displays the values of the two integers. */
294 int
295 test_assert_equal_int(const char *file, int line,
296     int v1, const char *e1, int v2, const char *e2, void *extra)
297 {
298         count_assertion(file, line);
299         if (v1 == v2) {
300                 msg[0] = '\0';
301                 return (1);
302         }
303         failures ++;
304         if (!verbose && previous_failures(file, line, 1))
305                 return (0);
306         fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
307             file, line);
308         fprintf(stderr, "      %s=%d\n", e1, v1);
309         fprintf(stderr, "      %s=%d\n", e2, v2);
310         report_failure(extra);
311         return (0);
312 }
313
314 static void strdump(const char *p)
315 {
316         if (p == NULL) {
317                 fprintf(stderr, "(null)");
318                 return;
319         }
320         fprintf(stderr, "\"");
321         while (*p != '\0') {
322                 unsigned int c = 0xff & *p++;
323                 switch (c) {
324                 case '\a': fprintf(stderr, "\a"); break;
325                 case '\b': fprintf(stderr, "\b"); break;
326                 case '\n': fprintf(stderr, "\n"); break;
327                 case '\r': fprintf(stderr, "\r"); break;
328                 default:
329                         if (c >= 32 && c < 127)
330                                 fprintf(stderr, "%c", c);
331                         else
332                                 fprintf(stderr, "\\x%02X", c);
333                 }
334         }
335         fprintf(stderr, "\"");
336 }
337
338 /* assertEqualString() displays the values of the two strings. */
339 int
340 test_assert_equal_string(const char *file, int line,
341     const char *v1, const char *e1,
342     const char *v2, const char *e2,
343     void *extra)
344 {
345         count_assertion(file, line);
346         if (v1 == NULL || v2 == NULL) {
347                 if (v1 == v2) {
348                         msg[0] = '\0';
349                         return (1);
350                 }
351         } else if (strcmp(v1, v2) == 0) {
352                 msg[0] = '\0';
353                 return (1);
354         }
355         failures ++;
356         if (!verbose && previous_failures(file, line, 1))
357                 return (0);
358         fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
359             file, line);
360         fprintf(stderr, "      %s = ", e1);
361         strdump(v1);
362         fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : (int)strlen(v1));
363         fprintf(stderr, "      %s = ", e2);
364         strdump(v2);
365         fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : (int)strlen(v2));
366         report_failure(extra);
367         return (0);
368 }
369
370 static void wcsdump(const wchar_t *w)
371 {
372         if (w == NULL) {
373                 fprintf(stderr, "(null)");
374                 return;
375         }
376         fprintf(stderr, "\"");
377         while (*w != L'\0') {
378                 unsigned int c = *w++;
379                 if (c >= 32 && c < 127)
380                         fprintf(stderr, "%c", c);
381                 else if (c < 256)
382                         fprintf(stderr, "\\x%02X", c);
383                 else if (c < 0x10000)
384                         fprintf(stderr, "\\u%04X", c);
385                 else
386                         fprintf(stderr, "\\U%08X", c);
387         }
388         fprintf(stderr, "\"");
389 }
390
391 /* assertEqualWString() displays the values of the two strings. */
392 int
393 test_assert_equal_wstring(const char *file, int line,
394     const wchar_t *v1, const char *e1,
395     const wchar_t *v2, const char *e2,
396     void *extra)
397 {
398         count_assertion(file, line);
399         if (v1 == NULL) {
400                 if (v2 == NULL) {
401                         msg[0] = '\0';
402                         return (1);
403                 }
404         } else if (v2 == NULL) {
405                 if (v1 == NULL) {
406                         msg[0] = '\0';
407                         return (1);
408                 }
409         } else if (wcscmp(v1, v2) == 0) {
410                 msg[0] = '\0';
411                 return (1);
412         }
413         failures ++;
414         if (!verbose && previous_failures(file, line, 1))
415                 return (0);
416         fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
417             file, line);
418         fprintf(stderr, "      %s = ", e1);
419         wcsdump(v1);
420         fprintf(stderr, "\n");
421         fprintf(stderr, "      %s = ", e2);
422         wcsdump(v2);
423         fprintf(stderr, "\n");
424         report_failure(extra);
425         return (0);
426 }
427
428 /*
429  * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
430  * any bytes in p that differ from ref will be highlighted with '_'
431  * before and after the hex value.
432  */
433 static void
434 hexdump(const char *p, const char *ref, size_t l, size_t offset)
435 {
436         size_t i, j;
437         char sep;
438
439         for(i=0; i < l; i+=16) {
440                 fprintf(stderr, "%04x", (unsigned)(i + offset));
441                 sep = ' ';
442                 for (j = 0; j < 16 && i + j < l; j++) {
443                         if (ref != NULL && p[i + j] != ref[i + j])
444                                 sep = '_';
445                         fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]);
446                         if (ref != NULL && p[i + j] == ref[i + j])
447                                 sep = ' ';
448                 }
449                 for (; j < 16; j++) {
450                         fprintf(stderr, "%c  ", sep);
451                         sep = ' ';
452                 }
453                 fprintf(stderr, "%c", sep);
454                 for (j=0; j < 16 && i + j < l; j++) {
455                         int c = p[i + j];
456                         if (c >= ' ' && c <= 126)
457                                 fprintf(stderr, "%c", c);
458                         else
459                                 fprintf(stderr, ".");
460                 }
461                 fprintf(stderr, "\n");
462         }
463 }
464
465 /* assertEqualMem() displays the values of the two memory blocks. */
466 int
467 test_assert_equal_mem(const char *file, int line,
468     const void *_v1, const char *e1,
469     const void *_v2, const char *e2,
470     size_t l, const char *ld, void *extra)
471 {
472         const char *v1 = (const char *)_v1;
473         const char *v2 = (const char *)_v2;
474         size_t offset;
475
476         count_assertion(file, line);
477         if (v1 == NULL || v2 == NULL) {
478                 if (v1 == v2) {
479                         msg[0] = '\0';
480                         return (1);
481                 }
482         } else if (memcmp(v1, v2, l) == 0) {
483                 msg[0] = '\0';
484                 return (1);
485         }
486         failures ++;
487         if (!verbose && previous_failures(file, line, 1))
488                 return (0);
489         fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n",
490             file, line);
491         fprintf(stderr, "      size %s = %d\n", ld, (int)l);
492         /* Dump 48 bytes (3 lines) so that the first difference is
493          * in the second line. */
494         offset = 0;
495         while (l > 64 && memcmp(v1, v2, 32) == 0) {
496                 /* The first two lines agree, so step forward one line. */
497                 v1 += 16;
498                 v2 += 16;
499                 l -= 16;
500                 offset += 16;
501         }
502         fprintf(stderr, "      Dump of %s\n", e1);
503         hexdump(v1, v2, l < 64 ? l : 64, offset);
504         fprintf(stderr, "      Dump of %s\n", e2);
505         hexdump(v2, v1, l < 64 ? l : 64, offset);
506         fprintf(stderr, "\n");
507         report_failure(extra);
508         return (0);
509 }
510
511 int
512 test_assert_empty_file(const char *f1fmt, ...)
513 {
514         char buff[1024];
515         char f1[1024];
516         struct stat st;
517         va_list ap;
518         ssize_t s;
519         int fd;
520
521
522         va_start(ap, f1fmt);
523         vsprintf(f1, f1fmt, ap);
524         va_end(ap);
525
526         if (stat(f1, &st) != 0) {
527                 fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1);
528                 report_failure(NULL);
529                 return (0);
530         }
531         if (st.st_size == 0)
532                 return (1);
533
534         failures ++;
535         if (!verbose && previous_failures(test_filename, test_line, 1))
536                 return (0);
537
538         fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1);
539         fprintf(stderr, "    File size: %d\n", (int)st.st_size);
540         fprintf(stderr, "    Contents:\n");
541         fd = open(f1, O_RDONLY);
542         if (fd < 0) {
543                 fprintf(stderr, "    Unable to open %s\n", f1);
544         } else {
545                 s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size;
546                 s = read(fd, buff, s);
547                 hexdump(buff, NULL, s, 0);
548         }
549         report_failure(NULL);
550         return (0);
551 }
552
553 /* assertEqualFile() asserts that two files have the same contents. */
554 /* TODO: hexdump the first bytes that actually differ. */
555 int
556 test_assert_equal_file(const char *f1, const char *f2pattern, ...)
557 {
558         char f2[1024];
559         va_list ap;
560         char buff1[1024];
561         char buff2[1024];
562         int fd1, fd2;
563         int n1, n2;
564
565         va_start(ap, f2pattern);
566         vsprintf(f2, f2pattern, ap);
567         va_end(ap);
568
569         fd1 = open(f1, O_RDONLY);
570         fd2 = open(f2, O_RDONLY);
571         for (;;) {
572                 n1 = read(fd1, buff1, sizeof(buff1));
573                 n2 = read(fd2, buff2, sizeof(buff2));
574                 if (n1 != n2)
575                         break;
576                 if (n1 == 0 && n2 == 0)
577                         return (1);
578                 if (memcmp(buff1, buff2, n1) != 0)
579                         break;
580         }
581         failures ++;
582         if (!verbose && previous_failures(test_filename, test_line, 1))
583                 return (0);
584         fprintf(stderr, "%s:%d: Files are not identical\n",
585             test_filename, test_line);
586         fprintf(stderr, "  file1=\"%s\"\n", f1);
587         fprintf(stderr, "  file2=\"%s\"\n", f2);
588         report_failure(test_extra);
589         return (0);
590 }
591
592 int
593 test_assert_file_exists(const char *fpattern, ...)
594 {
595         char f[1024];
596         va_list ap;
597
598         va_start(ap, fpattern);
599         vsprintf(f, fpattern, ap);
600         va_end(ap);
601
602         if (!access(f, F_OK))
603                 return (1);
604         if (!previous_failures(test_filename, test_line, 1)) {
605                 fprintf(stderr, "%s:%d: File doesn't exist\n",
606                     test_filename, test_line);
607                 fprintf(stderr, "  file=\"%s\"\n", f);
608                 report_failure(test_extra);
609         }
610         return (0);
611 }
612
613 int
614 test_assert_file_not_exists(const char *fpattern, ...)
615 {
616         char f[1024];
617         va_list ap;
618
619         va_start(ap, fpattern);
620         vsprintf(f, fpattern, ap);
621         va_end(ap);
622
623         if (access(f, F_OK))
624                 return (1);
625         if (!previous_failures(test_filename, test_line, 1)) {
626                 fprintf(stderr, "%s:%d: File exists and shouldn't\n",
627                     test_filename, test_line);
628                 fprintf(stderr, "  file=\"%s\"\n", f);
629                 report_failure(test_extra);
630         }
631         return (0);
632 }
633
634 /* assertFileContents() asserts the contents of a file. */
635 int
636 test_assert_file_contents(const void *buff, int s, const char *fpattern, ...)
637 {
638         char f[1024];
639         va_list ap;
640         char *contents;
641         int fd;
642         int n;
643
644         va_start(ap, fpattern);
645         vsprintf(f, fpattern, ap);
646         va_end(ap);
647
648         fd = open(f, O_RDONLY);
649         contents = malloc(s * 2);
650         n = read(fd, contents, s * 2);
651         if (n == s && memcmp(buff, contents, s) == 0) {
652                 free(contents);
653                 return (1);
654         }
655         failures ++;
656         if (!previous_failures(test_filename, test_line, 1)) {
657                 fprintf(stderr, "%s:%d: File contents don't match\n",
658                     test_filename, test_line);
659                 fprintf(stderr, "  file=\"%s\"\n", f);
660                 if (n > 0)
661                         hexdump(contents, buff, n, 0);
662                 else {
663                         fprintf(stderr, "  File empty, contents should be:\n");
664                         hexdump(buff, NULL, s, 0);
665                 }
666                 report_failure(test_extra);
667         }
668         free(contents);
669         return (0);
670 }
671
672 /*
673  * Call standard system() call, but build up the command line using
674  * sprintf() conventions.
675  */
676 int
677 systemf(const char *fmt, ...)
678 {
679         char buff[8192];
680         va_list ap;
681         int r;
682
683         va_start(ap, fmt);
684         vsprintf(buff, fmt, ap);
685         r = system(buff);
686         va_end(ap);
687         return (r);
688 }
689
690 /*
691  * Slurp a file into memory for ease of comparison and testing.
692  * Returns size of file in 'sizep' if non-NULL, null-terminates
693  * data in memory for ease of use.
694  */
695 char *
696 slurpfile(size_t * sizep, const char *fmt, ...)
697 {
698         char filename[8192];
699         struct stat st;
700         va_list ap;
701         char *p;
702         ssize_t bytes_read;
703         int fd;
704         int r;
705
706         va_start(ap, fmt);
707         vsprintf(filename, fmt, ap);
708         va_end(ap);
709
710         fd = open(filename, O_RDONLY);
711         if (fd < 0) {
712                 /* Note: No error; non-existent file is okay here. */
713                 return (NULL);
714         }
715         r = fstat(fd, &st);
716         if (r != 0) {
717                 fprintf(stderr, "Can't stat file %s\n", filename);
718                 close(fd);
719                 return (NULL);
720         }
721         p = malloc(st.st_size + 1);
722         if (p == NULL) {
723                 fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename);
724                 close(fd);
725                 return (NULL);
726         }
727         bytes_read = read(fd, p, st.st_size);
728         if (bytes_read < st.st_size) {
729                 fprintf(stderr, "Can't read file %s\n", filename);
730                 close(fd);
731                 free(p);
732                 return (NULL);
733         }
734         p[st.st_size] = '\0';
735         if (sizep != NULL)
736                 *sizep = (size_t)st.st_size;
737         close(fd);
738         return (p);
739 }
740
741 /*
742  * "list.h" is automatically generated; it just has a lot of lines like:
743  *      DEFINE_TEST(function_name)
744  * It's used above to declare all of the test functions.
745  * We reuse it here to define a list of all tests (functions and names).
746  */
747 #undef DEFINE_TEST
748 #define DEFINE_TEST(n) { n, #n },
749 struct { void (*func)(void); const char *name; } tests[] = {
750         #include "list.h"
751 };
752
753 /*
754  * This is well-intentioned, but sometimes the standard libraries
755  * leave open file descriptors and expect to be able to come back to
756  * them (e.g., for username lookups or logging).  Closing these
757  * descriptors out from under those libraries creates havoc.
758  *
759  * Maybe there's some reasonably portable way to tell if a descriptor
760  * is open without using close()?
761  */
762 #if 0
763 static void
764 close_descriptors(int warn)
765 {
766         int i;
767         int left_open = 0;
768
769         for (i = 3; i < 100; ++i) {
770                 if (close(i) == 0)
771                         ++left_open;
772         }
773         if (warn && left_open > 0) {
774                 fprintf(stderr, " ** %d descriptors unclosed\n", left_open);
775                 failures += left_open;
776                 report_failure(NULL);
777         }
778 }
779 #endif
780
781 /*
782  * Each test is run in a private work dir.  Those work dirs
783  * do have consistent and predictable names, in case a group
784  * of tests need to collaborate.  However, there is no provision
785  * for requiring that tests run in a certain order.
786  */
787 static int test_run(int i, const char *tmpdir)
788 {
789         int failures_before = failures;
790
791         if (!quiet_flag) {
792                 printf("%d: %s\n", i, tests[i].name);
793                 fflush(stdout);
794         }
795
796         /*
797          * Always explicitly chdir() in case the last test moved us to
798          * a strange place.
799          */
800         if (chdir(tmpdir)) {
801                 fprintf(stderr,
802                     "ERROR: Couldn't chdir to temp dir %s\n",
803                     tmpdir);
804                 exit(1);
805         }
806         /* Create a temp directory for this specific test. */
807         if (mkdir(tests[i].name, 0755)) {
808                 fprintf(stderr,
809                     "ERROR: Couldn't create temp dir ``%s''\n",
810                     tests[i].name);
811                 exit(1);
812         }
813         /* Chdir() to that work directory. */
814         if (chdir(tests[i].name)) {
815                 fprintf(stderr,
816                     "ERROR: Couldn't chdir to temp dir ``%s''\n",
817                     tests[i].name);
818                 exit(1);
819         }
820         /* Explicitly reset the locale before each test. */
821         setlocale(LC_ALL, "C");
822         /* Make sure there are no stray descriptors going into the test. */
823         /* TODO: Find a better way to identify file descriptor leaks. */
824         //close_descriptors(0);
825         /* Run the actual test. */
826         (*tests[i].func)();
827         /* Close stray descriptors, record as errors against this test. */
828         //close_descriptors(1);
829         /* Summarize the results of this test. */
830         summarize();
831         /* If there were no failures, we can remove the work dir. */
832         if (failures == failures_before) {
833                 if (!keep_temp_files && chdir(tmpdir) == 0) {
834 #if !defined(_WIN32) || defined(__CYGWIN__)
835                         systemf("rm -rf %s", tests[i].name);
836 #else
837                         systemf("rmdir /S /Q %s", tests[i].name);
838 #endif
839                 }
840         }
841         /* Return appropriate status. */
842         return (failures == failures_before ? 0 : 1);
843 }
844
845 static void usage(const char *program)
846 {
847         static const int limit = sizeof(tests) / sizeof(tests[0]);
848         int i;
849
850         printf("Usage: %s [options] <test> <test> ...\n", program);
851         printf("Default is to run all tests.\n");
852         printf("Otherwise, specify the numbers of the tests you wish to run.\n");
853         printf("Options:\n");
854         printf("  -d  Dump core after any failure, for debugging.\n");
855         printf("  -k  Keep all temp files.\n");
856         printf("      Default: temp files for successful tests deleted.\n");
857 #ifdef PROGRAM
858         printf("  -p <path>  Path to executable to be tested.\n");
859         printf("      Default: path taken from " ENVBASE " environment variable.\n");
860 #endif
861         printf("  -q  Quiet.\n");
862         printf("  -r <dir>   Path to dir containing reference files.\n");
863         printf("      Default: Current directory.\n");
864         printf("  -v  Verbose.\n");
865         printf("Available tests:\n");
866         for (i = 0; i < limit; i++)
867                 printf("  %d: %s\n", i, tests[i].name);
868         exit(1);
869 }
870
871 #define UUDECODE(c) (((c) - 0x20) & 0x3f)
872
873 void
874 extract_reference_file(const char *name)
875 {
876         char buff[1024];
877         FILE *in, *out;
878
879         sprintf(buff, "%s/%s.uu", refdir, name);
880         in = fopen(buff, "r");
881         failure("Couldn't open reference file %s", buff);
882         assert(in != NULL);
883         if (in == NULL)
884                 return;
885         /* Read up to and including the 'begin' line. */
886         for (;;) {
887                 if (fgets(buff, sizeof(buff), in) == NULL) {
888                         /* TODO: This is a failure. */
889                         return;
890                 }
891                 if (memcmp(buff, "begin ", 6) == 0)
892                         break;
893         }
894         /* Now, decode the rest and write it. */
895         /* Not a lot of error checking here; the input better be right. */
896         out = fopen(name, "w");
897         while (fgets(buff, sizeof(buff), in) != NULL) {
898                 char *p = buff;
899                 int bytes;
900
901                 if (memcmp(buff, "end", 3) == 0)
902                         break;
903
904                 bytes = UUDECODE(*p++);
905                 while (bytes > 0) {
906                         int n = 0;
907                         /* Write out 1-3 bytes from that. */
908                         if (bytes > 0) {
909                                 n = UUDECODE(*p++) << 18;
910                                 n |= UUDECODE(*p++) << 12;
911                                 fputc(n >> 16, out);
912                                 --bytes;
913                         }
914                         if (bytes > 0) {
915                                 n |= UUDECODE(*p++) << 6;
916                                 fputc((n >> 8) & 0xFF, out);
917                                 --bytes;
918                         }
919                         if (bytes > 0) {
920                                 n |= UUDECODE(*p++);
921                                 fputc(n & 0xFF, out);
922                                 --bytes;
923                         }
924                 }
925         }
926         fclose(out);
927         fclose(in);
928 }
929
930
931 /* Since gzip is by far the most popular external compression program
932  * available, we try to use it in the read_program and write_program
933  * tests.  But if it's not available, then we can't use it.  This
934  * function just tries to run gzip/gunzip to see if they're available.
935  * If not, some of the external compression program tests will be
936  * skipped. */
937 const char *
938 external_gzip_program(int un)
939 {
940         static int tested = 0;
941         static const char *compress_prog = NULL;
942         static const char *decompress_prog = NULL;
943         /* Args vary depending on the command interpreter we're using. */
944 #if defined(_WIN32) && !defined(__CYGWIN__)
945         static const char *args = "-V >NUL 2>NUL"; /* Win32 cmd.exe */
946 #else
947         static const char *args = "-V >/dev/null 2>/dev/null"; /* POSIX 'sh' */
948 #endif
949
950         if (!tested) {
951                 if (systemf("gunzip %s", args) == 0)
952                         decompress_prog = "gunzip";
953                 if (systemf("gzip %s", args) == 0)
954                         compress_prog = "gzip";
955                 tested = 1;
956         }
957         return (un ? decompress_prog : compress_prog);
958 }
959
960 static char *
961 get_refdir(void)
962 {
963         char tried[512] = { '\0' };
964         char buff[128];
965         char *pwd, *p;
966
967         /* Get the current dir. */
968         pwd = getcwd(NULL, 0);
969         while (pwd[strlen(pwd) - 1] == '\n')
970                 pwd[strlen(pwd) - 1] = '\0';
971         printf("PWD: %s\n", pwd);
972
973         /* Look for a known file. */
974         snprintf(buff, sizeof(buff), "%s", pwd);
975         p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
976         if (p != NULL) goto success;
977         strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
978         strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
979
980         snprintf(buff, sizeof(buff), "%s/test", pwd);
981         p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
982         if (p != NULL) goto success;
983         strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
984         strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
985
986         snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY);
987         p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
988         if (p != NULL) goto success;
989         strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
990         strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
991
992         if (memcmp(pwd, "/usr/obj", 8) == 0) {
993                 snprintf(buff, sizeof(buff), "%s", pwd + 8);
994                 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
995                 if (p != NULL) goto success;
996                 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
997                 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
998
999                 snprintf(buff, sizeof(buff), "%s/test", pwd + 8);
1000                 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
1001                 if (p != NULL) goto success;
1002                 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
1003                 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
1004         }
1005
1006 #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
1007         DebugBreak();
1008 #endif
1009         printf("Unable to locate known reference file %s\n", KNOWNREF);
1010         printf("  Checked following directories:\n%s\n", tried);
1011         exit(1);
1012
1013 success:
1014         free(p);
1015         free(pwd);
1016         return strdup(buff);
1017 }
1018
1019 int main(int argc, char **argv)
1020 {
1021         static const int limit = sizeof(tests) / sizeof(tests[0]);
1022         int i, tests_run = 0, tests_failed = 0, option;
1023         time_t now;
1024         char *refdir_alloc = NULL;
1025         const char *progname = LIBRARY "_test";
1026         const char *tmp, *option_arg, *p;
1027         char tmpdir[256];
1028         char tmpdir_timestamp[256];
1029
1030         (void)argc; /* UNUSED */
1031
1032 #if defined(_WIN32) && !defined(__CYGWIN__)
1033         /* To stop to run the default invalid parameter handler. */
1034         _set_invalid_parameter_handler(invalid_parameter_handler);
1035         /* for open() to a binary mode. */
1036         _set_fmode(_O_BINARY);
1037         /* Disable annoying assertion message box. */
1038         _CrtSetReportMode(_CRT_ASSERT, 0);
1039 #endif
1040
1041 #ifdef PROGRAM
1042         /* Get the target program from environment, if available. */
1043         testprog = getenv(ENVBASE);
1044 #endif
1045
1046         if (getenv("TMPDIR") != NULL)
1047                 tmp = getenv("TMPDIR");
1048         else if (getenv("TMP") != NULL)
1049                 tmp = getenv("TMP");
1050         else if (getenv("TEMP") != NULL)
1051                 tmp = getenv("TEMP");
1052         else if (getenv("TEMPDIR") != NULL)
1053                 tmp = getenv("TEMPDIR");
1054         else
1055                 tmp = "/tmp";
1056
1057         /* Allow -d to be controlled through the environment. */
1058         if (getenv(ENVBASE "_DEBUG") != NULL)
1059                 dump_on_failure = 1;
1060
1061         /* Get the directory holding test files from environment. */
1062         refdir = getenv(ENVBASE "_TEST_FILES");
1063
1064         /*
1065          * Parse options, without using getopt(), which isn't available
1066          * on all platforms.
1067          */
1068         ++argv; /* Skip program name */
1069         while (*argv != NULL) {
1070                 if (**argv != '-')
1071                         break;
1072                 p = *argv++;
1073                 ++p; /* Skip '-' */
1074                 while (*p != '\0') {
1075                         option = *p++;
1076                         option_arg = NULL;
1077                         /* If 'opt' takes an argument, parse that. */
1078                         if (option == 'p' || option == 'r') {
1079                                 if (*p != '\0')
1080                                         option_arg = p;
1081                                 else if (*argv == NULL) {
1082                                         fprintf(stderr,
1083                                             "Option -%c requires argument.\n",
1084                                             option);
1085                                         usage(progname);
1086                                 } else
1087                                         option_arg = *argv++;
1088                                 p = ""; /* End of this option word. */
1089                         }
1090
1091                         /* Now, handle the option. */
1092                         switch (option) {
1093                         case 'd':
1094                                 dump_on_failure = 1;
1095                                 break;
1096                         case 'k':
1097                                 keep_temp_files = 1;
1098                                 break;
1099                         case 'p':
1100 #ifdef PROGRAM
1101                                 testprog = option_arg;
1102 #else
1103                                 usage(progname);
1104 #endif
1105                                 break;
1106                         case 'q':
1107                                 quiet_flag++;
1108                                 break;
1109                         case 'r':
1110                                 refdir = option_arg;
1111                                 break;
1112                         case 'v':
1113                                 verbose = 1;
1114                                 break;
1115                         default:
1116                                 usage(progname);
1117                         }
1118                 }
1119         }
1120
1121         /*
1122          * Sanity-check that our options make sense.
1123          */
1124 #ifdef PROGRAM
1125         if (testprog == NULL)
1126                 usage(progname);
1127 #endif
1128
1129         /*
1130          * Create a temp directory for the following tests.
1131          * Include the time the tests started as part of the name,
1132          * to make it easier to track the results of multiple tests.
1133          */
1134         now = time(NULL);
1135         for (i = 0; i < 1000; i++) {
1136                 strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
1137                     "%Y-%m-%dT%H.%M.%S",
1138                     localtime(&now));
1139                 sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname,
1140                     tmpdir_timestamp, i);
1141                 if (mkdir(tmpdir,0755) == 0)
1142                         break;
1143                 if (errno == EEXIST)
1144                         continue;
1145                 fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
1146                     tmpdir);
1147                 exit(1);
1148         }
1149
1150         /*
1151          * If the user didn't specify a directory for locating
1152          * reference files, try to find the reference files in
1153          * the "usual places."
1154          */
1155         if (refdir == NULL)
1156                 refdir = refdir_alloc = get_refdir();
1157
1158         /*
1159          * Banner with basic information.
1160          */
1161         if (!quiet_flag) {
1162                 printf("Running tests in: %s\n", tmpdir);
1163                 printf("Reference files will be read from: %s\n", refdir);
1164 #ifdef PROGRAM
1165                 printf("Running tests on: %s\n", testprog);
1166 #endif
1167                 printf("Exercising: ");
1168                 fflush(stdout);
1169                 printf("%s\n", EXTRA_VERSION);
1170         }
1171
1172         /*
1173          * Run some or all of the individual tests.
1174          */
1175         if (*argv == NULL) {
1176                 /* Default: Run all tests. */
1177                 for (i = 0; i < limit; i++) {
1178                         if (test_run(i, tmpdir))
1179                                 tests_failed++;
1180                         tests_run++;
1181                 }
1182         } else {
1183                 while (*(argv) != NULL) {
1184                         if (**argv >= '0' && **argv <= '9') {
1185                                 i = atoi(*argv);
1186                                 if (i < 0 || i >= limit) {
1187                                         printf("*** INVALID Test %s\n", *argv);
1188                                         free(refdir_alloc);
1189                                         usage(progname);
1190                                         /* usage() never returns */
1191                                 }
1192                         } else {
1193                                 for (i = 0; i < limit; ++i) {
1194                                         if (strcmp(*argv, tests[i].name) == 0)
1195                                                 break;
1196                                 }
1197                                 if (i >= limit) {
1198                                         printf("*** INVALID Test ``%s''\n",
1199                                                *argv);
1200                                         free(refdir_alloc);
1201                                         usage(progname);
1202                                         /* usage() never returns */
1203                                 }
1204                         }
1205                         if (test_run(i, tmpdir))
1206                                 tests_failed++;
1207                         tests_run++;
1208                         argv++;
1209                 }
1210         }
1211
1212         /*
1213          * Report summary statistics.
1214          */
1215         if (!quiet_flag) {
1216                 printf("\n");
1217                 printf("%d of %d tests reported failures\n",
1218                     tests_failed, tests_run);
1219                 printf(" Total of %d assertions checked.\n", assertions);
1220                 printf(" Total of %d assertions failed.\n", failures);
1221                 printf(" Total of %d reported skips.\n", skips);
1222         }
1223
1224         free(refdir_alloc);
1225
1226         /* If the final tmpdir is empty, we can remove it. */
1227         /* This should be the usual case when all tests succeed. */
1228         rmdir(tmpdir);
1229
1230         return (tests_failed);
1231 }