cutest.h 22.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * CUTest -- C/C++ Unit Test facility
 * <http://github.com/mity/cutest>
 *
 * Copyright (c) 2013-2017 Martin Mitas
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
13
 *
14 15
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
16
 *
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef CUTEST_H__
#define CUTEST_H__

/************************
 *** Public interface ***
 ************************/

/* By default, <cutest.h> provides the main program entry point (function
 * main()). However, if the test suite is composed of multiple source files
 * which include <cutest.h>, then this causes a problem of multiple main()
 * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all
 * compilation units but one.
 */

/* Macro to specify list of unit tests in the suite.
 * The unit test implementation MUST provide list of unit tests it implements
 * with this macro:
 *
 *   TEST_LIST = {
 *       { "test1_name", test1_func_ptr },
 *       { "test2_name", test2_func_ptr },
 *       ...
 *       { 0 }
 *   };
 *
 * The list specifies names of each test (must be unique) and pointer to
 * a function implementing it. The function does not take any arguments
 * and has no return values, i.e. every test function has tp be compatible
 * with this prototype:
 *
 *   void test_func(void);
 */
58
#define TEST_LIST const struct test__ test_list__[]
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

/* Macros for testing whether an unit test succeeds or fails. These macros
 * can be used arbitrarily in functions implementing the unit tests.
 *
 * If any condition fails throughout execution of a test, the test fails.
 *
 * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows
 * also to specify an error message to print out if the condition fails.
 * (It expects printf-like format string and its parameters). The macros
 * return non-zero (condition passes) or 0 (condition fails).
 *
 * That can be useful when more conditions should be checked only if some
 * preceding condition passes, as illustrated in this code snippet:
 *
 *   SomeStruct* ptr = allocate_some_struct();
 *   if(TEST_CHECK(ptr != NULL)) {
 *       TEST_CHECK(ptr->member1 < 100);
 *       TEST_CHECK(ptr->member2 > 200);
 *   }
 */
79 80 81
#define TEST_CHECK_(cond, ...)                                                 \
    test_check__((cond), __FILE__, __LINE__, __VA_ARGS__)
#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond)
82 83 84 85 86 87 88 89 90 91 92 93 94

/**********************
 *** Implementation ***
 **********************/

/* The unit test files should not rely on anything below. */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
95 96 97 98 99 100
#define CUTEST_UNIX__ 1
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
101 102 103
#endif

#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
104 105 106
#define CUTEST_WIN__ 1
#include <windows.h>
#include <io.h>
107 108 109
#endif

#ifdef __cplusplus
110
#include <exception>
111 112 113 114 115 116
#endif

/* Note our global private identifiers end with '__' to mitigate risk of clash
 * with the unit tests implementation. */

