size_t RunTests()

in src/ctest.c [28:284]


size_t RunTests(const TEST_FUNCTION_DATA* testListHead, const char* testSuiteName, bool useLeakCheckRetries)
{
#ifdef USE_VLD
    VLD_UINT initial_leak_count = VLDGetLeaksCount();
#endif
    size_t totalTestCount = 0;
    size_t failedTestCount = 0;
    const TEST_FUNCTION_DATA* currentTestFunction = (const TEST_FUNCTION_DATA*)testListHead->NextTestFunctionData;
    const TEST_FUNCTION_DATA* testSuiteInitialize = NULL;
    const TEST_FUNCTION_DATA* testSuiteCleanup = NULL;
    const TEST_FUNCTION_DATA* testFunctionInitialize = NULL;
    const TEST_FUNCTION_DATA* testFunctionCleanup = NULL;
    int testSuiteInitializeFailed = 0;

#if defined _MSC_VER && !defined(WINCE)
    _set_abort_behavior(_CALL_REPORTFAULT, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);

    // Set output mode to handle virtual terminal sequences
    HANDLE std_out_handle = GetStdHandle(STD_OUTPUT_HANDLE);
    bool SetConsoleMode_succeeded = false;
    DWORD console_mode_initial = 0;
    if (std_out_handle == INVALID_HANDLE_VALUE)
    {
        LogWarning("Error getting console handle, no coloring available. GetLastError()=%" PRIx32 "", (uint32_t)GetLastError());
    }
    else
    {
        if (!GetConsoleMode(std_out_handle, &console_mode_initial))
        {
            LogWarning("Error getting console mode, no coloring available. GetLastError()=%" PRIx32 "", (uint32_t)GetLastError());
        }
        else
        {
            if (!SetConsoleMode(std_out_handle, console_mode_initial | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
            {
                LogWarning("Error setting console mode, no coloring available. GetLastError()=%" PRIx32 "", (uint32_t)GetLastError());
            }
            else
            {
                SetConsoleMode_succeeded = true;
            }
        }
    }

#endif

    g_CurrentTestFunction = NULL;

    LogInfo(" === Executing test suite %s ===", testSuiteName);

    while (currentTestFunction->TestFunction != NULL)
    {
        if (currentTestFunction->FunctionType == CTEST_TEST_FUNCTION_INITIALIZE)
        {
            testFunctionInitialize = currentTestFunction;
        }

        if (currentTestFunction->FunctionType == CTEST_TEST_FUNCTION_CLEANUP)
        {
            testFunctionCleanup = currentTestFunction;
        }

        if (currentTestFunction->FunctionType == CTEST_TEST_SUITE_INITIALIZE)
        {
            testSuiteInitialize = currentTestFunction;
        }

        if (currentTestFunction->FunctionType == CTEST_TEST_SUITE_CLEANUP)
        {
            testSuiteCleanup = currentTestFunction;
        }

        currentTestFunction = (TEST_FUNCTION_DATA*)currentTestFunction->NextTestFunctionData;
    }

    if (testSuiteInitialize != NULL)
    {
        if (setjmp(g_ExceptionJump) == 0)
        {
            testSuiteInitialize->TestFunction();
        }
        else
        {
            testSuiteInitializeFailed = 1;
            LogInfo("TEST_SUITE_INITIALIZE failed - suite ending");
        }
    }

    if (testSuiteInitializeFailed == 1)
    {
        /* print results */
        LogInfo(CTEST_ANSI_COLOR_RED "0 tests ran, ALL failed, NONE succeeded." CTEST_ANSI_COLOR_RESET);
        failedTestCount = 1;
    }
    else
    {
        unsigned int is_test_runner_ok = 1;

        currentTestFunction = (const TEST_FUNCTION_DATA*)testListHead->NextTestFunctionData;
        while (currentTestFunction->TestFunction != NULL)
        {
            if (currentTestFunction->FunctionType == CTEST_TEST_FUNCTION)
            {
                if (is_test_runner_ok == 1)
                {
                    int testFunctionInitializeFailed = 0;

                    if (testFunctionInitialize != NULL)
                    {
                        if (setjmp(g_ExceptionJump) == 0)
                        {
                            testFunctionInitialize->TestFunction();
                        }
                        else
                        {
                            testFunctionInitializeFailed = 1;
                            LogInfo(CTEST_ANSI_COLOR_RED "TEST_FUNCTION_INITIALIZE failed - next TEST_FUNCTION will fail" CTEST_ANSI_COLOR_RESET);
                        }
                    }

                    if (testFunctionInitializeFailed)
                    {
                        *currentTestFunction->TestResult = TEST_FAILED;
                        LogInfo(CTEST_ANSI_COLOR_YELLOW "Not executing test %s ..." CTEST_ANSI_COLOR_RESET, currentTestFunction->TestFunctionName);
                    }
                    else
                    {
                        LogInfo("Executing test %s ...", currentTestFunction->TestFunctionName);

                        g_CurrentTestFunction = currentTestFunction;

                        if (setjmp(g_ExceptionJump) == 0)
                        {
                            currentTestFunction->TestFunction();
                        }
                        else
                        {
                            /*can only get here if there was a longjmp called while executing currentTestFunction->TestFunction();*/
                            /*we don't do anything*/
                        }
                        g_CurrentTestFunction = NULL;/*g_CurrentTestFunction is limited to actually executing a TEST_FUNCTION, otherwise it should be NULL*/

                        /*in the case when the cleanup can assert... have to prepare the long jump*/
                        if (setjmp(g_ExceptionJump) == 0)
                        {
                            if (testFunctionCleanup != NULL)
                            {
                                testFunctionCleanup->TestFunction();
                            }
                        }
                        else
                        {
                            /* this is a fatal error, if we got a fail in cleanup we can't do much */
                            *currentTestFunction->TestResult = TEST_FAILED;
                            is_test_runner_ok = 0;
                        }
                    }
                }
                else
                {
                    *currentTestFunction->TestResult = TEST_NOT_EXECUTED;
                }

                if (*currentTestFunction->TestResult == TEST_FAILED)
                {
                    failedTestCount++;
                    LogInfo(CTEST_ANSI_COLOR_RED "Test %s result = !!! FAILED !!!" CTEST_ANSI_COLOR_RESET, currentTestFunction->TestFunctionName);
                }
                else if (*currentTestFunction->TestResult == TEST_NOT_EXECUTED)
                {
                    failedTestCount++;
                    LogInfo(CTEST_ANSI_COLOR_YELLOW "Test %s ... SKIPPED due to a failure in test function cleanup. " CTEST_ANSI_COLOR_RESET, currentTestFunction->TestFunctionName);
                }
                else
                {
                    LogInfo(CTEST_ANSI_COLOR_GREEN "Test %s result = Succeeded." CTEST_ANSI_COLOR_RESET, currentTestFunction->TestFunctionName);
                }
                totalTestCount++;
            }

            currentTestFunction = (TEST_FUNCTION_DATA*)currentTestFunction->NextTestFunctionData;
        }

        if (setjmp(g_ExceptionJump) == 0)
        {
            if (testSuiteCleanup != NULL)
            {
                testSuiteCleanup->TestFunction();
            }
        }
        else
        {
            /*only get here when testSuiteCleanup did asserted*/
            /*should fail the tests*/
            LogInfo(CTEST_ANSI_COLOR_RED "TEST_SUITE_CLEANUP failed - all tests are marked as failed" CTEST_ANSI_COLOR_RESET);
            failedTestCount = (totalTestCount > 0) ? totalTestCount : SIZE_MAX;
        }

        /* print results */
        LogInfo("%s%d tests ran, %d failed, %d succeeded." CTEST_ANSI_COLOR_RESET, (failedTestCount > 0) ? (CTEST_ANSI_COLOR_RED) : (CTEST_ANSI_COLOR_GREEN), (int)totalTestCount, (int)failedTestCount, (int)(totalTestCount - failedTestCount));
    }

#if defined _MSC_VER && !defined(WINCE)
    if (std_out_handle != INVALID_HANDLE_VALUE)
    {
        if (SetConsoleMode_succeeded)
        {
            /*revert console to initial state*/
            if (!SetConsoleMode(std_out_handle, console_mode_initial))
            {
                LogWarning("Error resetting console mode to initial value of %" PRIx32 ". GetLastError()=%" PRIx32 "", console_mode_initial, GetLastError());
            }
        }
    }
#endif

#ifdef USE_VLD
    if (useLeakCheckRetries)
    {
        if (failedTestCount == 0)
        {
            VLD_UINT leaks_count = VLDGetLeaksCount();
            do
            {
                if (leaks_count - initial_leak_count > 0)
                {
                    LogWarning("Leaks count is %u (initial count %u)", leaks_count, initial_leak_count);
                    Sleep(5000);
                    VLD_UINT new_leaks_count = VLDGetLeaksCount();

                    if (new_leaks_count == leaks_count)
                    {
                        // Leaks are stable so there must be real leaks
                        LogWarning("Leaks count has not changed...");
                        break;
                    }
                    else
                    {
                        // Leaks have gone down, try again
                        leaks_count = new_leaks_count;
                    }
                }
                else
                {
                    // No leaks, we are done
                    break;
                }
            } while (1);
        }
    }
    failedTestCount = (failedTestCount > 0) ? failedTestCount : (size_t)(-(int)(VLDGetLeaksCount() - initial_leak_count));
#else
    (void)useLeakCheckRetries;
#endif

    return failedTestCount;
}