inc/ctest.h (354 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef CTEST_H #define CTEST_H #ifdef __cplusplus #include <cstddef> #include <cstring> #include <cstdio> #include <csetjmp> #include <setjmp.h> /* Some compilers do not want to play by the standard, specifically ARM CC */ #include <stdio.h> /* Some compilers do not want to play by the standard, specifically ARM CC */ #define C_LINKAGE "C" #define C_LINKAGE_PREFIX extern "C" #else #include <stdbool.h> #include <stddef.h> #include <string.h> #include <stdio.h> #include <setjmp.h> #include <wchar.h> #define C_LINKAGE #define C_LINKAGE_PREFIX #endif // The pragma on the next line tells iwyu (https://github.com/include-what-you-use/include-what-you-use) to count symbols from macro_utils.h // as exported, so that when using the testrunnerswitcher APIs we do not get false positives #include "macro_utils/macro_utils.h" // IWYU pragma: export #include "c_logging/logger.h" #if defined _MSC_VER #include "ctest_windows.h" #define CTEST_USE_STDINT #if _MSC_VER < 1900 /*https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/snprintf-snprintf-snprintf-l-snwprintf-snwprintf-l?view=vs-2019 says snprintf is C99 compliant since VS 2015*/ /*https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 says VS 2015 is 1900*/ /*so for all the "old" VSes use _snprintf*/ #define snprintf _snprintf #endif #elif defined __cplusplus #define CTEST_USE_STDINT #elif defined __STDC_VERSION__ #if (__STDC_VERSION__ >= 199901L) #define CTEST_USE_STDINT #else C_LINKAGE_PREFIX int snprintf(char * s, size_t n, const char * format, ...); #endif #endif #if defined _MSC_VER && _MSC_VER <= 1700 #pragma warning(disable: 4054 4127 4510 4512 4610) /* MSC 1500 (VS2008) incorrectly fires this */ #endif #if defined CTEST_USE_STDINT #include <stdint.h> #endif #ifdef __cplusplus extern "C" { #endif #ifdef USE_COLORING #define CTEST_ANSI_COLOR_RED "\x1b[31m" #define CTEST_ANSI_COLOR_GREEN "\x1b[32m" #define CTEST_ANSI_COLOR_YELLOW "\x1b[33m" #define CTEST_ANSI_COLOR_RESET "\x1b[0m" #else #define CTEST_ANSI_COLOR_RED "" #define CTEST_ANSI_COLOR_GREEN "" #define CTEST_ANSI_COLOR_YELLOW "" #define CTEST_ANSI_COLOR_RESET "" #endif typedef void(*TEST_FUNC)(void); #define CTEST_FUNCTION_TYPE_VALUES \ CTEST_BEGIN_SUITE, \ CTEST_END_SUITE, \ CTEST_TEST_FUNCTION, \ CTEST_TEST_SUITE_INITIALIZE, \ CTEST_TEST_SUITE_CLEANUP, \ CTEST_TEST_FUNCTION_INITIALIZE, \ CTEST_TEST_FUNCTION_CLEANUP MU_DEFINE_ENUM(CTEST_FUNCTION_TYPE, CTEST_FUNCTION_TYPE_VALUES) #define TEST_RESULT_VALUES \ TEST_SUCCESS, \ TEST_FAILED, \ TEST_NOT_EXECUTED \ MU_DEFINE_ENUM(TEST_RESULT, TEST_RESULT_VALUES) typedef struct TEST_FUNCTION_DATA_TAG { const TEST_FUNC TestFunction; const char* TestFunctionName; const void* const NextTestFunctionData; TEST_RESULT* const TestResult; const CTEST_FUNCTION_TYPE FunctionType; } TEST_FUNCTION_DATA; #define EXPAND_1(A) A extern const TEST_FUNCTION_DATA* g_CurrentTestFunction; extern jmp_buf g_ExceptionJump; #ifndef CTEST_CUSTOM_TEST_SUITE_INITIALIZE_CODE #define CTEST_CUSTOM_TEST_SUITE_INITIALIZE_CODE(funcName) #endif #ifndef CTEST_CUSTOM_TEST_SUITE_CLEANUP_CODE #define CTEST_CUSTOM_TEST_SUITE_CLEANUP_CODE(funcName) #endif #ifndef CTEST_CUSTOM_TEST_FUNCTION_INITIALIZE_CODE #define CTEST_CUSTOM_TEST_FUNCTION_INITIALIZE_CODE(funcName) #endif #ifndef CTEST_CUSTOM_TEST_FUNCTION_CLEANUP_CODE #define CTEST_CUSTOM_TEST_FUNCTION_CLEANUP_CODE(funcName) #endif #ifndef CTEST_CUSTOM_TEST_FUNCTION_CODE #define CTEST_CUSTOM_TEST_FUNCTION_CODE(funcName) #endif #define CTEST_BEGIN_TEST_SUITE(testSuiteName) \ C_LINKAGE_PREFIX const int TestListHead_Begin_##testSuiteName = 0; \ static const TEST_FUNCTION_DATA MU_C2(TestFunctionData, MU_C1(__COUNTER__)) = { NULL, NULL, NULL, NULL, CTEST_BEGIN_SUITE }; \ #define CTEST_FUNCTION(funcName) \ static void funcName(void); \ static TEST_RESULT funcName##_TestResult; \ static const TEST_FUNCTION_DATA MU_C2(TestFunctionData, MU_C1(MU_INC(__COUNTER__))) = \ { funcName, #funcName, &MU_C2(TestFunctionData, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), &funcName##_TestResult, CTEST_TEST_FUNCTION }; \ CTEST_CUSTOM_TEST_FUNCTION_CODE(funcName) \ static void funcName(void) #define CTEST_CALL_FIXTURE(A) \ A(); #define CTEST_SUITE_INITIALIZE(funcName, ...) \ static void TestSuiteInitialize(void); \ static const TEST_FUNCTION_DATA MU_C2(TestFunctionData, MU_C1(MU_INC(__COUNTER__))) = \ { TestSuiteInitialize, "TestSuiteInitialize", &MU_C2(TestFunctionData, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), NULL, CTEST_TEST_SUITE_INITIALIZE }; \ CTEST_CUSTOM_TEST_SUITE_INITIALIZE_CODE(funcName) \ static void TestSuiteInitialize_user(void); \ static void TestSuiteInitialize(void) \ { \ MU_IF(MU_COUNT_ARG(__VA_ARGS__),MU_FOR_EACH_1(CTEST_CALL_FIXTURE, __VA_ARGS__),) \ TestSuiteInitialize_user(); \ } \ static void TestSuiteInitialize_user(void) #define CTEST_SUITE_CLEANUP(funcName, ...) \ static void TestSuiteCleanup(void); \ static const TEST_FUNCTION_DATA MU_C2(TestFunctionData, MU_C1(MU_INC(__COUNTER__))) = \ { &TestSuiteCleanup, "TestSuiteCleanup", &MU_C2(TestFunctionData, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), NULL, CTEST_TEST_SUITE_CLEANUP }; \ CTEST_CUSTOM_TEST_SUITE_CLEANUP_CODE(funcName) \ static void TestSuiteCleanup_user(void); \ static void TestSuiteCleanup(void) \ { \ TestSuiteCleanup_user(); \ MU_IF(MU_COUNT_ARG(__VA_ARGS__),MU_FOR_EACH_1(CTEST_CALL_FIXTURE, __VA_ARGS__),) \ } \ static void TestSuiteCleanup_user(void) #define CTEST_FUNCTION_INITIALIZE(funcName, ...) \ static void TestFunctionInitialize(void); \ static const TEST_FUNCTION_DATA MU_C2(TestFunctionData, MU_C1(MU_INC(__COUNTER__))) = \ { TestFunctionInitialize, "TestFunctionInitialize", &MU_C2(TestFunctionData, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), NULL, CTEST_TEST_FUNCTION_INITIALIZE }; \ CTEST_CUSTOM_TEST_FUNCTION_INITIALIZE_CODE(funcName) \ static void TestFunctionInitialize_user(void); \ static void TestFunctionInitialize(void) \ { \ MU_IF(MU_COUNT_ARG(__VA_ARGS__),MU_FOR_EACH_1(CTEST_CALL_FIXTURE, __VA_ARGS__),) \ TestFunctionInitialize_user(); \ } \ static void TestFunctionInitialize_user(void) #define CTEST_FUNCTION_CLEANUP(funcName, ...) \ static void TestFunctionCleanup(void); \ static const TEST_FUNCTION_DATA MU_C2(TestFunctionData, MU_C1(MU_INC(__COUNTER__))) = \ { &TestFunctionCleanup, "TestFunctionCleanup", &MU_C2(TestFunctionData, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), NULL, CTEST_TEST_FUNCTION_CLEANUP }; \ CTEST_CUSTOM_TEST_FUNCTION_CLEANUP_CODE(funcName) \ static void TestFunctionCleanup_user(void); \ static void TestFunctionCleanup(void) \ { \ TestFunctionCleanup_user(); \ MU_IF(MU_COUNT_ARG(__VA_ARGS__),MU_FOR_EACH_1(CTEST_CALL_FIXTURE, __VA_ARGS__),) \ } \ static void TestFunctionCleanup_user(void) #define CTEST_END_TEST_SUITE(testSuiteName) \ C_LINKAGE_PREFIX const TEST_FUNCTION_DATA TestListHead_##testSuiteName = { NULL, NULL, &MU_C2(TestFunctionData, MU_C1(MU_DEC(__COUNTER__))), NULL, CTEST_END_SUITE }; \ #define PRINT_MY_ARG_2(A) #define PRINT_MY_ARG_1(A) \ A += #if defined(_MSC_VER) && defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL #define PRINT_SECOND_ARG(argCount, B) \ MU_C2(PRINT_MY_ARG_,argCount) MU_LPAREN B ) #else #define PRINT_SECOND_ARG(argCount, B) \ MU_C2(PRINT_MY_ARG_,argCount) (B) #endif #define FIRST_ARG(A, ...) A #define CTEST_RUN_TEST_SUITE(...) \ do \ { \ extern C_LINKAGE const TEST_FUNCTION_DATA MU_C2(TestListHead_,FIRST_ARG(__VA_ARGS__)); \ MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)),MU_FOR_EACH_1_COUNTED(PRINT_SECOND_ARG, __VA_ARGS__),) RunTests(&MU_C2(TestListHead_, FIRST_ARG(__VA_ARGS__)), MU_TOSTRING(FIRST_ARG(__VA_ARGS__)), false); \ } while ((void)0,0) #define CTEST_RUN_TEST_SUITE_WITH_LEAK_CHECK_RETRIES(...) \ do \ { \ extern C_LINKAGE const TEST_FUNCTION_DATA MU_C2(TestListHead_,FIRST_ARG(__VA_ARGS__)); \ MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)),MU_FOR_EACH_1_COUNTED(PRINT_SECOND_ARG, __VA_ARGS__),) RunTests(&MU_C2(TestListHead_, FIRST_ARG(__VA_ARGS__)), MU_TOSTRING(FIRST_ARG(__VA_ARGS__)), true); \ } while ((void)0,0) typedef char* char_ptr; typedef wchar_t* wchar_ptr; typedef void* void_ptr; typedef long double long_double; typedef unsigned long unsigned_long; extern C_LINKAGE char* ctest_sprintf_char(const char* format, ...); extern C_LINKAGE void ctest_sprintf_free(char* string); #define CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(type) \ extern C_LINKAGE void MU_C2(type,_AssertAreEqual)(type left, type right, char* ctest_message, int line_no); \ extern C_LINKAGE void MU_C2(type,_AssertAreNotEqual)(type left, type right, char* ctest_message, int line_no); #define CTEST_EQUALITY_ASSERT_IMPL_FOR_TYPE(type, check_for_is_equal, line_no) \ char expectedString[1024]; \ char actualString[1024]; \ MU_C2(type,_ToString)(expectedString, sizeof(expectedString), left); \ MU_C2(type,_ToString)(actualString, sizeof(actualString), right); \ if (!!MU_C2(type,_Compare)(left, right) == check_for_is_equal) \ { \ LogError(" Assert failed in line %d %s Expected: %s, Actual: %s\n", line_no, (ctest_message == NULL) ? "" : ctest_message, expectedString, actualString); \ ctest_sprintf_free(ctest_message); \ if (g_CurrentTestFunction != NULL) *g_CurrentTestFunction->TestResult = TEST_FAILED; \ do_jump(&g_ExceptionJump, expectedString, actualString); \ } \ ctest_sprintf_free(ctest_message); #define CTEST_DEFINE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(type, qualifier) \ MU_SUPPRESS_WARNING(4505) /* warning C4505: 'xxx_AssertAreNotEqual': unreferenced local function has been removed */ \ qualifier void MU_C2(type,_AssertAreEqual)(type left, type right, char* ctest_message, int line_no) \ { \ CTEST_EQUALITY_ASSERT_IMPL_FOR_TYPE(type, true, line_no) \ } \ qualifier void MU_C2(type,_AssertAreNotEqual)(type left, type right, char* ctest_message, int line_no) \ { \ CTEST_EQUALITY_ASSERT_IMPL_FOR_TYPE(type, false, line_no) \ } // This macro expands the function signature for the user type's // custom Compare function. It also defines the AssertAreEqual // and AssertAreNotEqual functions for this type to avoid requiring // the user to manually call CTEST_DEFINE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE. // Forward declarations for the Compare and ToString functions are // added because the assertion function calls them and their declarations // need to exist. #define CTEST_COMPARE(toStringType, cType) \ typedef cType toStringType; \ static int MU_C2(toStringType,_Compare)(toStringType left, toStringType right); \ static void MU_C2(toStringType,_ToString)(char* string, size_t bufferSize, cType value); \ CTEST_DEFINE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(toStringType, static) \ static int MU_C2(toStringType,_Compare)(toStringType left, toStringType right) #define CTEST_TO_STRING(toStringType, cType, string, bufferSize, value) \ static void MU_C2(toStringType,_ToString)(char* string, size_t bufferSize, cType value) // these are generic macros for formatting the optional message // they can be used in all the ASSERT macros without repeating the code over and over again #define GET_MESSAGE_FORMATTED(format, ...) \ MU_IF(MU_COUNT_ARG(__VA_ARGS__), ctest_sprintf_char(format, __VA_ARGS__);(void)(0 && printf(format, __VA_ARGS__)), ctest_sprintf_char(format)); #define GET_MESSAGE_FORMATTED_EMPTY(...) \ NULL #define GET_MESSAGE(...) \ MU_IF(MU_COUNT_ARG(__VA_ARGS__), GET_MESSAGE_FORMATTED, GET_MESSAGE_FORMATTED_EMPTY)(__VA_ARGS__) void do_jump(jmp_buf *exceptionJump, const volatile void* expected, const volatile void* actual); #define CTEST_ASSERT_ARE_EQUAL(type, A, B, ...) \ do \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ MU_C2(type,_AssertAreEqual)((type)(A), (type)(B), ctest_message, __LINE__); \ } while (0) #define CTEST_ASSERT_ARE_NOT_EQUAL(type, A, B, ...) \ do \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ MU_C2(type,_AssertAreNotEqual)((type)(A), (type)(B), ctest_message, __LINE__); \ } while (0) #define CTEST_ASSERT_IS_NULL(value, ...) \ do \ { \ const void* copy_of_value = (void*)(value); \ if (copy_of_value != NULL) \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ LogError(" Assert failed in line %d: NULL expected, actual: 0x%p. %s\n", __LINE__, copy_of_value, (ctest_message == NULL) ? "" : ctest_message); \ ctest_sprintf_free(ctest_message); \ if (g_CurrentTestFunction != NULL) *g_CurrentTestFunction->TestResult = TEST_FAILED; \ do_jump(&g_ExceptionJump, "expected it to be NULL (actual is the value)", copy_of_value); \ } \ } \ while(0) #define CTEST_ASSERT_IS_NOT_NULL(value, ...) \ do \ { \ const void* copy_of_value = (void*)(value); \ if (copy_of_value == NULL) \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ LogError(" Assert failed in line %d: non-NULL expected. %s\n", __LINE__, (ctest_message == NULL) ? "" : ctest_message); \ ctest_sprintf_free(ctest_message); \ if (g_CurrentTestFunction != NULL) *g_CurrentTestFunction->TestResult = TEST_FAILED; \ do_jump(&g_ExceptionJump, "expected it not to be NULL (actual is value)", copy_of_value); \ } \ }while(0) #define CTEST_ASSERT_IS_TRUE(expression, ...) \ do { \ int expression_is_false = ((expression)==0);/*one evaluation per argument*/ \ if (expression_is_false) \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ LogError(" Assert failed in line %d: Expression should be true: %s. %s\n", __LINE__, #expression, (ctest_message == NULL) ? "" : ctest_message); \ ctest_sprintf_free(ctest_message); \ if (g_CurrentTestFunction != NULL) *g_CurrentTestFunction->TestResult = TEST_FAILED; \ do_jump(&g_ExceptionJump, "expected it to be true", "but it wasn't"); \ } \ }while(0) #define CTEST_ASSERT_IS_FALSE(expression, ...) \ do { \ int expression_is_true = ((expression)!=0);/*one evaluation per argument*/ \ if (expression_is_true) \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ LogError(" Assert failed in line %d: Expression should be false: %s. %s\n", __LINE__, #expression, (ctest_message == NULL) ? "" : ctest_message); \ ctest_sprintf_free(ctest_message); \ if (g_CurrentTestFunction != NULL) *g_CurrentTestFunction->TestResult = TEST_FAILED; \ do_jump(&g_ExceptionJump, "expected it to be false", "but it was true"); \ } \ }while(0) #define CTEST_ASSERT_FAIL(...) \ do \ { \ char* ctest_message = GET_MESSAGE(__VA_ARGS__); \ LogError(" Assert failed in line %d: %s \n" , __LINE__, (ctest_message == NULL) ? "" : ctest_message); \ ctest_sprintf_free(ctest_message); \ if (g_CurrentTestFunction != NULL) *g_CurrentTestFunction->TestResult = TEST_FAILED; \ do_jump(&g_ExceptionJump, (void*)"nothing expected, 100% fail", (void*)"nothing actual, 100% fail"); \ } \ while(0) extern C_LINKAGE void bool_AssertAreEqual(int left, int right, char* ctest_message, int line_no); extern C_LINKAGE void _Bool_AssertAreEqual(int left, int right, char* ctest_message, int line_no); extern C_LINKAGE void bool_AssertAreNotEqual(int left, int right, char* ctest_message, int line_no); extern C_LINKAGE void _Bool_AssertAreNotEqual(int left, int right, char* ctest_message, int line_no); CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(int) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(char) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(short) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(long) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(size_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(float) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(double) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(long_double) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(char_ptr) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(wchar_ptr) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(void_ptr) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(unsigned_long) #if defined CTEST_USE_STDINT CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(uint8_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(int8_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(uint16_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(int16_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(uint32_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(int32_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(uint64_t) CTEST_DECLARE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(int64_t) #endif #define CTEST_DEFINE_ENUM_TYPE_COMMON(enum_name, ...) \ static void MU_C2(enum_name, _ToString)(char* dest, size_t bufferSize, enum_name enumValue) \ { \ (void)snprintf(dest, bufferSize, "%s", MU_ENUM_TO_STRING(MU_C2(enum_name,_for_ctest), enumValue)); \ } \ static int MU_C2(enum_name, _Compare)(enum_name left, enum_name right) \ { \ return left != right; \ } \ CTEST_DEFINE_EQUALITY_ASSERTION_FUNCTIONS_FOR_TYPE(enum_name, static) // this macro expands to the needed _ToString and _Compare functions for an enum, // while using the macro utils ENUM_TO_STRING #define CTEST_DEFINE_ENUM_TYPE(enum_name, ...) \ typedef enum_name MU_C2(enum_name,_for_ctest); \ MU_DEFINE_ENUM_STRINGS(MU_C2(enum_name,_for_ctest), __VA_ARGS__); \ CTEST_DEFINE_ENUM_TYPE_COMMON(enum_name, __VA_ARGS__) // this macro expands to the needed _ToString and _Compare functions for an enum without INVALID entry, // while using the macro utils ENUM_TO_STRING #define CTEST_DEFINE_ENUM_TYPE_WITHOUT_INVALID(enum_name, ...) \ typedef enum_name MU_C2(enum_name,_for_ctest); \ MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(MU_C2(enum_name,_for_ctest), __VA_ARGS__); \ CTEST_DEFINE_ENUM_TYPE_COMMON(enum_name, __VA_ARGS__) extern C_LINKAGE size_t RunTests(const TEST_FUNCTION_DATA* testListHead, const char* testSuiteName, bool useLeakCheckRetries); #ifdef __cplusplus } #endif #endif