lily ==== **lily** is a testing library for C. It provides assertions and clean test registration. To incorporate lily into a project, just copy `lily-test.h` into your source code. usage ----- A basic unit test file looks like this: ``` #include "lily-test.h" LILY_FILE_BEGIN(example_file) LILY_TEST("example test 1") { CHECK_EQ(1+1, 2, "%d"); } #include LILY_PUSH_TEST() LILY_TEST("example test 2") { CHECK_LT(3.0, 4.0, "%0.2f"); } #include LILY_PUSH_TEST() #define LILY_FILE_END #include LILY_REGISTER_TESTS() ``` This will define a function pointer `example_file` with signature `void ()` that, when called, will execute the two tests and display to stderr any errors that they generate. While there is more boilerplate than in comparable C++ libraries, this means that lily-test supports test auto-registration. To add a new test, just use the LILY_TEST macro as shown, and then follow the function body with `#include LILY_PUSH_TEST()` to register it. These macros will only work correctly when used between a LILY_FILE_BEGIN and LILY_FILE_END block, and only when used together; using LILY_TEST without a LILY_PUSH_TEST is probably going to throw up a ton of compile errors. ### Using a single file For very small projects, you may be able to get away with using only a single unit test file. In this case, you should set it up along the following lines: ``` #define LILY_IMPLEMENTATION #include "lily-test.h" /* any includes or code to get your tests working */ LILY_FILE_BEGIN(tests) /* test definitions... */ #define LILY_FILE_END #include LILY_REGISTER_TESTS() int main() { lily_begin(); tests(); lily_finish(); return 0; } ``` However, in most cases you will want to have multiple test files. ### Multiple test files You will need, in addition to your unit test files, a header file that contains extern declarations of the function pointers defined by your files and a main C source file that contains a main function to run all of the function pointers. A nice way to set this up is to use X-macros, like this: ``` /* tests.h */ #define TESTS \ X(suite_1) \ X(suite_2) \ X(suite_3) \ #define X(suite) extern void (*suite)(); TESTS #undef X /* tests_main.c */ #define LILY_IMPLEMENTATION #include "lily-test.h" int main() { lily_begin(); #define X(suite) suite(); TESTS #undef X lily_finish(); return 0; } ``` This is convenient, because it means that when you add a new test file you need only add a single line to the definition of the TESTS macro in tests.h and all of the other relevant code is added for you automatically. Note that exactly ONE file should define LILY_IMPLEMENTATION. I find it reasonable to make this the same file with the implementation of main but YMMV. assertions ---------- There are two different basic types of assertions: checks and requirements. Both checks and requires will mark the overall test as failed if they are false, but a require will also immediately stop executing the test in addition. ### check macros #### CHECK(x) Marks the test as failed if x is false and prints stringified x. #### CHECK_EQ(x, y, fmt) Marks the test as failed if x == y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### CHECK_NEQ(x, y, fmt) Marks the test as failed if x != y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### CHECK_LT(x, y, fmt) Marks the test as failed if x < y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### CHECK_LE(x, y, fmt) Marks the test as failed if x <= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### CHECK_GT(x, y, fmt) Marks the test as failed if x > y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### CHECK_GE(x, y, fmt) Marks the test as failed if x >= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. ### require macros #### REQUIRE(x) Marks the test as failed and immediately ends test execution if x is false and prints stringified x. #### REQUIRE_EQ(x, y, fmt) Marks the test as failed and immediately ends test execution if x == y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### REQUIRE_NEQ(x, y, fmt) Marks the test as failed and immediately ends test execution if x != y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### REQUIRE_LT(x, y, fmt) Marks the test as failed and immediately ends test execution if x < y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### REQUIRE_LE(x, y, fmt) Marks the test as failed and immediately ends test execution if x <= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### REQUIRE_GT(x, y, fmt) Marks the test as failed and immediately ends test execution if x > y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. #### REQUIRE_GE(x, y, fmt) Marks the test as failed and immediately ends test execution if x >= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt.