Fri, 23 May 2025 12:44:24 +0200
make test-compile depend on both static and shared
the shared lib is not needed for the tests,
but when run with coverage, gcov will be confused
when outdated line information is available from
a previous shared build
# 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 #include <cx/test.h> 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"><![CDATA[ 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"><![CDATA[ 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"><![CDATA[ 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>