~akopytov/percona-xtrabackup/bug1166888-2.1

« back to all changes in this revision

Viewing changes to src/libarchive/tar/test/main.c

  • Committer: Alexey Kopytov
  • Date: 2012-02-10 20:05:56 UTC
  • mto: (391.1.5 staging)
  • mto: This revision was merged to the branch mainline in revision 390.
  • Revision ID: akopytov@gmail.com-20120210200556-6kx41z8wwrqfucro
Rebase of the parallel compression patch on new trunk + post-review
fixes.

Implementation of parallel compression and streaming for XtraBackup.

This revision implements the following changes:

* InnoDB files are now streamed by the xtrabackup binary rather than
innobackupex. As a result, integrity is now verified by xtrabackup and
thus tar4ibd is no longer needed, so it was removed.

* xtrabackup binary now accepts the new '--stream' option which has
exactly the same semantics as the '--stream' option in
innobackupex: it tells xtrabackup to stream all files to the standard
output in the specified format rather than storing them locally.

* The xtrabackup binary can now do parallel compression using the
quicklz library. Two new options were added to xtrabackup to support
this feature:

- '--compress' tells xtrabackup to compress all output data, including
the transaction log file and meta data files, using the specified
compression algorithm. The only currently supported algorithm is
'quicklz'. The resulting files have the qpress archive format,
i.e. every *.qp file produced by xtrabackup is essentially a one-file
qpress archive and can be extracted and uncompressed by the qpress
file archiver (http://www.quicklz.com/).

- '--compress-threads' specifies the number of worker threads used by
xtrabackup for parallel data compression. This option defaults to 1.

Parallel compression ('--compress-threads') can be used together with
parallel file copying ('--parallel'). For example, '--parallel=4
--compress --compress-threads=2' will create 4 IO threads that will
read the data and pipe it to 2 compression threads.

* To support simultaneous compression and streaming, a new custom
streaming format called 'xbstream' was introduced to XtraBackup in
addition to the 'tar' format. That was required to overcome some
limitations of traditional archive formats such as 'tar', 'cpio' and
others that do not allow streaming dynamically generated files, for
example dynamically compressed files.  Other advantages of xbstream over
traditional streaming/archive formats include ability to stream multiple
files concurrently (so it is possible to use streaming in the xbstream
format together with the --parallel option) and more compact data
storage.

* To allow streaming and extracting files to/from the xbstream format
produced by xtrabackup, a new utility aptly called 'xbstream' was
added to the XtraBackup distribution. This utility has a tar-like
interface:

- with the '-x' option it extracts files from the stream read from its
standard input to the current directory unless specified otherwise
with the '-C' option.

- with the '-c' option it streams files specified on the command line
to its standard output.

The utility also tries to minimize its impact on the OS page cache by
using the appropriate posix_fadvise() calls when available.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2003-2009 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
#include "test.h"
 
27
#include <errno.h>
 
28
#include <locale.h>
 
29
#include <stdarg.h>
 
30
#include <time.h>
 
31
 
 
32
/*
 
33
 * This same file is used pretty much verbatim for all test harnesses.
 
34
 *
 
35
 * The next few lines are the only differences.
 
36
 * TODO: Move this into a separate configuration header, have all test
 
37
 * suites share one copy of this file.
 
38
 */
 
39
__FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.6 2008/11/05 06:40:53 kientzle Exp $");
 
40
#define KNOWNREF        "test_patterns_2.tar.uu"
 
41
#define ENVBASE "BSDTAR"  /* Prefix for environment variables. */
 
42
#define PROGRAM "bsdtar"  /* Name of program being tested. */
 
43
#undef LIBRARY            /* Not testing a library. */
 
44
#undef  EXTRA_DUMP           /* How to dump extra data */
 
45
/* How to generate extra version info. */
 
46
#define EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
 
47
 
 
48
/*
 
49
 *
 
50
 * Windows support routines
 
51
 *
 
52
 * Note: Configuration is a tricky issue.  Using HAVE_* feature macros
 
53
 * in the test harness is dangerous because they cover up
 
54
 * configuration errors.  The classic example of this is omitting a
 
55
 * configure check.  If libarchive and libarchive_test both look for
 
56
 * the same feature macro, such errors are hard to detect.  Platform
 
57
 * macros (e.g., _WIN32 or __GNUC__) are a little better, but can
 
58
 * easily lead to very messy code.  It's best to limit yourself
 
59
 * to only the most generic programming techniques in the test harness
 
60
 * and thus avoid conditionals altogether.  Where that's not possible,
 
61
 * try to minimize conditionals by grouping platform-specific tests in
 
62
 * one place (e.g., test_acl_freebsd) or by adding new assert()
 
63
 * functions (e.g., assertMakeHardlink()) to cover up platform
 
64
 * differences.  Platform-specific coding in libarchive_test is often
 
65
 * a symptom that some capability is missing from libarchive itself.
 
66
 */
 
67
#if defined(_WIN32) && !defined(__CYGWIN__)
 
68
#include <io.h>
 
69
#include <windows.h>
 
70
#ifndef F_OK
 
71
#define F_OK (0)
 
72
#endif
 
73
#ifndef S_ISDIR
 
74
#define S_ISDIR(m)  ((m) & _S_IFDIR)
 
75
#endif
 
76
#ifndef S_ISREG
 
77
#define S_ISREG(m)  ((m) & _S_IFREG)
 
78
#endif
 
79
#if !defined(__BORLANDC__)
 
80
#define access _access
 
81
#undef chdir
 
82
#define chdir _chdir
 
83
#endif
 
84
#ifndef fileno
 
85
#define fileno _fileno
 
86
#endif
 
87
/*#define fstat _fstat64*/
 
88
#if !defined(__BORLANDC__)
 
89
#define getcwd _getcwd
 
90
#endif
 
91
#define lstat stat
 
92
/*#define lstat _stat64*/
 
93
/*#define stat _stat64*/
 
94
#define rmdir _rmdir
 
95
#if !defined(__BORLANDC__)
 
96
#define strdup _strdup
 
97
#define umask _umask
 
98
#endif
 
99
#define int64_t __int64
 
100
#endif
 
101
 
 
102
#if defined(HAVE__CrtSetReportMode)
 
103
# include <crtdbg.h>
 
104
#endif
 
105
 
 
106
#if defined(_WIN32) && !defined(__CYGWIN__)
 
107
void *GetFunctionKernel32(const char *name)
 
108
{
 
109
        static HINSTANCE lib;
 
110
        static int set;
 
111
        if (!set) {
 
112
                set = 1;
 
113
                lib = LoadLibrary("kernel32.dll");
 
114
        }
 
115
        if (lib == NULL) {
 
116
                fprintf(stderr, "Can't load kernel32.dll?!\n");
 
117
                exit(1);
 
118
        }
 
119
        return (void *)GetProcAddress(lib, name);
 
120
}
 
121
 
 
122
static int
 
123
my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
 
124
{
 
125
        static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
 
126
        static int set;
 
127
        if (!set) {
 
128
                set = 1;
 
129
                f = GetFunctionKernel32("CreateSymbolicLinkA");
 
130
        }
 
131
        return f == NULL ? 0 : (*f)(linkname, target, flags);
 
132
}
 
133
 
 
134
static int
 
135
my_CreateHardLinkA(const char *linkname, const char *target)
 
136
{
 
137
        static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES);
 
138
        static int set;
 
139
        if (!set) {
 
140
                set = 1;
 
141
                f = GetFunctionKernel32("CreateHardLinkA");
 
142
        }
 
143
        return f == NULL ? 0 : (*f)(linkname, target, NULL);
 
144
}
 
145
 
 
146
int
 
147
my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi)
 
148
{
 
149
        HANDLE h;
 
150
        int r;
 
151
 
 
152
        memset(bhfi, 0, sizeof(*bhfi));
 
153
        h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL,
 
154
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
155
        if (h == INVALID_HANDLE_VALUE)
 
156
                return (0);
 
157
        r = GetFileInformationByHandle(h, bhfi);
 
158
        CloseHandle(h);
 
159
        return (r);
 
160
}
 
161
#endif
 
162
 
 
163
#if defined(HAVE__CrtSetReportMode)
 
164
static void
 
165
invalid_parameter_handler(const wchar_t * expression,
 
166
    const wchar_t * function, const wchar_t * file,
 
167
    unsigned int line, uintptr_t pReserved)
 
168
{
 
169
        /* nop */
 
170
}
 
171
#endif
 
172
 
 
173
/*
 
174
 *
 
175
 * OPTIONS FLAGS
 
176
 *
 
177
 */
 
178
 
 
179
/* Enable core dump on failure. */
 
180
static int dump_on_failure = 0;
 
181
/* Default is to remove temp dirs and log data for successful tests. */
 
182
static int keep_temp_files = 0;
 
183
/* Default is to just report pass/fail for each test. */
 
184
static int verbosity = 0;
 
185
#define VERBOSITY_SUMMARY_ONLY -1 /* -q */
 
186
#define VERBOSITY_PASSFAIL 0   /* Default */
 
187
#define VERBOSITY_LIGHT_REPORT 1 /* -v */
 
188
#define VERBOSITY_FULL 2 /* -vv */
 
189
/* A few places generate even more output for verbosity > VERBOSITY_FULL,
 
190
 * mostly for debugging the test harness itself. */
 
191
/* Cumulative count of assertion failures. */
 
192
static int failures = 0;
 
193
/* Cumulative count of reported skips. */
 
194
static int skips = 0;
 
195
/* Cumulative count of assertions checked. */
 
196
static int assertions = 0;
 
197
 
 
198
/* Directory where uuencoded reference files can be found. */
 
199
static const char *refdir;
 
200
 
 
201
/*
 
202
 * Report log information selectively to console and/or disk log.
 
203
 */
 
204
static int log_console = 0;
 
205
static FILE *logfile;
 
206
static void
 
207
vlogprintf(const char *fmt, va_list ap)
 
208
{
 
209
#ifdef va_copy
 
210
        va_list lfap;
 
211
        va_copy(lfap, ap);
 
212
#endif
 
213
        if (log_console)
 
214
                vfprintf(stdout, fmt, ap);
 
215
        if (logfile != NULL)
 
216
#ifdef va_copy
 
217
                vfprintf(logfile, fmt, lfap);
 
218
        va_end(lfap);
 
219
#else
 
220
                vfprintf(logfile, fmt, ap);
 
221
#endif
 
222
}
 
223
 
 
224
static void
 
225
logprintf(const char *fmt, ...)
 
226
{
 
227
        va_list ap;
 
228
        va_start(ap, fmt);
 
229
        vlogprintf(fmt, ap);
 
230
        va_end(ap);
 
231
}
 
232
 
 
233
/* Set up a message to display only if next assertion fails. */
 
234
static char msgbuff[4096];
 
235
static const char *msg, *nextmsg;
 
236
void
 
237
failure(const char *fmt, ...)
 
238
{
 
239
        va_list ap;
 
240
        va_start(ap, fmt);
 
241
        vsprintf(msgbuff, fmt, ap);
 
242
        va_end(ap);
 
243
        nextmsg = msgbuff;
 
244
}
 
