docs/Writerside/topics/test.h.md

Sun, 23 Nov 2025 13:15:19 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 23 Nov 2025 13:15:19 +0100
changeset 1508
dfc0ddd9571e
parent 1424
563033aa998c
permissions
-rw-r--r--

optimize sorted insertion by using the infimum instead of the supremum

The reason is that the supremum returns the equal element with the smallest index, and we want the largest.
Therefore, we use the infimum, which already gives us the largest index when there are equal elements, and increase the index by one. The infimum is also guaranteed to exist in that case.

# 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.

```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);
}
```

> 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, you can 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>

mercurial