summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md239
1 files changed, 164 insertions, 75 deletions
diff --git a/README.md b/README.md
index 0e6692a..ab05a60 100644
--- a/README.md
+++ b/README.md
@@ -1,96 +1,185 @@
lily
====
-**lily** is a testing library for C. It provides both basic assertions as well as mocking.
+**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` and `lily-test.c` into your
-source code.
+To incorporate lily into a project, just copy `lily-test.h` into
+your source code.
usage
-----
-Tests are any function with the signature `void (void)`, and are run with the macro `lily_run_test(test)`.
-Tests are intended to be run inside of suite functions, which have the same signature as a test.
-To add suite setup or teardown operations, just do them inside the suite function.
-Suites are run with the `lily_run_suite(suite)` macro.
-
-Tests can contain assertions! You should not try to use these assertions outside of tests run with
-`lily_run_test()` because you'll probably just crash your program. Tests, if they fail, generate
-error messages that detail the mismatch between their expected and actual results and also indicate
-the file and line of the failed assertion. On some compilers `__FILE__` is the complete path of the
-file (i.e. all the way from root), so lily lets you trim N characters from the front of it by
-defining a `SOURCE_PATH_SIZE` macro whose value is N. (I usually define this from within my build tool
-so that it'll still work if other people download the software).
-
-The following assertions are available:
-
-* `lily_assert_true(bool)`
-* `lily_assert_false(bool)`
-* `lily_assert_not_null(void *)`
-* `lily_assert_null(void *)`
-* `lily_assert_ptr_equal(void *, void *)`
-* `lily_assert_ptr_not_equal(void *, void *)`
-* `lily_assert_int_equal(int, int)`
-* `lily_assert_int_not_equal(int, int)`
-* `lily_assert_float_equal(float, float, float epsilon)`
-* `lily_assert_float_not_equal(float, float, float epsilon)`
-* `lily_assert_string_equal(char *, char *)`
-* `lily_assert_string_not_equal(char *, char *)`
-* `lily_assert_memory_equal(void *, void *, size_t size)`
-* `lily_assert_memory_not_equal(void *, void *, size_t size)`
-
-lily also provides some features to make mocking functions a little nicer. First,
-there is the `lily_queue_t` object, which contains a FIFO queue for arbitrary data.
-They support the following operations:
-
-* `lily_queue_t * lily_queue_create()` - create a new queue
-* `lily_queue_destroy(lily_queue_t *)` - destroy a queue
-* `lily_enqueue(lily_queue_t*, TYPE, value)` - enqueue some value. Any type can be queued; just be careful to dequeue the same data types in the same order!
-* `lily_dequeue(lily_queue_t*, TYPE, ptr)` - pops a value from the queue into a pointer of **an appropriate type**. Be careful not to dequeue data types in the same order you queued them!
-
-This system is intended to allow you to control the behavior/return types of mock functions
-by queueing things before a call that the mock will dequeue.
-
-lily also provides the `lily_mock_t` object, which uses these queues to easily store the arguments
-of a call to a function.
-
-* `lily_mock_t * lily_mock_create()` - create a new mock object
-* `lily_mock_destroy(lily_mock_t *)` - destroy a mock object
-* `lily_mock_call(lily_mock_t*, struct lily_mock_arg_t*)` - store arguments (see below)
-* `lily_get_call(lily_mock_t*, struct lily_mock_arg_t*, int)` - store arguments (see below)
-
-Each `struct lily_mock_arg_t` contains a size and void pointer, so storing a set of arguments
-looks something like this:
+A basic unit test file looks like this:
+
```
-lily_mock_t *m; // initialized somewhere
-void some_func(int a, const char *b)
+#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")
{
- struct lily_mock_arg_t args[] = {
- { sizeof(int), &a },
- { sizeof(const char *), &b },
- };
- lily_mock_call(m, args);
- /* ... */
+ CHECK_LT(3.0, 4.0, "%0.2f");
}
+#include LILY_PUSH_TEST()
+
+
+#define LILY_FILE_END
+#include LILY_REGISTER_TESTS()
```
-Retrieving calls looks much the same, except the pointers in the array are to destinations
-and not sources. (You also need to specify the zero-indexed number of the call -- that's the `int` argument).
+
+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.
-~ the future ~
---------------
+**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.
-Some features I'd like to add:
+**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.
-A `prepare-tests.sh` script that will automatically scan
-the given source root for all files matching `*.test.c` and qenerate a header file,
-main function, and Makefile to run all of the functions matching `void ()` in those
-test files. (If you want a utility function or something, just make it `static`).
+**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.
-It might be nice to switch to an stb-style header-only library, where the contents of
-`lily-test.c` are put inside of a macro gate, and the user just has to `#define LILY_IMPLEMENTATION`
-in some file that includes `lily-test.h`.
+**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.