245
 
 
246
/*
 
247
 * Copy arguments into file-local variables.
 
248
 * This was added to permit vararg assert() functions without needing
 
249
 * variadic wrapper macros.  Turns out that the vararg capability is almost
 
250
 * never used, so almost all of the vararg assertions can be simplified
 
251
 * by removing the vararg capability and reworking the wrapper macro to
 
252
 * pass __FILE__, __LINE__ directly into the function instead of using
 
253
 * this hook.  I suspect this machinery is used so rarely that we
 
254
 * would be better off just removing it entirely.  That would simplify
 
255
 * the code here noticably.
 
256
 */
 
257
static const char *test_filename;
 
258
static int test_line;
 
259
static void *test_extra;
 
260
void assertion_setup(const char *filename, int line)
 
261
{
 
262
        test_filename = filename;
 
263
        test_line = line;
 
264
}
 
265
 
 
266
/* Called at the beginning of each assert() function. */
 
267
static void
 
268
assertion_count(const char *file, int line)
 
269
{
 
270
        (void)file; /* UNUSED */
 
271
        (void)line; /* UNUSED */
 
272
        ++assertions;
 
273
        /* Proper handling of "failure()" message. */
 
274
        msg = nextmsg;
 
275
        nextmsg = NULL;
 
276
        /* Uncomment to print file:line after every assertion.
 
277
         * Verbose, but occasionally useful in tracking down crashes. */
 
278
        /* printf("Checked %s:%d\n", file, line); */
 
279
}
 
280
 
 
281
/*
 
282
 * For each test source file, we remember how many times each
 
283
 * assertion was reported.  Cleared before each new test,
 
284
 * used by test_summarize().
 
285
 */
 
286
static struct line {
 
287
        int count;
 
288
        int skip;
 
289
}  failed_lines[10000];
 
290
 
 
291
/* Count this failure, setup up log destination and handle initial report. */
 
292
static void
 
293
failure_start(const char *filename, int line, const char *fmt, ...)
 
294
{
 
295
        va_list ap;
 
296
 
 
297
        /* Record another failure for this line. */
 
298
        ++failures;
 
299
        /* test_filename = filename; */
 
300
        failed_lines[line].count++;
 
301
 
 
302
        /* Determine whether to log header to console. */
 
303
        switch (verbosity) {
 
304
        case VERBOSITY_FULL:
 
305
                log_console = 1;
 
306
                break;
 
307
        case VERBOSITY_LIGHT_REPORT:
 
308
                log_console = (failed_lines[line].count < 2);
 
309
                break;
 
310
        default:
 
311
                log_console = 0;
 
312
        }
 
313
 
 
314
        /* Log file:line header for this failure */
 
315
        va_start(ap, fmt);
 
316
#if _MSC_VER
 
317
        logprintf("%s(%d): ", filename, line);
 
318
#else
 
319
        logprintf("%s:%d: ", filename, line);
 
320
#endif
 
321
        vlogprintf(fmt, ap);
 
322
        va_end(ap);
 
323
        logprintf("\n");
 
324
 
 
325
        if (msg != NULL && msg[0] != '\0') {
 
326
                logprintf("   Description: %s\n", msg);
 
327
                msg = NULL;
 
328
        }
 
329
 
 
330
        /* Determine whether to log details to console. */
 
331
        if (verbosity == VERBOSITY_LIGHT_REPORT)
 
332
                log_console = 0;
 
333
}
 
334
 
 
335
/* Complete reporting of failed tests. */
 
336
/*
 
337
 * The 'extra' hook here is used by libarchive to include libarchive
 
338
 * error messages with assertion failures.  It could also be used
 
339
 * to add strerror() output, for example.  Just define the EXTRA_DUMP()
 
340
 * macro appropriately.
 
341
 */
 
342
static void
 
343
failure_finish(void *extra)
 
344
{
 
345
        (void)extra; /* UNUSED (maybe) */
 
346
#ifdef EXTRA_DUMP
 
347
        if (extra != NULL)
 
348
                logprintf("   detail: %s\n", EXTRA_DUMP(extra));
 
349
#endif
 
350
 
 
351
        if (dump_on_failure) {
 
352
                fprintf(stderr,
 
353
                    " *** forcing core dump so failure can be debugged ***\n");
 
354
                *(char *)(NULL) = 0;
 
355
                exit(1);
 
356
        }
 
357
}
 
358
 
 
359
/* Inform user that we're skipping some checks. */
 
360
void
 
361
test_skipping(const char *fmt, ...)
 
362
{
 
363
        char buff[1024];
 
364
        va_list ap;
 
365
 
 
366
        va_start(ap, fmt);
 
367
        vsprintf(buff, fmt, ap);
 
368
        va_end(ap);
 
369
        /* failure_start() isn't quite right, but is awfully convenient. */
 
370
        failure_start(test_filename, test_line, "SKIPPING: %s", buff);
 
371
        --failures; /* Undo failures++ in failure_start() */
 
372
        /* Don't failure_finish() here. */
 
373
        /* Mark as skip, so doesn't count as failed test. */
 
374
        failed_lines[test_line].skip = 1;
 
375
        ++skips;
 
376
}
 
377
 
 
378
/*
 
379
 *
 
380
 * ASSERTIONS
 
381
 *
 
382
 */
 
383
 
 
384
/* Generic assert() just displays the failed condition. */
 
385
int
 
386
assertion_assert(const char *file, int line, int value,
 
387
    const char *condition, void *extra)
 
388
{
 
389
        assertion_count(file, line);
 
390
        if (!value) {
 
391
                failure_start(file, line, "Assertion failed: %s", condition);
 
392
                failure_finish(extra);
 
393
        }
 
394
        return (value);
 
395
}
 
396
 
 
397
/* chdir() and report any errors */
 
398
int
 
399
assertion_chdir(const char *file, int line, const char *pathname)
 
400
{
 
401
        assertion_count(file, line);
 
402
        if (chdir(pathname) == 0)
 
403
                return (1);
 
404
        failure_start(file, line, "chdir(\"%s\")", pathname);
 
405
        failure_finish(NULL);
 
406
        return (0);
 
407
 
 
408
}
 
409
 
 
410
/* Verify two integers are equal. */
 
411
int
 
412
assertion_equal_int(const char *file, int line,
 
413
    long long v1, const char *e1, long long v2, const char *e2, void *extra)
 
414
{
 
415
        assertion_count(file, line);
 
416
        if (v1 == v2)
 
417
                return (1);
 
418
        failure_start(file, line, "%s != %s", e1, e2);
 
419
        logprintf("      %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1);
 
420
        logprintf("      %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2);
 
421
        failure_finish(extra);
 
422
        return (0);
 
423
}
 
424
 
 
425
static void strdump(const char *e, const char *p)
 
426
{
 
427
        const char *q = p;
 
428
 
 
429
        logprintf("      %s = ", e);
 
430
        if (p == NULL) {
 
431
                logprintf("NULL");
 
432
                return;
 
433
        }
 
434
        logprintf("\"");
 
435
        while (*p != '\0') {
 
436
                unsigned int c = 0xff & *p++;
 
437
                switch (c) {
 
438
                case '\a': printf("\a"); break;
 
439
                case '\b': printf("\b"); break;
 
440
                case '\n': printf("\n"); break;
 
441
                case '\r': printf("\r"); break;
 
442
                default:
 
443
                        if (c >= 32 && c < 127)
 
444
                                logprintf("%c", c);
 
445
                        else
 
446
                                logprintf("\\x%02X", c);
 
447
                }
 
448
        }
 
449
        logprintf("\"");
 
450
        logprintf(" (length %d)\n", q == NULL ? -1 : (int)strlen(q));
 
451
}
 
452
 
 
453
/* Verify two strings are equal, dump them if not. */
 
454
int
 
455
assertion_equal_string(const char *file, int line,
 
456
    const char *v1, const char *e1,
 
457
    const char *v2, const char *e2,
 
458
    void *extra)
 
459
{
 
460
        assertion_count(file, line);
 
461
        if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0))
 
462
                return (1);
 
463
        failure_start(file, line, "%s != %s", e1, e2);
 
464
        strdump(e1, v1);
 
465
        strdump(e2, v2);
 
466
        failure_finish(extra);
 
467
        return (0);
 
468
}
 
469
 
 
470
static void
 
471
wcsdump(const char *e, const wchar_t *w)
 
472
{
 
473
        logprintf("      %s = ", e);
 
474
        if (w == NULL) {
 
475
                logprintf("(null)");
 
476
                return;
 
477
        }
 
478
        logprintf("\"");
 
479
        while (*w != L'\0') {
 
480
                unsigned int c = *w++;
 
481
                if (c >= 32 && c < 127)
 
482
                        logprintf("%c", c);
 
483
                else if (c < 256)
 
484
                        logprintf("\\x%02X", c);
 
485
                else if (c < 0x10000)
 
486
                        logprintf("\\u%04X", c);
 
487
                else
 
488
                        logprintf("\\U%08X", c);
 
489
        }
 
490
        logprintf("\"\n");
 
491
}
 
492
 
 
493
#ifndef HAVE_WCSCMP
 
494
static int
 
495
wcscmp(const wchar_t *s1, const wchar_t *s2)
 
496
{
 
497
 
 
498
        while (*s1 == *s2++) {
 
499
                if (*s1++ == L'\0')
 
500
                        return 0;
 
501
        }
 
502
        if (*s1 > *--s2)
 
503
                return 1;
 
504
        else
 
505
                return -1;
 
506
}
 
507
#endif
 
508
 
 
509
/* Verify that two wide strings are equal, dump them if not. */
 
510
int
 
511
assertion_equal_wstring(const char *file, int line,
 
512
    const wchar_t *v1, const char *e1,
 
513
    const wchar_t *v2, const char *e2,
 
514
    void *extra)
 
515
{
 
516
        assertion_count(file, line);
 
517
        if (v1 == v2 || wcscmp(v1, v2) == 0)
 
518
                return (1);
 
519
        failure_start(file, line, "%s != %s", e1, e2);
 
520
        wcsdump(e1, v1);
 
521
        wcsdump(e2, v2);
 
522
        failure_finish(extra);
 
523
        return (0);
 
524
}
 
525
 
 
526
/*
 
527
 * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
 
528
 * any bytes in p that differ from ref will be highlighted with '_'
 
529
 * before and after the hex value.
 
530
 */
 
531
static void
 
532
hexdump(const char *p, const char *ref, size_t l, size_t offset)
 
533
{
 
534
        size_t i, j;
 
535
        char sep;
 
536
 
 
537
        if (p == NULL) {
 
538
                logprintf("(null)\n");
 
539
                return;
 
540
        }
 
541
        for(i=0; i < l; i+=16) {
 
542
                logprintf("%04x", (unsigned)(i + offset));
 
543
                sep = ' ';
 
544
                for (j = 0; j < 16 && i + j < l; j++) {
 
545
                        if (ref != NULL && p[i + j] != ref[i + j])
 
546
                                sep = '_';
 
547
                        logprintf("%c%02x", sep, 0xff & (int)p[i+j]);
 
548
                        if (ref != NULL && p[i + j] == ref[i + j])
 
549
                                sep = ' ';
 
550
                }
 
551
                for (; j < 16; j++) {
 
552
                        logprintf("%c  ", sep);
 
553
                        sep = ' ';
 
554
                }
 
555
                logprintf("%c", sep);
 
556
                for (j=0; j < 16 && i + j < l; j++) {
 
557
                        int c = p[i + j];
 
558
                        if (c >= ' ' && c <= 126)
 
559
                                logprintf("%c", c);
 
560
                        else
 
561
                                logprintf(".");
 
562
                }
 
563
                logprintf("\n");
 
564
        }
 
565
}
 
