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