diff --git a/utest.h b/utest.h index 8486ea3..717b760 100644 --- a/utest.h +++ b/utest.h @@ -83,6 +83,7 @@ #if defined(_MSC_VER) #define UTEST_PRId64 "I64d" +#define UTEST_PRIu64 "I64u" #define UTEST_INLINE __forceinline #pragma section(".CRT$XCU", read) @@ -94,6 +95,7 @@ #include #define UTEST_PRId64 PRId64 +#define UTEST_PRIu64 PRIu64 #define UTEST_INLINE inline #define UTEST_INITIALIZER(f) \ @@ -139,6 +141,7 @@ utest_testcase_t *testcases; const char **testcase_names; size_t testcases_length; + const char *filter; }; #if defined(__cplusplus) @@ -307,7 +310,6 @@ } #define TESTCASE(set, name) \ - UTEST_EXTERN struct utest_state_s utest_state; \ static void utest_run_##set##_##name(int *utest_result); \ UTEST_INITIALIZER(utest_register_##set##_##name) { \ const size_t index = utest_state.testcases_length++; \ @@ -324,53 +326,73 @@ } \ void utest_run_##set##_##name(int *utest_result) +// extern to the global state utest needs to execute +UTEST_EXTERN struct utest_state_s utest_state; + +UTEST_WEAK int utest_main(int argc, const char* const argv[]); +UTEST_WEAK int utest_main(int argc, const char* const argv[]) { + size_t failed = 0; + size_t index = 0; + size_t *failed_testcases = 0; + size_t failed_testcases_length = 0; + + (void)argc; (void)argv; + printf("\033[32m[==========]\033[0m Running %" UTEST_PRIu64 " test cases.\n", + UTEST_CAST(uint64_t, utest_state.testcases_length)); + for (index = 0; index < utest_state.testcases_length; index++) { + int result = 0; + int64_t ns = 0; + printf("\033[32m[ RUN ]\033[0m %s\n", + utest_state.testcase_names[index]); + ns = utest_ns(); + utest_state.testcases[index](&result); + ns = utest_ns() - ns; + if (0 != result) { + const size_t failed_testcase_index = failed_testcases_length++; + failed_testcases = UTEST_PTR_CAST(size_t*, realloc(UTEST_PTR_CAST(void *, + failed_testcases), sizeof(size_t) * failed_testcases_length)); + failed_testcases[failed_testcase_index] = index; + failed++; + printf("\033[31m[ FAILED ]\033[0m %s (%" UTEST_PRId64 "ns)\n", + utest_state.testcase_names[index], ns); + } else { + printf("\033[32m[ OK ]\033[0m %s (%" UTEST_PRId64 "ns)\n", + utest_state.testcase_names[index], ns); + } + } + printf("\033[32m[==========]\033[0m %" UTEST_PRIu64 " test cases ran.\n", + UTEST_CAST(uint64_t, utest_state.testcases_length)); + printf("\033[32m[ PASSED ]\033[0m %" UTEST_PRIu64 " tests.\n", + UTEST_CAST(uint64_t, utest_state.testcases_length - failed)); + if (0 != failed) { + printf("\033[31m[ FAILED ]\033[0m %" UTEST_PRIu64 " tests, listed below:\n", + UTEST_CAST(uint64_t, failed)); + for (index = 0; index < failed_testcases_length; index++) { + printf("\033[31m[ FAILED ]\033[0m %s\n", + utest_state.testcase_names[failed_testcases[index]]); + } + } + free(UTEST_PTR_CAST(void *, failed_testcases)); + free(UTEST_PTR_CAST(void *, utest_state.testcases)); + free(UTEST_PTR_CAST(void *, utest_state.testcase_names)); + return UTEST_CAST(int, failed); +} + +// we need, in exactly one source file, define the global struct that will hold +// the data we need to run utest. This macro allows the user to declare the +// data without having to use the UTEST_MAIN macro, thus allowing them to write +// their own main() function. +#define UTEST_STATE() struct utest_state_s utest_state + +// define a main() function to call into utest.h and start executing tests! A +// user can optionally not use this macro, and instead define their own main() +// function and manually call utest_main. The user must, in exactly one source +// file, use the UTEST_STATE macro to declare a global struct variable that +// utest requires. #define UTEST_MAIN() \ - UTEST_EXTERN struct utest_state_s utest_state; \ - struct utest_state_s utest_state; \ - int main(void) { \ - size_t failed = 0; \ - size_t index = 0; \ - size_t *failed_testcases = 0; \ - size_t failed_testcases_length = 0; \ - printf("\033[32m[==========]\033[0m Running %u test cases.\n", \ - (unsigned)utest_state.testcases_length); \ - for (index = 0; index < utest_state.testcases_length; index++) { \ - int result = 0; \ - int64_t ns = 0; \ - printf("\033[32m[ RUN ]\033[0m %s\n", \ - utest_state.testcase_names[index]); \ - ns = utest_ns(); \ - utest_state.testcases[index](&result); \ - ns = utest_ns() - ns; \ - if (0 != result) { \ - const size_t failed_testcase_index = failed_testcases_length++; \ - failed_testcases = realloc(UTEST_PTR_CAST(void *, failed_testcases), \ - sizeof(size_t) * failed_testcases_length); \ - failed_testcases[failed_testcase_index] = index; \ - failed++; \ - printf("\033[31m[ FAILED ]\033[0m %s (%" UTEST_PRId64 "ns)\n", \ - utest_state.testcase_names[index], ns); \ - } else { \ - printf("\033[32m[ OK ]\033[0m %s (%" UTEST_PRId64 "ns)\n", \ - utest_state.testcase_names[index], ns); \ - } \ - } \ - printf("\033[32m[==========]\033[0m %u test cases ran.\n", \ - (unsigned)utest_state.testcases_length); \ - printf("\033[32m[ PASSED ]\033[0m %u tests.\n", \ - (unsigned)(utest_state.testcases_length - failed)); \ - if (0 != failed) { \ - printf("\033[31m[ FAILED ]\033[0m %u tests, listed below:\n", \ - (unsigned)failed); \ - for (index = 0; index < failed_testcases_length; index++) { \ - printf("\033[31m[ FAILED ]\033[0m %s\n", \ - utest_state.testcase_names[failed_testcases[index]]); \ - } \ - } \ - free(UTEST_PTR_CAST(void *, failed_testcases)); \ - free(UTEST_PTR_CAST(void *, utest_state.testcases)); \ - free(UTEST_PTR_CAST(void *, utest_state.testcase_names)); \ - return (int)failed; \ + UTEST_STATE(); \ + int main(int argc, const char* const argv[]) { \ + return utest_main(argc, argv); \ } #endif // SHEREDOM_UTEST_H_INCLUDED