simple-test =========== [simple-test.h](simple-test.h) is a single header file which implements quick-and-dirty unit testing for C. robust unit testing suites for C exist, but they require a degree of boilerplate which can be prohibitive for their use in spare-time projects. if you're writing something for fun, lots of boilerplate defeats the purpose of writing it. simple-test is made to be as easy to use as possible: include the header, compile, and run. _note:_ although it has no dependencies outside the standard library, this project makes heavy use of `_Generic`, so C11 support is required. brief summary ------------- test suites are to be written as a single .c file separate from the body of functionality to be tested. include the functionality needed (including headers and linking against objects) to produce an executable file from the .c file, then run the executable to test. simple-test internally defines a `main` function, so none is required, and the `argc` and `argv` arguments are made available to all tests. example ------- the following example file is available as [example.c](doc/example.c), which you can compile and play with yourself. ```C #include "../simple-test.h" /* global variables, functions, and includes must come before BEGIN_TEST */ char *global_s; /* a teardown function is called by TESTs which include USE_TEARDOWN. the call * occurs either after an ASSERT fails or after the TEST is finished. * * defining one is optional, but, if used, REGISTER_TEARDOWN must appear * between BEGIN_TEST and the first TEST statement. * * here i'm using it to free memory that's alloced inside tests below */ void teardown(void) { free(global_s); } /* must appear before an (optional) REGISTER_TEARDOWN and all TESTs */ BEGIN_TEST /* if used, must appear before first TEST and after BEGIN_TEST */ REGISTER_TEARDOWN(teardown); /* run a test. provided description must be a string literal */ TEST("basic assertion") { /* ASSERT fails if passed some sort of non-true value (0, false, NULL) */ ASSERT(1); } TEST("basic not assertion") { bool b = false; ASSERT_NOT(b); } TEST("boolean comparison") { bool a = false, b = true; /* fail if parameters are not equal */ ASSERT_EQ(a, b); } TEST("type mismatch") { char a = 'a'; int i = 97; char *b = NULL; /* for convenience's sake, when presented with mismatched types, assertions * try to resolve them in a way that's most likely to match the * programmer's intentions. here 'i' is interpreted as a char */ ASSERT_EQ(a, i); /* if there isn't a straightforward comparison to make, though (as is the * case here, with a 'char' and 'char *'), a type mismatch error occurs */ ASSERT_EQ(a, b); } TEST("ECHO example") { int i; /* ECHO can be used to neatly report information during a run */ if (true) ECHO("loop until i not less than 1"); for (i = 0; i < 2; i++) { /* it takes printf format strings and variable args as well */ ECHO("i == %d", i); ASSERT_LT(i, 1); } } TEST("string comparison") { char *s = "test"; global_s = strdup("test"); /* USE_TEARDOWN; tells this test to call the previously defined teardown * function on exiting (successfully or otherwise) */ USE_TEARDOWN; /* strings are compared by content, so this assertion succeeds */ ASSERT_EQ(s, global_s); } TEST("pointer comparison") { char *s = "test"; global_s = strdup("test"); USE_TEARDOWN; /* you can cast parameters in order to use a different type of comparison. * here i'm casting the 'char *' to 'void *' so assertion performs a * pointer comparison, which fails */ ASSERT_EQ((void*)s, (void*)global_s); } /* must come after all TESTs */ END_TEST ``` on compiling and running this file, the output will look something like this: ![run 1](doc/run_01.png) an error occurs because the types passed to the second assertion in the "type mismatch" test are... well, mismatched. assertions do their best to accommodate mismatched types when it makes sense to do so (e.g. comparing a signed and unsigned integer), but there is no meaningful comparison to be made between a `char` and a `char *`, so the error is returned. if that assertion is commented out: ```C /* if there isn't a straightforward comparison to make, though (as is the * case here, with a 'char' and 'char *'), a type mismatch error occurs */ /* ASSERT_EQ(a, b); */ ``` and the tests re-run, the output would look like this: ![run 2](doc/run_02.png) as you can see, there are multiple failing assertions. their failures are reported and a total count of failing tests printed. if all of these failing asserts are commented out and the tests re-run, the output looks like this: ![run 3](doc/run_03.png) hooray! ^_^ interface --------- simple-test's interface consists of a series of preprocessor macros: | **NAME** | **DESCRIPTION** | |---------:|:----------------| | **BEGIN_TEST** | must be included once, after includes and global declarations and before the first `TEST` statement | | **END_TEST** | must be included once, after the final `TEST` statement | | **TEST()**{} | declare a test, described by `description`, which consists of statements between the {} | | **REGISTER_TEARDOWN()** | optionally included after `BEGIN_TEST` and before the first `TEST` statement. registers function `func`, which must be of type `void func(void)`, as a teardown function to be called after subscribing tests exit | | **USE_TEARDOWN;** | if included within a `TEST` body (before any `ASSERT` statements), the designated `TEST` will call the function registered with `REGISTER_TEARDOWN` on terminating | | **ECHO(<...>)** | print a formatted description of the state within the current test | | **ASSERT<_condition>()** | assert, on either one or two `args`, that `_condition` is true | valid assertions are: | **NAME** | **DESCRIPTION** | |---------:|:----------------| | **ASSERT()** | assert that the value of `a` is not a "not" value, i.e. NULL, 0, or '\0' | | **ASSERT_NOT()** | assert that the value of `a` is a "not" value | | **ASSERT_EQ(, )** | fail if the values of `a` and `b` are not equal | | **ASSERT_NEQ(, )** | fail if the values of `a` and `b` are equal | | **ASSERT_GT(, )** | fail if the value of `a` is not greater the value of `b` | | **ASSERT_GEQ(, )** | fail if the value of `a` is not greater than or equal to the value of `b` | | **ASSERT_LT(, )** | fail if the value of `a` is not less than the value of `b` | | **ASSERT_LEQ(, )** | fail if the value of `a` is not less than or equal to the value of `b` | note ---- simple-test.h internally uses macros prefixed with `SIMPLE_TEST_` and functions and variables prefixed with `simple_test_`. these should not be used directly.