diff --git a/README.md b/README.md index 0ba77ea5..e6157fc1 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,13 @@ test.h A very simple, light weight, header only C unit test framework. -## Features +# Features * Easy to use, no dependencies, no setup needed. * Keep test cases close to the code they test. * Automatic registration of the test cases. +# Usage ## Setup Just include the header @@ -17,8 +18,6 @@ Just include the header #include "test.h" ``` -To enable the test cases in your code, compile your program with `-DUNIT_TEST`. - ## Defining test cases ```c @@ -31,20 +30,8 @@ TEST_CASE(test_case_name) { ## Run the test cases -In your main function, call `run_tests`: +Build your program with `-DUNIT_TEST`, then run your program with `./program --unittest`. -```c -int main(int argc, char *const *argv) { - // necessary setup code - // ... - if (!run_tests(argc, argv, NULL)) { - // test failed - abort(); - } - // cleanup - // usual code -} +# Hooks -``` - -Then, run your program with `./program --unittest`. +If you define a function `test_h_unittest_setup`, it will be called before any test cases are run. diff --git a/examples/trivial_tests/test_test1.c b/examples/trivial_tests/test_test1.c index a2da9fe8..16d2b6fb 100644 --- a/examples/trivial_tests/test_test1.c +++ b/examples/trivial_tests/test_test1.c @@ -3,7 +3,7 @@ #include "test.h" int main(int argc, char *const *argv) { - run_tests(argc, argv, NULL); + printf("Hello World!\n"); } TEST_CASE(test1) { diff --git a/test.h b/test.h index 99c7db35..b784a2d4 100644 --- a/test.h +++ b/test.h @@ -60,50 +60,55 @@ struct test_file_metadata __attribute__((weak)) * test_file_head; SET_FAILURE(#a " != " #b); \ return; \ } \ - } while(0) + } while (0) -#define TEST_CASE(_name) \ - static void __test_h_##_name(struct test_case_metadata *, \ - struct test_file_metadata *); \ - static struct test_file_metadata __test_h_file; \ - static struct test_case_metadata __test_h_meta_##_name = { \ - .name = #_name, \ - .fn = __test_h_##_name, \ - }; \ - static void __attribute__((constructor)) __test_h_##_name##_register(void) { \ - __test_h_meta_##_name.next = __test_h_file.tests; \ - __test_h_file.tests = &__test_h_meta_##_name; \ - if (!__test_h_file.registered) { \ - __test_h_file.name = __FILE__; \ - __test_h_file.next = test_file_head; \ - test_file_head = &__test_h_file; \ - __test_h_file.registered = true; \ - } \ - } \ - static void __test_h_##_name(struct test_case_metadata *metadata, \ +#define TEST_CASE(_name) \ + static void __test_h_##_name(struct test_case_metadata *, \ + struct test_file_metadata *); \ + static struct test_file_metadata __test_h_file; \ + static struct test_case_metadata __test_h_meta_##_name = { \ + .name = #_name, \ + .fn = __test_h_##_name, \ + }; \ + static void __attribute__((constructor(101))) __test_h_##_name##_register(void) { \ + __test_h_meta_##_name.next = __test_h_file.tests; \ + __test_h_file.tests = &__test_h_meta_##_name; \ + if (!__test_h_file.registered) { \ + __test_h_file.name = __FILE__; \ + __test_h_file.next = test_file_head; \ + test_file_head = &__test_h_file; \ + __test_h_file.registered = true; \ + } \ + } \ + static void __test_h_##_name(struct test_case_metadata *metadata, \ struct test_file_metadata *file_metadata) +extern void __attribute__((weak)) (*test_h_unittest_setup)(void); /// Run defined tests, return true if all tests succeeds /// @param[out] tests_run if not NULL, set to whether tests were run -static inline bool __attribute__((unused)) -run_tests(int argc, char *const *argv, bool *tests_run) { +static inline void __attribute__((constructor(102))) run_tests(void) { bool should_run = false; - if (tests_run) { - *tests_run = false; - } - for (int i = 0; i < argc; i++) { - if (strcmp(argv[i], "--unittest") == 0) { + FILE *cmdlinef = fopen("/proc/self/cmdline", "r"); + char *arg = NULL; + int arglen; + fscanf(cmdlinef, "%ms%n", &arg, &arglen); + fclose(cmdlinef); + for (char *pos = arg; pos < arg + arglen; pos += strlen(pos) + 1) { + if (strcmp(pos, "--unittest") == 0) { should_run = true; break; } } + free(arg); + if (!should_run) { - return true; + return; } - if (tests_run) { - *tests_run = true; + if (&test_h_unittest_setup) { + test_h_unittest_setup(); } + struct test_file_metadata *i = test_file_head; int failed = 0, success = 0; while (i) { @@ -129,7 +134,7 @@ run_tests(int argc, char *const *argv, bool *tests_run) { int total = failed + success; fprintf(stderr, "Test results: passed %d/%d, failed %d/%d\n", success, total, failed, total); - return failed == 0; + exit(failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } #else @@ -146,11 +151,4 @@ run_tests(int argc, char *const *argv, bool *tests_run) { (void)(a); \ (void)(b) -static inline bool __attribute__((unused)) -run_tests(int argc, char *const *argv, bool *tests_run) { - if (tests_run) { - *tests_run = false; - } - return true; -} #endif