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