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/* Verify that two wide strings are equal, dump them if not. */ 493int 494assertion_equal_wstring(const char *file, int line, 495 const wchar_t *v1, const char *e1, 496 const wchar_t *v2, const char *e2, 497 void *extra) 498{ 499 assertion_count(file, line); 500 if (v1 == v2 || wcscmp(v1, v2) == 0) 501 return (1); 502 failure_start(file, line, "%s != %s", e1, e2); 503 wcsdump(e1, v1); 504 wcsdump(e2, v2); 505 failure_finish(extra); 506 return (0); 507} 508 509/* 510 * Pretty standard hexdump routine. As a bonus, if ref != NULL, then 511 * any bytes in p that differ from ref will be highlighted with '_' 512 * before and after the hex value. 513 */ 514static void 515hexdump(const char *p, const char *ref, size_t l, size_t offset) 516{ 517 size_t i, j; 518 char sep; 519 520 if (p == NULL) { 521 logprintf("(null)\n"); 522 return; 523 } 524 for(i=0; i < l; i+=16) { 525 logprintf("%04x", (unsigned)(i + offset)); 526 sep = ' '; 527 for (j = 0; j < 16 && i + j < l; j++) { 528 if (ref != NULL && p[i + j] != ref[i + j]) 529 sep = '_'; 530 logprintf("%c%02x", sep, 0xff & (int)p[i+j]); 531 if (ref != NULL && p[i + j] == ref[i + j]) 532 sep = ' '; 533 } 534 for (; j < 16; j++) { 535 logprintf("%c ", sep); 536 sep = ' '; 537 } 538 logprintf("%c", sep); 539 for (j=0; j < 16 && i + j < l; j++) { 540 int c = p[i + j]; 541 if (c >= ' ' && c <= 126) 542 logprintf("%c", c); 543 else 544 logprintf("."); 545 } 546 logprintf("\n"); 547 } 548} 549 550/* Verify that two blocks of memory are the same, display the first 551 * block of differences if they're not. */ 552int 553assertion_equal_mem(const char *file, int line, 554 const void *_v1, const char *e1, 555 const void *_v2, const char *e2, 556 size_t l, const char *ld, void *extra) 557{ 558 const char *v1 = (const char *)_v1; 559 const char *v2 = (const char *)_v2; 560 size_t offset; 561 562 assertion_count(file, line); 563 if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) 564 return (1); 565 566 failure_start(file, line, "%s != %s", e1, e2); 567 logprintf(" size %s = %d\n", ld, (int)l); 568 /* Dump 48 bytes (3 lines) so that the first difference is 569 * in the second line. */ 570 offset = 0; 571 while (l > 64 && memcmp(v1, v2, 32) == 0) { 572 /* Two lines agree, so step forward one line. */ 573 v1 += 16; 574 v2 += 16; 575 l -= 16; 576 offset += 16; 577 } 578 logprintf(" Dump of %s\n", e1); 579 hexdump(v1, v2, l < 64 ? l : 64, offset); 580 logprintf(" Dump of %s\n", e2); 581 hexdump(v2, v1, l < 64 ? l : 64, offset); 582 logprintf("\n"); 583 failure_finish(extra); 584 return (0); 585} 586 587/* Verify that the named file exists and is empty. */ 588int 589assertion_empty_file(const char *f1fmt, ...) 590{ 591 char buff[1024]; 592 char f1[1024]; 593 struct stat st; 594 va_list ap; 595 ssize_t s; 596 FILE *f; 597 598 assertion_count(test_filename, test_line); 599 va_start(ap, f1fmt); 600 vsprintf(f1, f1fmt, ap); 601 va_end(ap); 602 603 if (stat(f1, &st) != 0) { 604 failure_start(test_filename, test_line, "Stat failed: %s", f1); 605 failure_finish(NULL); 606 return (0); 607 } 608 if (st.st_size == 0) 609 return (1); 610 611 failure_start(test_filename, test_line, "File should be empty: %s", f1); 612 logprintf(" File size: %d\n", (int)st.st_size); 613 logprintf(" Contents:\n"); 614 f = fopen(f1, "rb"); 615 if (f == NULL) { 616 logprintf(" Unable to open %s\n", f1); 617 } else { 618 s = ((off_t)sizeof(buff) < st.st_size) ? 619 (ssize_t)sizeof(buff) : (ssize_t)st.st_size; 620 s = fread(buff, 1, s, f); 621 hexdump(buff, NULL, s, 0); 622 fclose(f); 623 } 624 failure_finish(NULL); 625 return (0); 626} 627 628/* Verify that the named file exists and is not empty. */ 629int 630assertion_non_empty_file(const char *f1fmt, ...) 631{ 632 char f1[1024]; 633 struct stat st; 634 va_list ap; 635 636 assertion_count(test_filename, test_line); 637 va_start(ap, f1fmt); 638 vsprintf(f1, f1fmt, ap); 639 va_end(ap); 640 641 if (stat(f1, &st) != 0) { 642 failure_start(test_filename, test_line, "Stat failed: %s", f1); 643 failure_finish(NULL); 644 return (0); 645 } 646 if (st.st_size == 0) { 647 failure_start(test_filename, test_line, "File empty: %s", f1); 648 failure_finish(NULL); 649 return (0); 650 } 651 return (1); 652} 653 654/* Verify that two files have the same contents. */ 655/* TODO: hexdump the first bytes that actually differ. */ 656int 657assertion_equal_file(const char *fn1, const char *f2pattern, ...) 658{ 659 char fn2[1024]; 660 va_list ap; 661 char buff1[1024]; 662 char buff2[1024]; 663 FILE *f1, *f2; 664 int n1, n2; 665 666 assertion_count(test_filename, test_line); 667 va_start(ap, f2pattern); 668 vsprintf(fn2, f2pattern, ap); 669 va_end(ap); 670 671 f1 = fopen(fn1, "rb"); 672 f2 = fopen(fn2, "rb"); 673 for (;;) { 674 n1 = fread(buff1, 1, sizeof(buff1), f1); 675 n2 = fread(buff2, 1, sizeof(buff2), f2); 676 if (n1 != n2) 677 break; 678 if (n1 == 0 && n2 == 0) { 679 fclose(f1); 680 fclose(f2); 681 return (1); 682 } 683 if (memcmp(buff1, buff2, n1) != 0) 684 break; 685 } 686 fclose(f1); 687 fclose(f2); 688 failure_start(test_filename, test_line, "Files not identical"); 689 logprintf(" file1=\"%s\"\n", fn1); 690 logprintf(" file2=\"%s\"\n", fn2); 691 failure_finish(test_extra); 692 return (0); 693} 694 695/* Verify that the named file does exist. */ 696int 697assertion_file_exists(const char *fpattern, ...) 698{ 699 char f[1024]; 700 va_list ap; 701 702 assertion_count(test_filename, test_line); 703 va_start(ap, fpattern); 704 vsprintf(f, fpattern, ap); 705 va_end(ap); 706 707#if defined(_WIN32) && !defined(__CYGWIN__) 708 if (!_access(f, 0)) 709 return (1); 710#else 711 if (!access(f, F_OK)) 712 return (1); 713#endif 714 failure_start(test_filename, test_line, "File should exist: %s", f); 715 failure_finish(test_extra); 716 return (0); 717} 718 719/* Verify that the named file doesn't exist. */ 720int 721assertion_file_not_exists(const char *fpattern, ...) 722{ 723 char f[1024]; 724 va_list ap; 725 726 assertion_count(test_filename, test_line); 727 va_start(ap, fpattern); 728 vsprintf(f, fpattern, ap); 729 va_end(ap); 730 731#if defined(_WIN32) && !defined(__CYGWIN__) 732 if (_access(f, 0)) 733 return (1); 734#else 735 if (access(f, F_OK)) 736 return (1); 737#endif 738 failure_start(test_filename, test_line, "File should not exist: %s", f); 739 failure_finish(test_extra); 740 return (0); 741} 742 743/* Compare the contents of a file to a block of memory. */ 744int 745assertion_file_contents(const void *buff, int s, const char *fpattern, ...) 746{ 747 char fn[1024]; 748 va_list ap; 749 char *contents; 750 FILE *f; 751 int n; 752 753 assertion_count(test_filename, test_line); 754 va_start(ap, fpattern); 755 vsprintf(fn, fpattern, ap); 756 va_end(ap); 757 758 f = fopen(fn, "rb"); 759 if (f == NULL) { 760 failure_start(test_filename, test_line, 761 "File should exist: %s", fn); 762 failure_finish(test_extra); 763 return (0); 764 } 765 contents = malloc(s * 2); 766 n = fread(contents, 1, s * 2, f); 767 fclose(f); 768 if (n == s && memcmp(buff, contents, s) == 0) { 769 free(contents); 770 return (1); 771 } 772 failure_start(test_filename, test_line, "File contents don't match"); 773 logprintf(" file=\"%s\"\n", fn); 774 if (n > 0) 775 hexdump(contents, buff, n > 512 ? 512 : n, 0); 776 else { 777 logprintf(" File empty, contents should be:\n"); 778 hexdump(buff, NULL, s > 512 ? 512 : n, 0); 779 } 780 failure_finish(test_extra); 781 free(contents); 782 return (0); 783} 784 785/* Check the contents of a text file, being tolerant of line endings. */ 786int 787assertion_text_file_contents(const char *buff, const char *fn) 788{ 789 char *contents; 790 const char *btxt, *ftxt; 791 FILE *f; 792 int n, s; 793 794 assertion_count(test_filename, test_line); 795 f = fopen(fn, "r"); 796 if (f == NULL) { 797 failure_start(test_filename, test_line, 798 "File doesn't exist: %s", fn); 799 failure_finish(test_extra); 800 return (0); 801 } 802 s = strlen(buff); 803 contents = malloc(s * 2 + 128); 804 n = fread(contents, 1, s * 2 + 128 - 1, f); 805 if (n >= 0) 806 contents[n] = '\0'; 807 fclose(f); 808 /* Compare texts. */ 809 btxt = buff; 810 ftxt = (const char *)contents; 811 while (*btxt != '\0' && *ftxt != '\0') { 812 if (*btxt == *ftxt) { 813 ++btxt; 814 ++ftxt; 815 continue; 816 } 817 if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { 818 /* Pass over different new line characters. */ 819 ++btxt; 820 ftxt += 2; 821 continue; 822 } 823 break; 824 } 825 if (*btxt == '\0' && *ftxt == '\0') { 826 free(contents); 827 return (1); 828 } 829 failure_start(test_filename, test_line, "Contents don't match"); 830 logprintf(" file=\"%s\"\n", fn); 831 if (n > 0) 832 hexdump(contents, buff, n, 0); 833 else { 834 logprintf(" File empty, contents should be:\n"); 835 hexdump(buff, NULL, s, 0); 836 } 837 failure_finish(test_extra); 838 free(contents); 839 return (0); 840} 841 842/* Test that two paths point to the same file. */ 843/* As a side-effect, asserts that both files exist. */ 844static int 845is_hardlink(const char *file, int line, 846 const char *path1, const char *path2) 847{ 848#if defined(_WIN32) && !defined(__CYGWIN__) 849 BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; 850 int r; 851 852 assertion_count(file, line); 853 r = my_GetFileInformationByName(path1, &bhfi1); 854 if (r == 0) { 855 failure_start(file, line, "File %s can't be inspected?", path1); 856 failure_finish(NULL); 857 return (0); 858 } 859 r = my_GetFileInformationByName(path2, &bhfi2); 860 if (r == 0) { 861 failure_start(file, line, "File %s can't be inspected?", path2); 862 failure_finish(NULL); 863 return (0); 864 } 865 return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber 866 && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh 867 && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); 868#else 869 struct stat st1, st2; 870 int r; 871 872 assertion_count(file, line); 873 r = lstat(path1, &st1); 874 if (r != 0) { 875 failure_start(file, line, "File should exist: %s", path1); 876 failure_finish(NULL); 877 return (0); 878 } 879 r = lstat(path2, &st2); 880 if (r != 0) { 881 failure_start(file, line, "File should exist: %s", path2); 882 failure_finish(NULL); 883 return (0); 884 } 885 return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); 886#endif 887} 888 889int 890assertion_is_hardlink(const char *file, int line, 891 const char *path1, const char *path2) 892{ 893 if (is_hardlink(file, line, path1, path2)) 894 return (1); 895 failure_start(file, line, 896 "Files %s and %s are not hardlinked", path1, path2); 897 failure_finish(NULL); 898 return (0); 899} 900 901int 902assertion_is_not_hardlink(const char *file, int line, 903 const char *path1, const char *path2) 904{ 905 if (!is_hardlink(file, line, path1, path2)) 906 return (1); 907 failure_start(file, line, 908 "Files %s and %s should not be hardlinked", path1, path2); 909 failure_finish(NULL); 910 return (0); 911} 912 913/* Verify a/b/mtime of 'pathname'. */ 914/* If 'recent', verify that it's within last 10 seconds. */ 915static int 916assertion_file_time(const char *file, int line, 917 const char *pathname, long t, long nsec, char type, int recent) 918{ 919 long long filet, filet_nsec; 920 int r; 921 922#if defined(_WIN32) && !defined(__CYGWIN__) 923#define EPOC_TIME (116444736000000000ULL) 924 FILETIME ftime, fbirthtime, fatime, fmtime; 925 ULARGE_INTEGER wintm; 926 HANDLE h; 927 ftime.dwLowDateTime = 0; 928 ftime.dwHighDateTime = 0; 929 930 assertion_count(file, line); 931 h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, 932 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 933 if (h == INVALID_HANDLE_VALUE) { 934 failure_start(file, line, "Can't access %s\n", pathname); 935 failure_finish(NULL); 936 return (0); 937 } 938 r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); 939 switch (type) { 940 case 'a': ftime = fatime; break; 941 case 'b': ftime = fbirthtime; break; 942 case 'm': ftime = fmtime; break; 943 } 944 CloseHandle(h); 945 if (r == 0) { 946 failure_start(file, line, "Can't GetFileTime %s\n", pathname); 947 failure_finish(NULL); 948 return (0); 949 } 950 wintm.LowPart = ftime.dwLowDateTime; 951 wintm.HighPart = ftime.dwHighDateTime; 952 filet = (wintm.QuadPart - EPOC_TIME) / 10000000; 953 filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; 954 nsec = (nsec / 100) * 100; /* Round the request */ 955#else 956 struct stat st; 957 958 assertion_count(file, line); 959 r = lstat(pathname, &st); 960 if (r != 0) { 961 failure_start(file, line, "Can't stat %s\n", pathname); 962 failure_finish(NULL); 963 return (0); 964 } 965 switch (type) { 966 case 'a': filet = st.st_atime; break; 967 case 'm': filet = st.st_mtime; break; 968 case 'b': filet = 0; break; 969 default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); 970 exit(1); 971 } 972#if defined(__FreeBSD__) 973 switch (type) { 974 case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; 975 case 'b': filet = st.st_birthtime; 976 filet_nsec = st.st_birthtimespec.tv_nsec; break; 977 case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; 978 default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); 979 exit(1); 980 } 981 /* FreeBSD generally only stores to microsecond res, so round. */ 982 filet_nsec = (filet_nsec / 1000) * 1000; 983 nsec = (nsec / 1000) * 1000; 984#else 985 filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ 986 if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ 987#if defined(__HAIKU__) 988 if (type == 'a') return (1); /* Haiku doesn't have atime. */ 989#endif 990#endif 991#endif 992 if (recent) { 993 /* Check that requested time is up-to-date. */ 994 time_t now = time(NULL); 995 if (filet < now - 10 || filet > now + 1) { 996 failure_start(file, line, 997 "File %s has %ctime %ld, %ld seconds ago\n", 998 pathname, type, filet, now - filet); 999 failure_finish(NULL); 1000 return (0); 1001 } 1002 } else if (filet != t || filet_nsec != nsec) { 1003 failure_start(file, line, 1004 "File %s has %ctime %ld.%09ld, expected %ld.%09ld", 1005 pathname, type, filet, filet_nsec, t, nsec); 1006 failure_finish(NULL); 1007 return (0); 1008 } 1009 return (1); 1010} 1011 1012/* Verify atime of 'pathname'. */ 1013int 1014assertion_file_atime(const char *file, int line, 1015 const char *pathname, long t, long nsec) 1016{ 1017 return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); 1018} 1019 1020/* Verify atime of 'pathname' is up-to-date. */ 1021int 1022assertion_file_atime_recent(const char *file, int line, const char *pathname) 1023{ 1024 return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); 1025} 1026 1027/* Verify birthtime of 'pathname'. */ 1028int 1029assertion_file_birthtime(const char *file, int line, 1030 const char *pathname, long t, long nsec) 1031{ 1032 return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); 1033} 1034 1035/* Verify birthtime of 'pathname' is up-to-date. */ 1036int 1037assertion_file_birthtime_recent(const char *file, int line, 1038 const char *pathname) 1039{ 1040 return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); 1041} 1042 1043/* Verify mtime of 'pathname'. */ 1044int 1045assertion_file_mtime(const char *file, int line, 1046 const char *pathname, long t, long nsec) 1047{ 1048 return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); 1049} 1050 1051/* Verify mtime of 'pathname' is up-to-date. */ 1052int 1053assertion_file_mtime_recent(const char *file, int line, const char *pathname) 1054{ 1055 return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); 1056} 1057 1058/* Verify number of links to 'pathname'. */ 1059int 1060assertion_file_nlinks(const char *file, int line, 1061 const char *pathname, int nlinks) 1062{ 1063#if defined(_WIN32) && !defined(__CYGWIN__) 1064 BY_HANDLE_FILE_INFORMATION bhfi; 1065 int r; 1066 1067 assertion_count(file, line); 1068 r = my_GetFileInformationByName(pathname, &bhfi); 1069 if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) 1070 return (1); 1071 failure_start(file, line, "File %s has %d links, expected %d", 1072 pathname, bhfi.nNumberOfLinks, nlinks); 1073 failure_finish(NULL); 1074 return (0); 1075#else 1076 struct stat st; 1077 int r; 1078 1079 assertion_count(file, line); 1080 r = lstat(pathname, &st); 1081 if (r == 0 && st.st_nlink == nlinks) 1082 return (1); 1083 failure_start(file, line, "File %s has %d links, expected %d", 1084 pathname, st.st_nlink, nlinks); 1085 failure_finish(NULL); 1086 return (0); 1087#endif 1088} 1089 1090/* Verify size of 'pathname'. */ 1091int 1092assertion_file_size(const char *file, int line, const char *pathname, long size) 1093{ 1094 int64_t filesize; 1095 int r; 1096 1097 assertion_count(file, line); 1098#if defined(_WIN32) && !defined(__CYGWIN__) 1099 { 1100 BY_HANDLE_FILE_INFORMATION bhfi; 1101 r = !my_GetFileInformationByName(pathname, &bhfi); 1102 filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; 1103 } 1104#else 1105 { 1106 struct stat st; 1107 r = lstat(pathname, &st); 1108 filesize = st.st_size; 1109 } 1110#endif 1111 if (r == 0 && filesize == size) 1112 return (1); 1113 failure_start(file, line, "File %s has size %ld, expected %ld", 1114 pathname, (long)filesize, (long)size); 1115 failure_finish(NULL); 1116 return (0); 1117} 1118 1119/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ 1120int 1121assertion_is_dir(const char *file, int line, const char *pathname, int mode) 1122{ 1123 struct stat st; 1124 int r; 1125 1126#if defined(_WIN32) && !defined(__CYGWIN__) 1127 (void)mode; /* UNUSED */ 1128#endif 1129 assertion_count(file, line); 1130 r = lstat(pathname, &st); 1131 if (r != 0) { 1132 failure_start(file, line, "Dir should exist: %s", pathname); 1133 failure_finish(NULL); 1134 return (0); 1135 } 1136 if (!S_ISDIR(st.st_mode)) { 1137 failure_start(file, line, "%s is not a dir", pathname); 1138 failure_finish(NULL); 1139 return (0); 1140 } 1141#if !defined(_WIN32) || defined(__CYGWIN__) 1142 /* Windows doesn't handle permissions the same way as POSIX, 1143 * so just ignore the mode tests. */ 1144 /* TODO: Can we do better here? */ 1145 if (mode >= 0 && mode != (st.st_mode & 07777)) { 1146 failure_start(file, line, "Dir %s has wrong mode", pathname); 1147 logprintf(" Expected: 0%3o\n", mode); 1148 logprintf(" Found: 0%3o\n", st.st_mode & 07777); 1149 failure_finish(NULL); 1150 return (0); 1151 } 1152#endif 1153 return (1); 1154} 1155 1156/* Verify that 'pathname' is a regular file. If 'mode' is >= 0, 1157 * verify that too. */ 1158int 1159assertion_is_reg(const char *file, int line, const char *pathname, int mode) 1160{ 1161 struct stat st; 1162 int r; 1163 1164#if defined(_WIN32) && !defined(__CYGWIN__) 1165 (void)mode; /* UNUSED */ 1166#endif 1167 assertion_count(file, line); 1168 r = lstat(pathname, &st); 1169 if (r != 0 || !S_ISREG(st.st_mode)) { 1170 failure_start(file, line, "File should exist: %s", pathname); 1171 failure_finish(NULL); 1172 return (0); 1173 } 1174#if !defined(_WIN32) || defined(__CYGWIN__) 1175 /* Windows doesn't handle permissions the same way as POSIX, 1176 * so just ignore the mode tests. */ 1177 /* TODO: Can we do better here? */ 1178 if (mode >= 0 && mode != (st.st_mode & 07777)) { 1179 failure_start(file, line, "File %s has wrong mode", pathname); 1180 logprintf(" Expected: 0%3o\n", mode); 1181 logprintf(" Found: 0%3o\n", st.st_mode & 07777); 1182 failure_finish(NULL); 1183 return (0); 1184 } 1185#endif 1186 return (1); 1187} 1188 1189/* Check whether 'pathname' is a symbolic link. If 'contents' is 1190 * non-NULL, verify that the symlink has those contents. */ 1191static int 1192is_symlink(const char *file, int line, 1193 const char *pathname, const char *contents) 1194{ 1195#if defined(_WIN32) && !defined(__CYGWIN__) 1196 (void)pathname; /* UNUSED */ 1197 (void)contents; /* UNUSED */ 1198 assertion_count(file, line); 1199 /* Windows sort-of has real symlinks, but they're only usable 1200 * by privileged users and are crippled even then, so there's 1201 * really not much point in bothering with this. */ 1202 return (0); 1203#else 1204 char buff[300]; 1205 struct stat st; 1206 ssize_t linklen; 1207 int r; 1208 1209 assertion_count(file, line); 1210 r = lstat(pathname, &st); 1211 if (r != 0) { 1212 failure_start(file, line, 1213 "Symlink should exist: %s", pathname); 1214 failure_finish(NULL); 1215 return (0); 1216 } 1217 if (!S_ISLNK(st.st_mode)) 1218 return (0); 1219 if (contents == NULL) 1220 return (1); 1221 linklen = readlink(pathname, buff, sizeof(buff)); 1222 if (linklen < 0) { 1223 failure_start(file, line, "Can't read symlink %s", pathname); 1224 failure_finish(NULL); 1225 return (0); 1226 } 1227 buff[linklen] = '\0'; 1228 if (strcmp(buff, contents) != 0) 1229 return (0); 1230 return (1); 1231#endif 1232} 1233 1234/* Assert that path is a symlink that (optionally) contains contents. */ 1235int 1236assertion_is_symlink(const char *file, int line, 1237 const char *path, const char *contents) 1238{ 1239 if (is_symlink(file, line, path, contents)) 1240 return (1); 1241 if (contents) 1242 failure_start(file, line, "File %s is not a symlink to %s", 1243 path, contents); 1244 else 1245 failure_start(file, line, "File %s is not a symlink", path); 1246 failure_finish(NULL); 1247 return (0); 1248} 1249 1250 1251/* Create a directory and report any errors. */ 1252int 1253assertion_make_dir(const char *file, int line, const char *dirname, int mode) 1254{ 1255 assertion_count(file, line); 1256#if defined(_WIN32) && !defined(__CYGWIN__) 1257 (void)mode; /* UNUSED */ 1258 if (0 == _mkdir(dirname)) 1259 return (1); 1260#else 1261 if (0 == mkdir(dirname, mode)) 1262 return (1); 1263#endif 1264 failure_start(file, line, "Could not create directory %s", dirname); 1265 failure_finish(NULL); 1266 return(0); 1267} 1268 1269/* Create a file with the specified contents and report any failures. */ 1270int 1271assertion_make_file(const char *file, int line, 1272 const char *path, int mode, const char *contents) 1273{ 1274#if defined(_WIN32) && !defined(__CYGWIN__) 1275 /* TODO: Rework this to set file mode as well. */ 1276 FILE *f; 1277 (void)mode; /* UNUSED */ 1278 assertion_count(file, line); 1279 f = fopen(path, "wb"); 1280 if (f == NULL) { 1281 failure_start(file, line, "Could not create file %s", path); 1282 failure_finish(NULL); 1283 return (0); 1284 } 1285 if (contents != NULL) { 1286 if (strlen(contents) 1287 != fwrite(contents, 1, strlen(contents), f)) { 1288 fclose(f); 1289 failure_start(file, line, 1290 "Could not write file %s", path); 1291 failure_finish(NULL); 1292 return (0); 1293 } 1294 } 1295 fclose(f); 1296 return (1); 1297#else 1298 int fd; 1299 assertion_count(file, line); 1300 fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); 1301 if (fd < 0) { 1302 failure_start(file, line, "Could not create %s", path); 1303 failure_finish(NULL); 1304 return (0); 1305 } 1306 if (contents != NULL) { 1307 if ((ssize_t)strlen(contents) 1308 != write(fd, contents, strlen(contents))) { 1309 close(fd); 1310 failure_start(file, line, "Could not write to %s", path); 1311 failure_finish(NULL); 1312 return (0); 1313 } 1314 } 1315 close(fd); 1316 return (1); 1317#endif 1318} 1319 1320/* Create a hardlink and report any failures. */ 1321int 1322assertion_make_hardlink(const char *file, int line, 1323 const char *newpath, const char *linkto) 1324{ 1325 int succeeded; 1326 1327 assertion_count(file, line); 1328#if defined(_WIN32) && !defined(__CYGWIN__) 1329 succeeded = my_CreateHardLinkA(newpath, linkto); 1330#elif HAVE_LINK 1331 succeeded = !link(linkto, newpath); 1332#else 1333 succeeded = 0; 1334#endif 1335 if (succeeded) 1336 return (1); 1337 failure_start(file, line, "Could not create hardlink"); 1338 logprintf(" New link: %s\n", newpath); 1339 logprintf(" Old name: %s\n", linkto); 1340 failure_finish(NULL); 1341 return(0); 1342} 1343 1344/* Create a symlink and report any failures. */ 1345int 1346assertion_make_symlink(const char *file, int line, 1347 const char *newpath, const char *linkto) 1348{ 1349#if defined(_WIN32) && !defined(__CYGWIN__) 1350 int targetIsDir = 0; /* TODO: Fix this */ 1351 assertion_count(file, line); 1352 if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) 1353 return (1); 1354#elif HAVE_SYMLINK 1355 assertion_count(file, line); 1356 if (0 == symlink(linkto, newpath)) 1357 return (1); 1358#endif 1359 failure_start(file, line, "Could not create symlink"); 1360 logprintf(" New link: %s\n", newpath); 1361 logprintf(" Old name: %s\n", linkto); 1362 failure_finish(NULL); 1363 return(0); 1364} 1365 1366/* Set umask, report failures. */ 1367int 1368assertion_umask(const char *file, int line, int mask) 1369{ 1370 assertion_count(file, line); 1371 (void)file; /* UNUSED */ 1372 (void)line; /* UNUSED */ 1373 umask(mask); 1374 return (1); 1375} 1376 1377/* 1378 * 1379 * UTILITIES for use by tests. 1380 * 1381 */ 1382 1383/* 1384 * Check whether platform supports symlinks. This is intended 1385 * for tests to use in deciding whether to bother testing symlink 1386 * support; if the platform doesn't support symlinks, there's no point 1387 * in checking whether the program being tested can create them. 1388 * 1389 * Note that the first time this test is called, we actually go out to 1390 * disk to create and verify a symlink. This is necessary because 1391 * symlink support is actually a property of a particular filesystem 1392 * and can thus vary between directories on a single system. After 1393 * the first call, this returns the cached result from memory, so it's 1394 * safe to call it as often as you wish. 1395 */ 1396int 1397canSymlink(void) 1398{ 1399 /* Remember the test result */ 1400 static int value = 0, tested = 0; 1401 if (tested) 1402 return (value); 1403 1404 ++tested; 1405 assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, "a"); 1406 /* Note: Cygwin has its own symlink() emulation that does not 1407 * use the Win32 CreateSymbolicLink() function. */ 1408#if defined(_WIN32) && !defined(__CYGWIN__) 1409 value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) 1410 && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); 1411#elif HAVE_SYMLINK 1412 value = (0 == symlink("canSymlink.0", "canSymlink.1")) 1413 && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); 1414#endif 1415 return (value); 1416} 1417 1418/* 1419 * Can this platform run the gzip program? 1420 */ 1421/* Platform-dependent options for hiding the output of a subcommand. */ 1422#if defined(_WIN32) && !defined(__CYGWIN__) 1423static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ 1424#else 1425static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ 1426#endif 1427int 1428canGzip(void) 1429{ 1430 static int tested = 0, value = 0; 1431 if (!tested) { 1432 tested = 1; 1433 if (systemf("gzip -V %s", redirectArgs) == 0) 1434 value = 1; 1435 } 1436 return (value); 1437} 1438 1439/* 1440 * Can this platform run the gunzip program? 1441 */ 1442int 1443canGunzip(void) 1444{ 1445 static int tested = 0, value = 0; 1446 if (!tested) { 1447 tested = 1; 1448 if (systemf("gunzip -V %s", redirectArgs) == 0) 1449 value = 1; 1450 } 1451 return (value); 1452} 1453 1454/* 1455 * Sleep as needed; useful for verifying disk timestamp changes by 1456 * ensuring that the wall-clock time has actually changed before we 1457 * go back to re-read something from disk. 1458 */ 1459void 1460sleepUntilAfter(time_t t) 1461{ 1462 while (t >= time(NULL)) 1463#if defined(_WIN32) && !defined(__CYGWIN__) 1464 Sleep(500); 1465#else 1466 sleep(1); 1467#endif 1468} 1469 1470/* 1471 * Call standard system() call, but build up the command line using 1472 * sprintf() conventions. 1473 */ 1474int 1475systemf(const char *fmt, ...) 1476{ 1477 char buff[8192]; 1478 va_list ap; 1479 int r; 1480 1481 va_start(ap, fmt); 1482 vsprintf(buff, fmt, ap); 1483 if (verbosity > VERBOSITY_FULL) 1484 logprintf("Cmd: %s\n", buff); 1485 r = system(buff); 1486 va_end(ap); 1487 return (r); 1488} 1489 1490/* 1491 * Slurp a file into memory for ease of comparison and testing. 1492 * Returns size of file in 'sizep' if non-NULL, null-terminates 1493 * data in memory for ease of use. 1494 */ 1495char * 1496slurpfile(size_t * sizep, const char *fmt, ...) 1497{ 1498 char filename[8192]; 1499 struct stat st; 1500 va_list ap; 1501 char *p; 1502 ssize_t bytes_read; 1503 FILE *f; 1504 int r; 1505 1506 va_start(ap, fmt); 1507 vsprintf(filename, fmt, ap); 1508 va_end(ap); 1509 1510 f = fopen(filename, "rb"); 1511 if (f == NULL) { 1512 /* Note: No error; non-existent file is okay here. */ 1513 return (NULL); 1514 } 1515 r = fstat(fileno(f), &st); 1516 if (r != 0) { 1517 logprintf("Can't stat file %s\n", filename); 1518 fclose(f); 1519 return (NULL); 1520 } 1521 p = malloc((size_t)st.st_size + 1); 1522 if (p == NULL) { 1523 logprintf("Can't allocate %ld bytes of memory to read file %s\n", 1524 (long int)st.st_size, filename); 1525 fclose(f); 1526 return (NULL); 1527 } 1528 bytes_read = fread(p, 1, (size_t)st.st_size, f); 1529 if (bytes_read < st.st_size) { 1530 logprintf("Can't read file %s\n", filename); 1531 fclose(f); 1532 free(p); 1533 return (NULL); 1534 } 1535 p[st.st_size] = '\0'; 1536 if (sizep != NULL) 1537 *sizep = (size_t)st.st_size; 1538 fclose(f); 1539 return (p); 1540} 1541 1542/* Read a uuencoded file from the reference directory, decode, and 1543 * write the result into the current directory. */ 1544#define UUDECODE(c) (((c) - 0x20) & 0x3f) 1545void 1546extract_reference_file(const char *name) 1547{ 1548 char buff[1024]; 1549 FILE *in, *out; 1550 1551 sprintf(buff, "%s/%s.uu", refdir, name); 1552 in = fopen(buff, "r"); 1553 failure("Couldn't open reference file %s", buff); 1554 assert(in != NULL); 1555 if (in == NULL) 1556 return; 1557 /* Read up to and including the 'begin' line. */ 1558 for (;;) { 1559 if (fgets(buff, sizeof(buff), in) == NULL) { 1560 /* TODO: This is a failure. */ 1561 return; 1562 } 1563 if (memcmp(buff, "begin ", 6) == 0) 1564 break; 1565 } 1566 /* Now, decode the rest and write it. */ 1567 /* Not a lot of error checking here; the input better be right. */ 1568 out = fopen(name, "wb"); 1569 while (fgets(buff, sizeof(buff), in) != NULL) { 1570 char *p = buff; 1571 int bytes; 1572 1573 if (memcmp(buff, "end", 3) == 0) 1574 break; 1575 1576 bytes = UUDECODE(*p++); 1577 while (bytes > 0) { 1578 int n = 0; 1579 /* Write out 1-3 bytes from that. */ 1580 if (bytes > 0) { 1581 n = UUDECODE(*p++) << 18; 1582 n |= UUDECODE(*p++) << 12; 1583 fputc(n >> 16, out); 1584 --bytes; 1585 } 1586 if (bytes > 0) { 1587 n |= UUDECODE(*p++) << 6; 1588 fputc((n >> 8) & 0xFF, out); 1589 --bytes; 1590 } 1591 if (bytes > 0) { 1592 n |= UUDECODE(*p++); 1593 fputc(n & 0xFF, out); 1594 --bytes; 1595 } 1596 } 1597 } 1598 fclose(out); 1599 fclose(in); 1600} 1601 1602/* 1603 * 1604 * TEST management 1605 * 1606 */ 1607 1608/* 1609 * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has 1610 * a line like 1611 * DEFINE_TEST(test_function) 1612 * for each test. 1613 */ 1614 1615/* Use "list.h" to declare all of the test functions. */ 1616#undef DEFINE_TEST 1617#define DEFINE_TEST(name) void name(void); 1618#include "list.h" 1619 1620/* Use "list.h" to create a list of all tests (functions and names). */ 1621#undef DEFINE_TEST 1622#define DEFINE_TEST(n) { n, #n, 0 }, 1623struct { void (*func)(void); const char *name; int failures; } tests[] = { 1624 #include "list.h" 1625}; 1626 1627/* 1628 * Summarize repeated failures in the just-completed test. 1629 */ 1630static void 1631test_summarize(const char *filename, int failed) 1632{ 1633 unsigned int i; 1634 1635 switch (verbosity) { 1636 case VERBOSITY_SUMMARY_ONLY: 1637 printf(failed ? "E" : "."); 1638 fflush(stdout); 1639 break; 1640 case VERBOSITY_PASSFAIL: 1641 printf(failed ? "FAIL\n" : "ok\n"); 1642 break; 1643 } 1644 1645 log_console = (verbosity == VERBOSITY_LIGHT_REPORT); 1646 1647 for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { 1648 if (failed_lines[i].count > 1 && !failed_lines[i].skip) 1649 logprintf("%s:%d: Summary: Failed %d times\n", 1650 filename, i, failed_lines[i].count); 1651 } 1652 /* Clear the failure history for the next file. */ 1653 memset(failed_lines, 0, sizeof(failed_lines)); 1654} 1655 1656/* 1657 * Actually run a single test, with appropriate setup and cleanup. 1658 */ 1659static int 1660test_run(int i, const char *tmpdir) 1661{ 1662 char logfilename[64]; 1663 int failures_before = failures; 1664 int oldumask; 1665 1666 switch (verbosity) { 1667 case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ 1668 break; 1669 case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ 1670 printf("%3d: %-50s", i, tests[i].name); 1671 fflush(stdout); 1672 break; 1673 default: /* Title of test, details will follow */ 1674 printf("%3d: %s\n", i, tests[i].name); 1675 } 1676 1677 /* Chdir to the top-level work directory. */ 1678 if (!assertChdir(tmpdir)) { 1679 fprintf(stderr, 1680 "ERROR: Can't chdir to top work dir %s\n", tmpdir); 1681 exit(1); 1682 } 1683 /* Create a log file for this test. */ 1684 sprintf(logfilename, "%s.log", tests[i].name); 1685 logfile = fopen(logfilename, "w"); 1686 fprintf(logfile, "%s\n\n", tests[i].name); 1687 /* Chdir() to a work dir for this specific test. */ 1688 if (!assertMakeDir(tests[i].name, 0755) 1689 || !assertChdir(tests[i].name)) { 1690 fprintf(stderr, 1691 "ERROR: Can't chdir to work dir %s/%s\n", 1692 tmpdir, tests[i].name); 1693 exit(1); 1694 } 1695 /* Explicitly reset the locale before each test. */ 1696 setlocale(LC_ALL, "C"); 1697 /* Record the umask before we run the test. */ 1698 umask(oldumask = umask(0)); 1699 /* 1700 * Run the actual test. 1701 */ 1702 (*tests[i].func)(); 1703 /* 1704 * Clean up and report afterwards. 1705 */ 1706 /* Restore umask */ 1707 umask(oldumask); 1708 /* Reset locale. */ 1709 setlocale(LC_ALL, "C"); 1710 /* Reset directory. */ 1711 if (!assertChdir(tmpdir)) { 1712 fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", 1713 tmpdir); 1714 exit(1); 1715 } 1716 /* Report per-test summaries. */ 1717 tests[i].failures = failures - failures_before; 1718 test_summarize(test_filename, tests[i].failures); 1719 /* Close the per-test log file. */ 1720 fclose(logfile); 1721 logfile = NULL; 1722 /* If there were no failures, we can remove the work dir and logfile. */ 1723 if (tests[i].failures == 0) { 1724 if (!keep_temp_files && assertChdir(tmpdir)) { 1725#if defined(_WIN32) && !defined(__CYGWIN__) 1726 /* Make sure not to leave empty directories. 1727 * Sometimes a processing of closing files used by tests 1728 * is not done, then rmdir will be failed and it will 1729 * leave a empty test directory. So we should wait a few 1730 * seconds and retry rmdir. */ 1731 int r, t; 1732 for (t = 0; t < 10; t++) { 1733 if (t > 0) 1734 Sleep(1000); 1735 r = systemf("rmdir /S /Q %s", tests[i].name); 1736 if (r == 0) 1737 break; 1738 } 1739 systemf("del %s", logfilename); 1740#else 1741 systemf("rm -rf %s", tests[i].name); 1742 systemf("rm %s", logfilename); 1743#endif 1744 } 1745 } 1746 /* Return appropriate status. */ 1747 return (tests[i].failures); 1748} 1749 1750/* 1751 * 1752 * 1753 * MAIN and support routines. 1754 * 1755 * 1756 */ 1757 1758static void 1759usage(const char *program) 1760{ 1761 static const int limit = sizeof(tests) / sizeof(tests[0]); 1762 int i; 1763 1764 printf("Usage: %s [options] <test> <test> ...\n", program); 1765 printf("Default is to run all tests.\n"); 1766 printf("Otherwise, specify the numbers of the tests you wish to run.\n"); 1767 printf("Options:\n"); 1768 printf(" -d Dump core after any failure, for debugging.\n"); 1769 printf(" -k Keep all temp files.\n"); 1770 printf(" Default: temp files for successful tests deleted.\n"); 1771#ifdef PROGRAM 1772 printf(" -p <path> Path to executable to be tested.\n"); 1773 printf(" Default: path taken from " ENVBASE " environment variable.\n"); 1774#endif 1775 printf(" -q Quiet.\n"); 1776 printf(" -r <dir> Path to dir containing reference files.\n"); 1777 printf(" Default: Current directory.\n"); 1778 printf(" -v Verbose.\n"); 1779 printf("Available tests:\n"); 1780 for (i = 0; i < limit; i++) 1781 printf(" %d: %s\n", i, tests[i].name); 1782 exit(1); 1783} 1784 1785static char * 1786get_refdir(const char *d) 1787{ 1788 char tried[512] = { '\0' }; 1789 char buff[128]; 1790 char *pwd, *p; 1791 1792 /* If a dir was specified, try that */ 1793 if (d != NULL) { 1794 pwd = NULL; 1795 snprintf(buff, sizeof(buff), "%s", d); 1796 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); 1797 if (p != NULL) goto success; 1798 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); 1799 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); 1800 goto failure; 1801 } 1802 1803 /* Get the current dir. */ 1804 pwd = getcwd(NULL, 0); 1805 while (pwd[strlen(pwd) - 1] == '\n') 1806 pwd[strlen(pwd) - 1] = '\0'; 1807 1808 /* Look for a known file. */ 1809 snprintf(buff, sizeof(buff), "%s", pwd); 1810 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); 1811 if (p != NULL) goto success; 1812 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); 1813 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); 1814 1815 snprintf(buff, sizeof(buff), "%s/test", pwd); 1816 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); 1817 if (p != NULL) goto success; 1818 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); 1819 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); 1820 1821#if defined(LIBRARY) 1822 snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); 1823#else 1824 snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM); 1825#endif 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 if (memcmp(pwd, "/usr/obj", 8) == 0) { 1832 snprintf(buff, sizeof(buff), "%s", pwd + 8); 1833 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); 1834 if (p != NULL) goto success; 1835 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); 1836 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); 1837 1838 snprintf(buff, sizeof(buff), "%s/test", pwd + 8); 1839 p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); 1840 if (p != NULL) goto success; 1841 strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); 1842 strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); 1843 } 1844 1845failure: 1846 printf("Unable to locate known reference file %s\n", KNOWNREF); 1847 printf(" Checked following directories:\n%s\n", tried); 1848#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) 1849 DebugBreak(); 1850#endif 1851 exit(1); 1852 1853success: 1854 free(p); 1855 free(pwd); 1856 return strdup(buff); 1857} 1858 1859int 1860main(int argc, char **argv) 1861{ 1862 static const int limit = sizeof(tests) / sizeof(tests[0]); 1863 int i, tests_run = 0, tests_failed = 0, option; 1864 time_t now; 1865 char *refdir_alloc = NULL; 1866 const char *progname; 1867 const char *tmp, *option_arg, *p; 1868 char tmpdir[256]; 1869 char tmpdir_timestamp[256]; 1870 1871 (void)argc; /* UNUSED */ 1872 1873#if defined(HAVE__CrtSetReportMode) 1874 /* To stop to run the default invalid parameter handler. */ 1875 _set_invalid_parameter_handler(invalid_parameter_handler); 1876 /* Disable annoying assertion message box. */ 1877 _CrtSetReportMode(_CRT_ASSERT, 0); 1878#endif 1879 1880 /* 1881 * Name of this program, used to build root of our temp directory 1882 * tree. 1883 */ 1884 progname = p = argv[0]; 1885 while (*p != '\0') { 1886 /* Support \ or / dir separators for Windows compat. */ 1887 if (*p == '/' || *p == '\\') 1888 progname = p + 1; 1889 ++p; 1890 } 1891 1892#ifdef PROGRAM 1893 /* Get the target program from environment, if available. */ 1894 testprogfile = getenv(ENVBASE); 1895#endif 1896 1897 if (getenv("TMPDIR") != NULL) 1898 tmp = getenv("TMPDIR"); 1899 else if (getenv("TMP") != NULL) 1900 tmp = getenv("TMP"); 1901 else if (getenv("TEMP") != NULL) 1902 tmp = getenv("TEMP"); 1903 else if (getenv("TEMPDIR") != NULL) 1904 tmp = getenv("TEMPDIR"); 1905 else 1906 tmp = "/tmp"; 1907 1908 /* Allow -d to be controlled through the environment. */ 1909 if (getenv(ENVBASE "_DEBUG") != NULL) 1910 dump_on_failure = 1; 1911 1912 /* Get the directory holding test files from environment. */ 1913 refdir = getenv(ENVBASE "_TEST_FILES"); 1914 1915 /* 1916 * Parse options, without using getopt(), which isn't available 1917 * on all platforms. 1918 */ 1919 ++argv; /* Skip program name */ 1920 while (*argv != NULL) { 1921 if (**argv != '-') 1922 break; 1923 p = *argv++; 1924 ++p; /* Skip '-' */ 1925 while (*p != '\0') { 1926 option = *p++; 1927 option_arg = NULL; 1928 /* If 'opt' takes an argument, parse that. */ 1929 if (option == 'p' || option == 'r') { 1930 if (*p != '\0') 1931 option_arg = p; 1932 else if (*argv == NULL) { 1933 fprintf(stderr, 1934 "Option -%c requires argument.\n", 1935 option); 1936 usage(progname); 1937 } else 1938 option_arg = *argv++; 1939 p = ""; /* End of this option word. */ 1940 } 1941 1942 /* Now, handle the option. */ 1943 switch (option) { 1944 case 'd': 1945 dump_on_failure = 1; 1946 break; 1947 case 'k': 1948 keep_temp_files = 1; 1949 break; 1950 case 'p': 1951#ifdef PROGRAM 1952 testprogfile = option_arg; 1953#else 1954 usage(progname); 1955#endif 1956 break; 1957 case 'q': 1958 verbosity--; 1959 break; 1960 case 'r': 1961 refdir = option_arg; 1962 break; 1963 case 'v': 1964 verbosity++; 1965 break; 1966 default: 1967 usage(progname); 1968 } 1969 } 1970 } 1971 1972 /* 1973 * Sanity-check that our options make sense. 1974 */ 1975#ifdef PROGRAM 1976 if (testprogfile == NULL) 1977 usage(progname); 1978 { 1979 char *testprg; 1980#if defined(_WIN32) && !defined(__CYGWIN__) 1981 /* Command.com sometimes rejects '/' separators. */ 1982 testprg = strdup(testprogfile); 1983 for (i = 0; testprg[i] != '\0'; i++) { 1984 if (testprg[i] == '/') 1985 testprg[i] = '\\'; 1986 } 1987 testprogfile = testprg; 1988#endif 1989 /* Quote the name that gets put into shell command lines. */ 1990 testprg = malloc(strlen(testprogfile) + 3); 1991 strcpy(testprg, "\""); 1992 strcat(testprg, testprogfile); 1993 strcat(testprg, "\""); 1994 testprog = testprg; 1995 } 1996#endif 1997 1998 /* 1999 * Create a temp directory for the following tests. 2000 * Include the time the tests started as part of the name, 2001 * to make it easier to track the results of multiple tests. 2002 */ 2003 now = time(NULL); 2004 for (i = 0; ; i++) { 2005 strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), 2006 "%Y-%m-%dT%H.%M.%S", 2007 localtime(&now)); 2008 sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, 2009 tmpdir_timestamp, i); 2010 if (assertMakeDir(tmpdir,0755)) 2011 break; 2012 if (i >= 999) { 2013 fprintf(stderr, 2014 "ERROR: Unable to create temp directory %s\n", 2015 tmpdir); 2016 exit(1); 2017 } 2018 } 2019 2020 /* 2021 * If the user didn't specify a directory for locating 2022 * reference files, try to find the reference files in 2023 * the "usual places." 2024 */ 2025 refdir = refdir_alloc = get_refdir(refdir); 2026 2027 /* 2028 * Banner with basic information. 2029 */ 2030 printf("\n"); 2031 printf("If tests fail or crash, details will be in:\n"); 2032 printf(" %s\n", tmpdir); 2033 printf("\n"); 2034 if (verbosity > VERBOSITY_SUMMARY_ONLY) { 2035 printf("Reference files will be read from: %s\n", refdir); 2036#ifdef PROGRAM 2037 printf("Running tests on: %s\n", testprog); 2038#endif 2039 printf("Exercising: "); 2040 fflush(stdout); 2041 printf("%s\n", EXTRA_VERSION); 2042 } else { 2043 printf("Running "); 2044 fflush(stdout); 2045 } 2046 2047 /* 2048 * Run some or all of the individual tests. 2049 */ 2050 if (*argv == NULL) { 2051 /* Default: Run all tests. */ 2052 for (i = 0; i < limit; i++) { 2053 if (test_run(i, tmpdir)) 2054 tests_failed++; 2055 tests_run++; 2056 } 2057 } else { 2058 while (*(argv) != NULL) { 2059 if (**argv >= '0' && **argv <= '9') { 2060 i = atoi(*argv); 2061 if (i < 0 || i >= limit) { 2062 printf("*** INVALID Test %s\n", *argv); 2063 free(refdir_alloc); 2064 usage(progname); 2065 /* usage() never returns */ 2066 } 2067 } else { 2068 for (i = 0; i < limit; ++i) { 2069 if (strcmp(*argv, tests[i].name) == 0) 2070 break; 2071 } 2072 if (i >= limit) { 2073 printf("*** INVALID Test ``%s''\n", 2074 *argv); 2075 free(refdir_alloc); 2076 usage(progname); 2077 /* usage() never returns */ 2078 } 2079 } 2080 if (test_run(i, tmpdir)) 2081 tests_failed++; 2082 tests_run++; 2083 argv++; 2084 } 2085 } 2086 2087 /* 2088 * Report summary statistics. 2089 */ 2090 if (verbosity > VERBOSITY_SUMMARY_ONLY) { 2091 printf("\n"); 2092 printf("Totals:\n"); 2093 printf(" Tests run: %8d\n", tests_run); 2094 printf(" Tests failed: %8d\n", tests_failed); 2095 printf(" Assertions checked:%8d\n", assertions); 2096 printf(" Assertions failed: %8d\n", failures); 2097 printf(" Skips reported: %8d\n", skips); 2098 } 2099 if (failures) { 2100 printf("\n"); 2101 printf("Failing tests:\n"); 2102 for (i = 0; i < limit; ++i) { 2103 if (tests[i].failures) 2104 printf(" %d: %s (%d failures)\n", i, 2105 tests[i].name, tests[i].failures); 2106 } 2107 printf("\n"); 2108 printf("Details for failing tests: %s\n", tmpdir); 2109 printf("\n"); 2110 } else { 2111 if (verbosity == VERBOSITY_SUMMARY_ONLY) 2112 printf("\n"); 2113 printf("%d tests passed, no failures\n", tests_run); 2114 } 2115 2116 free(refdir_alloc); 2117 2118 /* If the final tmpdir is empty, we can remove it. */ 2119 /* This should be the usual case when all tests succeed. */ 2120 assertChdir(".."); 2121 rmdir(tmpdir); 2122 2123 return (tests_failed ? 1 : 0); 2124} 2125