Files
dusk/.claude/tests.md
T
2026-06-16 10:15:59 -05:00

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 assertTrue for clarity (e.g. use assertNotNull instead of assertTrue(ptr != NULL, ...)).
  • Use assertUnreachable in default: cases of exhaustive switches.
  • Use assertStructSize to guard struct layouts that must match a known binary format or a platform ABI.
  • Do not use asserts for expected error paths -- use errorThrow instead. Asserts are for programmer mistakes, not runtime errors.