diff --git a/utest.h b/utest.h index 2d680c5..c385c4b 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) \ @@ -136,7 +138,7 @@ typedef void (*utest_testcase_t)(int *); struct utest_test_state_s { - utest_testcase_t testcase; + utest_testcase_t func; const char *name; }; @@ -145,47 +147,75 @@ size_t tests_length; }; -#define UTEST_ASSERT(x, y, cond) \ - if (!((x)cond(y))) { \ - printf("%s:%u: Failure\n", __FILE__, __LINE__); \ - *utest_result = 1; \ - return; \ - } +#if defined(_MSC_VER) +#define UTEST_WEAK __forceinline +#else +#define UTEST_WEAK __attribute__((weak)) +#endif -#define ASSERT_TRUE(x) \ - if (!(x)) { \ - printf("%s:%u: Failure\n", __FILE__, __LINE__); \ - *utest_result = 1; \ - return; \ - } +#if defined(__cplusplus) +// if we are using c++ we can use overloaded methods (its in the language) +#define UTEST_OVERLOADABLE +#elif defined(__clang__) +// otherwise, if we are using clang with c we can use the overloadable attribute +#define UTEST_OVERLOADABLE __attribute__((overloadable)) +#endif -#define ASSERT_FALSE(x) \ - if (x) { \ - printf("%s:%u: Failure\n", __FILE__, __LINE__); \ - *utest_result = 1; \ - return; \ - } +#if defined(UTEST_OVERLOADABLE) +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { + printf("%f", UTEST_CAST(double, f)); +} -#define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, ==) -#define ASSERT_NE(x, y) UTEST_ASSERT(x, y, !=) -#define ASSERT_LT(x, y) UTEST_ASSERT(x, y, <) -#define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <=) -#define ASSERT_GT(x, y) UTEST_ASSERT(x, y, >) -#define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >=) +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { + printf("%f", d); +} -#define ASSERT_STREQ(x, y) \ - if (0 != strcmp(x, y)) { \ - printf("%s:%u: Failure\n", __FILE__, __LINE__); \ - *utest_result = 1; \ - return; \ - } +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { + printf("%Lf", d); +} -#define ASSERT_STRNE(x, y) \ - if (0 == strcmp(x, y)) { \ - printf("%s:%u: Failure\n", __FILE__, __LINE__); \ - *utest_result = 1; \ - return; \ - } +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { + printf("%d", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { + printf("%u", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { + printf("%ld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { + printf("%lu", i); +} + +// long long is a c++11 extension +// TODO: grok for c++11 version here +#if !defined(__cplusplus) +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { + printf("%lld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void +utest_type_printer(long long unsigned int i) { + printf("%llu", i); +} +#endif +#else +// we don't have the ability to print the values we got, so we create a macro to +// tell our users we can't do anything fancy +#define utest_type_printer(...) printf("undef") +#endif #define UTEST_EXPECT(x, y, cond) \ if (!((x)cond(y))) { \ @@ -196,12 +226,16 @@ #define EXPECT_TRUE(x) \ if (!(x)) { \ printf("%s:%u: Failure\n", __FILE__, __LINE__); \ + printf(" Expected : true\n"); \ + printf(" Actual : %s\n", (x) ? "true" : "false"); \ *utest_result = 1; \ } #define EXPECT_FALSE(x) \ if (x) { \ printf("%s:%u: Failure\n", __FILE__, __LINE__); \ + printf(" Expected : false\n"); \ + printf(" Actual : %s\n", (x) ? "true" : "false"); \ *utest_result = 1; \ } @@ -215,15 +249,56 @@ #define EXPECT_STREQ(x, y) \ if (0 != strcmp(x, y)) { \ printf("%s:%u: Failure\n", __FILE__, __LINE__); \ + printf(" Expected : \"%s\"\n", x); \ + printf(" Actual : \"%s\"\n", y); \ *utest_result = 1; \ } #define EXPECT_STRNE(x, y) \ if (0 == strcmp(x, y)) { \ printf("%s:%u: Failure\n", __FILE__, __LINE__); \ + printf(" Expected : \"%s\"\n", x); \ + printf(" Actual : \"%s\"\n", y); \ *utest_result = 1; \ } +#define UTEST_ASSERT(x, y, cond) \ + UTEST_EXPECT(x, y, cond); \ + if (!((x)cond(y))) { \ + return; \ + } + +#define ASSERT_TRUE(x) \ + EXPECT_TRUE(x); \ + if (!(x)) { \ + return; \ + } + +#define ASSERT_FALSE(x) \ + EXPECT_FALSE(x); \ + if (x) { \ + return; \ + } + +#define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, ==) +#define ASSERT_NE(x, y) UTEST_ASSERT(x, y, !=) +#define ASSERT_LT(x, y) UTEST_ASSERT(x, y, <) +#define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <=) +#define ASSERT_GT(x, y) UTEST_ASSERT(x, y, >) +#define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >=) + +#define ASSERT_STREQ(x, y) \ + EXPECT_STREQ(x, y); \ + if (0 != strcmp(x, y)) { \ + return; \ + } + +#define ASSERT_STRNE(x, y) \ + EXPECT_STRNE(x, y); \ + if (0 == strcmp(x, y)) { \ + return; \ + } + #define UTEST(SET, NAME) \ UTEST_EXTERN struct utest_state_s utest_state; \ static void utest_run_##SET##_##NAME(int *utest_result); \ @@ -234,7 +309,7 @@ realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ sizeof(struct utest_test_state_s) * \ utest_state.tests_length)); \ - utest_state.tests[index].testcase = &utest_run_##SET##_##NAME; \ + utest_state.tests[index].func = &utest_run_##SET##_##NAME; \ utest_state.tests[index].name = #SET "." #NAME; \ } \ void utest_run_##SET##_##NAME(int *utest_result) @@ -265,57 +340,169 @@ realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ sizeof(struct utest_test_state_s) * \ utest_state.tests_length)); \ - utest_state.tests[index].testcase = &utest_fixture_##FIXTURE##_##NAME; \ + utest_state.tests[index].func = &utest_fixture_##FIXTURE##_##NAME; \ utest_state.tests[index].name = #FIXTURE "." #NAME; \ } \ void utest_run_##FIXTURE##_##NAME(int *utest_result, struct FIXTURE *fixture) +// extern to the global state utest needs to execute +UTEST_EXTERN struct utest_state_s utest_state; + +UTEST_WEAK int utest_should_filter_test(const char *filter, + const char *testcase); +UTEST_WEAK int utest_should_filter_test(const char *filter, + const char *testcase) { + if (filter) { + const char *filter_cur = filter; + const char *testcase_cur = testcase; + const char *filter_wildcard = 0; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + // store the position of the wildcard + filter_wildcard = filter_cur; + + // skip the wildcard character + filter_cur++; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + // we found another wildcard (filter is something like *foo*) so we + // exit the current loop, and return to the parent loop to handle + // the wildcard case + break; + } else if (*filter_cur != *testcase_cur) { + // otherwise our filter didn't match, so reset it + filter_cur = filter_wildcard; + } + + // move testcase along + testcase_cur++; + + // move filter along + filter_cur++; + } + + if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { + return 0; + } + + // if the testcase has been exhausted, we don't have a match! + if ('\0' == *testcase_cur) { + return 1; + } + } else { + if (*testcase_cur != *filter_cur) { + // test case doesn't match filter + return 1; + } else { + // move our filter and testcase forward + testcase_cur++; + filter_cur++; + } + } + } + + if (('\0' != *filter_cur) || + (('\0' != *testcase_cur) && + ((filter == filter_cur) || ('*' != filter_cur[-1])))) { + // we have a mismatch! + return 1; + } + } + + return 0; +} + +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; + const char *filter = 0; + size_t ran_tests = 0; + + // loop through all arguments looking for our options + for (index = 1; index < UTEST_CAST(size_t, argc); index++) { + const char filter_str[] = "--filter="; + + if (0 < strcmp(argv[index], filter_str)) { + // user wants to filter what test cases run! + filter = argv[index] + strlen(filter_str); + } + } + + for (index = 0; index < utest_state.tests_length; index++) { + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + ran_tests++; + } + + printf("\033[32m[==========]\033[0m Running %" UTEST_PRIu64 " test cases.\n", + UTEST_CAST(uint64_t, ran_tests)); + + for (index = 0; index < utest_state.tests_length; index++) { + int result = 0; + int64_t ns = 0; + + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + printf("\033[32m[ RUN ]\033[0m %s\n", utest_state.tests[index].name); + ns = utest_ns(); + utest_state.tests[index].func(&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.tests[index].name, ns); + } else { + printf("\033[32m[ OK ]\033[0m %s (%" UTEST_PRId64 "ns)\n", + utest_state.tests[index].name, ns); + } + } + printf("\033[32m[==========]\033[0m %" UTEST_PRIu64 " test cases ran.\n", + UTEST_CAST(uint64_t, ran_tests)); + printf("\033[32m[ PASSED ]\033[0m %" UTEST_PRIu64 " tests.\n", + UTEST_CAST(uint64_t, ran_tests - 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.tests[failed_testcases[index]].name); + } + } + free(UTEST_PTR_CAST(void *, failed_testcases)); + free(UTEST_PTR_CAST(void *, utest_state.tests)); + 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_tests = 0; \ - size_t failed_tests_length = 0; \ - printf("\033[32m[==========]\033[0m Running %u test cases.\n", \ - (unsigned)utest_state.tests_length); \ - for (index = 0; index < utest_state.tests_length; index++) { \ - int result = 0; \ - int64_t ns = 0; \ - printf("\033[32m[ RUN ]\033[0m %s\n", \ - utest_state.tests[index].name); \ - ns = utest_ns(); \ - utest_state.tests[index].testcase(&result); \ - ns = utest_ns() - ns; \ - if (0 != result) { \ - const size_t failed_test_index = failed_tests_length++; \ - failed_tests = realloc(UTEST_PTR_CAST(void *, failed_tests), \ - sizeof(size_t) * failed_tests_length); \ - failed_tests[failed_test_index] = index; \ - failed++; \ - printf("\033[31m[ FAILED ]\033[0m %s (%" UTEST_PRId64 "ns)\n", \ - utest_state.tests[index].name, ns); \ - } else { \ - printf("\033[32m[ OK ]\033[0m %s (%" UTEST_PRId64 "ns)\n", \ - utest_state.tests[index].name, ns); \ - } \ - } \ - printf("\033[32m[==========]\033[0m %u test cases ran.\n", \ - (unsigned)utest_state.tests_length); \ - printf("\033[32m[ PASSED ]\033[0m %u tests.\n", \ - (unsigned)(utest_state.tests_length - failed)); \ - if (0 != failed) { \ - printf("\033[31m[ FAILED ]\033[0m %u tests, listed below:\n", \ - (unsigned)failed); \ - for (index = 0; index < failed_tests_length; index++) { \ - printf("\033[31m[ FAILED ]\033[0m %s\n", \ - utest_state.tests[failed_tests[index]].name); \ - } \ - } \ - free(UTEST_PTR_CAST(void *, failed_tests)); \ - free(UTEST_PTR_CAST(void *, utest_state.tests)); \ - return (int)failed; \ + UTEST_STATE(); \ + int main(int argc, const char *const argv[]) { \ + return utest_main(argc, argv); \ } #endif // SHEREDOM_UTEST_H_INCLUDED