#ifdef __cplusplus
117
extern "C" {
118 119 120
#endif

struct test__ {
121
    const char *name;
122 123 124 125 126
    void (*func)(void);
};

extern const struct test__ test_list__[];

127
int test_check__(int cond, const char *file, int line, const char *fmt, ...);
128 129 130

#ifndef TEST_NO_MAIN

131
static char *test_argv0__ = NULL;
132 133 134 135 136 137 138 139
static int test_count__ = 0;
static int test_no_exec__ = 0;
static int test_no_summary__ = 0;
static int test_skip_mode__ = 0;

static int test_stat_failed_units__ = 0;
static int test_stat_run_units__ = 0;

140
static const struct test__ *test_current_unit__ = NULL;
141 142 143 144 145
static int test_current_already_logged__ = 0;
static int test_verbose_level__ = 2;
static int test_current_failures__ = 0;
static int test_colorize__ = 0;

146 147 148 149 150 151
#define CUTEST_COLOR_DEFAULT__ 0
#define CUTEST_COLOR_GREEN__ 1
#define CUTEST_COLOR_RED__ 2
#define CUTEST_COLOR_DEFAULT_INTENSIVE__ 3
#define CUTEST_COLOR_GREEN_INTENSIVE__ 4
#define CUTEST_COLOR_RED_INTENSIVE__ 5
152

153
static size_t test_print_in_color__(int color, const char *fmt, ...)
154 155 156 157 158 159 160 161
{
    va_list args;
    char buffer[256];
    size_t n;

    va_start(args, fmt);
    vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);
162
    buffer[sizeof(buffer) - 1] = '\0';
163

164
    if (!test_colorize__) {
165 166 167 168 169
        return printf("%s", buffer);
    }

#if defined CUTEST_UNIX__
    {
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        const char *col_str;
        switch (color) {
        case CUTEST_COLOR_GREEN__:
            col_str = "\033[0;32m";
            break;
        case CUTEST_COLOR_RED__:
            col_str = "\033[0;31m";
            break;
        case CUTEST_COLOR_GREEN_INTENSIVE__:
            col_str = "\033[1;32m";
            break;
        case CUTEST_COLOR_RED_INTENSIVE__:
            col_str = "\033[1;30m";
            break;
        case CUTEST_COLOR_DEFAULT_INTENSIVE__:
            col_str = "\033[1m";
            break;
        default:
            col_str = "\033[0m";
            break;
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
        }
        printf("%s", col_str);
        n = printf("%s", buffer);
        printf("\033[0m");
        return n;
    }
#elif defined CUTEST_WIN__
    {
        HANDLE h;
        CONSOLE_SCREEN_BUFFER_INFO info;
        WORD attr;

        h = GetStdHandle(STD_OUTPUT_HANDLE);
        GetConsoleScreenBufferInfo(h, &info);

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        switch (color) {
        case CUTEST_COLOR_GREEN__:
            attr = FOREGROUND_GREEN;
            break;
        case CUTEST_COLOR_RED__:
            attr = FOREGROUND_RED;
            break;
        case CUTEST_COLOR_GREEN_INTENSIVE__:
            attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
            break;
        case CUTEST_COLOR_RED_INTENSIVE__:
            attr = FOREGROUND_RED | FOREGROUND_INTENSITY;
            break;
        case CUTEST_COLOR_DEFAULT_INTENSIVE__:
            attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED |
                   FOREGROUND_INTENSITY;
            break;
        default:
            attr = 0;
            break;
225
        }
226
        if (attr != 0)
227 228 229 230 231 232 233 234 235 236 237
            SetConsoleTextAttribute(h, attr);
        n = printf("%s", buffer);
        SetConsoleTextAttribute(h, info.wAttributes);
        return n;
    }
#else
    n = printf("%s", buffer);
    return n;
#endif
}

238
int test_check__(int cond, const char *file, int line, const char *fmt, ...)
239 240 241 242 243
{
    const char *result_str;
    int result_color;
    int verbose_level;

244
    if (cond) {
245 246 247 248
        result_str = "ok";
        result_color = CUTEST_COLOR_GREEN__;
        verbose_level = 3;
    } else {
249
        if (!test_current_already_logged__ && test_current_unit__ != NULL) {
250 251 252 253 254 255 256 257 258 259 260
            printf("[ ");
            test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, "FAILED");
            printf(" ]\n");
        }
        result_str = "failed";
        result_color = CUTEST_COLOR_RED__;
        verbose_level = 2;
        test_current_failures__++;
        test_current_already_logged__++;
    }

261
    if (test_verbose_level__ >= verbose_level) {
262 263 264 265 266
        size_t n = 0;
        va_list args;

        printf("  ");

267
        if (file != NULL)
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
            n += printf("%s:%d: Check ", file, line);

        va_start(args, fmt);
        n += vprintf(fmt, args);
        va_end(args);

        printf("... ");
        test_print_in_color__(result_color, result_str);
        printf("\n");
        test_current_already_logged__++;
    }

    return (cond != 0);
}

283
static void test_list_names__(void)
284
{
285
    const struct test__ *test;
286 287

    printf("Unit tests:\n");
288
    for (test = &test_list__[0]; test->func != NULL; test++)
289 290 291
        printf("  %s\n", test->name);
}

292
static const struct test__ *test_by_name__(const char *name)
293
{
294
    const struct test__ *test;
295

296 297
    for (test = &test_list__[0]; test->func != NULL; test++) {
        if (strcmp(test->name, name) == 0)
298 299 300 301 302 303 304
            return test;
    }

    return NULL;
}