566
 
 
567
/* Verify that two blocks of memory are the same, display the first
 
568
 * block of differences if they're not. */
 
569
int
 
570
assertion_equal_mem(const char *file, int line,
 
571
    const void *_v1, const char *e1,
 
572
    const void *_v2, const char *e2,
 
573
    size_t l, const char *ld, void *extra)
 
574
{
 
575
        const char *v1 = (const char *)_v1;
 
576
        const char *v2 = (const char *)_v2;
 
577
        size_t offset;
 
578
 
 
579
        assertion_count(file, line);
 
580
        if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0))
 
581
                return (1);
 
582
 
 
583
        failure_start(file, line, "%s != %s", e1, e2);
 
584
        logprintf("      size %s = %d\n", ld, (int)l);
 
585
        /* Dump 48 bytes (3 lines) so that the first difference is
 
586
         * in the second line. */
 
587
        offset = 0;
 
588
        while (l > 64 && memcmp(v1, v2, 32) == 0) {
 
589
                /* Two lines agree, so step forward one line. */
 
590
                v1 += 16;
 
591
                v2 += 16;
 
592
                l -= 16;
 
593
                offset += 16;
 
594
        }
 
595
        logprintf("      Dump of %s\n", e1);
 
596
        hexdump(v1, v2, l < 64 ? l : 64, offset);
 
597
        logprintf("      Dump of %s\n", e2);
 
598
        hexdump(v2, v1, l < 64 ? l : 64, offset);
 
599
        logprintf("\n");
 
600
        failure_finish(extra);
 
601
        return (0);
 
602
}
 
603
 
 
604
/* Verify that the named file exists and is empty. */
 
605
int
 
606
assertion_empty_file(const char *f1fmt, ...)
 
607
{
 
608
        char buff[1024];
 
609
        char f1[1024];
 
610
        struct stat st;
 
611
        va_list ap;
 
612
        ssize_t s;
 
613
        FILE *f;
 
614
 
 
615
        assertion_count(test_filename, test_line);
 
616
        va_start(ap, f1fmt);
 
617
        vsprintf(f1, f1fmt, ap);
 
618
        va_end(ap);
 
619
 
 
620
        if (stat(f1, &st) != 0) {
 
621
                failure_start(test_filename, test_line, "Stat failed: %s", f1);
 
622
                failure_finish(NULL);
 
623
                return (0);
 
624
        }
 
625
        if (st.st_size == 0)
 
626
                return (1);
 
627
 
 
628
        failure_start(test_filename, test_line, "File should be empty: %s", f1);
 
629
        logprintf("    File size: %d\n", (int)st.st_size);
 
630
        logprintf("    Contents:\n");
 
631
        f = fopen(f1, "rb");
 
632
        if (f == NULL) {
 
633
                logprintf("    Unable to open %s\n", f1);
 
634
        } else {
 
635
                s = ((off_t)sizeof(buff) < st.st_size) ?
 
636
                    (ssize_t)sizeof(buff) : (ssize_t)st.st_size;
 
637
                s = fread(buff, 1, s, f);
 
638
                hexdump(buff, NULL, s, 0);
 
639
                fclose(f);
 
640
        }
 
641
        failure_finish(NULL);
 
642
        return (0);
 
643
}
 
644
 
 
645
/* Verify that the named file exists and is not empty. */
 
646
int
 
647
assertion_non_empty_file(const char *f1fmt, ...)
 
648
{
 
649
        char f1[1024];
 
650
        struct stat st;
 
651
        va_list ap;
 
652
 
 
653
        assertion_count(test_filename, test_line);
 
654
        va_start(ap, f1fmt);
 
655
        vsprintf(f1, f1fmt, ap);
 
656
        va_end(ap);
 
657
 
 
658
        if (stat(f1, &st) != 0) {
 
659
                failure_start(test_filename, test_line, "Stat failed: %s", f1);
 
660
                failure_finish(NULL);
 
661
                return (0);
 
662
        }
 
663
        if (st.st_size == 0) {
 
664
                failure_start(test_filename, test_line, "File empty: %s", f1);
 
665
                failure_finish(NULL);
 
666
                return (0);
 
667
        }
 
668
        return (1);
 
669
}
 
670
 
 
671
/* Verify that two files have the same contents. */
 
672
/* TODO: hexdump the first bytes that actually differ. */
 
673
int
 
674
assertion_equal_file(const char *fn1, const char *f2pattern, ...)
 
675
{
 
676
        char fn2[1024];
 
677
        va_list ap;
 
678
        char buff1[1024];
 
679
        char buff2[1024];
 
680
        FILE *f1, *f2;
 
681
        int n1, n2;
 
682
 
 
683
        assertion_count(test_filename, test_line);
 
684
        va_start(ap, f2pattern);
 
685
        vsprintf(fn2, f2pattern, ap);
 
686
        va_end(ap);
 
687
 
 
688
        f1 = fopen(fn1, "rb");
 
689
        f2 = fopen(fn2, "rb");
 
690
        for (;;) {
 
691
                n1 = fread(buff1, 1, sizeof(buff1), f1);
 
692
                n2 = fread(buff2, 1, sizeof(buff2), f2);
 
693
                if (n1 != n2)
 
694
                        break;
 
695
                if (n1 == 0 && n2 == 0) {
 
696
                        fclose(f1);
 
697
                        fclose(f2);
 
698
                        return (1);
 
699
                }
 
700
                if (memcmp(buff1, buff2, n1) != 0)
 
701
                        break;
 
702
        }
 
703
        fclose(f1);
 
704
        fclose(f2);
 
705
        failure_start(test_filename, test_line, "Files not identical");
 
706
        logprintf("  file1=\"%s\"\n", fn1);
 
707
        logprintf("  file2=\"%s\"\n", fn2);
 
708
        failure_finish(test_extra);
 
709
        return (0);
 
710
}
 
711
 
 
712
/* Verify that the named file does exist. */
 
713
int
 
714
assertion_file_exists(const char *fpattern, ...)
 
715
{
 
716
        char f[1024];
 
717
        va_list ap;
 
718
 
 
719
        assertion_count(test_filename, test_line);
 
720
        va_start(ap, fpattern);
 
721
        vsprintf(f, fpattern, ap);
 
722
        va_end(ap);
 
723
 
 
724
#if defined(_WIN32) && !defined(__CYGWIN__)
 
725
        if (!_access(f, 0))
 
726
                return (1);
 
727
#else
 
728
        if (!access(f, F_OK))
 
729
                return (1);
 
730
#endif
 
731
        failure_start(test_filename, test_line, "File should exist: %s", f);
 
732
        failure_finish(test_extra);
 
733
        return (0);
 
734
}
 
735
 
 
736
/* Verify that the named file doesn't exist. */
 
737
int
 
738
assertion_file_not_exists(const char *fpattern, ...)
 
739
{
 
740
        char f[1024];
 
741
        va_list ap;
 
742
 
 
743
        assertion_count(test_filename, test_line);
 
744
        va_start(ap, fpattern);
 
745
        vsprintf(f, fpattern, ap);
 
746
        va_end(ap);
 
747
 
 
748
#if defined(_WIN32) && !defined(__CYGWIN__)
 
749
        if (_access(f, 0))
 
750
                return (1);
 
751
#else
 
752
        if (access(f, F_OK))
 
753
                return (1);
 
754
#endif
 
755
        failure_start(test_filename, test_line, "File should not exist: %s", f);
 
756
        failure_finish(test_extra);
 
757
        return (0);
 
758
}
 
759
 
 
760
/* Compare the contents of a file to a block of memory. */
 
761
int
 
762
assertion_file_contents(const void *buff, int s, const char *fpattern, ...)
 
763
{
 
764
        char fn[1024];
 
765
        va_list ap;
 
766
        char *contents;
 
767
        FILE *f;
 
768
        int n;
 
769
 
 
770
        assertion_count(test_filename, test_line);
 
771
        va_start(ap, fpattern);
 
772
        vsprintf(fn, fpattern, ap);
 
773
        va_end(ap);
 
774
 
 
775
        f = fopen(fn, "rb");
 
776
        if (f == NULL) {
 
777
                failure_start(test_filename, test_line,
 
778
                    "File should exist: %s", fn);
 
779
                failure_finish(test_extra);
 
780
                return (0);
 
781
        }
 
782
        contents = malloc(s * 2);
 
783
        n = fread(contents, 1, s * 2, f);
 
784
        fclose(f);
 
785
        if (n == s && memcmp(buff, contents, s) == 0) {
 
786
                free(contents);
 
787
                return (1);
 
788
        }
 
789
        failure_start(test_filename, test_line, "File contents don't match");
 
790
        logprintf("  file=\"%s\"\n", fn);
 
791
        if (n > 0)
 
792
                hexdump(contents, buff, n > 512 ? 512 : n, 0);
 
793
        else {
 
794
                logprintf("  File empty, contents should be:\n");
 
795
                hexdump(buff, NULL, s > 512 ? 512 : s, 0);
 
796
        }
 
797
        failure_finish(test_extra);
 
798
        free(contents);
 
799
        return (0);
 
800
}
 
801
 
 
802
/* Check the contents of a text file, being tolerant of line endings. */
 
803
int
 
804
assertion_text_file_contents(const char *buff, const char *fn)
 
805
{
 
806
        char *contents;
 
807
        const char *btxt, *ftxt;
 
808
        FILE *f;
 
809
        int n, s;
 
810
 
 
811
        assertion_count(test_filename, test_line);
 
812
        f = fopen(fn, "r");
 
813
        s = strlen(buff);
 
814
        contents = malloc(s * 2 + 128);
 
815
        n = fread(contents, 1, s * 2 + 128 - 1, f);
 
816
        if (n >= 0)
 
817
                contents[n] = '\0';
 
818
        fclose(f);
 
819
        /* Compare texts. */
 
820
        btxt = buff;
 
821
        ftxt = (const char *)contents;
 
822
        while (*btxt != '\0' && *ftxt != '\0') {
 
823
                if (*btxt == *ftxt) {
 
824
                        ++btxt;
 
825
                        ++ftxt;
 
826
                        continue;
 
827
                }
 
828
                if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') {
 
829
                        /* Pass over different new line characters. */
 
830
                        ++btxt;
 
831
                        ftxt += 2;
 
832
                        continue;
 
833
                }
 
834
                break;
 
835
        }
 
836
        if (*btxt == '\0' && *ftxt == '\0') {
 
837
                free(contents);
 
838
                return (1);
 
839
        }
 
840
        failure_start(test_filename, test_line, "Contents don't match");
 
841
        logprintf("  file=\"%s\"\n", fn);
 
