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 "test_utils.h" |
28 |
#ifdef HAVE_SYS_IOCTL_H |
29 |
#include <sys/ioctl.h> |
30 |
#endif |
31 |
#ifdef HAVE_SYS_TIME_H |
32 |
#include <sys/time.h> |
33 |
#endif |
34 |
#include <errno.h> |
35 |
#ifdef HAVE_ICONV_H |
36 |
#include <iconv.h> |
37 |
#endif |
38 |
/* |
39 |
* Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. |
40 |
* As the include guards don't agree, the order of include is important. |
41 |
*/ |
42 |
#ifdef HAVE_LINUX_EXT2_FS_H |
43 |
#include <linux/ext2_fs.h> /* for Linux file flags */ |
44 |
#endif |
45 |
#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) |
46 |
#include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */ |
47 |
#endif |
48 |
#include <limits.h> |
49 |
#include <locale.h> |
50 |
#ifdef HAVE_SIGNAL_H |
51 |
#include <signal.h> |
52 |
#endif |
53 |
#include <stdarg.h> |
54 |
#include <time.h> |
55 |
|
56 |
/* |
57 |
* This same file is used pretty much verbatim for all test harnesses. |
58 |
* |
59 |
* The next few lines are the only differences. |
60 |
* TODO: Move this into a separate configuration header, have all test |
61 |
* suites share one copy of this file. |
62 |
*/ |
63 |
__FBSDID("$FreeBSD: src/usr.bin/cpio/test/main.c,v 1.3 2008/08/24 04:58:22 kientzle Exp $"); |
64 |
#define KNOWNREF "test_option_f.cpio.uu" |
65 |
#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */ |
66 |
#define PROGRAM "bsdcpio" /* Name of program being tested. */ |
67 |
#define PROGRAM_ALIAS "cpio" /* Generic alias for program */ |
68 |
#undef LIBRARY /* Not testing a library. */ |
69 |
#undef EXTRA_DUMP /* How to dump extra data */ |
70 |
#undef EXTRA_ERRNO /* How to dump errno */ |
71 |
/* How to generate extra version info. */ |
72 |
#define EXTRA_VERSION (systemf("%s --version", testprog) ? "" : "") |
73 |
|
74 |
/* |
75 |
* |
76 |
* Windows support routines |
77 |
* |
78 |
* Note: Configuration is a tricky issue. Using HAVE_* feature macros |
79 |
* in the test harness is dangerous because they cover up |
80 |
* configuration errors. The classic example of this is omitting a |
81 |
* configure check. If libarchive and libarchive_test both look for |
82 |
* the same feature macro, such errors are hard to detect. Platform |
83 |
* macros (e.g., _WIN32 or __GNUC__) are a little better, but can |
84 |
* easily lead to very messy code. It's best to limit yourself |
85 |
* to only the most generic programming techniques in the test harness |
86 |
* and thus avoid conditionals altogether. Where that's not possible, |
87 |
* try to minimize conditionals by grouping platform-specific tests in |
88 |
* one place (e.g., test_acl_freebsd) or by adding new assert() |
89 |
* functions (e.g., assertMakeHardlink()) to cover up platform |
90 |
* differences. Platform-specific coding in libarchive_test is often |
91 |
* a symptom that some capability is missing from libarchive itself. |
92 |
*/ |
93 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
94 |
#include <io.h> |
95 |
#include <direct.h> |
96 |
#include <windows.h> |
97 |
#ifndef F_OK |
98 |
#define F_OK (0) |
99 |
#endif |
100 |
#ifndef S_ISDIR |
101 |
#define S_ISDIR(m) ((m) & _S_IFDIR) |
102 |
#endif |
103 |
#ifndef S_ISREG |
104 |
#define S_ISREG(m) ((m) & _S_IFREG) |
105 |
#endif |
106 |
#if !defined(__BORLANDC__) |
107 |
#define access _access |
108 |
#undef chdir |
109 |
#define chdir _chdir |
110 |
#endif |
111 |
#ifndef fileno |
112 |
#define fileno _fileno |
113 |
#endif |
114 |
/*#define fstat _fstat64*/ |
115 |
#if !defined(__BORLANDC__) |
116 |
#define getcwd _getcwd |
117 |
#endif |
118 |
#define lstat stat |
119 |
/*#define lstat _stat64*/ |
120 |
/*#define stat _stat64*/ |
121 |
#define rmdir _rmdir |
122 |
#if !defined(__BORLANDC__) |
123 |
#define strdup _strdup |
124 |
#define umask _umask |
125 |
#endif |
126 |
#define int64_t __int64 |
127 |
#endif |
128 |
|
129 |
#if defined(HAVE__CrtSetReportMode) |
130 |
# include <crtdbg.h> |
131 |
#endif |
132 |
|
133 |
/* Path to working directory for current test */ |
134 |
const char *testworkdir; |
135 |
#ifdef PROGRAM |
136 |
/* Pathname of exe to be tested. */ |
137 |
const char *testprogfile; |
138 |
/* Name of exe to use in printf-formatted command strings. */ |
139 |
/* On Windows, this includes leading/trailing quotes. */ |
140 |
const char *testprog; |
141 |
#endif |
142 |
|
143 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
144 |
static void *GetFunctionKernel32(const char *); |
145 |
static int my_CreateSymbolicLinkA(const char *, const char *, int); |
146 |
static int my_CreateHardLinkA(const char *, const char *); |
147 |
static int my_GetFileInformationByName(const char *, |
148 |
BY_HANDLE_FILE_INFORMATION *); |
149 |
|
150 |
static void * |
151 |
GetFunctionKernel32(const char *name) |
152 |
{ |
153 |
static HINSTANCE lib; |
154 |
static int set; |
155 |
if (!set) { |
156 |
set = 1; |
157 |
lib = LoadLibrary("kernel32.dll"); |
158 |
} |
159 |
if (lib == NULL) { |
160 |
fprintf(stderr, "Can't load kernel32.dll?!\n"); |
161 |
exit(1); |
162 |
} |
163 |
return (void *)GetProcAddress(lib, name); |
164 |
} |
165 |
|
166 |
static int |
167 |
my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) |
168 |
{ |
169 |
static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); |
170 |
static int set; |
171 |
if (!set) { |
172 |
set = 1; |
173 |
f = GetFunctionKernel32("CreateSymbolicLinkA"); |
174 |
} |
175 |
return f == NULL ? 0 : (*f)(linkname, target, flags); |
176 |
} |
177 |
|
178 |
static int |
179 |
my_CreateHardLinkA(const char *linkname, const char *target) |
180 |
{ |
181 |
static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); |
182 |
static int set; |
183 |
if (!set) { |
184 |
set = 1; |
185 |
f = GetFunctionKernel32("CreateHardLinkA"); |
186 |
} |
187 |
return f == NULL ? 0 : (*f)(linkname, target, NULL); |
188 |
} |
189 |
|
190 |
static int |
191 |
my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) |
192 |
{ |
193 |
HANDLE h; |
194 |
int r; |
195 |
|
196 |
memset(bhfi, 0, sizeof(*bhfi)); |
197 |
h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, |
198 |
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
199 |
if (h == INVALID_HANDLE_VALUE) |
200 |
return (0); |
201 |
r = GetFileInformationByHandle(h, bhfi); |
202 |
CloseHandle(h); |
203 |
return (r); |
204 |
} |
205 |
#endif |
206 |
|
207 |
#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) |
208 |
static void |
209 |
invalid_parameter_handler(const wchar_t * expression, |
210 |
const wchar_t * function, const wchar_t * file, |
211 |
unsigned int line, uintptr_t pReserved) |
212 |
{ |
213 |
/* nop */ |
214 |
} |
215 |
#endif |
216 |
|
217 |
/* |
218 |
* |
219 |
* OPTIONS FLAGS |
220 |
* |
221 |
*/ |
222 |
|
223 |
/* Enable core dump on failure. */ |
224 |
static int dump_on_failure = 0; |
225 |
/* Default is to remove temp dirs and log data for successful tests. */ |
226 |
static int keep_temp_files = 0; |
227 |
/* Default is to run the specified tests once and report errors. */ |
228 |
static int until_failure = 0; |
229 |
/* Default is to just report pass/fail for each test. */ |
230 |
static int verbosity = 0; |
231 |
#define VERBOSITY_SUMMARY_ONLY -1 /* -q */ |
232 |
#define VERBOSITY_PASSFAIL 0 /* Default */ |
233 |
#define VERBOSITY_LIGHT_REPORT 1 /* -v */ |
234 |
#define VERBOSITY_FULL 2 /* -vv */ |
235 |
/* A few places generate even more output for verbosity > VERBOSITY_FULL, |
236 |
* mostly for debugging the test harness itself. */ |
237 |
/* Cumulative count of assertion failures. */ |
238 |
static int failures = 0; |
239 |
/* Cumulative count of reported skips. */ |
240 |
static int skips = 0; |
241 |
/* Cumulative count of assertions checked. */ |
242 |
static int assertions = 0; |
243 |
|
244 |
/* Directory where uuencoded reference files can be found. */ |
245 |
static const char *refdir; |
246 |
|
247 |
/* |
248 |
* Report log information selectively to console and/or disk log. |
249 |
*/ |
250 |
static int log_console = 0; |
251 |
static FILE *logfile; |
252 |
static void |
253 |
vlogprintf(const char *fmt, va_list ap) |
254 |
{ |
255 |
#ifdef va_copy |
256 |
va_list lfap; |
257 |
va_copy(lfap, ap); |
258 |
#endif |
259 |
if (log_console) |
260 |
vfprintf(stdout, fmt, ap); |
261 |
if (logfile != NULL) |
262 |
#ifdef va_copy |
263 |
vfprintf(logfile, fmt, lfap); |
264 |
va_end(lfap); |
265 |
#else |
266 |
vfprintf(logfile, fmt, ap); |
267 |
#endif |
268 |
} |
269 |
|
270 |
static void |
271 |
logprintf(const char *fmt, ...) |
272 |
{ |
273 |
va_list ap; |
274 |
va_start(ap, fmt); |
275 |
vlogprintf(fmt, ap); |
276 |
va_end(ap); |
277 |
} |
278 |
|
279 |
/* Set up a message to display only if next assertion fails. */ |
280 |
static char msgbuff[4096]; |
281 |
static const char *msg, *nextmsg; |
282 |
void |
283 |
failure(const char *fmt, ...) |
284 |
{ |
285 |
va_list ap; |
286 |
if (fmt == NULL) { |
287 |
nextmsg = NULL; |
288 |
} else { |
289 |
va_start(ap, fmt); |
290 |
vsprintf(msgbuff, fmt, ap); |
291 |
va_end(ap); |
292 |
nextmsg = msgbuff; |
293 |
} |
294 |
} |
295 |
|
296 |
/* |
297 |
* Copy arguments into file-local variables. |
298 |
* This was added to permit vararg assert() functions without needing |
299 |
* variadic wrapper macros. Turns out that the vararg capability is almost |
300 |
* never used, so almost all of the vararg assertions can be simplified |
301 |
* by removing the vararg capability and reworking the wrapper macro to |
302 |
* pass __FILE__, __LINE__ directly into the function instead of using |
303 |
* this hook. I suspect this machinery is used so rarely that we |
304 |
* would be better off just removing it entirely. That would simplify |
305 |
* the code here noticeably. |
306 |
*/ |
307 |
static const char *skipping_filename; |
308 |
static int skipping_line; |
309 |
void skipping_setup(const char *filename, int line) |
310 |
{ |
311 |
skipping_filename = filename; |
312 |
skipping_line = line; |
313 |
} |
314 |
|
315 |
/* Called at the beginning of each assert() function. */ |
316 |
static void |
317 |
assertion_count(const char *file, int line) |
318 |
{ |
319 |
(void)file; /* UNUSED */ |
320 |
(void)line; /* UNUSED */ |
321 |
++assertions; |
322 |
/* Proper handling of "failure()" message. */ |
323 |
msg = nextmsg; |
324 |
nextmsg = NULL; |
325 |
/* Uncomment to print file:line after every assertion. |
326 |
* Verbose, but occasionally useful in tracking down crashes. */ |
327 |
/* printf("Checked %s:%d\n", file, line); */ |
328 |
} |
329 |
|
330 |
/* |
331 |
* For each test source file, we remember how many times each |
332 |
* assertion was reported. Cleared before each new test, |
333 |
* used by test_summarize(). |
334 |
*/ |
335 |
static struct line { |
336 |
int count; |
337 |
int skip; |
338 |
} failed_lines[10000]; |
339 |
const char *failed_filename; |
340 |
|
341 |
/* Count this failure, setup up log destination and handle initial report. */ |
342 |
static void |
343 |
failure_start(const char *filename, int line, const char *fmt, ...) |
344 |
{ |
345 |
va_list ap; |
346 |
|
347 |
/* Record another failure for this line. */ |
348 |
++failures; |
349 |
failed_filename = filename; |
350 |
failed_lines[line].count++; |
351 |
|
352 |
/* Determine whether to log header to console. */ |
353 |
switch (verbosity) { |
354 |
case VERBOSITY_LIGHT_REPORT: |
355 |
log_console = (failed_lines[line].count < 2); |
356 |
break; |
357 |
default: |
358 |
log_console = (verbosity >= VERBOSITY_FULL); |
359 |
} |
360 |
|
361 |
/* Log file:line header for this failure */ |
362 |
va_start(ap, fmt); |
363 |
#if _MSC_VER |
364 |
logprintf("%s(%d): ", filename, line); |
365 |
#else |
366 |
logprintf("%s:%d: ", filename, line); |
367 |
#endif |
368 |
vlogprintf(fmt, ap); |
369 |
va_end(ap); |
370 |
logprintf("\n"); |
371 |
|
372 |
if (msg != NULL && msg[0] != '\0') { |
373 |
logprintf(" Description: %s\n", msg); |
374 |
msg = NULL; |
375 |
} |
376 |
|
377 |
/* Determine whether to log details to console. */ |
378 |
if (verbosity == VERBOSITY_LIGHT_REPORT) |
379 |
log_console = 0; |
380 |
} |
381 |
|
382 |
/* Complete reporting of failed tests. */ |
383 |
/* |
384 |
* The 'extra' hook here is used by libarchive to include libarchive |
385 |
* error messages with assertion failures. It could also be used |
386 |
* to add strerror() output, for example. Just define the EXTRA_DUMP() |
387 |
* macro appropriately. |
388 |
*/ |
389 |
static void |
390 |
failure_finish(void *extra) |
391 |
{ |
392 |
(void)extra; /* UNUSED (maybe) */ |
393 |
#ifdef EXTRA_DUMP |
394 |
if (extra != NULL) { |
395 |
logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); |
396 |
logprintf(" detail: %s\n", EXTRA_DUMP(extra)); |
397 |
} |
398 |
#endif |
399 |
|
400 |
if (dump_on_failure) { |
401 |
fprintf(stderr, |
402 |
" *** forcing core dump so failure can be debugged ***\n"); |
403 |
abort(); |
404 |
} |
405 |
} |
406 |
|
407 |
/* Inform user that we're skipping some checks. */ |
408 |
void |
409 |
test_skipping(const char *fmt, ...) |
410 |
{ |
411 |
char buff[1024]; |
412 |
va_list ap; |
413 |
|
414 |
va_start(ap, fmt); |
415 |
vsprintf(buff, fmt, ap); |
416 |
va_end(ap); |
417 |
/* Use failure() message if set. */ |
418 |
msg = nextmsg; |
419 |
nextmsg = NULL; |
420 |
/* failure_start() isn't quite right, but is awfully convenient. */ |
421 |
failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); |
422 |
--failures; /* Undo failures++ in failure_start() */ |
423 |
/* Don't failure_finish() here. */ |
424 |
/* Mark as skip, so doesn't count as failed test. */ |
425 |
failed_lines[skipping_line].skip = 1; |
426 |
++skips; |
427 |
} |
428 |
|
429 |
/* |
430 |
* |
431 |
* ASSERTIONS |
432 |
* |
433 |
*/ |
434 |
|
435 |
/* Generic assert() just displays the failed condition. */ |
436 |
int |
437 |
assertion_assert(const char *file, int line, int value, |
438 |
const char *condition, void *extra) |
439 |
{ |
440 |
assertion_count(file, line); |
441 |
if (!value) { |
442 |
failure_start(file, line, "Assertion failed: %s", condition); |
443 |
failure_finish(extra); |
444 |
} |
445 |
return (value); |
446 |
} |
447 |
|
448 |
/* chdir() and report any errors */ |
449 |
int |
450 |
assertion_chdir(const char *file, int line, const char *pathname) |
451 |
{ |
452 |
assertion_count(file, line); |
453 |
if (chdir(pathname) == 0) |
454 |
return (1); |
455 |
failure_start(file, line, "chdir(\"%s\")", pathname); |
456 |
failure_finish(NULL); |
457 |
return (0); |
458 |
|
459 |
} |
460 |
|
461 |
/* Verify two integers are equal. */ |
462 |
int |
463 |
assertion_equal_int(const char *file, int line, |
464 |
long long v1, const char *e1, long long v2, const char *e2, void *extra) |
465 |
{ |
466 |
assertion_count(file, line); |
467 |
if (v1 == v2) |
468 |
return (1); |
469 |
failure_start(file, line, "%s != %s", e1, e2); |
470 |
logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); |
471 |
logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); |
472 |
failure_finish(extra); |
473 |
return (0); |
474 |
} |
475 |
|
476 |
/* |
477 |
* Utility to convert a single UTF-8 sequence. |
478 |
*/ |
479 |
static int |
480 |
_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) |
481 |
{ |
482 |
static const char utf8_count[256] = { |
483 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ |
484 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ |
485 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ |
486 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ |
487 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ |
488 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ |
489 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ |
490 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ |
491 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ |
492 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ |
493 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ |
494 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ |
495 |
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ |
496 |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ |
497 |
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ |
498 |
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ |
499 |
}; |
500 |
int ch; |
501 |
int cnt; |
502 |
uint32_t wc; |
503 |
|
504 |
*pwc = 0; |
505 |
|
506 |
/* Sanity check. */ |
507 |
if (n == 0) |
508 |
return (0); |
509 |
/* |
510 |
* Decode 1-4 bytes depending on the value of the first byte. |
511 |
*/ |
512 |
ch = (unsigned char)*s; |
513 |
if (ch == 0) |
514 |
return (0); /* Standard: return 0 for end-of-string. */ |
515 |
cnt = utf8_count[ch]; |
516 |
|
517 |
/* Invalide sequence or there are not plenty bytes. */ |
518 |
if (n < (size_t)cnt) |
519 |
return (-1); |
520 |
|
521 |
/* Make a Unicode code point from a single UTF-8 sequence. */ |
522 |
switch (cnt) { |
523 |
case 1: /* 1 byte sequence. */ |
524 |
*pwc = ch & 0x7f; |
525 |
return (cnt); |
526 |
case 2: /* 2 bytes sequence. */ |
527 |
if ((s[1] & 0xc0) != 0x80) return (-1); |
528 |
*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); |
529 |
return (cnt); |
530 |
case 3: /* 3 bytes sequence. */ |
531 |
if ((s[1] & 0xc0) != 0x80) return (-1); |
532 |
if ((s[2] & 0xc0) != 0x80) return (-1); |
533 |
wc = ((ch & 0x0f) << 12) |
534 |
| ((s[1] & 0x3f) << 6) |
535 |
| (s[2] & 0x3f); |
536 |
if (wc < 0x800) |
537 |
return (-1);/* Overlong sequence. */ |
538 |
break; |
539 |
case 4: /* 4 bytes sequence. */ |
540 |
if (n < 4) |
541 |
return (-1); |
542 |
if ((s[1] & 0xc0) != 0x80) return (-1); |
543 |
if ((s[2] & 0xc0) != 0x80) return (-1); |
544 |
if ((s[3] & 0xc0) != 0x80) return (-1); |
545 |
wc = ((ch & 0x07) << 18) |
546 |
| ((s[1] & 0x3f) << 12) |
547 |
| ((s[2] & 0x3f) << 6) |
548 |
| (s[3] & 0x3f); |
549 |
if (wc < 0x10000) |
550 |
return (-1);/* Overlong sequence. */ |
551 |
break; |
552 |
default: |
553 |
return (-1); |
554 |
} |
555 |
|
556 |
/* The code point larger than 0x10FFFF is not leagal |
557 |
* Unicode values. */ |
558 |
if (wc > 0x10FFFF) |
559 |
return (-1); |
560 |
/* Correctly gets a Unicode, returns used bytes. */ |
561 |
*pwc = wc; |
562 |
return (cnt); |
563 |
} |
564 |
|
565 |
static void strdump(const char *e, const char *p, int ewidth, int utf8) |
566 |
{ |
567 |
const char *q = p; |
568 |
|
569 |
logprintf(" %*s = ", ewidth, e); |
570 |
if (p == NULL) { |
571 |
logprintf("NULL\n"); |
572 |
return; |
573 |
} |
574 |
logprintf("\""); |
575 |
while (*p != '\0') { |
576 |
unsigned int c = 0xff & *p++; |
577 |
switch (c) { |
578 |
case '\a': logprintf("\\a"); break; |
579 |
case '\b': logprintf("\\b"); break; |
580 |
case '\n': logprintf("\\n"); break; |
581 |
case '\r': logprintf("\\r"); break; |
582 |
default: |
583 |
if (c >= 32 && c < 127) |
584 |
logprintf("%c", c); |
585 |
else |
586 |
logprintf("\\x%02X", c); |
587 |
} |
588 |
} |
589 |
logprintf("\""); |
590 |
logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); |
591 |
|
592 |
/* |
593 |
* If the current string is UTF-8, dump its code points. |
594 |
*/ |
595 |
if (utf8) { |
596 |
size_t len; |
597 |
uint32_t uc; |
598 |
int n; |
599 |
int cnt = 0; |
600 |
|
601 |
p = q; |
602 |
len = strlen(p); |
603 |
logprintf(" ["); |
604 |
while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { |
605 |
if (p != q) |
606 |
logprintf(" "); |
607 |
logprintf("%04X", uc); |
608 |
p += n; |
609 |
len -= n; |
610 |
cnt++; |
611 |
} |
612 |
logprintf("]"); |
613 |
logprintf(" (count %d", cnt); |
614 |
if (n < 0) { |
615 |
logprintf(",unknown %d bytes", len); |
616 |
} |
617 |
logprintf(")"); |
618 |
|
619 |
} |
620 |
logprintf("\n"); |
621 |
} |
622 |
|
623 |
/* Verify two strings are equal, dump them if not. */ |
624 |
int |
625 |
assertion_equal_string(const char *file, int line, |
626 |
const char *v1, const char *e1, |
627 |
const char *v2, const char *e2, |
628 |
void *extra, int utf8) |
629 |
{ |
630 |
int l1, l2; |
631 |
|
632 |
assertion_count(file, line); |
633 |
if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) |
634 |
return (1); |
635 |
failure_start(file, line, "%s != %s", e1, e2); |
636 |
l1 = (int)strlen(e1); |
637 |
l2 = (int)strlen(e2); |
638 |
if (l1 < l2) |
639 |
l1 = l2; |
640 |
strdump(e1, v1, l1, utf8); |
641 |
strdump(e2, v2, l1, utf8); |
642 |
failure_finish(extra); |
643 |
return (0); |
644 |
} |
645 |
|
646 |
static void |
647 |
wcsdump(const char *e, const wchar_t *w) |
648 |
{ |
649 |
logprintf(" %s = ", e); |
650 |
if (w == NULL) { |
651 |
logprintf("(null)"); |
652 |
return; |
653 |
} |
654 |
logprintf("\""); |
655 |
while (*w != L'\0') { |
656 |
unsigned int c = *w++; |
657 |
if (c >= 32 && c < 127) |
658 |
logprintf("%c", c); |
659 |
else if (c < 256) |
660 |
logprintf("\\x%02X", c); |
661 |
else if (c < 0x10000) |
662 |
logprintf("\\u%04X", c); |
663 |
else |
664 |
logprintf("\\U%08X", c); |
665 |
} |
666 |
logprintf("\"\n"); |
667 |
} |
668 |
|
669 |
#ifndef HAVE_WCSCMP |
670 |
static int |
671 |
wcscmp(const wchar_t *s1, const wchar_t *s2) |
672 |
{ |
673 |
|
674 |
while (*s1 == *s2++) { |
675 |
if (*s1++ == L'\0') |
676 |
return 0; |
677 |
} |
678 |
if (*s1 > *--s2) |
679 |
return 1; |
680 |
else |
681 |
return -1; |
682 |
} |
683 |
#endif |
684 |
|
685 |
/* Verify that two wide strings are equal, dump them if not. */ |
686 |
int |
687 |
assertion_equal_wstring(const char *file, int line, |
688 |
const wchar_t *v1, const char *e1, |
689 |
const wchar_t *v2, const char *e2, |
690 |
void *extra) |
691 |
{ |
692 |
assertion_count(file, line); |
693 |
if (v1 == v2) |
694 |
return (1); |
695 |
if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) |
696 |
return (1); |
697 |
failure_start(file, line, "%s != %s", e1, e2); |
698 |
wcsdump(e1, v1); |
699 |
wcsdump(e2, v2); |
700 |
failure_finish(extra); |
701 |
return (0); |
702 |
} |
703 |
|
704 |
/* |
705 |
* Pretty standard hexdump routine. As a bonus, if ref != NULL, then |
706 |
* any bytes in p that differ from ref will be highlighted with '_' |
707 |
* before and after the hex value. |
708 |
*/ |
709 |
static void |
710 |
hexdump(const char *p, const char *ref, size_t l, size_t offset) |
711 |
{ |
712 |
size_t i, j; |
713 |
char sep; |
714 |
|
715 |
if (p == NULL) { |
716 |
logprintf("(null)\n"); |
717 |
return; |
718 |
} |
719 |
for(i=0; i < l; i+=16) { |
720 |
logprintf("%04x", (unsigned)(i + offset)); |
721 |
sep = ' '; |
722 |
for (j = 0; j < 16 && i + j < l; j++) { |
723 |
if (ref != NULL && p[i + j] != ref[i + j]) |
724 |
sep = '_'; |
725 |
logprintf("%c%02x", sep, 0xff & (int)p[i+j]); |
726 |
if (ref != NULL && p[i + j] == ref[i + j]) |
727 |
sep = ' '; |
728 |
} |
729 |
for (; j < 16; j++) { |
730 |
logprintf("%c ", sep); |
731 |
sep = ' '; |
732 |
} |
733 |
logprintf("%c", sep); |
734 |
for (j=0; j < 16 && i + j < l; j++) { |
735 |
int c = p[i + j]; |
736 |
if (c >= ' ' && c <= 126) |
737 |
logprintf("%c", c); |
738 |
else |
739 |
logprintf("."); |
740 |
} |
741 |
logprintf("\n"); |
742 |
} |
743 |
} |
744 |
|
745 |
/* Verify that two blocks of memory are the same, display the first |
746 |
* block of differences if they're not. */ |
747 |
int |
748 |
assertion_equal_mem(const char *file, int line, |
749 |
const void *_v1, const char *e1, |
750 |
const void *_v2, const char *e2, |
751 |
size_t l, const char *ld, void *extra) |
752 |
{ |
753 |
const char *v1 = (const char *)_v1; |
754 |
const char *v2 = (const char *)_v2; |
755 |
size_t offset; |
756 |
|
757 |
assertion_count(file, line); |
758 |
if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) |
759 |
return (1); |
760 |
if (v1 == NULL || v2 == NULL) |
761 |
return (0); |
762 |
|
763 |
failure_start(file, line, "%s != %s", e1, e2); |
764 |
logprintf(" size %s = %d\n", ld, (int)l); |
765 |
/* Dump 48 bytes (3 lines) so that the first difference is |
766 |
* in the second line. */ |
767 |
offset = 0; |
768 |
while (l > 64 && memcmp(v1, v2, 32) == 0) { |
769 |
/* Two lines agree, so step forward one line. */ |
770 |
v1 += 16; |
771 |
v2 += 16; |
772 |
l -= 16; |
773 |
offset += 16; |
774 |
} |
775 |
logprintf(" Dump of %s\n", e1); |
776 |
hexdump(v1, v2, l < 128 ? l : 128, offset); |
777 |
logprintf(" Dump of %s\n", e2); |
778 |
hexdump(v2, v1, l < 128 ? l : 128, offset); |
779 |
logprintf("\n"); |
780 |
failure_finish(extra); |
781 |
return (0); |
782 |
} |
783 |
|
784 |
/* Verify that a block of memory is filled with the specified byte. */ |
785 |
int |
786 |
assertion_memory_filled_with(const char *file, int line, |
787 |
const void *_v1, const char *vd, |
788 |
size_t l, const char *ld, |
789 |
char b, const char *bd, void *extra) |
790 |
{ |
791 |
const char *v1 = (const char *)_v1; |
792 |
size_t c = 0; |
793 |
size_t i; |
794 |
(void)ld; /* UNUSED */ |
795 |
|
796 |
assertion_count(file, line); |
797 |
|
798 |
for (i = 0; i < l; ++i) { |
799 |
if (v1[i] == b) { |
800 |
++c; |
801 |
} |
802 |
} |
803 |
if (c == l) |
804 |
return (1); |
805 |
|
806 |
failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); |
807 |
logprintf(" Only %d bytes were correct\n", (int)c); |
808 |
failure_finish(extra); |
809 |
return (0); |
810 |
} |
811 |
|
812 |
/* Verify that the named file exists and is empty. */ |
813 |
int |
814 |
assertion_empty_file(const char *filename, int line, const char *f1) |
815 |
{ |
816 |
char buff[1024]; |
817 |
struct stat st; |
818 |
ssize_t s; |
819 |
FILE *f; |
820 |
|
821 |
assertion_count(filename, line); |
822 |
|
823 |
if (stat(f1, &st) != 0) { |
824 |
failure_start(filename, line, "Stat failed: %s", f1); |
825 |
failure_finish(NULL); |
826 |
return (0); |
827 |
} |
828 |
if (st.st_size == 0) |
829 |
return (1); |
830 |
|
831 |
failure_start(filename, line, "File should be empty: %s", f1); |
832 |
logprintf(" File size: %d\n", (int)st.st_size); |
833 |
logprintf(" Contents:\n"); |
834 |
f = fopen(f1, "rb"); |
835 |
if (f == NULL) { |
836 |
logprintf(" Unable to open %s\n", f1); |
837 |
} else { |
838 |
s = ((off_t)sizeof(buff) < st.st_size) ? |
839 |
(ssize_t)sizeof(buff) : (ssize_t)st.st_size; |
840 |
s = fread(buff, 1, s, f); |
841 |
hexdump(buff, NULL, s, 0); |
842 |
fclose(f); |
843 |
} |
844 |
failure_finish(NULL); |
845 |
return (0); |
846 |
} |
847 |
|
848 |
/* Verify that the named file exists and is not empty. */ |
849 |
int |
850 |
assertion_non_empty_file(const char *filename, int line, const char *f1) |
851 |
{ |
852 |
struct stat st; |
853 |
|
854 |
assertion_count(filename, line); |
855 |
|
856 |
if (stat(f1, &st) != 0) { |
857 |
failure_start(filename, line, "Stat failed: %s", f1); |
858 |
failure_finish(NULL); |
859 |
return (0); |
860 |
} |
861 |
if (st.st_size == 0) { |
862 |
failure_start(filename, line, "File empty: %s", f1); |
863 |
failure_finish(NULL); |
864 |
return (0); |
865 |
} |
866 |
return (1); |
867 |
} |
868 |
|
869 |
/* Verify that two files have the same contents. */ |
870 |
/* TODO: hexdump the first bytes that actually differ. */ |
871 |
int |
872 |
assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) |
873 |
{ |
874 |
char buff1[1024]; |
875 |
char buff2[1024]; |
876 |
FILE *f1, *f2; |
877 |
int n1, n2; |
878 |
|
879 |
assertion_count(filename, line); |
880 |
|
881 |
f1 = fopen(fn1, "rb"); |
882 |
f2 = fopen(fn2, "rb"); |
883 |
if (f1 == NULL || f2 == NULL) { |
884 |
if (f1) fclose(f1); |
885 |
if (f2) fclose(f2); |
886 |
return (0); |
887 |
} |
888 |
for (;;) { |
889 |
n1 = (int)fread(buff1, 1, sizeof(buff1), f1); |
890 |
n2 = (int)fread(buff2, 1, sizeof(buff2), f2); |
891 |
if (n1 != n2) |
892 |
break; |
893 |
if (n1 == 0 && n2 == 0) { |
894 |
fclose(f1); |
895 |
fclose(f2); |
896 |
return (1); |
897 |
} |
898 |
if (memcmp(buff1, buff2, n1) != 0) |
899 |
break; |
900 |
} |
901 |
fclose(f1); |
902 |
fclose(f2); |
903 |
failure_start(filename, line, "Files not identical"); |
904 |
logprintf(" file1=\"%s\"\n", fn1); |
905 |
logprintf(" file2=\"%s\"\n", fn2); |
906 |
failure_finish(NULL); |
907 |
return (0); |
908 |
} |
909 |
|
910 |
/* Verify that the named file does exist. */ |
911 |
int |
912 |
assertion_file_exists(const char *filename, int line, const char *f) |
913 |
{ |
914 |
assertion_count(filename, line); |
915 |
|
916 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
917 |
if (!_access(f, 0)) |
918 |
return (1); |
919 |
#else |
920 |
if (!access(f, F_OK)) |
921 |
return (1); |
922 |
#endif |
923 |
failure_start(filename, line, "File should exist: %s", f); |
924 |
failure_finish(NULL); |
925 |
return (0); |
926 |
} |
927 |
|
928 |
/* Verify that the named file doesn't exist. */ |
929 |
int |
930 |
assertion_file_not_exists(const char *filename, int line, const char *f) |
931 |
{ |
932 |
assertion_count(filename, line); |
933 |
|
934 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
935 |
if (_access(f, 0)) |
936 |
return (1); |
937 |
#else |
938 |
if (access(f, F_OK)) |
939 |
return (1); |
940 |
#endif |
941 |
failure_start(filename, line, "File should not exist: %s", f); |
942 |
failure_finish(NULL); |
943 |
return (0); |
944 |
} |
945 |
|
946 |
/* Compare the contents of a file to a block of memory. */ |
947 |
int |
948 |
assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) |
949 |
{ |
950 |
char *contents; |
951 |
FILE *f; |
952 |
int n; |
953 |
|
954 |
assertion_count(filename, line); |
955 |
|
956 |
f = fopen(fn, "rb"); |
957 |
if (f == NULL) { |
958 |
failure_start(filename, line, |
959 |
"File should exist: %s", fn); |
960 |
failure_finish(NULL); |
961 |
return (0); |
962 |
} |
963 |
contents = malloc(s * 2); |
964 |
n = (int)fread(contents, 1, s * 2, f); |
965 |
fclose(f); |
966 |
if (n == s && memcmp(buff, contents, s) == 0) { |
967 |
free(contents); |
968 |
return (1); |
969 |
} |
970 |
failure_start(filename, line, "File contents don't match"); |
971 |
logprintf(" file=\"%s\"\n", fn); |
972 |
if (n > 0) |
973 |
hexdump(contents, buff, n > 512 ? 512 : n, 0); |
974 |
else { |
975 |
logprintf(" File empty, contents should be:\n"); |
976 |
hexdump(buff, NULL, s > 512 ? 512 : s, 0); |
977 |
} |
978 |
failure_finish(NULL); |
979 |
free(contents); |
980 |
return (0); |
981 |
} |
982 |
|
983 |
/* Check the contents of a text file, being tolerant of line endings. */ |
984 |
int |
985 |
assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) |
986 |
{ |
987 |
char *contents; |
988 |
const char *btxt, *ftxt; |
989 |
FILE *f; |
990 |
int n, s; |
991 |
|
992 |
assertion_count(filename, line); |
993 |
f = fopen(fn, "r"); |
994 |
if (f == NULL) { |
995 |
failure_start(filename, line, |
996 |
"File doesn't exist: %s", fn); |
997 |
failure_finish(NULL); |
998 |
return (0); |
999 |
} |
1000 |
s = (int)strlen(buff); |
1001 |
contents = malloc(s * 2 + 128); |
1002 |
n = (int)fread(contents, 1, s * 2 + 128 - 1, f); |
1003 |
if (n >= 0) |
1004 |
contents[n] = '\0'; |
1005 |
fclose(f); |
1006 |
/* Compare texts. */ |
1007 |
btxt = buff; |
1008 |
ftxt = (const char *)contents; |
1009 |
while (*btxt != '\0' && *ftxt != '\0') { |
1010 |
if (*btxt == *ftxt) { |
1011 |
++btxt; |
1012 |
++ftxt; |
1013 |
continue; |
1014 |
} |
1015 |
if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { |
1016 |
/* Pass over different new line characters. */ |
1017 |
++btxt; |
1018 |
ftxt += 2; |
1019 |
continue; |
1020 |
} |
1021 |
break; |
1022 |
} |
1023 |
if (*btxt == '\0' && *ftxt == '\0') { |
1024 |
free(contents); |
1025 |
return (1); |
1026 |
} |
1027 |
failure_start(filename, line, "Contents don't match"); |
1028 |
logprintf(" file=\"%s\"\n", fn); |
1029 |
if (n > 0) { |
1030 |
hexdump(contents, buff, n, 0); |
1031 |
logprintf(" expected\n", fn); |
1032 |
hexdump(buff, contents, s, 0); |
1033 |
} else { |
1034 |
logprintf(" File empty, contents should be:\n"); |
1035 |
hexdump(buff, NULL, s, 0); |
1036 |
} |
1037 |
failure_finish(NULL); |
1038 |
free(contents); |
1039 |
return (0); |
1040 |
} |
1041 |
|
1042 |
/* Verify that a text file contains the specified lines, regardless of order */ |
1043 |
/* This could be more efficient if we sorted both sets of lines, etc, but |
1044 |
* since this is used only for testing and only ever deals with a dozen or so |
1045 |
* lines at a time, this relatively crude approach is just fine. */ |
1046 |
int |
1047 |
assertion_file_contains_lines_any_order(const char *file, int line, |
1048 |
const char *pathname, const char *lines[]) |
1049 |
{ |
1050 |
char *buff; |
1051 |
size_t buff_size; |
1052 |
size_t expected_count, actual_count, i, j; |
1053 |
char **expected = NULL; |
1054 |
char *p, **actual = NULL; |
1055 |
char c; |
1056 |
int expected_failure = 0, actual_failure = 0; |
1057 |
|
1058 |
assertion_count(file, line); |
1059 |
|
1060 |
buff = slurpfile(&buff_size, "%s", pathname); |
1061 |
if (buff == NULL) { |
1062 |
failure_start(pathname, line, "Can't read file: %s", pathname); |
1063 |
failure_finish(NULL); |
1064 |
return (0); |
1065 |
} |
1066 |
|
1067 |
/* Make a copy of the provided lines and count up the expected |
1068 |
* file size. */ |
1069 |
for (i = 0; lines[i] != NULL; ++i) { |
1070 |
} |
1071 |
expected_count = i; |
1072 |
if (expected_count) { |
1073 |
expected = malloc(sizeof(char *) * expected_count); |
1074 |
if (expected == NULL) { |
1075 |
failure_start(pathname, line, "Can't allocate memory"); |
1076 |
failure_finish(NULL); |
1077 |
free(expected); |
1078 |
return (0); |
1079 |
} |
1080 |
for (i = 0; lines[i] != NULL; ++i) { |
1081 |
expected[i] = strdup(lines[i]); |
1082 |
} |
1083 |
} |
1084 |
|
1085 |
/* Break the file into lines */ |
1086 |
actual_count = 0; |
1087 |
for (c = '\0', p = buff; p < buff + buff_size; ++p) { |
1088 |
if (*p == '\x0d' || *p == '\x0a') |
1089 |
*p = '\0'; |
1090 |
if (c == '\0' && *p != '\0') |
1091 |
++actual_count; |
1092 |
c = *p; |
1093 |
} |
1094 |
if (actual_count) { |
1095 |
actual = calloc(sizeof(char *), actual_count); |
1096 |
if (actual == NULL) { |
1097 |
failure_start(pathname, line, "Can't allocate memory"); |
1098 |
failure_finish(NULL); |
1099 |
free(expected); |
1100 |
return (0); |
1101 |
} |
1102 |
for (j = 0, p = buff; p < buff + buff_size; |
1103 |
p += 1 + strlen(p)) { |
1104 |
if (*p != '\0') { |
1105 |
actual[j] = p; |
1106 |
++j; |
1107 |
} |
1108 |
} |
1109 |
} |
1110 |
|
1111 |
/* Erase matching lines from both lists */ |
1112 |
for (i = 0; i < expected_count; ++i) { |
1113 |
if (expected[i] == NULL) |
1114 |
continue; |
1115 |
for (j = 0; j < actual_count; ++j) { |
1116 |
if (actual[j] == NULL) |
1117 |
continue; |
1118 |
if (strcmp(expected[i], actual[j]) == 0) { |
1119 |
free(expected[i]); |
1120 |
expected[i] = NULL; |
1121 |
actual[j] = NULL; |
1122 |
break; |
1123 |
} |
1124 |
} |
1125 |
} |
1126 |
|
1127 |
/* If there's anything left, it's a failure */ |
1128 |
for (i = 0; i < expected_count; ++i) { |
1129 |
if (expected[i] != NULL) |
1130 |
++expected_failure; |
1131 |
} |
1132 |
for (j = 0; j < actual_count; ++j) { |
1133 |
if (actual[j] != NULL) |
1134 |
++actual_failure; |
1135 |
} |
1136 |
if (expected_failure == 0 && actual_failure == 0) { |
1137 |
free(buff); |
1138 |
free(expected); |
1139 |
free(actual); |
1140 |
return (1); |
1141 |
} |
1142 |
failure_start(file, line, "File doesn't match: %s", pathname); |
1143 |
for (i = 0; i < expected_count; ++i) { |
1144 |
if (expected[i] != NULL) { |
1145 |
logprintf(" Expected but not present: %s\n", expected[i]); |
1146 |
free(expected[i]); |
1147 |
} |
1148 |
} |
1149 |
for (j = 0; j < actual_count; ++j) { |
1150 |
if (actual[j] != NULL) |
1151 |
logprintf(" Present but not expected: %s\n", actual[j]); |
1152 |
} |
1153 |
failure_finish(NULL); |
1154 |
free(buff); |
1155 |
free(expected); |
1156 |
free(actual); |
1157 |
return (0); |
1158 |
} |
1159 |
|
1160 |
/* Test that two paths point to the same file. */ |
1161 |
/* As a side-effect, asserts that both files exist. */ |
1162 |
static int |
1163 |
is_hardlink(const char *file, int line, |
1164 |
const char *path1, const char *path2) |
1165 |
{ |
1166 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1167 |
BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; |
1168 |
int r; |
1169 |
|
1170 |
assertion_count(file, line); |
1171 |
r = my_GetFileInformationByName(path1, &bhfi1); |
1172 |
if (r == 0) { |
1173 |
failure_start(file, line, "File %s can't be inspected?", path1); |
1174 |
failure_finish(NULL); |
1175 |
return (0); |
1176 |
} |
1177 |
r = my_GetFileInformationByName(path2, &bhfi2); |
1178 |
if (r == 0) { |
1179 |
failure_start(file, line, "File %s can't be inspected?", path2); |
1180 |
failure_finish(NULL); |
1181 |
return (0); |
1182 |
} |
1183 |
return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber |
1184 |
&& bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh |
1185 |
&& bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); |
1186 |
#else |
1187 |
struct stat st1, st2; |
1188 |
int r; |
1189 |
|
1190 |
assertion_count(file, line); |
1191 |
r = lstat(path1, &st1); |
1192 |
if (r != 0) { |
1193 |
failure_start(file, line, "File should exist: %s", path1); |
1194 |
failure_finish(NULL); |
1195 |
return (0); |
1196 |
} |
1197 |
r = lstat(path2, &st2); |
1198 |
if (r != 0) { |
1199 |
failure_start(file, line, "File should exist: %s", path2); |
1200 |
failure_finish(NULL); |
1201 |
return (0); |
1202 |
} |
1203 |
return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); |
1204 |
#endif |
1205 |
} |
1206 |
|
1207 |
int |
1208 |
assertion_is_hardlink(const char *file, int line, |
1209 |
const char *path1, const char *path2) |
1210 |
{ |
1211 |
if (is_hardlink(file, line, path1, path2)) |
1212 |
return (1); |
1213 |
failure_start(file, line, |
1214 |
"Files %s and %s are not hardlinked", path1, path2); |
1215 |
failure_finish(NULL); |
1216 |
return (0); |
1217 |
} |
1218 |
|
1219 |
int |
1220 |
assertion_is_not_hardlink(const char *file, int line, |
1221 |
const char *path1, const char *path2) |
1222 |
{ |
1223 |
if (!is_hardlink(file, line, path1, path2)) |
1224 |
return (1); |
1225 |
failure_start(file, line, |
1226 |
"Files %s and %s should not be hardlinked", path1, path2); |
1227 |
failure_finish(NULL); |
1228 |
return (0); |
1229 |
} |
1230 |
|
1231 |
/* Verify a/b/mtime of 'pathname'. */ |
1232 |
/* If 'recent', verify that it's within last 10 seconds. */ |
1233 |
static int |
1234 |
assertion_file_time(const char *file, int line, |
1235 |
const char *pathname, long t, long nsec, char type, int recent) |
1236 |
{ |
1237 |
long long filet, filet_nsec; |
1238 |
int r; |
1239 |
|
1240 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1241 |
#define EPOC_TIME (116444736000000000ULL) |
1242 |
FILETIME fxtime, fbirthtime, fatime, fmtime; |
1243 |
ULARGE_INTEGER wintm; |
1244 |
HANDLE h; |
1245 |
fxtime.dwLowDateTime = 0; |
1246 |
fxtime.dwHighDateTime = 0; |
1247 |
|
1248 |
assertion_count(file, line); |
1249 |
/* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open |
1250 |
* a directory file. If not, CreateFile() will fail when |
1251 |
* the pathname is a directory. */ |
1252 |
h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, |
1253 |
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
1254 |
if (h == INVALID_HANDLE_VALUE) { |
1255 |
failure_start(file, line, "Can't access %s\n", pathname); |
1256 |
failure_finish(NULL); |
1257 |
return (0); |
1258 |
} |
1259 |
r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); |
1260 |
switch (type) { |
1261 |
case 'a': fxtime = fatime; break; |
1262 |
case 'b': fxtime = fbirthtime; break; |
1263 |
case 'm': fxtime = fmtime; break; |
1264 |
} |
1265 |
CloseHandle(h); |
1266 |
if (r == 0) { |
1267 |
failure_start(file, line, "Can't GetFileTime %s\n", pathname); |
1268 |
failure_finish(NULL); |
1269 |
return (0); |
1270 |
} |
1271 |
wintm.LowPart = fxtime.dwLowDateTime; |
1272 |
wintm.HighPart = fxtime.dwHighDateTime; |
1273 |
filet = (wintm.QuadPart - EPOC_TIME) / 10000000; |
1274 |
filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; |
1275 |
nsec = (nsec / 100) * 100; /* Round the request */ |
1276 |
#else |
1277 |
struct stat st; |
1278 |
|
1279 |
assertion_count(file, line); |
1280 |
r = lstat(pathname, &st); |
1281 |
if (r != 0) { |
1282 |
failure_start(file, line, "Can't stat %s\n", pathname); |
1283 |
failure_finish(NULL); |
1284 |
return (0); |
1285 |
} |
1286 |
switch (type) { |
1287 |
case 'a': filet = st.st_atime; break; |
1288 |
case 'm': filet = st.st_mtime; break; |
1289 |
case 'b': filet = 0; break; |
1290 |
default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); |
1291 |
exit(1); |
1292 |
} |
1293 |
#if defined(__FreeBSD__) |
1294 |
switch (type) { |
1295 |
case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; |
1296 |
case 'b': filet = st.st_birthtime; |
1297 |
filet_nsec = st.st_birthtimespec.tv_nsec; break; |
1298 |
case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; |
1299 |
default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); |
1300 |
exit(1); |
1301 |
} |
1302 |
/* FreeBSD generally only stores to microsecond res, so round. */ |
1303 |
filet_nsec = (filet_nsec / 1000) * 1000; |
1304 |
nsec = (nsec / 1000) * 1000; |
1305 |
#else |
1306 |
filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ |
1307 |
if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ |
1308 |
#if defined(__HAIKU__) |
1309 |
if (type == 'a') return (1); /* Haiku doesn't have atime. */ |
1310 |
#endif |
1311 |
#endif |
1312 |
#endif |
1313 |
if (recent) { |
1314 |
/* Check that requested time is up-to-date. */ |
1315 |
time_t now = time(NULL); |
1316 |
if (filet < now - 10 || filet > now + 1) { |
1317 |
failure_start(file, line, |
1318 |
"File %s has %ctime %lld, %lld seconds ago\n", |
1319 |
pathname, type, filet, now - filet); |
1320 |
failure_finish(NULL); |
1321 |
return (0); |
1322 |
} |
1323 |
} else if (filet != t || filet_nsec != nsec) { |
1324 |
failure_start(file, line, |
1325 |
"File %s has %ctime %lld.%09lld, expected %lld.%09lld", |
1326 |
pathname, type, filet, filet_nsec, t, nsec); |
1327 |
failure_finish(NULL); |
1328 |
return (0); |
1329 |
} |
1330 |
return (1); |
1331 |
} |
1332 |
|
1333 |
/* Verify atime of 'pathname'. */ |
1334 |
int |
1335 |
assertion_file_atime(const char *file, int line, |
1336 |
const char *pathname, long t, long nsec) |
1337 |
{ |
1338 |
return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); |
1339 |
} |
1340 |
|
1341 |
/* Verify atime of 'pathname' is up-to-date. */ |
1342 |
int |
1343 |
assertion_file_atime_recent(const char *file, int line, const char *pathname) |
1344 |
{ |
1345 |
return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); |
1346 |
} |
1347 |
|
1348 |
/* Verify birthtime of 'pathname'. */ |
1349 |
int |
1350 |
assertion_file_birthtime(const char *file, int line, |
1351 |
const char *pathname, long t, long nsec) |
1352 |
{ |
1353 |
return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); |
1354 |
} |
1355 |
|
1356 |
/* Verify birthtime of 'pathname' is up-to-date. */ |
1357 |
int |
1358 |
assertion_file_birthtime_recent(const char *file, int line, |
1359 |
const char *pathname) |
1360 |
{ |
1361 |
return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); |
1362 |
} |
1363 |
|
1364 |
/* Verify mtime of 'pathname'. */ |
1365 |
int |
1366 |
assertion_file_mtime(const char *file, int line, |
1367 |
const char *pathname, long t, long nsec) |
1368 |
{ |
1369 |
return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); |
1370 |
} |
1371 |
|
1372 |
/* Verify mtime of 'pathname' is up-to-date. */ |
1373 |
int |
1374 |
assertion_file_mtime_recent(const char *file, int line, const char *pathname) |
1375 |
{ |
1376 |
return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); |
1377 |
} |
1378 |
|
1379 |
/* Verify number of links to 'pathname'. */ |
1380 |
int |
1381 |
assertion_file_nlinks(const char *file, int line, |
1382 |
const char *pathname, int nlinks) |
1383 |
{ |
1384 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1385 |
BY_HANDLE_FILE_INFORMATION bhfi; |
1386 |
int r; |
1387 |
|
1388 |
assertion_count(file, line); |
1389 |
r = my_GetFileInformationByName(pathname, &bhfi); |
1390 |
if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) |
1391 |
return (1); |
1392 |
failure_start(file, line, "File %s has %d links, expected %d", |
1393 |
pathname, bhfi.nNumberOfLinks, nlinks); |
1394 |
failure_finish(NULL); |
1395 |
return (0); |
1396 |
#else |
1397 |
struct stat st; |
1398 |
int r; |
1399 |
|
1400 |
assertion_count(file, line); |
1401 |
r = lstat(pathname, &st); |
1402 |
if (r == 0 && (int)st.st_nlink == nlinks) |
1403 |
return (1); |
1404 |
failure_start(file, line, "File %s has %d links, expected %d", |
1405 |
pathname, st.st_nlink, nlinks); |
1406 |
failure_finish(NULL); |
1407 |
return (0); |
1408 |
#endif |
1409 |
} |
1410 |
|
1411 |
/* Verify size of 'pathname'. */ |
1412 |
int |
1413 |
assertion_file_size(const char *file, int line, const char *pathname, long size) |
1414 |
{ |
1415 |
int64_t filesize; |
1416 |
int r; |
1417 |
|
1418 |
assertion_count(file, line); |
1419 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1420 |
{ |
1421 |
BY_HANDLE_FILE_INFORMATION bhfi; |
1422 |
r = !my_GetFileInformationByName(pathname, &bhfi); |
1423 |
filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; |
1424 |
} |
1425 |
#else |
1426 |
{ |
1427 |
struct stat st; |
1428 |
r = lstat(pathname, &st); |
1429 |
filesize = st.st_size; |
1430 |
} |
1431 |
#endif |
1432 |
if (r == 0 && filesize == size) |
1433 |
return (1); |
1434 |
failure_start(file, line, "File %s has size %ld, expected %ld", |
1435 |
pathname, (long)filesize, (long)size); |
1436 |
failure_finish(NULL); |
1437 |
return (0); |
1438 |
} |
1439 |
|
1440 |
/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ |
1441 |
int |
1442 |
assertion_is_dir(const char *file, int line, const char *pathname, int mode) |
1443 |
{ |
1444 |
struct stat st; |
1445 |
int r; |
1446 |
|
1447 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1448 |
(void)mode; /* UNUSED */ |
1449 |
#endif |
1450 |
assertion_count(file, line); |
1451 |
r = lstat(pathname, &st); |
1452 |
if (r != 0) { |
1453 |
failure_start(file, line, "Dir should exist: %s", pathname); |
1454 |
failure_finish(NULL); |
1455 |
return (0); |
1456 |
} |
1457 |
if (!S_ISDIR(st.st_mode)) { |
1458 |
failure_start(file, line, "%s is not a dir", pathname); |
1459 |
failure_finish(NULL); |
1460 |
return (0); |
1461 |
} |
1462 |
#if !defined(_WIN32) || defined(__CYGWIN__) |
1463 |
/* Windows doesn't handle permissions the same way as POSIX, |
1464 |
* so just ignore the mode tests. */ |
1465 |
/* TODO: Can we do better here? */ |
1466 |
if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { |
1467 |
failure_start(file, line, "Dir %s has wrong mode", pathname); |
1468 |
logprintf(" Expected: 0%3o\n", mode); |
1469 |
logprintf(" Found: 0%3o\n", st.st_mode & 07777); |
1470 |
failure_finish(NULL); |
1471 |
return (0); |
1472 |
} |
1473 |
#endif |
1474 |
return (1); |
1475 |
} |
1476 |
|
1477 |
/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, |
1478 |
* verify that too. */ |
1479 |
int |
1480 |
assertion_is_reg(const char *file, int line, const char *pathname, int mode) |
1481 |
{ |
1482 |
struct stat st; |
1483 |
int r; |
1484 |
|
1485 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1486 |
(void)mode; /* UNUSED */ |
1487 |
#endif |
1488 |
assertion_count(file, line); |
1489 |
r = lstat(pathname, &st); |
1490 |
if (r != 0 || !S_ISREG(st.st_mode)) { |
1491 |
failure_start(file, line, "File should exist: %s", pathname); |
1492 |
failure_finish(NULL); |
1493 |
return (0); |
1494 |
} |
1495 |
#if !defined(_WIN32) || defined(__CYGWIN__) |
1496 |
/* Windows doesn't handle permissions the same way as POSIX, |
1497 |
* so just ignore the mode tests. */ |
1498 |
/* TODO: Can we do better here? */ |
1499 |
if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { |
1500 |
failure_start(file, line, "File %s has wrong mode", pathname); |
1501 |
logprintf(" Expected: 0%3o\n", mode); |
1502 |
logprintf(" Found: 0%3o\n", st.st_mode & 07777); |
1503 |
failure_finish(NULL); |
1504 |
return (0); |
1505 |
} |
1506 |
#endif |
1507 |
return (1); |
1508 |
} |
1509 |
|
1510 |
/* Check whether 'pathname' is a symbolic link. If 'contents' is |
1511 |
* non-NULL, verify that the symlink has those contents. */ |
1512 |
static int |
1513 |
is_symlink(const char *file, int line, |
1514 |
const char *pathname, const char *contents) |
1515 |
{ |
1516 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1517 |
(void)pathname; /* UNUSED */ |
1518 |
(void)contents; /* UNUSED */ |
1519 |
assertion_count(file, line); |
1520 |
/* Windows sort-of has real symlinks, but they're only usable |
1521 |
* by privileged users and are crippled even then, so there's |
1522 |
* really not much point in bothering with this. */ |
1523 |
return (0); |
1524 |
#else |
1525 |
char buff[300]; |
1526 |
struct stat st; |
1527 |
ssize_t linklen; |
1528 |
int r; |
1529 |
|
1530 |
assertion_count(file, line); |
1531 |
r = lstat(pathname, &st); |
1532 |
if (r != 0) { |
1533 |
failure_start(file, line, |
1534 |
"Symlink should exist: %s", pathname); |
1535 |
failure_finish(NULL); |
1536 |
return (0); |
1537 |
} |
1538 |
if (!S_ISLNK(st.st_mode)) |
1539 |
return (0); |
1540 |
if (contents == NULL) |
1541 |
return (1); |
1542 |
linklen = readlink(pathname, buff, sizeof(buff)); |
1543 |
if (linklen < 0) { |
1544 |
failure_start(file, line, "Can't read symlink %s", pathname); |
1545 |
failure_finish(NULL); |
1546 |
return (0); |
1547 |
} |
1548 |
buff[linklen] = '\0'; |
1549 |
if (strcmp(buff, contents) != 0) |
1550 |
return (0); |
1551 |
return (1); |
1552 |
#endif |
1553 |
} |
1554 |
|
1555 |
/* Assert that path is a symlink that (optionally) contains contents. */ |
1556 |
int |
1557 |
assertion_is_symlink(const char *file, int line, |
1558 |
const char *path, const char *contents) |
1559 |
{ |
1560 |
if (is_symlink(file, line, path, contents)) |
1561 |
return (1); |
1562 |
if (contents) |
1563 |
failure_start(file, line, "File %s is not a symlink to %s", |
1564 |
path, contents); |
1565 |
else |
1566 |
failure_start(file, line, "File %s is not a symlink", path); |
1567 |
failure_finish(NULL); |
1568 |
return (0); |
1569 |
} |
1570 |
|
1571 |
|
1572 |
/* Create a directory and report any errors. */ |
1573 |
int |
1574 |
assertion_make_dir(const char *file, int line, const char *dirname, int mode) |
1575 |
{ |
1576 |
assertion_count(file, line); |
1577 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1578 |
(void)mode; /* UNUSED */ |
1579 |
if (0 == _mkdir(dirname)) |
1580 |
return (1); |
1581 |
#else |
1582 |
if (0 == mkdir(dirname, mode)) |
1583 |
return (1); |
1584 |
#endif |
1585 |
failure_start(file, line, "Could not create directory %s", dirname); |
1586 |
failure_finish(NULL); |
1587 |
return(0); |
1588 |
} |
1589 |
|
1590 |
/* Create a file with the specified contents and report any failures. */ |
1591 |
int |
1592 |
assertion_make_file(const char *file, int line, |
1593 |
const char *path, int mode, int csize, const void *contents) |
1594 |
{ |
1595 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1596 |
/* TODO: Rework this to set file mode as well. */ |
1597 |
FILE *f; |
1598 |
(void)mode; /* UNUSED */ |
1599 |
assertion_count(file, line); |
1600 |
f = fopen(path, "wb"); |
1601 |
if (f == NULL) { |
1602 |
failure_start(file, line, "Could not create file %s", path); |
1603 |
failure_finish(NULL); |
1604 |
return (0); |
1605 |
} |
1606 |
if (contents != NULL) { |
1607 |
size_t wsize; |
1608 |
|
1609 |
if (csize < 0) |
1610 |
wsize = strlen(contents); |
1611 |
else |
1612 |
wsize = (size_t)csize; |
1613 |
if (wsize != fwrite(contents, 1, wsize, f)) { |
1614 |
fclose(f); |
1615 |
failure_start(file, line, |
1616 |
"Could not write file %s", path); |
1617 |
failure_finish(NULL); |
1618 |
return (0); |
1619 |
} |
1620 |
} |
1621 |
fclose(f); |
1622 |
return (1); |
1623 |
#else |
1624 |
int fd; |
1625 |
assertion_count(file, line); |
1626 |
fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); |
1627 |
if (fd < 0) { |
1628 |
failure_start(file, line, "Could not create %s", path); |
1629 |
failure_finish(NULL); |
1630 |
return (0); |
1631 |
} |
1632 |
if (contents != NULL) { |
1633 |
ssize_t wsize; |
1634 |
|
1635 |
if (csize < 0) |
1636 |
wsize = (ssize_t)strlen(contents); |
1637 |
else |
1638 |
wsize = (ssize_t)csize; |
1639 |
if (wsize != write(fd, contents, wsize)) { |
1640 |
close(fd); |
1641 |
failure_start(file, line, |
1642 |
"Could not write to %s", path); |
1643 |
failure_finish(NULL); |
1644 |
return (0); |
1645 |
} |
1646 |
} |
1647 |
close(fd); |
1648 |
return (1); |
1649 |
#endif |
1650 |
} |
1651 |
|
1652 |
/* Create a hardlink and report any failures. */ |
1653 |
int |
1654 |
assertion_make_hardlink(const char *file, int line, |
1655 |
const char *newpath, const char *linkto) |
1656 |
{ |
1657 |
int succeeded; |
1658 |
|
1659 |
assertion_count(file, line); |
1660 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1661 |
succeeded = my_CreateHardLinkA(newpath, linkto); |
1662 |
#elif HAVE_LINK |
1663 |
succeeded = !link(linkto, newpath); |
1664 |
#else |
1665 |
succeeded = 0; |
1666 |
#endif |
1667 |
if (succeeded) |
1668 |
return (1); |
1669 |
failure_start(file, line, "Could not create hardlink"); |
1670 |
logprintf(" New link: %s\n", newpath); |
1671 |
logprintf(" Old name: %s\n", linkto); |
1672 |
failure_finish(NULL); |
1673 |
return(0); |
1674 |
} |
1675 |
|
1676 |
/* Create a symlink and report any failures. */ |
1677 |
int |
1678 |
assertion_make_symlink(const char *file, int line, |
1679 |
const char *newpath, const char *linkto) |
1680 |
{ |
1681 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1682 |
int targetIsDir = 0; /* TODO: Fix this */ |
1683 |
assertion_count(file, line); |
1684 |
if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) |
1685 |
return (1); |
1686 |
#elif HAVE_SYMLINK |
1687 |
assertion_count(file, line); |
1688 |
if (0 == symlink(linkto, newpath)) |
1689 |
return (1); |
1690 |
#endif |
1691 |
failure_start(file, line, "Could not create symlink"); |
1692 |
logprintf(" New link: %s\n", newpath); |
1693 |
logprintf(" Old name: %s\n", linkto); |
1694 |
failure_finish(NULL); |
1695 |
return(0); |
1696 |
} |
1697 |
|
1698 |
/* Set umask, report failures. */ |
1699 |
int |
1700 |
assertion_umask(const char *file, int line, int mask) |
1701 |
{ |
1702 |
assertion_count(file, line); |
1703 |
(void)file; /* UNUSED */ |
1704 |
(void)line; /* UNUSED */ |
1705 |
umask(mask); |
1706 |
return (1); |
1707 |
} |
1708 |
|
1709 |
/* Set times, report failures. */ |
1710 |
int |
1711 |
assertion_utimes(const char *file, int line, |
1712 |
const char *pathname, long at, long at_nsec, long mt, long mt_nsec) |
1713 |
{ |
1714 |
int r; |
1715 |
|
1716 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1717 |
#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ |
1718 |
+ (((nsec)/1000)*10)) |
1719 |
HANDLE h; |
1720 |
ULARGE_INTEGER wintm; |
1721 |
FILETIME fatime, fmtime; |
1722 |
FILETIME *pat, *pmt; |
1723 |
|
1724 |
assertion_count(file, line); |
1725 |
h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, |
1726 |
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
1727 |
FILE_FLAG_BACKUP_SEMANTICS, NULL); |
1728 |
if (h == INVALID_HANDLE_VALUE) { |
1729 |
failure_start(file, line, "Can't access %s\n", pathname); |
1730 |
failure_finish(NULL); |
1731 |
return (0); |
1732 |
} |
1733 |
|
1734 |
if (at > 0 || at_nsec > 0) { |
1735 |
wintm.QuadPart = WINTIME(at, at_nsec); |
1736 |
fatime.dwLowDateTime = wintm.LowPart; |
1737 |
fatime.dwHighDateTime = wintm.HighPart; |
1738 |
pat = &fatime; |
1739 |
} else |
1740 |
pat = NULL; |
1741 |
if (mt > 0 || mt_nsec > 0) { |
1742 |
wintm.QuadPart = WINTIME(mt, mt_nsec); |
1743 |
fmtime.dwLowDateTime = wintm.LowPart; |
1744 |
fmtime.dwHighDateTime = wintm.HighPart; |
1745 |
pmt = &fmtime; |
1746 |
} else |
1747 |
pmt = NULL; |
1748 |
if (pat != NULL || pmt != NULL) |
1749 |
r = SetFileTime(h, NULL, pat, pmt); |
1750 |
else |
1751 |
r = 1; |
1752 |
CloseHandle(h); |
1753 |
if (r == 0) { |
1754 |
failure_start(file, line, "Can't SetFileTime %s\n", pathname); |
1755 |
failure_finish(NULL); |
1756 |
return (0); |
1757 |
} |
1758 |
return (1); |
1759 |
#else /* defined(_WIN32) && !defined(__CYGWIN__) */ |
1760 |
struct stat st; |
1761 |
struct timeval times[2]; |
1762 |
|
1763 |
#if !defined(__FreeBSD__) |
1764 |
mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ |
1765 |
#endif |
1766 |
if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) |
1767 |
return (1); |
1768 |
|
1769 |
r = lstat(pathname, &st); |
1770 |
if (r < 0) { |
1771 |
failure_start(file, line, "Can't stat %s\n", pathname); |
1772 |
failure_finish(NULL); |
1773 |
return (0); |
1774 |
} |
1775 |
|
1776 |
if (mt == 0 && mt_nsec == 0) { |
1777 |
mt = st.st_mtime; |
1778 |
#if defined(__FreeBSD__) |
1779 |
mt_nsec = st.st_mtimespec.tv_nsec; |
1780 |
/* FreeBSD generally only stores to microsecond res, so round. */ |
1781 |
mt_nsec = (mt_nsec / 1000) * 1000; |
1782 |
#endif |
1783 |
} |
1784 |
if (at == 0 && at_nsec == 0) { |
1785 |
at = st.st_atime; |
1786 |
#if defined(__FreeBSD__) |
1787 |
at_nsec = st.st_atimespec.tv_nsec; |
1788 |
/* FreeBSD generally only stores to microsecond res, so round. */ |
1789 |
at_nsec = (at_nsec / 1000) * 1000; |
1790 |
#endif |
1791 |
} |
1792 |
|
1793 |
times[1].tv_sec = mt; |
1794 |
times[1].tv_usec = mt_nsec / 1000; |
1795 |
|
1796 |
times[0].tv_sec = at; |
1797 |
times[0].tv_usec = at_nsec / 1000; |
1798 |
|
1799 |
#ifdef HAVE_LUTIMES |
1800 |
r = lutimes(pathname, times); |
1801 |
#else |
1802 |
r = utimes(pathname, times); |
1803 |
#endif |
1804 |
if (r < 0) { |
1805 |
failure_start(file, line, "Can't utimes %s\n", pathname); |
1806 |
failure_finish(NULL); |
1807 |
return (0); |
1808 |
} |
1809 |
return (1); |
1810 |
#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ |
1811 |
} |
1812 |
|
1813 |
/* Set nodump, report failures. */ |
1814 |
int |
1815 |
assertion_nodump(const char *file, int line, const char *pathname) |
1816 |
{ |
1817 |
#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) |
1818 |
int r; |
1819 |
|
1820 |
assertion_count(file, line); |
1821 |
r = chflags(pathname, UF_NODUMP); |
1822 |
if (r < 0) { |
1823 |
failure_start(file, line, "Can't set nodump %s\n", pathname); |
1824 |
failure_finish(NULL); |
1825 |
return (0); |
1826 |
} |
1827 |
#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ |
1828 |
&& defined(EXT2_NODUMP_FL) |
1829 |
int fd, r, flags; |
1830 |
|
1831 |
assertion_count(file, line); |
1832 |
fd = open(pathname, O_RDONLY | O_NONBLOCK); |
1833 |
if (fd < 0) { |
1834 |
failure_start(file, line, "Can't open %s\n", pathname); |
1835 |
failure_finish(NULL); |
1836 |
return (0); |
1837 |
} |
1838 |
r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); |
1839 |
if (r < 0) { |
1840 |
failure_start(file, line, "Can't get flags %s\n", pathname); |
1841 |
failure_finish(NULL); |
1842 |
return (0); |
1843 |
} |
1844 |
flags |= EXT2_NODUMP_FL; |
1845 |
r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); |
1846 |
if (r < 0) { |
1847 |
failure_start(file, line, "Can't set nodump %s\n", pathname); |
1848 |
failure_finish(NULL); |
1849 |
return (0); |
1850 |
} |
1851 |
close(fd); |
1852 |
#else |
1853 |
(void)pathname; /* UNUSED */ |
1854 |
assertion_count(file, line); |
1855 |
#endif |
1856 |
return (1); |
1857 |
} |
1858 |
|
1859 |
/* |
1860 |
* |
1861 |
* UTILITIES for use by tests. |
1862 |
* |
1863 |
*/ |
1864 |
|
1865 |
/* |
1866 |
* Check whether platform supports symlinks. This is intended |
1867 |
* for tests to use in deciding whether to bother testing symlink |
1868 |
* support; if the platform doesn't support symlinks, there's no point |
1869 |
* in checking whether the program being tested can create them. |
1870 |
* |
1871 |
* Note that the first time this test is called, we actually go out to |
1872 |
* disk to create and verify a symlink. This is necessary because |
1873 |
* symlink support is actually a property of a particular filesystem |
1874 |
* and can thus vary between directories on a single system. After |
1875 |
* the first call, this returns the cached result from memory, so it's |
1876 |
* safe to call it as often as you wish. |
1877 |
*/ |
1878 |
int |
1879 |
canSymlink(void) |
1880 |
{ |
1881 |
/* Remember the test result */ |
1882 |
static int value = 0, tested = 0; |
1883 |
if (tested) |
1884 |
return (value); |
1885 |
|
1886 |
++tested; |
1887 |
assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); |
1888 |
/* Note: Cygwin has its own symlink() emulation that does not |
1889 |
* use the Win32 CreateSymbolicLink() function. */ |
1890 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1891 |
value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) |
1892 |
&& is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); |
1893 |
#elif HAVE_SYMLINK |
1894 |
value = (0 == symlink("canSymlink.0", "canSymlink.1")) |
1895 |
&& is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); |
1896 |
#endif |
1897 |
return (value); |
1898 |
} |
1899 |
|
1900 |
/* Platform-dependent options for hiding the output of a subcommand. */ |
1901 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
1902 |
static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ |
1903 |
#else |
1904 |
static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ |
1905 |
#endif |
1906 |
/* |
1907 |
* Can this platform run the bzip2 program? |
1908 |
*/ |
1909 |
int |
1910 |
canBzip2(void) |
1911 |
{ |
1912 |
static int tested = 0, value = 0; |
1913 |
if (!tested) { |
1914 |
tested = 1; |
1915 |
if (systemf("bzip2 -d -V %s", redirectArgs) == 0) |
1916 |
value = 1; |
1917 |
} |
1918 |
return (value); |
1919 |
} |
1920 |
|
1921 |
/* |
1922 |
* Can this platform run the grzip program? |
1923 |
*/ |
1924 |
int |
1925 |
canGrzip(void) |
1926 |
{ |
1927 |
static int tested = 0, value = 0; |
1928 |
if (!tested) { |
1929 |
tested = 1; |
1930 |
if (systemf("grzip -V %s", redirectArgs) == 0) |
1931 |
value = 1; |
1932 |
} |
1933 |
return (value); |
1934 |
} |
1935 |
|
1936 |
/* |
1937 |
* Can this platform run the gzip program? |
1938 |
*/ |
1939 |
int |
1940 |
canGzip(void) |
1941 |
{ |
1942 |
static int tested = 0, value = 0; |
1943 |
if (!tested) { |
1944 |
tested = 1; |
1945 |
if (systemf("gzip -V %s", redirectArgs) == 0) |
1946 |
value = 1; |
1947 |
} |
1948 |
return (value); |
1949 |
} |
1950 |
|
1951 |
/* |
1952 |
* Can this platform run the lrzip program? |
1953 |
*/ |
1954 |
int |
1955 |
canRunCommand(const char *cmd) |
1956 |
{ |
1957 |
static int tested = 0, value = 0; |
1958 |
if (!tested) { |
1959 |
tested = 1; |
1960 |
if (systemf("%s %s", cmd, redirectArgs) == 0) |
1961 |
value = 1; |
1962 |
} |
1963 |
return (value); |
1964 |
} |
1965 |
|
1966 |
int |
1967 |
canLrzip(void) |
1968 |
{ |
1969 |
static int tested = 0, value = 0; |
1970 |
if (!tested) { |
1971 |
tested = 1; |
1972 |
if (systemf("lrzip -V %s", redirectArgs) == 0) |
1973 |
value = 1; |
1974 |
} |
1975 |
return (value); |
1976 |
} |
1977 |
|
1978 |
/* |
1979 |
* Can this platform run the lz4 program? |
1980 |
*/ |
1981 |
int |
1982 |
canLz4(void) |
1983 |
{ |
1984 |
static int tested = 0, value = 0; |
1985 |
if (!tested) { |
1986 |
tested = 1; |
1987 |
if (systemf("lz4 -V %s", redirectArgs) == 0) |
1988 |
value = 1; |
1989 |
} |
1990 |
return (value); |
1991 |
} |
1992 |
|
1993 |
/* |
1994 |
* Can this platform run the lzip program? |
1995 |
*/ |
1996 |
int |
1997 |
canLzip(void) |
1998 |
{ |
1999 |
static int tested = 0, value = 0; |
2000 |
if (!tested) { |
2001 |
tested = 1; |
2002 |
if (systemf("lzip -V %s", redirectArgs) == 0) |
2003 |
value = 1; |
2004 |
} |
2005 |
return (value); |
2006 |
} |
2007 |
|
2008 |
/* |
2009 |
* Can this platform run the lzma program? |
2010 |
*/ |
2011 |
int |
2012 |
canLzma(void) |
2013 |
{ |
2014 |
static int tested = 0, value = 0; |
2015 |
if (!tested) { |
2016 |
tested = 1; |
2017 |
if (systemf("lzma -V %s", redirectArgs) == 0) |
2018 |
value = 1; |
2019 |
} |
2020 |
return (value); |
2021 |
} |
2022 |
|
2023 |
/* |
2024 |
* Can this platform run the lzop program? |
2025 |
*/ |
2026 |
int |
2027 |
canLzop(void) |
2028 |
{ |
2029 |
static int tested = 0, value = 0; |
2030 |
if (!tested) { |
2031 |
tested = 1; |
2032 |
if (systemf("lzop -V %s", redirectArgs) == 0) |
2033 |
value = 1; |
2034 |
} |
2035 |
return (value); |
2036 |
} |
2037 |
|
2038 |
/* |
2039 |
* Can this platform run the xz program? |
2040 |
*/ |
2041 |
int |
2042 |
canXz(void) |
2043 |
{ |
2044 |
static int tested = 0, value = 0; |
2045 |
if (!tested) { |
2046 |
tested = 1; |
2047 |
if (systemf("xz -V %s", redirectArgs) == 0) |
2048 |
value = 1; |
2049 |
} |
2050 |
return (value); |
2051 |
} |
2052 |
|
2053 |
/* |
2054 |
* Can this filesystem handle nodump flags. |
2055 |
*/ |
2056 |
#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) |
2057 |
|
2058 |
int |
2059 |
canNodump(void) |
2060 |
{ |
2061 |
const char *path = "cannodumptest"; |
2062 |
struct stat sb; |
2063 |
|
2064 |
assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); |
2065 |
if (chflags(path, UF_NODUMP) < 0) |
2066 |
return (0); |
2067 |
if (stat(path, &sb) < 0) |
2068 |
return (0); |
2069 |
if (sb.st_flags & UF_NODUMP) |
2070 |
return (1); |
2071 |
return (0); |
2072 |
} |
2073 |
|
2074 |
#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\ |
2075 |
&& defined(EXT2_NODUMP_FL) |
2076 |
|
2077 |
int |
2078 |
canNodump(void) |
2079 |
{ |
2080 |
const char *path = "cannodumptest"; |
2081 |
int fd, r, flags; |
2082 |
|
2083 |
assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); |
2084 |
fd = open(path, O_RDONLY | O_NONBLOCK); |
2085 |
if (fd < 0) |
2086 |
return (0); |
2087 |
r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); |
2088 |
if (r < 0) |
2089 |
return (0); |
2090 |
flags |= EXT2_NODUMP_FL; |
2091 |
r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags); |
2092 |
if (r < 0) |
2093 |
return (0); |
2094 |
close(fd); |
2095 |
fd = open(path, O_RDONLY | O_NONBLOCK); |
2096 |
if (fd < 0) |
2097 |
return (0); |
2098 |
r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags); |
2099 |
if (r < 0) |
2100 |
return (0); |
2101 |
close(fd); |
2102 |
if (flags & EXT2_NODUMP_FL) |
2103 |
return (1); |
2104 |
return (0); |
2105 |
} |
2106 |
|
2107 |
#else |
2108 |
|
2109 |
int |
2110 |
canNodump() |
2111 |
{ |
2112 |
return (0); |
2113 |
} |
2114 |
|
2115 |
#endif |
2116 |
|
2117 |
/* |
2118 |
* Sleep as needed; useful for verifying disk timestamp changes by |
2119 |
* ensuring that the wall-clock time has actually changed before we |
2120 |
* go back to re-read something from disk. |
2121 |
*/ |
2122 |
void |
2123 |
sleepUntilAfter(time_t t) |
2124 |
{ |
2125 |
while (t >= time(NULL)) |
2126 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
2127 |
Sleep(500); |
2128 |
#else |
2129 |
sleep(1); |
2130 |
#endif |
2131 |
} |
2132 |
|
2133 |
/* |
2134 |
* Call standard system() call, but build up the command line using |
2135 |
* sprintf() conventions. |
2136 |
*/ |
2137 |
int |
2138 |
systemf(const char *fmt, ...) |
2139 |
{ |
2140 |
char buff[8192]; |
2141 |
va_list ap; |
2142 |
int r; |
2143 |
|
2144 |
va_start(ap, fmt); |
2145 |
vsprintf(buff, fmt, ap); |
2146 |
if (verbosity > VERBOSITY_FULL) |
2147 |
logprintf("Cmd: %s\n", buff); |
2148 |
r = system(buff); |
2149 |
va_end(ap); |
2150 |
return (r); |
2151 |
} |
2152 |
|
2153 |
/* |
2154 |
* Slurp a file into memory for ease of comparison and testing. |
2155 |
* Returns size of file in 'sizep' if non-NULL, null-terminates |
2156 |
* data in memory for ease of use. |
2157 |
*/ |
2158 |
char * |
2159 |
slurpfile(size_t * sizep, const char *fmt, ...) |
2160 |
{ |
2161 |
char filename[8192]; |
2162 |
struct stat st; |
2163 |
va_list ap; |
2164 |
char *p; |
2165 |
ssize_t bytes_read; |
2166 |
FILE *f; |
2167 |
int r; |
2168 |
|
2169 |
va_start(ap, fmt); |
2170 |
vsprintf(filename, fmt, ap); |
2171 |
va_end(ap); |
2172 |
|
2173 |
f = fopen(filename, "rb"); |
2174 |
if (f == NULL) { |
2175 |
/* Note: No error; non-existent file is okay here. */ |
2176 |
return (NULL); |
2177 |
} |
2178 |
r = fstat(fileno(f), &st); |
2179 |
if (r != 0) { |
2180 |
logprintf("Can't stat file %s\n", filename); |
2181 |
fclose(f); |
2182 |
return (NULL); |
2183 |
} |
2184 |
p = malloc((size_t)st.st_size + 1); |
2185 |
if (p == NULL) { |
2186 |
logprintf("Can't allocate %ld bytes of memory to read file %s\n", |
2187 |
(long int)st.st_size, filename); |
2188 |
fclose(f); |
2189 |
return (NULL); |
2190 |
} |
2191 |
bytes_read = fread(p, 1, (size_t)st.st_size, f); |
2192 |
if (bytes_read < st.st_size) { |
2193 |
logprintf("Can't read file %s\n", filename); |
2194 |
fclose(f); |
2195 |
free(p); |
2196 |
return (NULL); |
2197 |
} |
2198 |
p[st.st_size] = '\0'; |
2199 |
if (sizep != NULL) |
2200 |
*sizep = (size_t)st.st_size; |
2201 |
fclose(f); |
2202 |
return (p); |
2203 |
} |
2204 |
|
2205 |
/* |
2206 |
* Slurp a file into memory for ease of comparison and testing. |
2207 |
* Returns size of file in 'sizep' if non-NULL, null-terminates |
2208 |
* data in memory for ease of use. |
2209 |
*/ |
2210 |
void |
2211 |
dumpfile(const char *filename, void *data, size_t len) |
2212 |
{ |
2213 |
ssize_t bytes_written; |
2214 |
FILE *f; |
2215 |
|
2216 |
f = fopen(filename, "wb"); |
2217 |
if (f == NULL) { |
2218 |
logprintf("Can't open file %s for writing\n", filename); |
2219 |
return; |
2220 |
} |
2221 |
bytes_written = fwrite(data, 1, len, f); |
2222 |
if (bytes_written < (ssize_t)len) |
2223 |
logprintf("Can't write file %s\n", filename); |
2224 |
fclose(f); |
2225 |
} |
2226 |
|
2227 |
/* Read a uuencoded file from the reference directory, decode, and |
2228 |
* write the result into the current directory. */ |
2229 |
#define VALID_UUDECODE(c) (c >= 32 && c <= 96) |
2230 |
#define UUDECODE(c) (((c) - 0x20) & 0x3f) |
2231 |
void |
2232 |
extract_reference_file(const char *name) |
2233 |
{ |
2234 |
char buff[1024]; |
2235 |
FILE *in, *out; |
2236 |
|
2237 |
sprintf(buff, "%s/%s.uu", refdir, name); |
2238 |
in = fopen(buff, "r"); |
2239 |
failure("Couldn't open reference file %s", buff); |
2240 |
assert(in != NULL); |
2241 |
if (in == NULL) |
2242 |
return; |
2243 |
/* Read up to and including the 'begin' line. */ |
2244 |
for (;;) { |
2245 |
if (fgets(buff, sizeof(buff), in) == NULL) { |
2246 |
/* TODO: This is a failure. */ |
2247 |
return; |
2248 |
} |
2249 |
if (memcmp(buff, "begin ", 6) == 0) |
2250 |
break; |
2251 |
} |
2252 |
/* Now, decode the rest and write it. */ |
2253 |
out = fopen(name, "wb"); |
2254 |
while (fgets(buff, sizeof(buff), in) != NULL) { |
2255 |
char *p = buff; |
2256 |
int bytes; |
2257 |
|
2258 |
if (memcmp(buff, "end", 3) == 0) |
2259 |
break; |
2260 |
|
2261 |
bytes = UUDECODE(*p++); |
2262 |
while (bytes > 0) { |
2263 |
int n = 0; |
2264 |
/* Write out 1-3 bytes from that. */ |
2265 |
if (bytes > 0) { |
2266 |
assert(VALID_UUDECODE(p[0])); |
2267 |
assert(VALID_UUDECODE(p[1])); |
2268 |
n = UUDECODE(*p++) << 18; |
2269 |
n |= UUDECODE(*p++) << 12; |
2270 |
fputc(n >> 16, out); |
2271 |
--bytes; |
2272 |
} |
2273 |
if (bytes > 0) { |
2274 |
assert(VALID_UUDECODE(p[0])); |
2275 |
n |= UUDECODE(*p++) << 6; |
2276 |
fputc((n >> 8) & 0xFF, out); |
2277 |
--bytes; |
2278 |
} |
2279 |
if (bytes > 0) { |
2280 |
assert(VALID_UUDECODE(p[0])); |
2281 |
n |= UUDECODE(*p++); |
2282 |
fputc(n & 0xFF, out); |
2283 |
--bytes; |
2284 |
} |
2285 |
} |
2286 |
} |
2287 |
fclose(out); |
2288 |
fclose(in); |
2289 |
} |
2290 |
|
2291 |
void |
2292 |
copy_reference_file(const char *name) |
2293 |
{ |
2294 |
char buff[1024]; |
2295 |
FILE *in, *out; |
2296 |
size_t rbytes; |
2297 |
|
2298 |
sprintf(buff, "%s/%s", refdir, name); |
2299 |
in = fopen(buff, "rb"); |
2300 |
failure("Couldn't open reference file %s", buff); |
2301 |
assert(in != NULL); |
2302 |
if (in == NULL) |
2303 |
return; |
2304 |
/* Now, decode the rest and write it. */ |
2305 |
/* Not a lot of error checking here; the input better be right. */ |
2306 |
out = fopen(name, "wb"); |
2307 |
while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { |
2308 |
if (fwrite(buff, 1, rbytes, out) != rbytes) { |
2309 |
logprintf("Error: fwrite\n"); |
2310 |
break; |
2311 |
} |
2312 |
} |
2313 |
fclose(out); |
2314 |
fclose(in); |
2315 |
} |
2316 |
|
2317 |
int |
2318 |
is_LargeInode(const char *file) |
2319 |
{ |
2320 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
2321 |
BY_HANDLE_FILE_INFORMATION bhfi; |
2322 |
int r; |
2323 |
|
2324 |
r = my_GetFileInformationByName(file, &bhfi); |
2325 |
if (r != 0) |
2326 |
return (0); |
2327 |
return (bhfi.nFileIndexHigh & 0x0000FFFFUL); |
2328 |
#else |
2329 |
struct stat st; |
2330 |
int64_t ino; |
2331 |
|
2332 |
if (stat(file, &st) < 0) |
2333 |
return (0); |
2334 |
ino = (int64_t)st.st_ino; |
2335 |
return (ino > 0xffffffff); |
2336 |
#endif |
2337 |
} |
2338 |
|
2339 |
void |
2340 |
extract_reference_files(const char **names) |
2341 |
{ |
2342 |
while (names && *names) |
2343 |
extract_reference_file(*names++); |
2344 |
} |
2345 |
|
2346 |
/* |
2347 |
* |
2348 |
* TEST management |
2349 |
* |
2350 |
*/ |
2351 |
|
2352 |
/* |
2353 |
* "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has |
2354 |
* a line like |
2355 |
* DEFINE_TEST(test_function) |
2356 |
* for each test. |
2357 |
*/ |
2358 |
|
2359 |
/* Use "list.h" to declare all of the test functions. */ |
2360 |
#undef DEFINE_TEST |
2361 |
#define DEFINE_TEST(name) void name(void); |
2362 |
#include "list.h" |
2363 |
|
2364 |
/* Use "list.h" to create a list of all tests (functions and names). */ |
2365 |
#undef DEFINE_TEST |
2366 |
#define DEFINE_TEST(n) { n, #n, 0 }, |
2367 |
struct test_list_t tests[] = { |
2368 |
#include "list.h" |
2369 |
}; |
2370 |
|
2371 |
/* |
2372 |
* Summarize repeated failures in the just-completed test. |
2373 |
*/ |
2374 |
static void |
2375 |
test_summarize(int failed, int skips_num) |
2376 |
{ |
2377 |
unsigned int i; |
2378 |
|
2379 |
switch (verbosity) { |
2380 |
case VERBOSITY_SUMMARY_ONLY: |
2381 |
printf(failed ? "E" : "."); |
2382 |
fflush(stdout); |
2383 |
break; |
2384 |
case VERBOSITY_PASSFAIL: |
2385 |
printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); |
2386 |
break; |
2387 |
} |
2388 |
|
2389 |
log_console = (verbosity == VERBOSITY_LIGHT_REPORT); |
2390 |
|
2391 |
for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { |
2392 |
if (failed_lines[i].count > 1 && !failed_lines[i].skip) |
2393 |
logprintf("%s:%d: Summary: Failed %d times\n", |
2394 |
failed_filename, i, failed_lines[i].count); |
2395 |
} |
2396 |
/* Clear the failure history for the next file. */ |
2397 |
failed_filename = NULL; |
2398 |
memset(failed_lines, 0, sizeof(failed_lines)); |
2399 |
} |
2400 |
|
2401 |
/* |
2402 |
* Actually run a single test, with appropriate setup and cleanup. |
2403 |
*/ |
2404 |
static int |
2405 |
test_run(int i, const char *tmpdir) |
2406 |
{ |
2407 |
char workdir[1024]; |
2408 |
char logfilename[64]; |
2409 |
int failures_before = failures; |
2410 |
int skips_before = skips; |
2411 |
int oldumask; |
2412 |
|
2413 |
switch (verbosity) { |
2414 |
case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ |
2415 |
break; |
2416 |
case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ |
2417 |
printf("%3d: %-64s", i, tests[i].name); |
2418 |
fflush(stdout); |
2419 |
break; |
2420 |
default: /* Title of test, details will follow */ |
2421 |
printf("%3d: %s\n", i, tests[i].name); |
2422 |
} |
2423 |
|
2424 |
/* Chdir to the top-level work directory. */ |
2425 |
if (!assertChdir(tmpdir)) { |
2426 |
fprintf(stderr, |
2427 |
"ERROR: Can't chdir to top work dir %s\n", tmpdir); |
2428 |
exit(1); |
2429 |
} |
2430 |
/* Create a log file for this test. */ |
2431 |
sprintf(logfilename, "%s.log", tests[i].name); |
2432 |
logfile = fopen(logfilename, "w"); |
2433 |
fprintf(logfile, "%s\n\n", tests[i].name); |
2434 |
/* Chdir() to a work dir for this specific test. */ |
2435 |
snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); |
2436 |
testworkdir = workdir; |
2437 |
if (!assertMakeDir(testworkdir, 0755) |
2438 |
|| !assertChdir(testworkdir)) { |
2439 |
fprintf(stderr, |
2440 |
"ERROR: Can't chdir to work dir %s\n", testworkdir); |
2441 |
exit(1); |
2442 |
} |
2443 |
/* Explicitly reset the locale before each test. */ |
2444 |
setlocale(LC_ALL, "C"); |
2445 |
/* Record the umask before we run the test. */ |
2446 |
umask(oldumask = umask(0)); |
2447 |
/* |
2448 |
* Run the actual test. |
2449 |
*/ |
2450 |
(*tests[i].func)(); |
2451 |
/* |
2452 |
* Clean up and report afterwards. |
2453 |
*/ |
2454 |
testworkdir = NULL; |
2455 |
/* Restore umask */ |
2456 |
umask(oldumask); |
2457 |
/* Reset locale. */ |
2458 |
setlocale(LC_ALL, "C"); |
2459 |
/* Reset directory. */ |
2460 |
if (!assertChdir(tmpdir)) { |
2461 |
fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", |
2462 |
tmpdir); |
2463 |
exit(1); |
2464 |
} |
2465 |
/* Report per-test summaries. */ |
2466 |
tests[i].failures = failures - failures_before; |
2467 |
test_summarize(tests[i].failures, skips - skips_before); |
2468 |
/* Close the per-test log file. */ |
2469 |
fclose(logfile); |
2470 |
logfile = NULL; |
2471 |
/* If there were no failures, we can remove the work dir and logfile. */ |
2472 |
if (tests[i].failures == 0) { |
2473 |
if (!keep_temp_files && assertChdir(tmpdir)) { |
2474 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
2475 |
/* Make sure not to leave empty directories. |
2476 |
* Sometimes a processing of closing files used by tests |
2477 |
* is not done, then rmdir will be failed and it will |
2478 |
* leave a empty test directory. So we should wait a few |
2479 |
* seconds and retry rmdir. */ |
2480 |
int r, t; |
2481 |
for (t = 0; t < 10; t++) { |
2482 |
if (t > 0) |
2483 |
Sleep(1000); |
2484 |
r = systemf("rmdir /S /Q %s", tests[i].name); |
2485 |
if (r == 0) |
2486 |
break; |
2487 |
} |
2488 |
systemf("del %s", logfilename); |
2489 |
#else |
2490 |
systemf("rm -rf %s", tests[i].name); |
2491 |
systemf("rm %s", logfilename); |
2492 |
#endif |
2493 |
} |
2494 |
} |
2495 |
/* Return appropriate status. */ |
2496 |
return (tests[i].failures); |
2497 |
} |
2498 |
|
2499 |
/* |
2500 |
* |
2501 |
* |
2502 |
* MAIN and support routines. |
2503 |
* |
2504 |
* |
2505 |
*/ |
2506 |
|
2507 |
static void |
2508 |
usage(const char *program) |
2509 |
{ |
2510 |
static const int limit = sizeof(tests) / sizeof(tests[0]); |
2511 |
int i; |
2512 |
|
2513 |
printf("Usage: %s [options] <test> <test> ...\n", program); |
2514 |
printf("Default is to run all tests.\n"); |
2515 |
printf("Otherwise, specify the numbers of the tests you wish to run.\n"); |
2516 |
printf("Options:\n"); |
2517 |
printf(" -d Dump core after any failure, for debugging.\n"); |
2518 |
printf(" -k Keep all temp files.\n"); |
2519 |
printf(" Default: temp files for successful tests deleted.\n"); |
2520 |
#ifdef PROGRAM |
2521 |
printf(" -p <path> Path to executable to be tested.\n"); |
2522 |
printf(" Default: path taken from " ENVBASE " environment variable.\n"); |
2523 |
#endif |
2524 |
printf(" -q Quiet.\n"); |
2525 |
printf(" -r <dir> Path to dir containing reference files.\n"); |
2526 |
printf(" Default: Current directory.\n"); |
2527 |
printf(" -u Keep running specifies tests until one fails.\n"); |
2528 |
printf(" -v Verbose.\n"); |
2529 |
printf("Available tests:\n"); |
2530 |
for (i = 0; i < limit; i++) |
2531 |
printf(" %d: %s\n", i, tests[i].name); |
2532 |
exit(1); |
2533 |
} |
2534 |
|
2535 |
static char * |
2536 |
get_refdir(const char *d) |
2537 |
{ |
2538 |
size_t tried_size, buff_size; |
2539 |
char *buff, *tried, *pwd = NULL, *p = NULL; |
2540 |
|
2541 |
#ifdef PATH_MAX |
2542 |
buff_size = PATH_MAX; |
2543 |
#else |
2544 |
buff_size = 8192; |
2545 |
#endif |
2546 |
buff = calloc(buff_size, 1); |
2547 |
if (buff == NULL) { |
2548 |
fprintf(stderr, "Unable to allocate memory\n"); |
2549 |
exit(1); |
2550 |
} |
2551 |
|
2552 |
/* Allocate a buffer to hold the various directories we checked. */ |
2553 |
tried_size = buff_size * 2; |
2554 |
tried = calloc(tried_size, 1); |
2555 |
if (tried == NULL) { |
2556 |
fprintf(stderr, "Unable to allocate memory\n"); |
2557 |
exit(1); |
2558 |
} |
2559 |
|
2560 |
/* If a dir was specified, try that */ |
2561 |
if (d != NULL) { |
2562 |
pwd = NULL; |
2563 |
snprintf(buff, buff_size, "%s", d); |
2564 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2565 |
if (p != NULL) goto success; |
2566 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2567 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2568 |
goto failure; |
2569 |
} |
2570 |
|
2571 |
/* Get the current dir. */ |
2572 |
#ifdef PATH_MAX |
2573 |
pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ |
2574 |
#else |
2575 |
pwd = getcwd(NULL, 0); |
2576 |
#endif |
2577 |
while (pwd[strlen(pwd) - 1] == '\n') |
2578 |
pwd[strlen(pwd) - 1] = '\0'; |
2579 |
|
2580 |
/* Look for a known file. */ |
2581 |
snprintf(buff, buff_size, "%s", pwd); |
2582 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2583 |
if (p != NULL) goto success; |
2584 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2585 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2586 |
|
2587 |
snprintf(buff, buff_size, "%s/test", pwd); |
2588 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2589 |
if (p != NULL) goto success; |
2590 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2591 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2592 |
|
2593 |
#if defined(LIBRARY) |
2594 |
snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); |
2595 |
#else |
2596 |
snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); |
2597 |
#endif |
2598 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2599 |
if (p != NULL) goto success; |
2600 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2601 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2602 |
|
2603 |
#if defined(PROGRAM_ALIAS) |
2604 |
snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); |
2605 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2606 |
if (p != NULL) goto success; |
2607 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2608 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2609 |
#endif |
2610 |
|
2611 |
if (memcmp(pwd, "/usr/obj", 8) == 0) { |
2612 |
snprintf(buff, buff_size, "%s", pwd + 8); |
2613 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2614 |
if (p != NULL) goto success; |
2615 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2616 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2617 |
|
2618 |
snprintf(buff, buff_size, "%s/test", pwd + 8); |
2619 |
p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); |
2620 |
if (p != NULL) goto success; |
2621 |
strncat(tried, buff, tried_size - strlen(tried) - 1); |
2622 |
strncat(tried, "\n", tried_size - strlen(tried) - 1); |
2623 |
} |
2624 |
|
2625 |
failure: |
2626 |
printf("Unable to locate known reference file %s\n", KNOWNREF); |
2627 |
printf(" Checked following directories:\n%s\n", tried); |
2628 |
printf("Use -r option to specify full path to reference directory\n"); |
2629 |
#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) |
2630 |
DebugBreak(); |
2631 |
#endif |
2632 |
exit(1); |
2633 |
|
2634 |
success: |
2635 |
free(p); |
2636 |
free(pwd); |
2637 |
free(tried); |
2638 |
|
2639 |
/* Copy result into a fresh buffer to reduce memory usage. */ |
2640 |
p = strdup(buff); |
2641 |
free(buff); |
2642 |
return p; |
2643 |
} |
2644 |
|
2645 |
int |
2646 |
main(int argc, char **argv) |
2647 |
{ |
2648 |
static const int limit = sizeof(tests) / sizeof(tests[0]); |
2649 |
int test_set[sizeof(tests) / sizeof(tests[0])]; |
2650 |
int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; |
2651 |
time_t now; |
2652 |
char *refdir_alloc = NULL; |
2653 |
const char *progname; |
2654 |
char **saved_argv; |
2655 |
const char *tmp, *option_arg, *p; |
2656 |
char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; |
2657 |
char tmpdir_timestamp[256]; |
2658 |
|
2659 |
(void)argc; /* UNUSED */ |
2660 |
|
2661 |
/* Get the current dir. */ |
2662 |
#ifdef PATH_MAX |
2663 |
pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ |
2664 |
#else |
2665 |
pwd = getcwd(NULL, 0); |
2666 |
#endif |
2667 |
while (pwd[strlen(pwd) - 1] == '\n') |
2668 |
pwd[strlen(pwd) - 1] = '\0'; |
2669 |
|
2670 |
#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) |
2671 |
/* To stop to run the default invalid parameter handler. */ |
2672 |
_set_invalid_parameter_handler(invalid_parameter_handler); |
2673 |
/* Disable annoying assertion message box. */ |
2674 |
_CrtSetReportMode(_CRT_ASSERT, 0); |
2675 |
#endif |
2676 |
|
2677 |
/* |
2678 |
* Name of this program, used to build root of our temp directory |
2679 |
* tree. |
2680 |
*/ |
2681 |
progname = p = argv[0]; |
2682 |
if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) |
2683 |
{ |
2684 |
fprintf(stderr, "ERROR: Out of memory."); |
2685 |
exit(1); |
2686 |
} |
2687 |
strcpy(testprogdir, progname); |
2688 |
while (*p != '\0') { |
2689 |
/* Support \ or / dir separators for Windows compat. */ |
2690 |
if (*p == '/' || *p == '\\') |
2691 |
{ |
2692 |
progname = p + 1; |
2693 |
i = j; |
2694 |
} |
2695 |
++p; |
2696 |
j++; |
2697 |
} |
2698 |
testprogdir[i] = '\0'; |
2699 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
2700 |
if (testprogdir[0] != '/' && testprogdir[0] != '\\' && |
2701 |
!(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || |
2702 |
(testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && |
2703 |
testprogdir[1] == ':' && |
2704 |
(testprogdir[2] == '/' || testprogdir[2] == '\\'))) |
2705 |
#else |
2706 |
if (testprogdir[0] != '/') |
2707 |
#endif |
2708 |
{ |
2709 |
/* Fixup path for relative directories. */ |
2710 |
if ((testprogdir = (char *)realloc(testprogdir, |
2711 |
strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) |
2712 |
{ |
2713 |
fprintf(stderr, "ERROR: Out of memory."); |
2714 |
exit(1); |
2715 |
} |
2716 |
memmove(testprogdir + strlen(pwd) + 1, testprogdir, |
2717 |
strlen(testprogdir) + 1); |
2718 |
memcpy(testprogdir, pwd, strlen(pwd)); |
2719 |
testprogdir[strlen(pwd)] = '/'; |
2720 |
} |
2721 |
|
2722 |
#ifdef PROGRAM |
2723 |
/* Get the target program from environment, if available. */ |
2724 |
testprogfile = getenv(ENVBASE); |
2725 |
#endif |
2726 |
|
2727 |
if (getenv("TMPDIR") != NULL) |
2728 |
tmp = getenv("TMPDIR"); |
2729 |
else if (getenv("TMP") != NULL) |
2730 |
tmp = getenv("TMP"); |
2731 |
else if (getenv("TEMP") != NULL) |
2732 |
tmp = getenv("TEMP"); |
2733 |
else if (getenv("TEMPDIR") != NULL) |
2734 |
tmp = getenv("TEMPDIR"); |
2735 |
else |
2736 |
tmp = "/tmp"; |
2737 |
|
2738 |
/* Allow -d to be controlled through the environment. */ |
2739 |
if (getenv(ENVBASE "_DEBUG") != NULL) |
2740 |
dump_on_failure = 1; |
2741 |
|
2742 |
/* Allow -v to be controlled through the environment. */ |
2743 |
if (getenv("_VERBOSITY_LEVEL") != NULL) |
2744 |
{ |
2745 |
vlevel = getenv("_VERBOSITY_LEVEL"); |
2746 |
verbosity = atoi(vlevel); |
2747 |
if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) |
2748 |
{ |
2749 |
/* Unsupported verbosity levels are silently ignored */ |
2750 |
vlevel = NULL; |
2751 |
verbosity = VERBOSITY_PASSFAIL; |
2752 |
} |
2753 |
} |
2754 |
|
2755 |
/* Get the directory holding test files from environment. */ |
2756 |
refdir = getenv(ENVBASE "_TEST_FILES"); |
2757 |
|
2758 |
/* |
2759 |
* Parse options, without using getopt(), which isn't available |
2760 |
* on all platforms. |
2761 |
*/ |
2762 |
++argv; /* Skip program name */ |
2763 |
while (*argv != NULL) { |
2764 |
if (**argv != '-') |
2765 |
break; |
2766 |
p = *argv++; |
2767 |
++p; /* Skip '-' */ |
2768 |
while (*p != '\0') { |
2769 |
option = *p++; |
2770 |
option_arg = NULL; |
2771 |
/* If 'opt' takes an argument, parse that. */ |
2772 |
if (option == 'p' || option == 'r') { |
2773 |
if (*p != '\0') |
2774 |
option_arg = p; |
2775 |
else if (*argv == NULL) { |
2776 |
fprintf(stderr, |
2777 |
"Option -%c requires argument.\n", |
2778 |
option); |
2779 |
usage(progname); |
2780 |
} else |
2781 |
option_arg = *argv++; |
2782 |
p = ""; /* End of this option word. */ |
2783 |
} |
2784 |
|
2785 |
/* Now, handle the option. */ |
2786 |
switch (option) { |
2787 |
case 'd': |
2788 |
dump_on_failure = 1; |
2789 |
break; |
2790 |
case 'k': |
2791 |
keep_temp_files = 1; |
2792 |
break; |
2793 |
case 'p': |
2794 |
#ifdef PROGRAM |
2795 |
testprogfile = option_arg; |
2796 |
#else |
2797 |
fprintf(stderr, "-p option not permitted\n"); |
2798 |
usage(progname); |
2799 |
#endif |
2800 |
break; |
2801 |
case 'q': |
2802 |
if (!vlevel) |
2803 |
verbosity--; |
2804 |
break; |
2805 |
case 'r': |
2806 |
refdir = option_arg; |
2807 |
break; |
2808 |
case 'u': |
2809 |
until_failure++; |
2810 |
break; |
2811 |
case 'v': |
2812 |
if (!vlevel) |
2813 |
verbosity++; |
2814 |
break; |
2815 |
default: |
2816 |
fprintf(stderr, "Unrecognized option '%c'\n", |
2817 |
option); |
2818 |
usage(progname); |
2819 |
} |
2820 |
} |
2821 |
} |
2822 |
|
2823 |
/* |
2824 |
* Sanity-check that our options make sense. |
2825 |
*/ |
2826 |
#ifdef PROGRAM |
2827 |
if (testprogfile == NULL) |
2828 |
{ |
2829 |
if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + |
2830 |
strlen(PROGRAM) + 1)) == NULL) |
2831 |
{ |
2832 |
fprintf(stderr, "ERROR: Out of memory."); |
2833 |
exit(1); |
2834 |
} |
2835 |
strcpy(tmp2, testprogdir); |
2836 |
strcat(tmp2, "/"); |
2837 |
strcat(tmp2, PROGRAM); |
2838 |
testprogfile = tmp2; |
2839 |
} |
2840 |
|
2841 |
{ |
2842 |
char *testprg; |
2843 |
#if defined(_WIN32) && !defined(__CYGWIN__) |
2844 |
/* Command.com sometimes rejects '/' separators. */ |
2845 |
testprg = strdup(testprogfile); |
2846 |
for (i = 0; testprg[i] != '\0'; i++) { |
2847 |
if (testprg[i] == '/') |
2848 |
testprg[i] = '\\'; |
2849 |
} |
2850 |
testprogfile = testprg; |
2851 |
#endif |
2852 |
/* Quote the name that gets put into shell command lines. */ |
2853 |
testprg = malloc(strlen(testprogfile) + 3); |
2854 |
strcpy(testprg, "\""); |
2855 |
strcat(testprg, testprogfile); |
2856 |
strcat(testprg, "\""); |
2857 |
testprog = testprg; |
2858 |
} |
2859 |
#endif |
2860 |
|
2861 |
#if !defined(_WIN32) && defined(SIGPIPE) |
2862 |
{ /* Ignore SIGPIPE signals */ |
2863 |
struct sigaction sa; |
2864 |
sa.sa_handler = SIG_IGN; |
2865 |
sigemptyset(&sa.sa_mask); |
2866 |
sa.sa_flags = 0; |
2867 |
sigaction(SIGPIPE, &sa, NULL); |
2868 |
} |
2869 |
#endif |
2870 |
|
2871 |
/* |
2872 |
* Create a temp directory for the following tests. |
2873 |
* Include the time the tests started as part of the name, |
2874 |
* to make it easier to track the results of multiple tests. |
2875 |
*/ |
2876 |
now = time(NULL); |
2877 |
for (i = 0; ; i++) { |
2878 |
strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), |
2879 |
"%Y-%m-%dT%H.%M.%S", |
2880 |
localtime(&now)); |
2881 |
sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, |
2882 |
tmpdir_timestamp, i); |
2883 |
if (assertMakeDir(tmpdir,0755)) |
2884 |
break; |
2885 |
if (i >= 999) { |
2886 |
fprintf(stderr, |
2887 |
"ERROR: Unable to create temp directory %s\n", |
2888 |
tmpdir); |
2889 |
exit(1); |
2890 |
} |
2891 |
} |
2892 |
|
2893 |
/* |
2894 |
* If the user didn't specify a directory for locating |
2895 |
* reference files, try to find the reference files in |
2896 |
* the "usual places." |
2897 |
*/ |
2898 |
refdir = refdir_alloc = get_refdir(refdir); |
2899 |
|
2900 |
/* |
2901 |
* Banner with basic information. |
2902 |
*/ |
2903 |
printf("\n"); |
2904 |
printf("If tests fail or crash, details will be in:\n"); |
2905 |
printf(" %s\n", tmpdir); |
2906 |
printf("\n"); |
2907 |
if (verbosity > VERBOSITY_SUMMARY_ONLY) { |
2908 |
printf("Reference files will be read from: %s\n", refdir); |
2909 |
#ifdef PROGRAM |
2910 |
printf("Running tests on: %s\n", testprog); |
2911 |
#endif |
2912 |
printf("Exercising: "); |
2913 |
fflush(stdout); |
2914 |
printf("%s\n", EXTRA_VERSION); |
2915 |
} else { |
2916 |
printf("Running "); |
2917 |
fflush(stdout); |
2918 |
} |
2919 |
|
2920 |
/* |
2921 |
* Run some or all of the individual tests. |
2922 |
*/ |
2923 |
saved_argv = argv; |
2924 |
do { |
2925 |
argv = saved_argv; |
2926 |
do { |
2927 |
int test_num; |
2928 |
|
2929 |
test_num = get_test_set(test_set, limit, *argv, tests); |
2930 |
if (test_num < 0) { |
2931 |
printf("*** INVALID Test %s\n", *argv); |
2932 |
free(refdir_alloc); |
2933 |
free(testprogdir); |
2934 |
usage(progname); |
2935 |
return (1); |
2936 |
} |
2937 |
for (i = 0; i < test_num; i++) { |
2938 |
tests_run++; |
2939 |
if (test_run(test_set[i], tmpdir)) { |
2940 |
tests_failed++; |
2941 |
if (until_failure) |
2942 |
goto finish; |
2943 |
} |
2944 |
} |
2945 |
if (*argv != NULL) |
2946 |
argv++; |
2947 |
} while (*argv != NULL); |
2948 |
} while (until_failure); |
2949 |
|
2950 |
finish: |
2951 |
/* Must be freed after all tests run */ |
2952 |
free(tmp2); |
2953 |
free(testprogdir); |
2954 |
free(pwd); |
2955 |
|
2956 |
/* |
2957 |
* Report summary statistics. |
2958 |
*/ |
2959 |
if (verbosity > VERBOSITY_SUMMARY_ONLY) { |
2960 |
printf("\n"); |
2961 |
printf("Totals:\n"); |
2962 |
printf(" Tests run: %8d\n", tests_run); |
2963 |
printf(" Tests failed: %8d\n", tests_failed); |
2964 |
printf(" Assertions checked:%8d\n", assertions); |
2965 |
printf(" Assertions failed: %8d\n", failures); |
2966 |
printf(" Skips reported: %8d\n", skips); |
2967 |
} |
2968 |
if (failures) { |
2969 |
printf("\n"); |
2970 |
printf("Failing tests:\n"); |
2971 |
for (i = 0; i < limit; ++i) { |
2972 |
if (tests[i].failures) |
2973 |
printf(" %d: %s (%d failures)\n", i, |
2974 |
tests[i].name, tests[i].failures); |
2975 |
} |
2976 |
printf("\n"); |
2977 |
printf("Details for failing tests: %s\n", tmpdir); |
2978 |
printf("\n"); |
2979 |
} else { |
2980 |
if (verbosity == VERBOSITY_SUMMARY_ONLY) |
2981 |
printf("\n"); |
2982 |
printf("%d tests passed, no failures\n", tests_run); |
2983 |
} |
2984 |
|
2985 |
free(refdir_alloc); |
2986 |
|
2987 |
/* If the final tmpdir is empty, we can remove it. */ |
2988 |
/* This should be the usual case when all tests succeed. */ |
2989 |
assertChdir(".."); |
2990 |
rmdir(tmpdir); |
2991 |
|
2992 |
return (tests_failed ? 1 : 0); |
2993 |
} |