/* Call directly the given test unit function. */
305
static int test_do_run__(const struct test__ *test)
306 307 308 309 310
{
    test_current_unit__ = test;
    test_current_failures__ = 0;
    test_current_already_logged__ = 0;

311 312 313
    if (test_verbose_level__ >= 3) {
        test_print_in_color__(CUTEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n",
                              test->name);
314
        test_current_already_logged__++;
315
    } else if (test_verbose_level__ >= 1) {
316 317 318
        size_t n;
        char spaces[32];

319 320
        n = test_print_in_color__(CUTEST_COLOR_DEFAULT_INTENSIVE__,
                                  "Test %s... ", test->name);
321
        memset(spaces, ' ', sizeof(spaces));
322 323
        if (n < sizeof(spaces))
            printf("%.*s", (int)(sizeof(spaces) - n), spaces);
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
    } else {
        test_current_already_logged__ = 1;
    }

#ifdef __cplusplus
    try {
#endif

        /* This is good to do for case the test unit e.g. crashes. */
        fflush(stdout);
        fflush(stderr);

        test->func();

#ifdef __cplusplus
339 340 341
    } catch (std::exception &e) {
        const char *what = e.what();
        if (what != NULL)
342 343 344
            test_check__(0, NULL, 0, "Threw std::exception: %s", what);
        else
            test_check__(0, NULL, 0, "Threw std::exception");
345
    } catch (...) {
346 347 348 349
        test_check__(0, NULL, 0, "Threw an exception");
    }
#endif

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
    if (test_verbose_level__ >= 3) {
        switch (test_current_failures__) {
        case 0:
            test_print_in_color__(CUTEST_COLOR_GREEN_INTENSIVE__,
                                  "  All conditions have passed.\n\n");
            break;
        case 1:
            test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__,
                                  "  One condition has FAILED.\n\n");
            break;
        default:
            test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__,
                                  "  %d conditions have FAILED.\n\n",
                                  test_current_failures__);
            break;
365
        }
366
    } else if (test_verbose_level__ >= 1 && test_current_failures__ == 0) {
367 368 369 370 371 372 373 374 375 376 377 378 379
        printf("[   ");
        test_print_in_color__(CUTEST_COLOR_GREEN_INTENSIVE__, "OK");
        printf("   ]\n");
    }

    test_current_unit__ = NULL;
    return (test_current_failures__ == 0) ? 0 : -1;
}

#if defined(CUTEST_UNIX__) || defined(CUTEST_WIN__)
/* Called if anything goes bad in cutest, or if the unit test ends in other
 * way then by normal returning from its function (e.g. exception or some
 * abnormal child process termination). */
380
static void test_error__(const char *fmt, ...)
381 382 383
{
    va_list args;

384
    if (test_verbose_level__ == 0)
385 386
        return;

387 388
    if (test_verbose_level__ <= 2 && !test_current_already_logged__ &&
        test_current_unit__ != NULL) {
389 390 391 392 393
        printf("[ ");
        test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, "FAILED");
        printf(" ]\n");
    }

394
    if (test_verbose_level__ >= 2) {
395 396 397 398 399 400 401 402 403 404 405 406
        test_print_in_color__(CUTEST_COLOR_RED_INTENSIVE__, "  Error: ");
        va_start(args, fmt);
        vprintf(fmt, args);
        va_end(args);
        printf("\n");
    }
}
#endif

/* Trigger the unit test. If possible (and not suppressed) it starts a child
 * process who calls test_do_run__(), otherwise it calls test_do_run__()
 * directly. */
