Sat, 25 Jan 2025 15:22:01 +0100
documentation of test.h
relates to #451
docs/Writerside/topics/test.h.md | file | annotate | diff | comparison | revisions |
--- a/docs/Writerside/topics/test.h.md Sat Jan 25 14:05:35 2025 +0100 +++ b/docs/Writerside/topics/test.h.md Sat Jan 25 15:22:01 2025 +0100 @@ -1,1 +1,119 @@ # 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>