cutest.h (22815B)
1 /* 2 * CUTest -- C/C++ Unit Test facility 3 * <http://github.com/mity/cutest> 4 * 5 * Copyright (c) 2013-2017 Martin Mitas 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 * IN THE SOFTWARE. 24 */ 25 26 #ifndef CUTEST_H__ 27 #define CUTEST_H__ 28 29 /************************ 30 *** Public interface *** 31 ************************/ 32 33 /* By default, <cutest.h> provides the main program entry point (function 34 * main()). However, if the test suite is composed of multiple source files 35 * which include <cutest.h>, then this causes a problem of multiple main() 36 * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all 37 * compilation units but one. 38 */ 39 40 /* Macro to specify list of unit tests in the suite. 41 * The unit test implementation MUST provide list of unit tests it implements 42 * with this macro: 43 * 44 * TEST_LIST = { 45 * { "test1_name", test1_func_ptr }, 46 * { "test2_name", test2_func_ptr }, 47 * ... 48 * { 0 } 49 * }; 50 * 51 * The list specifies names of each test (must be unique) and pointer to 52 * a function implementing it. The function does not take any arguments 53 * and has no return values, i.e. every test function has tp be compatible 54 * with this prototype: 55 * 56 * void test_func(void); 57 */ 58 #define TEST_LIST const struct test__ test_list__[] 59 60 /* Macros for testing whether an unit test succeeds or fails. These macros 61 * can be used arbitrarily in functions implementing the unit tests. 62 * 63 * If any condition fails throughout execution of a test, the test fails. 64 * 65 * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows 66 * also to specify an error message to print out if the condition fails. 67 * (It expects printf-like format string and its parameters). The macros 68 * return non-zero (condition passes) or 0 (condition fails). 69 * 70 * That can be useful when more conditions should be checked only if some 71 * preceding condition passes, as illustrated in this code snippet: 72 * 73 * SomeStruct* ptr = allocate_some_struct(); 74 * if(TEST_CHECK(ptr != NULL)) { 75 * TEST_CHECK(ptr->member1 < 100); 76 * TEST_CHECK(ptr->member2 > 200); 77 * } 78 */ 79 #define TEST_CHECK_(cond, ...) \ 80 test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) 81 #define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) 82 83 /********************** 84 *** Implementation *** 85 **********************/ 86 87 /* The unit test files should not rely on anything below. */ 88 89 #include <stdarg.h> 90 #include <stdio.h> 91 #include <stdlib.h> 92 #include <string.h> 93 94 #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) 95 #define CUTEST_UNIX__ 1 96 #include <errno.h> 97 #include <unistd.h> 98 #include <sys/types.h> 99 #include <sys/wait.h> 100 #include <signal.h> 101 #endif 102 103 #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 104 #define CUTEST_WIN__ 1 105 #include <windows.h> 106 #include <io.h> 107 #endif 108 109 #ifdef __cplusplus 110 #include <exception> 111 #endif 112 113 /* Note our global private identifiers end with '__' to mitigate risk of clash 114 * with the unit tests implementation. */ 115 116 #ifdef __cplusplus 117 extern "C" { 118 #endif 119 120 struct test__ { 121 const char *name; 122 void (*func)(void); 123 }; 124 125 extern const struct test__ test_list__[]; 126 127 int test_check__(int cond, const char *file, int line, const char *fmt, ...); 128 129 #ifndef TEST_NO_MAIN 130 131 static char *test_argv0__ = NULL; 132 static int test_count__ = 0; 133 static int test_no_exec__ = 0; 134 static int test_no_summary__ = 0; 135 static int test_skip_mode__ = 0; 136 137 static int test_stat_failed_units__ = 0; 138 static int test_stat_run_units__ = 0; 139 140 static const struct test__ *test_current_unit__ = NULL; 141 static int test_current_already_logged__ = 0; 142 static int test_verbose_level__ = 2; 143 static int test_current_failures__ = 0; 144 static int test_colorize__ = 0; 145 146 #define CUTEST_COLOR_DEFAULT__ 0 147 #define CUTEST_COLOR_GREEN__ 1 148 #define CUTEST_COLOR_RED__ 2 149 #define CUTEST_COLOR_DEFAULT_INTENSIVE__ 3 150 #define CUTEST_COLOR_GREEN_INTENSIVE__ 4 151 #define CUTEST_COLOR_RED_INTENSIVE__ 5 152 153 static size_t test_print_in_color__(int color, const char *fmt, ...) 154 { 155 va_list args; 156 char buffer[256]; 157 size_t n; 158 159 va_start(args, fmt); 160 vsnprintf(buffer, sizeof(buffer), fmt, args); 161 va_end(args); 162 buffer[sizeof(buffer) - 1] = '\0'; 163 164 if (!test_colorize__) { 165 return printf("%s", buffer); 166 } 167 168 #if defined CUTEST_UNIX__ 169 { 170 const char *col_str; 171 switch (color) { 172 case CUTEST_COLOR_GREEN__: 173 col_str = "\033[0;32m"; 174 break; 175 case CUTEST_COLOR_RED__: 176 col_str = "\033[0;31m"; 177 break; 178 case CUTEST_COLOR_GREEN_INTENSIVE__: 179 col_str = "\033[1;32m"; 180 break; 181 case CUTEST_COLOR_RED_INTENSIVE__: 182 col_str = "\033[1;30m"; 183 break; 184 case CUTEST_COLOR_DEFAULT_INTENSIVE__: 185 col_str = "\033[1m"; 186 break; 187 default: 188 col_str = "\033[0m"; 189 break; 190 } 191 printf("%s", col_str); 192 n = printf("%s", buffer); 193 printf("\033[0m"); 194 return n; 195 } 196 #elif defined CUTEST_WIN__ 197 { 198 HANDLE h; 199 CONSOLE_SCREEN_BUFFER_INFO info; 200 WORD attr; 201 202 h = GetStdHandle(STD_OUTPUT_HANDLE); 203 GetConsoleScreenBufferInfo(h, &info); 204 205 switch (color) { 206 case CUTEST_COLOR_GREEN__: 207 attr = FOREGROUND_GREEN; 208 break; 209 case CUTEST_COLOR_RED__: 210 attr = FOREGROUND_RED; 211 break; 212 case CUTEST_COLOR_GREEN_INTENSIVE__: 213 attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; 214 break; 215 case CUTEST_COLOR_RED_INTENSIVE__: 216 attr = FOREGROUND_RED | FOREGROUND_INTENSITY; 217 break; 218 case CUTEST_COLOR_DEFAULT_INTENSIVE__: 219 attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | 220 FOREGROUND_INTENSITY; 221 break; 222 default: 223 attr = 0; 224 break; 225 } 226 if (attr != 0) 227 SetConsoleTextAttribute(h, attr); 228 n = printf("%s", buffer); 229 SetConsoleTextAttribute(h, info.wAttributes); 230 return n; 231 } 232 #else 233 n = printf("%s", buffer); 234 return n; 235 #endif 236 } 237 238 int test_check__(int cond, const char *file, int line, const char *fmt, ...) 239 { 240 const char *result_str; 241 int result_color; 242 int verbose_level; 243 244 if (cond) { 245 result_str = "ok"; 246 result_color = CUTEST_COLOR_GREEN__; 247 verbose_level = 3; 248 } else { 249 if (!test_current_already_logged__ && test_current_unit__ != NULL) { 250 printf("[ "); 251 test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, "FAILED"); 252 printf(" ]\n"); 253 } 254 result_str = "failed"; 255 result_color = CUTEST_COLOR_RED__; 256 verbose_level = 2; 257 test_current_failures__++; 258 test_current_already_logged__++; 259 } 260 261 if (test_verbose_level__ >= verbose_level) { 262 va_list args; 263 264 printf(" "); 265 266 if (file != NULL) 267 printf("%s:%d: Check ", file, line); 268 269 va_start(args, fmt); 270 vprintf(fmt, args); 271 va_end(args); 272 273 printf("... "); 274 test_print_in_color__(result_color, result_str); 275 printf("\n"); 276 test_current_already_logged__++; 277 } 278 279 return (cond != 0); 280 } 281 282 static void test_list_names__(void) 283 { 284 const struct test__ *test; 285 286 printf("Unit tests:\n"); 287 for (test = &test_list__[0]; test->func != NULL; test++) 288 printf(" %s\n", test->name); 289 } 290 291 static const struct test__ *test_by_name__(const char *name) 292 { 293 const struct test__ *test; 294 295 for (test = &test_list__[0]; test->func != NULL; test++) { 296 if (strcmp(test->name, name) == 0) 297 return test; 298 } 299 300 return NULL; 301 } 302 303 /* Call directly the given test unit function. */ 304 static int test_do_run__(const struct test__ *test) 305 { 306 test_current_unit__ = test; 307 test_current_failures__ = 0; 308 test_current_already_logged__ = 0; 309 310 if (test_verbose_level__ >= 3) { 311 test_print_in_color__(CUTEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", 312 test->name); 313 test_current_already_logged__++; 314 } else if (test_verbose_level__ >= 1) { 315 size_t n; 316 char spaces[32]; 317 318 n = test_print_in_color__(CUTEST_COLOR_DEFAULT_INTENSIVE__, 319 "Test %s... ", test->name); 320 memset(spaces, ' ', sizeof(spaces)); 321 if (n < sizeof(spaces)) 322 printf("%.*s", (int)(sizeof(spaces) - n), spaces); 323 } else { 324 test_current_already_logged__ = 1; 325 } 326 327 #ifdef __cplusplus 328 try { 329 #endif 330 331 /* This is good to do for case the test unit e.g. crashes. */ 332 fflush(stdout); 333 fflush(stderr); 334 335 test->func(); 336 337 #ifdef __cplusplus 338 } catch (std::exception &e) { 339 const char *what = e.what(); 340 if (what != NULL) 341 test_check__(0, NULL, 0, "Threw std::exception: %s", what); 342 else 343 test_check__(0, NULL, 0, "Threw std::exception"); 344 } catch (...) { 345 test_check__(0, NULL, 0, "Threw an exception"); 346 } 347 #endif 348 349 if (test_verbose_level__ >= 3) { 350 switch (test_current_failures__) { 351 case 0: 352 test_print_in_color__(CUTEST_COLOR_GREEN_INTENSIVE__, 353 " All conditions have passed.\n\n"); 354 break; 355 case 1: 356 test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, 357 " One condition has FAILED.\n\n"); 358 break; 359 default: 360 test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, 361 " %d conditions have FAILED.\n\n", 362 test_current_failures__); 363 break; 364 } 365 } else if (test_verbose_level__ >= 1 && test_current_failures__ == 0) { 366 printf("[ "); 367 test_print_in_color__(CUTEST_COLOR_GREEN_INTENSIVE__, "OK"); 368 printf(" ]\n"); 369 } 370 371 test_current_unit__ = NULL; 372 return (test_current_failures__ == 0) ? 0 : -1; 373 } 374 375 #if defined(CUTEST_UNIX__) || defined(CUTEST_WIN__) 376 /* Called if anything goes bad in cutest, or if the unit test ends in other 377 * way then by normal returning from its function (e.g. exception or some 378 * abnormal child process termination). */ 379 static void test_error__(const char *fmt, ...) 380 { 381 va_list args; 382 383 if (test_verbose_level__ == 0) 384 return; 385 386 if (test_verbose_level__ <= 2 && !test_current_already_logged__ && 387 test_current_unit__ != NULL) { 388 printf("[ "); 389 test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, "FAILED"); 390 printf(" ]\n"); 391 } 392 393 if (test_verbose_level__ >= 2) { 394 test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, " Error: "); 395 va_start(args, fmt); 396 vprintf(fmt, args); 397 va_end(args); 398 printf("\n"); 399 } 400 } 401 #endif 402 403 /* Trigger the unit test. If possible (and not suppressed) it starts a child 404 * process who calls test_do_run__(), otherwise it calls test_do_run__() 405 * directly. */ 406 static void test_run__(const struct test__ *test) 407 { 408 int failed = 1; 409 410 test_current_unit__ = test; 411 test_current_already_logged__ = 0; 412 413 if (!test_no_exec__) { 414 #if defined(CUTEST_UNIX__) 415 416 pid_t pid; 417 int exit_code; 418 419 pid = fork(); 420 if (pid == (pid_t)-1) { 421 test_error__("Cannot fork. %s [%d]", strerror(errno), errno); 422 failed = 1; 423 } else if (pid == 0) { 424 /* Child: Do the test. */ 425 failed = (test_do_run__(test) != 0); 426 exit(failed ? 1 : 0); 427 } else { 428 /* Parent: Wait until child terminates and analyze its exit code. */ 429 waitpid(pid, &exit_code, 0); 430 if (WIFEXITED(exit_code)) { 431 switch (WEXITSTATUS(exit_code)) { 432 case 0: 433 failed = 0; 434 break; /* test has passed. */ 435 case 1: /* noop */ 436 break; /* "normal" failure. */ 437 default: 438 test_error__("Unexpected exit code [%d]", 439 WEXITSTATUS(exit_code)); 440 } 441 } else if (WIFSIGNALED(exit_code)) { 442 char tmp[32]; 443 const char *signame; 444 switch (WTERMSIG(exit_code)) { 445 case SIGINT: 446 signame = "SIGINT"; 447 break; 448 case SIGHUP: 449 signame = "SIGHUP"; 450 break; 451 case SIGQUIT: 452 signame = "SIGQUIT"; 453 break; 454 case SIGABRT: 455 signame = "SIGABRT"; 456 break; 457 case SIGKILL: 458 signame = "SIGKILL"; 459 break; 460 case SIGSEGV: 461 signame = "SIGSEGV"; 462 break; 463 case SIGILL: 464 signame = "SIGILL"; 465 break; 466 case SIGTERM: 467 signame = "SIGTERM"; 468 break; 469 default: 470 snprintf(tmp, sizeof(tmp), "signal %d", 471 WTERMSIG(exit_code)); 472 signame = tmp; 473 break; 474 } 475 test_error__("Test interrupted by %s", signame); 476 } else { 477 test_error__("Test ended in an unexpected way [%d]", exit_code); 478 } 479 } 480 481 #elif defined(CUTEST_WIN__) 482 483 char buffer[512] = { 0 }; 484 STARTUPINFOA startupInfo = { 0 }; 485 PROCESS_INFORMATION processInfo; 486 DWORD exitCode; 487 488 /* Windows has no fork(). So we propagate all info into the child 489 * through a command line arguments. */ 490 _snprintf(buffer, sizeof(buffer) - 1, 491 "%s --no-exec --no-summary --verbose=%d --color=%s -- \"%s\"", 492 test_argv0__, test_verbose_level__, 493 test_colorize__ ? "always" : "never", test->name); 494 startupInfo.cb = sizeof(STARTUPINFO); 495 if (CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, 496 &startupInfo, &processInfo)) { 497 WaitForSingleObject(processInfo.hProcess, INFINITE); 498 GetExitCodeProcess(processInfo.hProcess, &exitCode); 499 CloseHandle(processInfo.hThread); 500 CloseHandle(processInfo.hProcess); 501 failed = (exitCode != 0); 502 } else { 503 test_error__("Cannot create unit test subprocess [%ld].", 504 GetLastError()); 505 failed = 1; 506 } 507 508 #else 509 510 /* A platform where we don't know how to run child process. */ 511 failed = (test_do_run__(test) != 0); 512 513 #endif 514 515 } else { 516 /* Child processes suppressed through --no-exec. */ 517 failed = (test_do_run__(test) != 0); 518 } 519 520 test_current_unit__ = NULL; 521 522 test_stat_run_units__++; 523 if (failed) 524 test_stat_failed_units__++; 525 } 526 527 #if defined(CUTEST_WIN__) 528 /* Callback for SEH events. */ 529 static LONG CALLBACK test_exception_filter__(EXCEPTION_POINTERS *ptrs) 530 { 531 test_error__("Unhandled SEH exception %08lx at %p.", 532 ptrs->ExceptionRecord->ExceptionCode, 533 ptrs->ExceptionRecord->ExceptionAddress); 534 fflush(stdout); 535 fflush(stderr); 536 return EXCEPTION_EXECUTE_HANDLER; 537 } 538 #endif 539 540 static void test_help__(void) 541 { 542 printf("Usage: %s [options] [test...]\n", test_argv0__); 543 printf("Run the specified unit tests; or if the option '--skip' is used, " 544 "run all\n"); 545 printf("tests in the suite but those listed. By default, if no tests are " 546 "specified\n"); 547 printf("on the command line, all unit tests in the suite are run.\n"); 548 printf("\n"); 549 printf("Options:\n"); 550 printf( 551 " -s, --skip Execute all unit tests but the listed ones\n"); 552 printf(" --no-exec Do not execute unit tests as child " 553 "processes\n"); 554 printf( 555 " --no-summary Suppress printing of test results summary\n"); 556 printf(" -l, --list List unit tests in the suite and exit\n"); 557 printf(" -v, --verbose Enable more verbose output\n"); 558 printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); 559 printf(" 0 ... Be silent\n"); 560 printf(" 1 ... Output one line per test (and " 561 "summary)\n"); 562 printf(" 2 ... As 1 and failed conditions (this " 563 "is default)\n"); 564 printf(" 3 ... As 1 and all conditions (and " 565 "extended summary)\n"); 566 printf(" --color=WHEN Enable colorized output (WHEN is one of " 567 "'auto', 'always', 'never')\n"); 568 printf(" -h, --help Display this help and exit\n"); 569 printf("\n"); 570 test_list_names__(); 571 } 572 573 int main(int argc, char **argv) 574 { 575 const struct test__ **tests = NULL; 576 int i, j, n = 0; 577 int seen_double_dash = 0; 578 579 test_argv0__ = argv[0]; 580 581 #if defined CUTEST_UNIX__ 582 test_colorize__ = isatty(STDOUT_FILENO); 583 #elif defined CUTEST_WIN__ 584 test_colorize__ = _isatty(_fileno(stdout)); 585 #else 586 test_colorize__ = 0; 587 #endif 588 589 /* Parse options */ 590 for (i = 1; i < argc; i++) { 591 if (seen_double_dash || argv[i][0] != '-') { 592 tests = (const struct test__ **)realloc( 593 (void *)tests, (n + 1) * sizeof(const struct test__ *)); 594 if (tests == NULL) { 595 fprintf(stderr, "Out of memory.\n"); 596 exit(2); 597 } 598 tests[n] = test_by_name__(argv[i]); 599 if (tests[n] == NULL) { 600 fprintf(stderr, "%s: Unrecognized unit test '%s'\n", argv[0], 601 argv[i]); 602 fprintf(stderr, "Try '%s --list' for list of unit tests.\n", 603 argv[0]); 604 exit(2); 605 } 606 n++; 607 } else if (strcmp(argv[i], "--") == 0) { 608 seen_double_dash = 1; 609 } else if (strcmp(argv[i], "--help") == 0 || 610 strcmp(argv[i], "-h") == 0) { 611 test_help__(); 612 exit(0); 613 } else if (strcmp(argv[i], "--verbose") == 0 || 614 strcmp(argv[i], "-v") == 0) { 615 test_verbose_level__++; 616 } else if (strncmp(argv[i], "--verbose=", 10) == 0) { 617 test_verbose_level__ = atoi(argv[i] + 10); 618 } else if (strcmp(argv[i], "--color=auto") == 0) { 619 /* noop (set from above) */ 620 } else if (strcmp(argv[i], "--color=always") == 0 || 621 strcmp(argv[i], "--color") == 0) { 622 test_colorize__ = 1; 623 } else if (strcmp(argv[i], "--color=never") == 0) { 624 test_colorize__ = 0; 625 } else if (strcmp(argv[i], "--skip") == 0 || 626 strcmp(argv[i], "-s") == 0) { 627 test_skip_mode__ = 1; 628 } else if (strcmp(argv[i], "--no-exec") == 0) { 629 test_no_exec__ = 1; 630 } else if (strcmp(argv[i], "--no-summary") == 0) { 631 test_no_summary__ = 1; 632 } else if (strcmp(argv[i], "--list") == 0 || 633 strcmp(argv[i], "-l") == 0) { 634 test_list_names__(); 635 exit(0); 636 } else { 637 fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[i]); 638 fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); 639 exit(2); 640 } 641 } 642 643 #if defined(CUTEST_WIN__) 644 SetUnhandledExceptionFilter(test_exception_filter__); 645 #endif 646 647 /* Count all test units */ 648 test_count__ = 0; 649 for (i = 0; test_list__[i].func != NULL; i++) 650 test_count__++; 651 652 /* Run the tests */ 653 if (n == 0) { 654 /* Run all tests */ 655 for (i = 0; test_list__[i].func != NULL; i++) 656 test_run__(&test_list__[i]); 657 } else if (!test_skip_mode__) { 658 /* Run the listed tests */ 659 for (i = 0; i < n; i++) 660 test_run__(tests[i]); 661 } else { 662 /* Run all tests except those listed */ 663 for (i = 0; test_list__[i].func != NULL; i++) { 664 int want_skip = 0; 665 for (j = 0; j < n; j++) { 666 if (tests[j] == &test_list__[i]) { 667 want_skip = 1; 668 break; 669 } 670 } 671 if (!want_skip) 672 test_run__(&test_list__[i]); 673 } 674 } 675 676 /* Write a summary */ 677 if (!test_no_summary__ && test_verbose_level__ >= 1) { 678 test_print_in_color__(CUTEST_COLOR_DEFAULT_INTENSIVE__, "\nSummary:\n"); 679 680 if (test_verbose_level__ >= 3) { 681 printf(" Count of all unit tests: %4d\n", test_count__); 682 printf(" Count of run unit tests: %4d\n", 683 test_stat_run_units__); 684 printf(" Count of failed unit tests: %4d\n", 685 test_stat_failed_units__); 686 printf(" Count of skipped unit tests: %4d\n", 687 test_count__ - test_stat_run_units__); 688 } 689 690 if (test_stat_failed_units__ == 0) { 691 test_print_in_color__(CUTEST_COLOR_GREEN_INTENSIVE__, 692 " SUCCESS: All unit tests have passed.\n"); 693 } else { 694 test_print_in_color__( 695 CUTEST_COLOR_RED_INTENSIVE__, 696 " FAILED: %d of %d unit tests have failed.\n", 697 test_stat_failed_units__, test_stat_run_units__); 698 } 699 } 700 701 if (tests != NULL) 702 free((void *)tests); 703 704 return (test_stat_failed_units__ == 0) ? 0 : 1; 705 } 706 707 #endif /* #ifndef TEST_NO_MAIN */ 708 709 #ifdef __cplusplus 710 } /* extern "C" */ 711 #endif 712 713 #endif /* #ifndef CUTEST_H__ */