407
static void test_run__(const struct test__ *test)
408 409 410 411 412 413
{
    int failed = 1;

    test_current_unit__ = test;
    test_current_already_logged__ = 0;

414
    if (!test_no_exec__) {
415 416 417 418 419 420
#if defined(CUTEST_UNIX__)

        pid_t pid;
        int exit_code;

        pid = fork();
421
        if (pid == (pid_t)-1) {
422 423
            test_error__("Cannot fork. %s [%d]", strerror(errno), errno);
            failed = 1;
424
        } else if (pid == 0) {
425 426 427 428 429 430
            /* Child: Do the test. */
            failed = (test_do_run__(test) != 0);
            exit(failed ? 1 : 0);
        } else {
            /* Parent: Wait until child terminates and analyze its exit code. */
            waitpid(pid, &exit_code, 0);
431 432 433 434 435 436 437 438 439 440
            if (WIFEXITED(exit_code)) {
                switch (WEXITSTATUS(exit_code)) {
                case 0:
                    failed = 0;
                    break; /* test has passed. */
                case 1:    /* noop */
                    break; /* "normal" failure. */
                default:
                    test_error__("Unexpected exit code [%d]",
                                 WEXITSTATUS(exit_code));
441
                }
442
            } else if (WIFSIGNALED(exit_code)) {
443
                char tmp[32];
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
                const char *signame;
                switch (WTERMSIG(exit_code)) {
                case SIGINT:
                    signame = "SIGINT";
                    break;
                case SIGHUP:
                    signame = "SIGHUP";
                    break;
                case SIGQUIT:
                    signame = "SIGQUIT";
                    break;
                case SIGABRT:
                    signame = "SIGABRT";
                    break;
                case SIGKILL:
                    signame = "SIGKILL";
                    break;
                case SIGSEGV:
                    signame = "SIGSEGV";
                    break;
                case SIGILL:
                    signame = "SIGILL";
                    break;
                case SIGTERM:
                    signame = "SIGTERM";
                    break;
                default:
                    sprintf(tmp, "signal %d", WTERMSIG(exit_code));
                    signame = tmp;
                    break;
474 475 476 477 478 479 480 481 482
                }
                test_error__("Test interrupted by %s", signame);
            } else {
                test_error__("Test ended in an unexpected way [%d]", exit_code);
            }
        }

#elif defined(CUTEST_WIN__)

483 484
        char buffer[512] = { 0 };
        STARTUPINFOA startupInfo = { 0 };
485 486 487 488 489
        PROCESS_INFORMATION processInfo;
        DWORD exitCode;

        /* Windows has no fork(). So we propagate all info into the child
         * through a command line arguments. */
490 491 492 493
        _snprintf(buffer, sizeof(buffer) - 1,
                  "%s --no-exec --no-summary --verbose=%d --color=%s -- \"%s\"",
                  test_argv0__, test_verbose_level__,
                  test_colorize__ ? "always" : "never", test->name);
494
        startupInfo.cb = sizeof(STARTUPINFO);
495 496
        if (CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL,
                           &startupInfo, &processInfo)) {
497 498 499 500 501 502
            WaitForSingleObject(processInfo.hProcess, INFINITE);
            GetExitCodeProcess(processInfo.hProcess, &exitCode);
            CloseHandle(processInfo.hThread);
            CloseHandle(processInfo.hProcess);
            failed = (exitCode != 0);
        } else {
503 504
            test_error__("Cannot create unit test subprocess [%ld].",
                         GetLastError());
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
            failed = 1;
        }

#else

        /* A platform where we don't know how to run child process. */
        failed = (test_do_run__(test) != 0);

#endif

    } else {
        /* Child processes suppressed through --no-exec. */
        failed = (test_do_run__(test) != 0);
    }

    test_current_unit__ = NULL;

    test_stat_run_units__++;
523
    if (failed)
524 525 526 527 528
        test_stat_failed_units__++;
}

#if defined(CUTEST_WIN__)
/* Callback for SEH events. */
529
static LONG CALLBACK test_exception_filter__(EXCEPTION_POINTERS *ptrs)
530 531 532 533 534 535 536 537 538 539
{
    test_error__("Unhandled SEH exception %08lx at %p.",
                 ptrs->ExceptionRecord->ExceptionCode,
                 ptrs->ExceptionRecord->ExceptionAddress);
    fflush(stdout);
    fflush(stderr);
    return EXCEPTION_EXECUTE_HANDLER;
}
#endif

540
static void test_help__(void)
541 542
{
    printf("Usage: %s [options] [test...]\n", test_argv0__);
543 544 545 546
    printf("Run the specified unit tests; or if the option '--skip' is used, "
           "run all\n");
    printf("tests in the suite but those listed.  By default, if no tests are "
           "specified\n");
547 548 549
    printf("on the command line, all unit tests in the suite are run.\n");
    printf("\n");
    printf("Options:\n");
550 551 552 553 554 555
    printf(
        "  -s, --skip            Execute all unit tests but the listed ones\n");
    printf("      --no-exec         Do not execute unit tests as child "
           "processes\n");
    printf(
        "      --no-summary      Suppress printing of test results summary\n");
556 557 558 559
    printf("  -l, --list            List unit tests in the suite and exit\n");
    printf("  -v, --verbose         Enable more verbose output\n");
    printf("      --verbose=LEVEL   Set verbose level to LEVEL:\n");
    printf("                          0 ... Be silent\n");
560 561 562 563 564 565 566 567
    printf("                          1 ... Output one line per test (and "
           "summary)\n");
    printf("                          2 ... As 1 and failed conditions (this "
           "is default)\n");
    printf("                          3 ... As 1 and all conditions (and "
           "extended summary)\n");
    printf("      --color=WHEN      Enable colorized output (WHEN is one of "
           "'auto', 'always', 'never')\n");
568 569 570 571 572
    printf("  -h, --help            Display this help and exit\n");
    printf("\n");
    test_list_names__();
}

