ccut = Clean C Unit Testing
After cloning the code
make install # install files in /opt/local
Then add these cflags for your tests
-std=c11 -lccut
If you are testing C++, just
-lccut
your_suite.c
#include "ccut.h"
// your custom eq function, must use void* for arg types
int str_eq(void* a, void* b) {
return strcmp(a, b) == 0;
}
// your custom inspect function, the type must be `void (*func)(void*)`
void str_inspect(void* a) {
fprintf(stderr, "%s", a);
}
// a macro for convenience
#define assert_str_eq(expected, actual) asser_eqf(expected, actual, str_eq, str_inspect)
void your_suite() {
ccut_test(foo1) {
assert_true(2 == 2, "wat?");
}
ccut_test(foo2) {
assert_false(1 == 2, "no way!");
}
ccut_test(bar) {
pending;
}
ccut_test(simple equal) {
assert_eq(expected, actual);
}
ccut_test(custom equal) {
assert_str_eq(expected, actual);
}
}test_runner.c
#include "ccut.h"
void your_suite();
int main (int argc, char const *argv[]) {
ccut_run_suite(your_suite);
ccut_print_stats();
return 0;
}Add ccut.h and ccut.c, then compile and run
cc std=c11 your_suite.c test_runner.c ccut.c && ./a.outEvery suite is a function of state machine, tests are run in the definition order.
Code outside the ccut_test(...){ ... } blocks will be executed n+1 times, where n is the number of tests (before_each/after_each are todos).
To compare pointers (data that failed assertions will be printed in hex format), you must hand-cast the pointer to void* first.
The header "ccut.h" should be added lastly, because some common headers may also define a macro named "test".
ccut_test(test_name) { ... }- define a test (must be put inside a void function)ccut_run_suite(your_suite)- run a test suiteccut_print_stats()- print test stats
assert_true(actual, message)- ifactual, then success, else show message and terminate current testassert_eq(expected, actual)- assert equal for integers or pointers
assert_false- assert the expression to be considered falseassert_neq- assert not equal
ccut_print_trace_on(signal)print stack trace when a certain signal is generated
NOTE: you also need -g -rdynamic for the compiler to ensure the correct symbols.
Example assertion failure program (bad_assert.c):
#include <ccut.h>
#include <assert.h>
void bad_assert_function() {
assert(0 == 1);
}
int main(int argc, char const *argv[]) {
ccut_print_trace_on(SIGABRT);
bad_assert_function(NULL);
return 0;
}Compile and run:
$ cc -g -rdynamic -lccut bad_assert.c -o bad_assert
$ ./bad_assertAnd you get the backtrace of function names -- but the pitfault of ccut_print_trace_on is it only contains function names and human-unreadable offsets, and the cross-platform way to find the file and lines is just hard. You can just just use a debugger by lldb ./bad_assert or gdb ./bad_assert, then run and bt for more infomation.
The following command will test ccut on C and C++.
make test
In-file BSD 3-clause.
