3.6 KiB
Tests and Assertions
Test infrastructure
Tests live in test/ and mirror the src/dusk/ directory structure.
Enable with -DDUSK_BUILD_TESTS=ON. The test runner is cmocka.
Entry point
Every test file includes dusktest.h, which pulls in dusk.h and
assert/assert.h. When DUSK_TEST_ASSERT is defined, assert.h
includes cmocka.h and redirects assertion failures through
mock_assert() instead of calling abort().
Test function signature
static void test_something(void **state) {
// ... setup ...
// ... exercise ...
// ... assert ...
assert_int_equal(memoryGetAllocatedCount(), 0); // leak check
}
Registering and running tests
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_errorThrow),
cmocka_unit_test(test_errorOk),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
Use cmocka_unit_test_setup_teardown() when a test needs per-test
setup or teardown callbacks.
Assertion mix
Tests use two sets of assertion macros:
| Origin | When to use |
|---|---|
cmocka: assert_int_equal(), assert_non_null() etc. |
Validate results inside test functions |
Dusk: assertTrue(), assertNotNull() etc. |
Exercise the code under test (these may fire and need catching) |
To assert that a Dusk assertion fires, use cmocka's mock system:
expect_assert_failure(assertTrueImpl(__FILE__, __LINE__, false, "msg"));
Memory leak discipline
Every test function must end by asserting:
assert_int_equal(memoryGetAllocatedCount(), 0);
This ensures all allocations from the code under test were freed.
Assertion system
Source: src/dusk/assert/
Runtime vs test mode
| Mode | Trigger | Effect on failure |
|---|---|---|
| Runtime (default) | Release / non-test builds | Logs the message + backtrace, then calls abort() |
Test (DUSK_TEST_ASSERT) |
-DDUSK_TEST_ASSERT build flag |
Routes through cmocka mock_assert() for controlled catching |
Faked (DUSK_ASSERTIONS_FAKED) |
Defined by platform or test | All macros become no-ops (((void)0)) |
Available macros
| Macro | Description |
|---|---|
assertTrue(x, msg) |
Fails if x is false |
assertFalse(x, msg) |
Fails if x is true |
assertNotNull(ptr, msg) |
Fails if ptr is NULL |
assertNull(ptr, msg) |
Fails if ptr is not NULL |
assertUnreachable(msg) |
Unconditional failure; marks unreachable code |
assertDeprecated(msg) |
Marks a code path as deprecated |
assertStringEqual(a, b, msg) |
Fails if strings differ |
assertStrLenMax(str, len, msg) |
Fails if strlen(str) >= len |
assertStrLenMin(str, len, msg) |
Fails if strlen(str) < len |
assertIsMainThread(msg) |
Fails if called from a non-main thread |
assertNotMainThread(msg) |
Fails if called from the main thread |
assertStructSize(type, size) |
Compile-time size check via _Static_assert |
Thread tracking
assertInit() records the main thread ID (pthreads). The main-thread
assertions compare against this stored ID. Call assertInit() once at
startup before spawning any threads.
Usage guidelines
- Prefer the specific macro over a bare
assertTruefor clarity (e.g. useassertNotNullinstead ofassertTrue(ptr != NULL, ...)). - Use
assertUnreachableindefault:cases of exhaustive switches. - Use
assertStructSizeto guard struct layouts that must match a known binary format or a platform ABI. - Do not use asserts for expected error paths -- use
errorThrowinstead. Asserts are for programmer mistakes, not runtime errors.