842
        if (n > 0)
 
843
                hexdump(contents, buff, n, 0);
 
844
        else {
 
845
                logprintf("  File empty, contents should be:\n");
 
846
                hexdump(buff, NULL, s, 0);
 
847
        }
 
848
        failure_finish(test_extra);
 
849
        free(contents);
 
850
        return (0);
 
851
}
 
852
 
 
853
/* Verify that a text file contains the specified lines, regardless of order */
 
854
/* This could be more efficient if we sorted both sets of lines, etc, but
 
855
 * since this is used only for testing and only ever deals with a dozen or so
 
856
 * lines at a time, this relatively crude approach is just fine. */
 
857
int
 
858
assertion_file_contains_lines_any_order(const char *file, int line,
 
859
    const char *pathname, const char *lines[])
 
860
{
 
861
        char *buff;
 
862
        size_t buff_size;
 
863
        size_t expected_count, actual_count, i, j;
 
864
        char **expected;
 
865
        char *p, **actual;
 
866
        char c;
 
867
        int expected_failure = 0, actual_failure = 0;
 
868
 
 
869
        assertion_count(file, line);
 
870
 
 
871
        buff = slurpfile(&buff_size, "%s", pathname);
 
872
        if (buff == NULL) {
 
873
                failure_start(pathname, line, "Can't read file: %s", pathname);
 
874
                failure_finish(NULL);
 
875
                return (0);
 
876
        }
 
877
 
 
878
        // Make a copy of the provided lines and count up the expected file size.
 
879
        expected_count = 0;
 
880
        for (i = 0; lines[i] != NULL; ++i) {
 
881
        }
 
882
        expected_count = i;
 
883
        expected = malloc(sizeof(char *) * expected_count);
 
884
        for (i = 0; lines[i] != NULL; ++i) {
 
885
                expected[i] = strdup(lines[i]);
 
886
        }
 
887
 
 
888
        // Break the file into lines
 
889
        actual_count = 0;
 
890
        for (c = '\0', p = buff; p < buff + buff_size; ++p) {
 
891
                if (*p == '\x0d' || *p == '\x0a')
 
892
                        *p = '\0';
 
893
                if (c == '\0' && *p != '\0')
 
894
                        ++actual_count;
 
895
                c = *p;
 
896
        }
 
897
        actual = malloc(sizeof(char *) * actual_count);
 
898
        for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) {
 
899
                if (*p != '\0') {
 
900
                        actual[j] = p;
 
901
                        ++j;
 
902
                }
 
903
        }
 
904
 
 
905
        // Erase matching lines from both lists
 
906
        for (i = 0; i < expected_count; ++i) {
 
907
                if (expected[i] == NULL)
 
908
                        continue;
 
909
                for (j = 0; j < actual_count; ++j) {
 
910
                        if (actual[j] == NULL)
 
911
                                continue;
 
912
                        if (strcmp(expected[i], actual[j]) == 0) {
 
913
                                free(expected[i]);
 
914
                                expected[i] = NULL;
 
915
                                actual[j] = NULL;
 
916
                                break;
 
917
                        }
 
918
                }
 
919
        }
 
920
 
 
921
        // If there's anything left, it's a failure
 
922
        for (i = 0; i < expected_count; ++i) {
 
923
                if (expected[i] != NULL)
 
924
                        ++expected_failure;
 
925
        }
 
926
        for (j = 0; j < actual_count; ++j) {
 
927
                if (actual[j] != NULL)
 
928
                        ++actual_failure;
 
929
        }
 
930
        if (expected_failure == 0 && actual_failure == 0) {
 
931
                free(buff);
 
932
                free(expected);
 
933
                free(actual);
 
934
                return (1);
 
935
        }
 
936
        failure_start(file, line, "File doesn't match: %s", pathname);
 
937
        for (i = 0; i < expected_count; ++i) {
 
938
                if (expected[i] != NULL) {
 
939
                        logprintf("  Expected but not present: %s\n", expected[i]);
 
940
                        free(expected[i]);
 
941
                }
 
942
        }
 
943
        for (j = 0; j < actual_count; ++j) {
 
944
                if (actual[j] != NULL)
 
945
                        logprintf("  Present but not expected: %s\n", actual[j]);
 
946
        }
 
947
        failure_finish(NULL);
 
948
        free(buff);
 
949
        free(expected);
 
950
        free(actual);
 
951
        return (0);
 
952
}
 
953
 
 
954
/* Test that two paths point to the same file. */
 
955
/* As a side-effect, asserts that both files exist. */
 
956
static int
 
957
is_hardlink(const char *file, int line,
 
958
    const char *path1, const char *path2)
 
959
{
 
960
#if defined(_WIN32) && !defined(__CYGWIN__)
 
961
        BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
 
962
        int r;
 
963
 
 
964
        assertion_count(file, line);
 
965
        r = my_GetFileInformationByName(path1, &bhfi1);
 
966
        if (r == 0) {
 
967
                failure_start(file, line, "File %s can't be inspected?", path1);
 
968
                failure_finish(NULL);
 
969
                return (0);
 
970
        }
 
971
        r = my_GetFileInformationByName(path2, &bhfi2);
 
972
        if (r == 0) {
 
973
                failure_start(file, line, "File %s can't be inspected?", path2);
 
974
                failure_finish(NULL);
 
975
                return (0);
 
976
        }
 
977
        return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
 
978
                && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
 
979
                && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow);
 
980
#else
 
981
        struct stat st1, st2;
 
982
        int r;
 
983
 
 
984
        assertion_count(file, line);
 
985
        r = lstat(path1, &st1);
 
986
        if (r != 0) {
 
987
                failure_start(file, line, "File should exist: %s", path1);
 
988
                failure_finish(NULL);
 
989
                return (0);
 
990
        }
 
991
        r = lstat(path2, &st2);
 
992
        if (r != 0) {
 
993
                failure_start(file, line, "File should exist: %s", path2);
 
994
                failure_finish(NULL);
 
995
                return (0);
 
996
        }
 
997
        return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev);
 
998
#endif
 
999
}
 
1000
 
 
1001
int
 
1002
assertion_is_hardlink(const char *file, int line,
 
1003
    const char *path1, const char *path2)
 
1004
{
 
1005
        if (is_hardlink(file, line, path1, path2))
 
1006
                return (1);
 
1007
        failure_start(file, line,
 
1008
            "Files %s and %s are not hardlinked", path1, path2);
 
1009
        failure_finish(NULL);
 
1010
        return (0);
 
1011
}
 
1012
 
 
1013
int
 
1014
assertion_is_not_hardlink(const char *file, int line,
 
1015
    const char *path1, const char *path2)
 
1016
{
 
1017
        if (!is_hardlink(file, line, path1, path2))
 
1018
                return (1);
 
1019
        failure_start(file, line,
 
1020
            "Files %s and %s should not be hardlinked", path1, path2);
 
1021
        failure_finish(NULL);
 
1022
        return (0);
 
1023
}
 
1024
 
 
1025
/* Verify a/b/mtime of 'pathname'. */
 
1026
/* If 'recent', verify that it's within last 10 seconds. */
 
1027
static int
 
1028
assertion_file_time(const char *file, int line,
 
1029
    const char *pathname, long t, long nsec, char type, int recent)
 
1030
{
 
1031
        long long filet, filet_nsec;
 
1032
        int r;
 
1033
 
 
1034
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1035
#define EPOC_TIME       (116444736000000000ULL)
 
1036
        FILETIME ftime, fbirthtime, fatime, fmtime;
 
1037
        ULARGE_INTEGER wintm;
 
1038
        HANDLE h;
 
1039
        ftime.dwLowDateTime = 0;
 
1040
        ftime.dwHighDateTime = 0;
 
1041
 
 
1042
        assertion_count(file, line);
 
1043
        h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
 
1044
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
1045
        if (h == INVALID_HANDLE_VALUE) {
 
1046
                failure_start(file, line, "Can't access %s\n", pathname);
 
1047
                failure_finish(NULL);
 
1048
                return (0);
 
1049
        }
 
1050
        r = GetFileTime(h, &fbirthtime, &fatime, &fmtime);
 
1051
        switch (type) {
 
1052
        case 'a': ftime = fatime; break;
 
1053
        case 'b': ftime = fbirthtime; break;
 
1054
        case 'm': ftime = fmtime; break;
 
1055
        }
 
1056
        CloseHandle(h);
 
1057
        if (r == 0) {
 
1058
                failure_start(file, line, "Can't GetFileTime %s\n", pathname);
 
1059
                failure_finish(NULL);
 
1060
                return (0);
 
1061
        }
 
1062
        wintm.LowPart = ftime.dwLowDateTime;
 
1063
        wintm.HighPart = ftime.dwHighDateTime;
 
1064
        filet = (wintm.QuadPart - EPOC_TIME) / 10000000;
 
1065
        filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100;
 
1066
        nsec = (nsec / 100) * 100; /* Round the request */
 
1067
#else
 
1068
        struct stat st;
 
1069
 
 
1070
        assertion_count(file, line);
 
1071
        r = lstat(pathname, &st);
 
1072
        if (r != 0) {
 
1073
                failure_start(file, line, "Can't stat %s\n", pathname);
 
1074
                failure_finish(NULL);
 
1075
                return (0);
 
1076
        }
 
1077
        switch (type) {
 
1078
        case 'a': filet = st.st_atime; break;
 
1079
        case 'm': filet = st.st_mtime; break;
 
1080
        case 'b': filet = 0; break;
 
1081
        default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
 
1082
                exit(1);
 
1083
        }
 
1084
#if defined(__FreeBSD__)
 
1085
        switch (type) {
 
1086
        case 'a': filet_nsec = st.st_atimespec.tv_nsec; break;
 
1087
        case 'b': filet = st.st_birthtime;
 
1088
                filet_nsec = st.st_birthtimespec.tv_nsec; break;
 
1089
        case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break;
 
1090
        default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
 
1091
                exit(1);
 
1092
        }
 
1093
        /* FreeBSD generally only stores to microsecond res, so round. */
 
1094
        filet_nsec = (filet_nsec / 1000) * 1000;
 
1095
        nsec = (nsec / 1000) * 1000;
 
1096
#else
 
1097
        filet_nsec = nsec = 0;  /* Generic POSIX only has whole seconds. */
 
1098
        if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */
 
1099
#if defined(__HAIKU__)
 
1100
        if (type == 'a') return (1); /* Haiku doesn't have atime. */
 
1101
#endif
 
1102
#endif
 
1103
#endif
 
1104
        if (recent) {
 
1105
                /* Check that requested time is up-to-date. */
 
1106
                time_t now = time(NULL);
 
1107
                if (filet < now - 10 || filet > now + 1) {
 
1108
                        failure_start(file, line,
 
1109
                            "File %s has %ctime %ld, %ld seconds ago\n",
 
1110
                            pathname, type, filet, now - filet);
 
1111
                        failure_finish(NULL);
 
1112
                        return (0);
 
1113
                }
 
1114
        } else if (filet != t || filet_nsec != nsec) {
 
1115
                failure_start(file, line,
 
1116
                    "File %s has %ctime %ld.%09ld, expected %ld.%09ld",
 
1117
                    pathname, type, filet, filet_nsec, t, nsec);
 
1118
                failure_finish(NULL);
 
1119
                return (0);
 
1120
        }
 
1121
        return (1);
 
1122
}
 