573
int main(int argc, char **argv)
574
{
575
    const struct test__ **tests = NULL;
576 577 578 579 580 581 582 583 584 585 586 587 588 589
    int i, j, n = 0;
    int seen_double_dash = 0;

    test_argv0__ = argv[0];

#if defined CUTEST_UNIX__
    test_colorize__ = isatty(STDOUT_FILENO);
#elif defined CUTEST_WIN__
    test_colorize__ = _isatty(_fileno(stdout));
#else
    test_colorize__ = 0;
#endif

    /* Parse options */
590 591 592 593 594
    for (i = 1; i < argc; i++) {
        if (seen_double_dash || argv[i][0] != '-') {
            tests = (const struct test__ **)realloc(
                (void *)tests, (n + 1) * sizeof(const struct test__ *));
            if (tests == NULL) {
595 596 597 598
                fprintf(stderr, "Out of memory.\n");
                exit(2);
            }
            tests[n] = test_by_name__(argv[i]);
599 600 601 602 603
            if (tests[n] == NULL) {
                fprintf(stderr, "%s: Unrecognized unit test '%s'\n", argv[0],
                        argv[i]);
                fprintf(stderr, "Try '%s --list' for list of unit tests.\n",
                        argv[0]);
604 605 606
                exit(2);
            }
            n++;
607
        } else if (strcmp(argv[i], "--") == 0) {
608
            seen_double_dash = 1;
609 610
        } else if (strcmp(argv[i], "--help") == 0 ||
                   strcmp(argv[i], "-h") == 0) {
611 612
            test_help__();
            exit(0);
613 614
        } else if (strcmp(argv[i], "--verbose") == 0 ||
                   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 621
        } else if (strcmp(argv[i], "--color=always") == 0 ||
                   strcmp(argv[i], "--color") == 0) {
622
            test_colorize__ = 1;
623
        } else if (strcmp(argv[i], "--color=never") == 0) {
624
            test_colorize__ = 0;
625 626
        } else if (strcmp(argv[i], "--skip") == 0 ||
                   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 633
        } else if (strcmp(argv[i], "--list") == 0 ||
                   strcmp(argv[i], "-l") == 0) {
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
            test_list_names__();
            exit(0);
        } else {
            fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[i]);
            fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
            exit(2);
        }
    }

#if defined(CUTEST_WIN__)
    SetUnhandledExceptionFilter(test_exception_filter__);
#endif

    /* Count all test units */
    test_count__ = 0;
649
    for (i = 0; test_list__[i].func != NULL; i++)
650 651 652
        test_count__++;

    /* 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 661 662
            test_run__(tests[i]);
    } else {
        /* Run all tests except those listed */
663
        for (i = 0; test_list__[i].func != NULL; i++) {
664
            int want_skip = 0;
665 666
            for (j = 0; j < n; j++) {
                if (tests[j] == &test_list__[i]) {
667 668 669 670
                    want_skip = 1;
                    break;
                }
            }
671
            if (!want_skip)
672 673 674 675 676
                test_run__(&test_list__[i]);
        }
    }

    /* Write a summary */
677
    if (!test_no_summary__ && test_verbose_level__ >= 1) {
678 679
        test_print_in_color__(CUTEST_COLOR_DEFAULT_INTENSIVE__, "\nSummary:\n");

680
        if (test_verbose_level__ >= 3) {
681
            printf("  Count of all unit tests:     %4d\n", test_count__);
682 683 684 685 686 687
            printf("  Count of run unit tests:     %4d\n",
                   test_stat_run_units__);
            printf("  Count of failed unit tests:  %4d\n",
                   test_stat_failed_units__);
            printf("  Count of skipped unit tests: %4d\n",
                   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 695 696 697
            test_print_in_color__(
                CUTEST_COLOR_RED_INTENSIVE__,
                "  FAILED: %d of %d unit tests have failed.\n",
                test_stat_failed_units__, test_stat_run_units__);
698 699 700
        }
    }

701 702
    if (tests != NULL)
        free((void *)tests);
703 704 705 706

    return (test_stat_failed_units__ == 0) ? 0 : 1;
}

707
#endif /* #ifndef TEST_NO_MAIN */
708 709

#ifdef __cplusplus
710
} /* extern "C" */
711 712
#endif

713
#endif /* #ifndef CUTEST_H__ */