Sat, 25 Jan 2025 15:22:01 +0100
documentation of test.h
relates to #451
# Test Framework UCX brings its own testing framework, which can also be used by your applications. A test new suite is created by `cx_test_suite_new()`. An arbitrary number of test cases can be registered with `cx_test_register()`. ```C CxTestSuite *suite = cx_test_suite_new("My Suite"); cx_test_register(suite, test_foo); cx_test_register(suite, test_bar); cx_test_register(suite, test_baz); ``` A test case needs to be defined with the `CX_TEST` macro. The general structure of a test case is 1) setup code, 2) the actual test, 3) tear down code. In UCX this is realized by the `CX_TEST_DO` macro with which you can define a scope for the actual test. Whenever a test assertion fails, the scope is exited and the tear down code is executed. <tabs> <tab title="Test"> <code-block lang="C"> CX_TEST(test_foo) { struct mystruct *x = malloc(sizeof(*x)); x->d = 42; CX_TEST_DO { int y = foo(x); // auto-generated failure message CX_TEST_ASSERT(y == 42); // alternatively: custom failure message CX_TEST_ASSERTM(y == 42, "foo does not return correct value"); } free(x); } </code-block> </tab> <tab title="Implementation"> <code-block lang="C"> struct mystruct { int d; }; int foo(struct mystruct *s) { return s->d; } </code-block> </tab> </tabs> Once you have registered all test cases, you can run the test suite with `cx_test_run()` or one of the convenience macros. <tabs> <tab title="stdout"> <code-block lang="C"> cx_test_run_stdout(suite); </code-block> </tab> <tab title="FILE*"> <code-block lang="C"> FILE *f = fopen("test-result.txt", "w"); cx_test_run_f(suite, f); fclose(f); </code-block> </tab> <tab title="Other"> <code-block lang="C"> // for example: UCX buffer CxBuffer buf; cxBufferInit(&buf, NULL, 1024, NULL, 0); cx_test_run(suite, &buf, cxBufferWriteFunc); // do something with the buffer cxBufferDestroy(&buf); </code-block> </tab> </tabs> Finally, clean up all resources consumed by the test suite. ```C cx_test_suite_free(suite); ``` ## Test Subroutines For parameterized testing you can define and call test subroutines. It is _not_ possible to call arbitrary functions and use the `CX_TEST_ASSERT` macro. Instead, you define a callable test subroutine with `CX_TEST_SUBROUTINE` and call it with `CX_TEST_CALL_SUBROUTINE`. The following example illustrates this with an adaption of the above test case. <code-block lang="C"> CX_TEST_SUBROUTINE(test_foo_params, struct mystruct *x, int d) { x->d = d; CX_TEST_ASSERT(foo(x) == d); } CX_TEST(test_foo) { struct mystruct *x = malloc(sizeof(*x)); CX_TEST_DO { CX_TEST_CALL_SUBROUTINE(test_foo_params, x, 42); CX_TEST_CALL_SUBROUTINE(test_foo_params, x, -42); CX_TEST_CALL_SUBROUTINE(test_foo_params, x, 1337); } free(x); } </code-block> > Any test function, test case or test subroutine, is a normal C function. > As such you can decide to make them `static` when you do not want external linkage. > For example, just define the test as `static CX_TEST(test_foo)` instead of `CX_TEST(test_foo)`. <seealso> <category ref="apidoc"> <a href="https://ucx.sourceforge.io/api/test_8h.html">test.h</a> </category> </seealso>