1123
 
 
1124
/* Verify atime of 'pathname'. */
 
1125
int
 
1126
assertion_file_atime(const char *file, int line,
 
1127
    const char *pathname, long t, long nsec)
 
1128
{
 
1129
        return assertion_file_time(file, line, pathname, t, nsec, 'a', 0);
 
1130
}
 
1131
 
 
1132
/* Verify atime of 'pathname' is up-to-date. */
 
1133
int
 
1134
assertion_file_atime_recent(const char *file, int line, const char *pathname)
 
1135
{
 
1136
        return assertion_file_time(file, line, pathname, 0, 0, 'a', 1);
 
1137
}
 
1138
 
 
1139
/* Verify birthtime of 'pathname'. */
 
1140
int
 
1141
assertion_file_birthtime(const char *file, int line,
 
1142
    const char *pathname, long t, long nsec)
 
1143
{
 
1144
        return assertion_file_time(file, line, pathname, t, nsec, 'b', 0);
 
1145
}
 
1146
 
 
1147
/* Verify birthtime of 'pathname' is up-to-date. */
 
1148
int
 
1149
assertion_file_birthtime_recent(const char *file, int line,
 
1150
    const char *pathname)
 
1151
{
 
1152
        return assertion_file_time(file, line, pathname, 0, 0, 'b', 1);
 
1153
}
 
1154
 
 
1155
/* Verify mtime of 'pathname'. */
 
1156
int
 
1157
assertion_file_mtime(const char *file, int line,
 
1158
    const char *pathname, long t, long nsec)
 
1159
{
 
1160
        return assertion_file_time(file, line, pathname, t, nsec, 'm', 0);
 
1161
}
 
1162
 
 
1163
/* Verify mtime of 'pathname' is up-to-date. */
 
1164
int
 
1165
assertion_file_mtime_recent(const char *file, int line, const char *pathname)
 
1166
{
 
1167
        return assertion_file_time(file, line, pathname, 0, 0, 'm', 1);
 
1168
}
 
1169
 
 
1170
/* Verify number of links to 'pathname'. */
 
1171
int
 
1172
assertion_file_nlinks(const char *file, int line,
 
1173
    const char *pathname, int nlinks)
 
1174
{
 
1175
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1176
        BY_HANDLE_FILE_INFORMATION bhfi;
 
1177
        int r;
 
1178
 
 
1179
        assertion_count(file, line);
 
1180
        r = my_GetFileInformationByName(pathname, &bhfi);
 
1181
        if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks)
 
1182
                return (1);
 
1183
        failure_start(file, line, "File %s has %d links, expected %d",
 
1184
            pathname, bhfi.nNumberOfLinks, nlinks);
 
1185
        failure_finish(NULL);
 
1186
        return (0);
 
1187
#else
 
1188
        struct stat st;
 
1189
        int r;
 
1190
 
 
1191
        assertion_count(file, line);
 
1192
        r = lstat(pathname, &st);
 
1193
        if (r == 0 && st.st_nlink == nlinks)
 
1194
                        return (1);
 
1195
        failure_start(file, line, "File %s has %d links, expected %d",
 
1196
            pathname, st.st_nlink, nlinks);
 
1197
        failure_finish(NULL);
 
1198
        return (0);
 
1199
#endif
 
1200
}
 
1201
 
 
1202
/* Verify size of 'pathname'. */
 
1203
int
 
1204
assertion_file_size(const char *file, int line, const char *pathname, long size)
 
1205
{
 
1206
        int64_t filesize;
 
1207
        int r;
 
1208
 
 
1209
        assertion_count(file, line);
 
1210
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1211
        {
 
1212
                BY_HANDLE_FILE_INFORMATION bhfi;
 
1213
                r = !my_GetFileInformationByName(pathname, &bhfi);
 
1214
                filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow;
 
1215
        }
 
1216
#else
 
1217
        {
 
1218
                struct stat st;
 
1219
                r = lstat(pathname, &st);
 
1220
                filesize = st.st_size;
 
1221
        }
 
1222
#endif
 
1223
        if (r == 0 && filesize == size)
 
1224
                        return (1);
 
1225
        failure_start(file, line, "File %s has size %ld, expected %ld",
 
1226
            pathname, (long)filesize, (long)size);
 
1227
        failure_finish(NULL);
 
1228
        return (0);
 
1229
}
 
1230
 
 
1231
/* Assert that 'pathname' is a dir.  If mode >= 0, verify that too. */
 
1232
int
 
1233
assertion_is_dir(const char *file, int line, const char *pathname, int mode)
 
1234
{
 
1235
        struct stat st;
 
1236
        int r;
 
1237
 
 
1238
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1239
        (void)mode; /* UNUSED */
 
1240
#endif
 
1241
        assertion_count(file, line);
 
1242
        r = lstat(pathname, &st);
 
1243
        if (r != 0) {
 
1244
                failure_start(file, line, "Dir should exist: %s", pathname);
 
1245
                failure_finish(NULL);
 
1246
                return (0);
 
1247
        }
 
1248
        if (!S_ISDIR(st.st_mode)) {
 
1249
                failure_start(file, line, "%s is not a dir", pathname);
 
1250
                failure_finish(NULL);
 
1251
                return (0);
 
1252
        }
 
1253
#if !defined(_WIN32) || defined(__CYGWIN__)
 
1254
        /* Windows doesn't handle permissions the same way as POSIX,
 
1255
         * so just ignore the mode tests. */
 
1256
        /* TODO: Can we do better here? */
 
1257
        if (mode >= 0 && mode != (st.st_mode & 07777)) {
 
1258
                failure_start(file, line, "Dir %s has wrong mode", pathname);
 
1259
                logprintf("  Expected: 0%3o\n", mode);
 
1260
                logprintf("  Found: 0%3o\n", st.st_mode & 07777);
 
1261
                failure_finish(NULL);
 
1262
                return (0);
 
1263
        }
 
1264
#endif
 
1265
        return (1);
 
1266
}
 
1267
 
 
1268
/* Verify that 'pathname' is a regular file.  If 'mode' is >= 0,
 
1269
 * verify that too. */
 
1270
int
 
1271
assertion_is_reg(const char *file, int line, const char *pathname, int mode)
 
1272
{
 
1273
        struct stat st;
 
1274
        int r;
 
1275
 
 
1276
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1277
        (void)mode; /* UNUSED */
 
1278
#endif
 
1279
        assertion_count(file, line);
 
1280
        r = lstat(pathname, &st);
 
1281
        if (r != 0 || !S_ISREG(st.st_mode)) {
 
1282
                failure_start(file, line, "File should exist: %s", pathname);
 
1283
                failure_finish(NULL);
 
1284
                return (0);
 
1285
        }
 
1286
#if !defined(_WIN32) || defined(__CYGWIN__)
 
1287
        /* Windows doesn't handle permissions the same way as POSIX,
 
1288
         * so just ignore the mode tests. */
 
1289
        /* TODO: Can we do better here? */
 
1290
        if (mode >= 0 && mode != (st.st_mode & 07777)) {
 
1291
                failure_start(file, line, "File %s has wrong mode", pathname);
 
1292
                logprintf("  Expected: 0%3o\n", mode);
 
1293
                logprintf("  Found: 0%3o\n", st.st_mode & 07777);
 
1294
                failure_finish(NULL);
 
1295
                return (0);
 
1296
        }
 
1297
#endif
 
1298
        return (1);
 
1299
}
 
1300
 
 
1301
/* Check whether 'pathname' is a symbolic link.  If 'contents' is
 
1302
 * non-NULL, verify that the symlink has those contents. */
 
1303
static int
 
1304
is_symlink(const char *file, int line,
 
1305
    const char *pathname, const char *contents)
 
1306
{
 
1307
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1308
        (void)pathname; /* UNUSED */
 
1309
        (void)contents; /* UNUSED */
 
1310
        assertion_count(file, line);
 
1311
        /* Windows sort-of has real symlinks, but they're only usable
 
1312
         * by privileged users and are crippled even then, so there's
 
1313
         * really not much point in bothering with this. */
 
1314
        return (0);
 
1315
#else
 
1316
        char buff[300];
 
1317
        struct stat st;
 
1318
        ssize_t linklen;
 
1319
        int r;
 
1320
 
 
1321
        assertion_count(file, line);
 
1322
        r = lstat(pathname, &st);
 
1323
        if (r != 0) {
 
1324
                failure_start(file, line,
 
1325
                    "Symlink should exist: %s", pathname);
 
1326
                failure_finish(NULL);
 
1327
                return (0);
 
1328
        }
 
1329
        if (!S_ISLNK(st.st_mode))
 
1330
                return (0);
 
1331
        if (contents == NULL)
 
1332
                return (1);
 
1333
        linklen = readlink(pathname, buff, sizeof(buff));
 
1334
        if (linklen < 0) {
 
1335
                failure_start(file, line, "Can't read symlink %s", pathname);
 
1336
                failure_finish(NULL);
 
1337
                return (0);
 
1338
        }
 
1339
        buff[linklen] = '\0';
 
1340
        if (strcmp(buff, contents) != 0)
 
1341
                return (0);
 
1342
        return (1);
 
1343
#endif
 
1344
}
 
1345
 
 
1346
/* Assert that path is a symlink that (optionally) contains contents. */
 
1347
int
 
1348
assertion_is_symlink(const char *file, int line,
 
1349
    const char *path, const char *contents)
 
1350
{
 
1351
        if (is_symlink(file, line, path, contents))
 
1352
                return (1);
 
1353
        if (contents)
 
1354
                failure_start(file, line, "File %s is not a symlink to %s",
 
1355
                    path, contents);
 
1356
        else
 
1357
                failure_start(file, line, "File %s is not a symlink", path);
 
1358
        failure_finish(NULL);
 
1359
        return (0);
 
1360
}
 
1361
 
 
1362
 
 
1363
/* Create a directory and report any errors. */
 
1364
int
 
1365
assertion_make_dir(const char *file, int line, const char *dirname, int mode)
 
1366
{
 
1367
        assertion_count(file, line);
 
1368
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1369
        (void)mode; /* UNUSED */
 
1370
        if (0 == _mkdir(dirname))
 
1371
                return (1);
 
1372
#else
 
1373
        if (0 == mkdir(dirname, mode))
 
1374
                return (1);
 
1375
#endif
 
1376
        failure_start(file, line, "Could not create directory %s", dirname);
 
1377
        failure_finish(NULL);
 
1378
        return(0);
 
1379
}
 
1380
 
 
1381
/* Create a file with the specified contents and report any failures. */
 
1382
int
 
1383
assertion_make_file(const char *file, int line,
 
1384
    const char *path, int mode, const char *contents)
 
