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