1385
{
 
1386
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1387
        /* TODO: Rework this to set file mode as well. */
 
1388
        FILE *f;
 
1389
        (void)mode; /* UNUSED */
 
1390
        assertion_count(file, line);
 
1391
        f = fopen(path, "wb");
 
1392
        if (f == NULL) {
 
1393
                failure_start(file, line, "Could not create file %s", path);
 
1394
                failure_finish(NULL);
 
1395
                return (0);
 
1396
        }
 
1397
        if (contents != NULL) {
 
1398
                if (strlen(contents)
 
1399
                    != fwrite(contents, 1, strlen(contents), f)) {
 
1400
                        fclose(f);
 
1401
                        failure_start(file, line,
 
1402
                            "Could not write file %s", path);
 
1403
                        failure_finish(NULL);
 
1404
                        return (0);
 
1405
                }
 
1406
        }
 
1407
        fclose(f);
 
1408
        return (1);
 
1409
#else
 
1410
        int fd;
 
1411
        assertion_count(file, line);
 
1412
        fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644);
 
1413
        if (fd < 0) {
 
1414
                failure_start(file, line, "Could not create %s", path);
 
1415
                failure_finish(NULL);
 
1416
                return (0);
 
1417
        }
 
1418
        if (contents != NULL) {
 
1419
                if ((ssize_t)strlen(contents)
 
1420
                    != write(fd, contents, strlen(contents))) {
 
1421
                        close(fd);
 
1422
                        failure_start(file, line, "Could not write to %s", path);
 
1423
                        failure_finish(NULL);
 
1424
                        return (0);
 
1425
                }
 
1426
        }
 
1427
        close(fd);
 
1428
        return (1);
 
1429
#endif
 
1430
}
 
1431
 
 
1432
/* Create a hardlink and report any failures. */
 
1433
int
 
1434
assertion_make_hardlink(const char *file, int line,
 
1435
    const char *newpath, const char *linkto)
 
1436
{
 
1437
        int succeeded;
 
1438
 
 
1439
        assertion_count(file, line);
 
1440
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1441
        succeeded = my_CreateHardLinkA(newpath, linkto);
 
1442
#elif HAVE_LINK
 
1443
        succeeded = !link(linkto, newpath);
 
1444
#else
 
1445
        succeeded = 0;
 
1446
#endif
 
1447
        if (succeeded)
 
1448
                return (1);
 
1449
        failure_start(file, line, "Could not create hardlink");
 
1450
        logprintf("   New link: %s\n", newpath);
 
1451
        logprintf("   Old name: %s\n", linkto);
 
1452
        failure_finish(NULL);
 
1453
        return(0);
 
1454
}
 
1455
 
 
1456
/* Create a symlink and report any failures. */
 
1457
int
 
1458
assertion_make_symlink(const char *file, int line,
 
1459
    const char *newpath, const char *linkto)
 
1460
{
 
1461
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1462
        int targetIsDir = 0;  /* TODO: Fix this */
 
1463
        assertion_count(file, line);
 
1464
        if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir))
 
1465
                return (1);
 
1466
#elif HAVE_SYMLINK
 
1467
        assertion_count(file, line);
 
1468
        if (0 == symlink(linkto, newpath))
 
1469
                return (1);
 
1470
#endif
 
1471
        failure_start(file, line, "Could not create symlink");
 
1472
        logprintf("   New link: %s\n", newpath);
 
1473
        logprintf("   Old name: %s\n", linkto);
 
1474
        failure_finish(NULL);
 
1475
        return(0);
 
1476
}
 
1477
 
 
1478
/* Set umask, report failures. */
 
1479
int
 
1480
assertion_umask(const char *file, int line, int mask)
 
1481
{
 
1482
        assertion_count(file, line);
 
1483
        (void)file; /* UNUSED */
 
1484
        (void)line; /* UNUSED */
 
1485
        umask(mask);
 
1486
        return (1);
 
1487
}
 
1488
 
 
1489
/*
 
1490
 *
 
1491
 *  UTILITIES for use by tests.
 
1492
 *
 
1493
 */
 
1494
 
 
1495
/*
 
1496
 * Check whether platform supports symlinks.  This is intended
 
1497
 * for tests to use in deciding whether to bother testing symlink
 
1498
 * support; if the platform doesn't support symlinks, there's no point
 
1499
 * in checking whether the program being tested can create them.
 
1500
 *
 
1501
 * Note that the first time this test is called, we actually go out to
 
1502
 * disk to create and verify a symlink.  This is necessary because
 
1503
 * symlink support is actually a property of a particular filesystem
 
1504
 * and can thus vary between directories on a single system.  After
 
1505
 * the first call, this returns the cached result from memory, so it's
 
1506
 * safe to call it as often as you wish.
 
1507
 */
 
1508
int
 
1509
canSymlink(void)
 
1510
{
 
1511
        /* Remember the test result */
 
1512
        static int value = 0, tested = 0;
 
1513
        if (tested)
 
1514
                return (value);
 
1515
 
 
1516
        ++tested;
 
1517
        assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, "a");
 
1518
        /* Note: Cygwin has its own symlink() emulation that does not
 
1519
         * use the Win32 CreateSymbolicLink() function. */
 
1520
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1521
        value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0)
 
1522
            && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0");
 
1523
#elif HAVE_SYMLINK
 
1524
        value = (0 == symlink("canSymlink.0", "canSymlink.1"))
 
1525
            && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0");
 
1526
#endif
 
1527
        return (value);
 
1528
}
 
1529
 
 
1530
/*
 
1531
 * Can this platform run the gzip program?
 
1532
 */
 
1533
/* Platform-dependent options for hiding the output of a subcommand. */
 
1534
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1535
static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */
 
1536
#else
 
1537
static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */
 
1538
#endif
 
1539
int
 
1540
canGzip(void)
 
1541
{
 
1542
        static int tested = 0, value = 0;
 
1543
        if (!tested) {
 
1544
                tested = 1;
 
1545
                if (systemf("gzip -V %s", redirectArgs) == 0)
 
1546
                        value = 1;
 
1547
        }
 
1548
        return (value);
 
1549
}
 
1550
 
 
1551
/*
 
1552
 * Can this platform run the gunzip program?
 
1553
 */
 
1554
int
 
1555
canGunzip(void)
 
1556
{
 
1557
        static int tested = 0, value = 0;
 
1558
        if (!tested) {
 
1559
                tested = 1;
 
1560
                if (systemf("gunzip -V %s", redirectArgs) == 0)
 
1561
                        value = 1;
 
1562
        }
 
1563
        return (value);
 
1564
}
 
1565
 
 
1566
/*
 
1567
 * Sleep as needed; useful for verifying disk timestamp changes by
 
1568
 * ensuring that the wall-clock time has actually changed before we
 
1569
 * go back to re-read something from disk.
 
1570
 */
 
1571
void
 
1572
sleepUntilAfter(time_t t)
 
1573
{
 
1574
        while (t >= time(NULL))
 
1575
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1576
                Sleep(500);
 
1577
#else
 
1578
                sleep(1);
 
1579
#endif
 
1580
}
 
1581
 
 
1582
/*
 
1583
 * Call standard system() call, but build up the command line using
 
1584
 * sprintf() conventions.
 
1585
 */
 
1586
int
 
1587
systemf(const char *fmt, ...)
 
1588
{
 
1589
        char buff[8192];
 
1590
        va_list ap;
 
1591
        int r;
 
1592
 
 
1593
        va_start(ap, fmt);
 
1594
        vsprintf(buff, fmt, ap);
 
1595
        if (verbosity > VERBOSITY_FULL)
 
1596
                logprintf("Cmd: %s\n", buff);
 
1597
        r = system(buff);
 
1598
        va_end(ap);
 
1599
        return (r);
 
1600
}
 
1601
 
 
1602
/*
 
1603
 * Slurp a file into memory for ease of comparison and testing.
 
1604
 * Returns size of file in 'sizep' if non-NULL, null-terminates
 
1605
 * data in memory for ease of use.
 
1606
 */
 
1607
char *
 
1608
slurpfile(size_t * sizep, const char *fmt, ...)
 
1609
{
 
1610
        char filename[8192];
 
1611
        struct stat st;
 
1612
        va_list ap;
 
1613
        char *p;
 
1614
        ssize_t bytes_read;
 
1615
        FILE *f;
 
1616
        int r;
 
1617
 
 
1618
        va_start(ap, fmt);
 
1619
        vsprintf(filename, fmt, ap);
 
1620
        va_end(ap);
 
1621
 
 
1622
        f = fopen(filename, "rb");
 
1623
        if (f == NULL) {
 
1624
                /* Note: No error; non-existent file is okay here. */
 
1625
                return (NULL);
 
1626
        }
 
1627
        r = fstat(fileno(f), &st);
 
1628
        if (r != 0) {
 
1629
                logprintf("Can't stat file %s\n", filename);
 
1630
                fclose(f);
 
1631
                return (NULL);
 
1632
        }
 
1633
        p = malloc((size_t)st.st_size + 1);
 
1634
        if (p == NULL) {
 
1635
                logprintf("Can't allocate %ld bytes of memory to read file %s\n",
 
1636
                    (long int)st.st_size, filename);
 
1637
                fclose(f);
 
1638
                return (NULL);
 
1639
        }
 
1640
        bytes_read = fread(p, 1, (size_t)st.st_size, f);
 
1641
        if (bytes_read < st.st_size) {
 
1642
                logprintf("Can't read file %s\n", filename);
 
1643
                fclose(f);
 
1644
                free(p);
 
1645
                return (NULL);
 
1646
        }
 
1647
        p[st.st_size] = '\0';
 
1648
        if (sizep != NULL)
 
1649
                *sizep = (size_t)st.st_size;
 
1650
        fclose(f);
 
1651
        return (p);
 
1652
}
 
1653
 
 
1654
/* Read a uuencoded file from the reference directory, decode, and
 
1655
 * write the result into the current directory. */
 
1656
#define UUDECODE(c) (((c) - 0x20) & 0x3f)
 
1657
void
 
1658
extract_reference_file(const char *name)
 
1659
{
 
1660
        char buff[1024];
 
1661
        FILE *in, *out;
 
1662
 
 
1663
        sprintf(buff, "%s/%s.uu", refdir, name);
 
1664
        in = fopen(buff, "r");
 
1665
        failure("Couldn't open reference file %s", buff);
 
1666
        assert(in != NULL);
 
1667
        if (in == NULL)
 
1668
                return;
 
1669
        /* Read up to and including the 'begin' line. */
 
1670
        for (;;) {
 
1671
                if (fgets(buff, sizeof(buff), in) == NULL) {
 
1672
                        /* TODO: This is a failure. */
 
1673
                        return;
 
1674
                }
 
1675
                if (memcmp(buff, "begin ", 6) == 0)
 
1676
                        break;
 
1677
        }
 
1678
        /* Now, decode the rest and write it. */
 
1679
        /* Not a lot of error checking here; the input better be right. */
 
1680
        out = fopen(name, "wb");
 
1681
        while (fgets(buff, sizeof(buff), in) != NULL) {
 
1682
                char *p = buff;
 
1683
                int bytes;
 
1684
 
 
1685
                if (memcmp(buff, "end", 3) == 0)
 
1686
                        break;
 
1687
 
 
1688
                bytes = UUDECODE(*p++);
 
1689
                while (bytes > 0) {
 
1690
                        int n = 0;
 
1691
                        /* Write out 1-3 bytes from that. */
 
1692
                        if (bytes > 0) {
 
1693
                                n = UUDECODE(*p++) << 18;
 
1694
                                n |= UUDECODE(*p++) << 12;
 
1695
                                fputc(n >> 16, out);
 
1696
                                --bytes;
 
1697
                        }
 
1698
                        if (bytes > 0) {
 
1699
                                n |= UUDECODE(*p++) << 6;
 
1700
                                fputc((n >> 8) & 0xFF, out);
 
1701
                                --bytes;
 
1702
                        }
 
1703
                        if (bytes > 0) {
 
1704
                                n |= UUDECODE(*p++);
 
1705
                                fputc(n & 0xFF, out);
 
1706
                                --bytes;
 
1707
                        }
 
1708
                }
 
1709
        }
 
1710
        fclose(out);
 
1711
        fclose(in);
 
1712
}
 
1713
 
 
1714
/*
 
1715
 *
 
1716
 * TEST management
 
1717
 *
 
1718
 */
 
1719
 
 
1720
/*
 
1721
 * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has
 
1722
 * a line like
 
1723
 *      DEFINE_TEST(test_function)
 
1724
 * for each test.
 
1725
 */
 
1726
 
 
1727
/* Use "list.h" to declare all of the test functions. */
 
1728
#undef DEFINE_TEST
 
1729
#define DEFINE_TEST(name) void name(void);
 
1730
#include "list.h"
 
1731
 
 
1732
/* Use "list.h" to create a list of all tests (functions and names). */
 
1733
#undef DEFINE_TEST
 
1734
#define DEFINE_TEST(n) { n, #n, 0 },
 
1735
struct { void (*func)(void); const char *name; int failures; } tests[] = {
 
1736
        #include "list.h"
 
1737
};
 
1738
 
 
1739
/*
 
1740
 * Summarize repeated failures in the just-completed test.
 
1741
 */
 
1742
static void
 
1743
test_summarize(const char *filename, int failed)
 
1744
{
 
1745
        unsigned int i;
 
1746
 
 
1747
        switch (verbosity) {
 
1748
        case VERBOSITY_SUMMARY_ONLY:
 
1749
                printf(failed ? "E" : ".");
 
1750
                fflush(stdout);
 
1751
                break;
 
1752
        case VERBOSITY_PASSFAIL:
 
1753
                printf(failed ? "FAIL\n" : "ok\n");
 
1754
                break;
 
1755
        }
 
1756
 
 
1757
        log_console = (verbosity == VERBOSITY_LIGHT_REPORT);
 
1758
 
 
1759
        for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
 
1760
                if (failed_lines[i].count > 1 && !failed_lines[i].skip)
 
1761
                        logprintf("%s:%d: Summary: Failed %d times\n",
 
1762
                            filename, i, failed_lines[i].count);
 
1763
        }
 
1764
        /* Clear the failure history for the next file. */
 
1765
        memset(failed_lines, 0, sizeof(failed_lines));
 
1766
}
 
1767
 
 
1768
/*
 
1769
 * Actually run a single test, with appropriate setup and cleanup.
 
1770
 */
 
1771
static int
 
1772
test_run(int i, const char *tmpdir)
 
1773
{
 
1774
        char logfilename[64];
 
1775
        int failures_before = failures;
 
1776
        int oldumask;
 
1777
 
 
1778
        switch (verbosity) {
 
1779
        case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */
 
1780
                break;
 
1781
        case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */
 
1782
                printf("%3d: %-50s", i, tests[i].name);
 
1783
                fflush(stdout);
 
1784
                break;
 
1785
        default: /* Title of test, details will follow */
 
1786
                printf("%3d: %s\n", i, tests[i].name);
 
1787
        }
 
1788
 
 
1789
        /* Chdir to the top-level work directory. */
 
1790
        if (!assertChdir(tmpdir)) {
 
1791
                fprintf(stderr,
 
1792
                    "ERROR: Can't chdir to top work dir %s\n", tmpdir);
 
1793
                exit(1);
 
1794
        }
 
1795
        /* Create a log file for this test. */
 
1796
        sprintf(logfilename, "%s.log", tests[i].name);
 
1797
        logfile = fopen(logfilename, "w");
 
1798
        fprintf(logfile, "%s\n\n", tests[i].name);
 
1799
        /* Chdir() to a work dir for this specific test. */
 
1800
        if (!assertMakeDir(tests[i].name, 0755)
 
1801
            || !assertChdir(tests[i].name)) {
 
1802
                fprintf(stderr,
 
1803
                    "ERROR: Can't chdir to work dir %s/%s\n",
 
1804
                    tmpdir, tests[i].name);
 
1805
                exit(1);
 
1806
        }
 
1807
        /* Explicitly reset the locale before each test. */
 
1808
        setlocale(LC_ALL, "C");
 
1809
        /* Record the umask before we run the test. */
 
1810
        umask(oldumask = umask(0));
 
1811
        /*
 
1812
         * Run the actual test.
 
1813
         */
 
1814
        (*tests[i].func)();
 
1815
        /*
 
1816
         * Clean up and report afterwards.
 
1817
         */
 
1818
        /* Restore umask */
 
1819
        umask(oldumask);
 
1820
        /* Reset locale. */
 
1821
        setlocale(LC_ALL, "C");
 
1822
        /* Reset directory. */
 
1823
        if (!assertChdir(tmpdir)) {
 
1824
                fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n",
 
1825
                    tmpdir);
 
1826
                exit(1);
 
1827
        }
 
1828
        /* Report per-test summaries. */
 
1829
        tests[i].failures = failures - failures_before;
 
1830
        test_summarize(test_filename, tests[i].failures);
 
1831
        /* Close the per-test log file. */
 
1832
        fclose(logfile);
 
1833
        logfile = NULL;
 
1834
        /* If there were no failures, we can remove the work dir and logfile. */
 
1835
        if (tests[i].failures == 0) {
 
1836
                if (!keep_temp_files && assertChdir(tmpdir)) {
 
1837
#if defined(_WIN32) && !defined(__CYGWIN__)
 
1838
                        /* Make sure not to leave empty directories.
 
1839
                         * Sometimes a processing of closing files used by tests
 
1840
                         * is not done, then rmdir will be failed and it will
 
1841
                         * leave a empty test directory. So we should wait a few
 
1842
                         * seconds and retry rmdir. */
 
1843
                        int r, t;
 
1844
                        for (t = 0; t < 10; t++) {
 
1845
                                if (t > 0)
 
1846
                                        Sleep(1000);
 
1847
                                r = systemf("rmdir /S /Q %s", tests[i].name);
 
1848
                                if (r == 0)
 
1849
                                        break;
 
1850
                        }
 
1851
                        systemf("del %s", logfilename);
 
1852
#else
 
1853
                        systemf("rm -rf %s", tests[i].name);
 
1854
                        systemf("rm %s", logfilename);
 
1855
#endif
 
1856
                }
 
1857
        }
 
1858
        /* Return appropriate status. */
 
1859
        return (tests[i].failures);
 
1860
}
 
1861
 
 
1862
/*
 
1863
 *
 
1864
 *
 
1865
 * MAIN and support routines.
 
1866
 *
 
1867
 *
 
1868
 */
 
1869
 
 
1870
static void
 
1871
usage(const char *program)
 
1872
{
 
1873
        static const int limit = sizeof(tests) / sizeof(tests[0]);
 
1874
        int i;
 
1875
 
 
1876
        printf("Usage: %s [options] <test> <test> ...\n", program);
 
1877
        printf("Default is to run all tests.\n");
 
1878
        printf("Otherwise, specify the numbers of the tests you wish to run.\n");
 
1879
        printf("Options:\n");
 
1880
        printf("  -d  Dump core after any failure, for debugging.\n");
 
1881
        printf("  -k  Keep all temp files.\n");
 
1882
        printf("      Default: temp files for successful tests deleted.\n");
 
1883
#ifdef PROGRAM
 
1884
        printf("  -p <path>  Path to executable to be tested.\n");
 
1885
        printf("      Default: path taken from " ENVBASE " environment variable.\n");
 
1886
#endif
 
1887
        printf("  -q  Quiet.\n");
 
1888
        printf("  -r <dir>   Path to dir containing reference files.\n");
 
1889
        printf("      Default: Current directory.\n");
 
1890
        printf("  -v  Verbose.\n");
 
1891
        printf("Available tests:\n");
 
1892
        for (i = 0; i < limit; i++)
 
1893
                printf("  %d: %s\n", i, tests[i].name);
 
1894
        exit(1);
 
1895
}
 
1896
 
 
1897
static char *
 
1898
get_refdir(const char *d)
 
1899
{
 
1900
        char tried[512] = { '\0' };
 
1901
        char buff[128];
 
1902
        char *pwd, *p;
 
1903
 
 
1904
        /* If a dir was specified, try that */
 
1905
        if (d != NULL) {
 
1906
                pwd = NULL;
 
1907
                snprintf(buff, sizeof(buff), "%s", d);
 
1908
                p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
 
1909
                if (p != NULL) goto success;
 
1910
                strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 
1911
                strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
1912
                goto failure;
 
1913
        }
 
1914
 
 
1915
        /* Get the current dir. */
 
1916
        pwd = getcwd(NULL, 0);
 
1917
        while (pwd[strlen(pwd) - 1] == '\n')
 
1918
                pwd[strlen(pwd) - 1] = '\0';
 
1919
 
 
1920
        /* Look for a known file. */
 
1921
        snprintf(buff, sizeof(buff), "%s", pwd);
 
1922
        p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
 
1923
        if (p != NULL) goto success;
 
1924
        strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 
1925
        strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
1926
 
 
1927
        snprintf(buff, sizeof(buff), "%s/test", pwd);
 
1928
        p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
 
1929
        if (p != NULL) goto success;
 
1930
        strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 
1931
        strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
1932
 
 
1933
#if defined(LIBRARY)
 
1934
        snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY);
 
1935
#else
 
1936
        snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM);
 
1937
#endif
 
1938
        p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
 
1939
        if (p != NULL) goto success;
 
1940
        strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 
1941
        strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
1942
 
 
1943
        if (memcmp(pwd, "/usr/obj", 8) == 0) {
 
1944
                snprintf(buff, sizeof(buff), "%s", pwd + 8);
 
1945
                p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
 
1946
                if (p != NULL) goto success;
 
1947
                strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 
1948
                strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
1949
 
 
1950
                snprintf(buff, sizeof(buff), "%s/test", pwd + 8);
 
1951
                p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
 
1952
                if (p != NULL) goto success;
 
1953
                strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 
1954
                strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
1955
        }
 
1956
 
 
1957
failure:
 
1958
        printf("Unable to locate known reference file %s\n", KNOWNREF);
 
1959
        printf("  Checked following directories:\n%s\n", tried);
 
1960
#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
 
1961
        DebugBreak();
 
1962
#endif
 
1963
        exit(1);
 
1964
 
 
1965
success:
 
1966
        free(p);
 
1967
        free(pwd);
 
1968
        return strdup(buff);
 
1969
}
 
1970
 
 
1971
int
 
1972
main(int argc, char **argv)
 
1973
{
 
1974
        static const int limit = sizeof(tests) / sizeof(tests[0]);
 
1975
        int i, tests_run = 0, tests_failed = 0, option;
 
1976
        time_t now;
 
1977
        char *refdir_alloc = NULL;
 
1978
        const char *progname;
 
1979
        const char *tmp, *option_arg, *p;
 
1980
        char tmpdir[256];
 
1981
        char tmpdir_timestamp[256];
 
1982
 
 
1983
        (void)argc; /* UNUSED */
 
1984
 
 
1985
#if defined(HAVE__CrtSetReportMode)
 
1986
        /* To stop to run the default invalid parameter handler. */
 
1987
        _set_invalid_parameter_handler(invalid_parameter_handler);
 
1988
        /* Disable annoying assertion message box. */
 
1989
        _CrtSetReportMode(_CRT_ASSERT, 0);
 
1990
#endif
 
1991
 
 
1992
        /*
 
1993
         * Name of this program, used to build root of our temp directory
 
1994
         * tree.
 
1995
         */
 
1996
        progname = p = argv[0];
 
1997
        while (*p != '\0') {
 
1998
                /* Support \ or / dir separators for Windows compat. */
 
1999
                if (*p == '/' || *p == '\\')
 
2000
                        progname = p + 1;
 
2001
                ++p;
 
2002
        }
 
2003
 
 
2004
#ifdef PROGRAM
 
2005
        /* Get the target program from environment, if available. */
 
2006
        testprogfile = getenv(ENVBASE);
 
2007
#endif
 
2008
 
 
2009
        if (getenv("TMPDIR") != NULL)
 
2010
                tmp = getenv("TMPDIR");
 
2011
        else if (getenv("TMP") != NULL)
 
2012
                tmp = getenv("TMP");
 
2013
        else if (getenv("TEMP") != NULL)
 
2014
                tmp = getenv("TEMP");
 
2015
        else if (getenv("TEMPDIR") != NULL)
 
2016
                tmp = getenv("TEMPDIR");
 
2017
        else
 
2018
                tmp = "/tmp";
 
2019
 
 
2020
        /* Allow -d to be controlled through the environment. */
 
2021
        if (getenv(ENVBASE "_DEBUG") != NULL)
 
2022
                dump_on_failure = 1;
 
2023
 
 
2024
        /* Get the directory holding test files from environment. */
 
2025
        refdir = getenv(ENVBASE "_TEST_FILES");
 
2026
 
 
2027
        /*
 
2028
         * Parse options, without using getopt(), which isn't available
 
2029
         * on all platforms.
 
2030
         */
 
2031
        ++argv; /* Skip program name */
 
2032
        while (*argv != NULL) {
 
2033
                if (**argv != '-')
 
2034
                        break;
 
2035
                p = *argv++;
 
2036
                ++p; /* Skip '-' */
 
2037
                while (*p != '\0') {
 
2038
                        option = *p++;
 
2039
                        option_arg = NULL;
 
2040
                        /* If 'opt' takes an argument, parse that. */
 
2041
                        if (option == 'p' || option == 'r') {
 
2042
                                if (*p != '\0')
 
2043
                                        option_arg = p;
 
2044
                                else if (*argv == NULL) {
 
2045
                                        fprintf(stderr,
 
2046
                                            "Option -%c requires argument.\n",
 
2047
                                            option);
 
2048
                                        usage(progname);
 
2049
                                } else
 
2050
                                        option_arg = *argv++;
 
2051
                                p = ""; /* End of this option word. */
 
2052
                        }
 
2053
 
 
2054
                        /* Now, handle the option. */
 
2055
                        switch (option) {
 
2056
                        case 'd':
 
2057
                                dump_on_failure = 1;
 
2058
                                break;
 
2059
                        case 'k':
 
2060
                                keep_temp_files = 1;
 
2061
                                break;
 
2062
                        case 'p':
 
2063
#ifdef PROGRAM
 
2064
                                testprogfile = option_arg;
 
2065
#else
 
2066
                                fprintf(stderr, "-p option not permitted\n");
 
2067
                                usage(progname);
 
2068
#endif
 
2069
                                break;
 
2070
                        case 'q':
 
2071
                                verbosity--;
 
2072
                                break;
 
2073
                        case 'r':
 
2074
                                refdir = option_arg;
 
2075
                                break;
 
2076
                        case 'v':
 
2077
                                verbosity++;
 
2078
                                break;
 
2079
                        default:
 
2080
                                fprintf(stderr, "Unrecognized option '%c'\n",
 
2081
                                    option);
 
2082
                                usage(progname);
 
2083
                        }
 
2084
                }
 
2085
        }
 
2086
 
 
2087
        /*
 
2088
         * Sanity-check that our options make sense.
 
2089
         */
 
2090
#ifdef PROGRAM
 
2091
        if (testprogfile == NULL) {
 
2092
                fprintf(stderr, "Program executable required\n");
 
2093
                usage(progname);
 
2094
        }
 
2095
 
 
2096
        {
 
2097
                char *testprg;
 
2098
#if defined(_WIN32) && !defined(__CYGWIN__)
 
2099
                /* Command.com sometimes rejects '/' separators. */
 
2100
                testprg = strdup(testprogfile);
 
2101
                for (i = 0; testprg[i] != '\0'; i++) {
 
2102
                        if (testprg[i] == '/')
 
2103
                                testprg[i] = '\\';
 
2104
                }
 
2105
                testprogfile = testprg;
 
2106
#endif
 
2107
                /* Quote the name that gets put into shell command lines. */
 
2108
                testprg = malloc(strlen(testprogfile) + 3);
 
2109
                strcpy(testprg, "\"");
 
2110
                strcat(testprg, testprogfile);
 
2111
                strcat(testprg, "\"");
 
2112
                testprog = testprg;
 
2113
        }
 
2114
#endif
 
2115
 
 
2116
        /*
 
2117
         * Create a temp directory for the following tests.
 
2118
         * Include the time the tests started as part of the name,
 
2119
         * to make it easier to track the results of multiple tests.
 
2120
         */
 
2121
        now = time(NULL);
 
2122
        for (i = 0; ; i++) {
 
2123
                strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
 
2124
                    "%Y-%m-%dT%H.%M.%S",
 
2125
                    localtime(&now));
 
2126
                sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname,
 
2127
                    tmpdir_timestamp, i);
 
2128
                if (assertMakeDir(tmpdir,0755))
 
2129
                        break;
 
2130
                if (i >= 999) {
 
2131
                        fprintf(stderr,
 
2132
                            "ERROR: Unable to create temp directory %s\n",
 
2133
                            tmpdir);
 
2134
                        exit(1);
 
2135
                }
 
2136
        }
 
2137
 
 
2138
        /*
 
2139
         * If the user didn't specify a directory for locating
 
2140
         * reference files, try to find the reference files in
 
2141
         * the "usual places."
 
2142
         */
 
2143
        refdir = refdir_alloc = get_refdir(refdir);
 
2144
 
 
2145
        /*
 
2146
         * Banner with basic information.
 
2147
         */
 
2148
        printf("\n");
 
2149
        printf("If tests fail or crash, details will be in:\n");
 
2150
        printf("   %s\n", tmpdir);
 
2151
        printf("\n");
 
2152
        if (verbosity > VERBOSITY_SUMMARY_ONLY) {
 
2153
                printf("Reference files will be read from: %s\n", refdir);
 
2154
#ifdef PROGRAM
 
2155
                printf("Running tests on: %s\n", testprog);
 
2156
#endif
 
2157
                printf("Exercising: ");
 
2158
                fflush(stdout);
 
2159
                printf("%s\n", EXTRA_VERSION);
 
2160
        } else {
 
2161
                printf("Running ");
 
2162
                fflush(stdout);
 
2163
        }
 
2164
 
 
2165
        /*
 
2166
         * Run some or all of the individual tests.
 
2167
         */
 
2168
        if (*argv == NULL) {
 
2169
                /* Default: Run all tests. */
 
2170
                for (i = 0; i < limit; i++) {
 
2171
                        if (test_run(i, tmpdir))
 
2172
                                tests_failed++;
 
2173
                        tests_run++;
 
2174
                }
 
2175
        } else {
 
2176
                while (*(argv) != NULL) {
 
2177
                        if (**argv >= '0' && **argv <= '9') {
 
2178
                                i = atoi(*argv);
 
2179
                                if (i < 0 || i >= limit) {
 
2180
                                        printf("*** INVALID Test %s\n", *argv);
 
2181
                                        free(refdir_alloc);
 
2182
                                        usage(progname);
 
2183
                                        /* usage() never returns */
 
2184
                                }
 
2185
                        } else {
 
2186
                                for (i = 0; i < limit; ++i) {
 
2187
                                        if (strcmp(*argv, tests[i].name) == 0)
 
2188
                                                break;
 
2189
                                }
 
2190
                                if (i >= limit) {
 
2191
                                        printf("*** INVALID Test ``%s''\n",
 
2192
                                               *argv);
 
2193
                                        free(refdir_alloc);
 
2194
                                        usage(progname);
 
2195
                                        /* usage() never returns */
 
2196
                                }
 
2197
                        }
 
2198
                        if (test_run(i, tmpdir))
 
2199
                                tests_failed++;
 
2200
                        tests_run++;
 
2201
                        argv++;
 
2202
                }
 
2203
        }
 
2204
 
 
2205
        /*
 
2206
         * Report summary statistics.
 
2207
         */
 
2208
        if (verbosity > VERBOSITY_SUMMARY_ONLY) {
 
2209
                printf("\n");
 
2210
                printf("Totals:\n");
 
2211
                printf("  Tests run:         %8d\n", tests_run);
 
2212
                printf("  Tests failed:      %8d\n", tests_failed);
 
2213
                printf("  Assertions checked:%8d\n", assertions);
 
2214
                printf("  Assertions failed: %8d\n", failures);
 
2215
                printf("  Skips reported:    %8d\n", skips);
 
2216
        }
 
2217
        if (failures) {
 
2218
                printf("\n");
 
2219
                printf("Failing tests:\n");
 
2220
                for (i = 0; i < limit; ++i) {
 
2221
                        if (tests[i].failures)
 
2222
                                printf("  %d: %s (%d failures)\n", i,
 
2223
                                    tests[i].name, tests[i].failures);
 
2224
                }
 
2225
                printf("\n");
 
2226
                printf("Details for failing tests: %s\n", tmpdir);
 
2227
                printf("\n");
 
2228
        } else {
 
2229
                if (verbosity == VERBOSITY_SUMMARY_ONLY)
 
2230
                        printf("\n");
 
2231
                printf("%d tests passed, no failures\n", tests_run);
 
2232
        }
 
2233
 
 
2234
        free(refdir_alloc);
 
2235
 
 
2236
        /* If the final tmpdir is empty, we can remove it. */
 
2237
        /* This should be the usual case when all tests succeed. */
 
2238
        assertChdir("..");
 
2239
        rmdir(tmpdir);
 
2240
 
 
2241
        return (tests_failed ? 1 : 0);
 
2242
}