Build a real test harness (TODO.md section 1.0)

The suite could not fail and did not run. test_linkedlist.c asserted nothing
at all -- it printed node names and returned 0 -- so every confirmed list
defect passed it. test_tree.c was red on every run because parms.steps was
never reset between its three searches, and four libakerror tests registered
but never built, reporting Not Run. CI, meanwhile, was not fetching the
submodule, so configure failed before reaching any of it.

- tests/aksl_capture.h: AKSL_CHECK (NDEBUG-proof), AKSL_CHECK_STATUS/_OK to
  run an akerror-returning call, assert its status and release the context,
  a capturing akerr_log_method, aksl_slots_in_use(), and an AKSL_RUN driver
  that fails any test leaking an error-pool slot.
- test_linkedlist.c: rewritten as 15 assertion cases over the append,
  iterate and pop behaviour that is correct today.
- test_tree.c: each search builds its own tree and params.
- Registration driven by AKSL_TESTS, plus AKSL_WILL_FAIL_TESTS (aborts by
  design) and AKSL_KNOWN_FAILING_TESTS, which marks WILL_FAIL the three new
  tests asserting correct behaviour for the confirmed defects in TODO.md
  2.1.1-2.1.3. Fixing a defect flips its test to "unexpectedly passed",
  which is the cue to promote it into AKSL_TESTS.
- add_test/set_tests_properties are shadowed across the libakerror
  add_subdirectory call: CMake cannot un-register a test and
  set_tests_properties cannot cross directory scopes. The dependency has
  its own CI.
- AKSL_SANITIZE=ON builds library, tests and dependency with ASan+UBSan.
- scripts/mutation_test.py ported and retargeted at src/stdlib.c; wired to
  a manual `mutation` target, not a CI gate until 1.1-1.9 exist.
- CI: checkout with submodules: recursive, and ctest --output-on-failure.

ctest is now 5/5 green in both the default and sanitizer builds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-28 12:39:54 -04:00
parent 5793f6a178
commit 3e296c3bff
10 changed files with 1888 additions and 130 deletions

View File

@@ -0,0 +1,71 @@
/*
* KNOWN FAILING -- TODO.md section 2.1.2
*
* aksl_list_iterate runs a Floyd cycle check that leaves `slow` sitting at the
* list midpoint, and then starts the visiting loop from `slow` instead of from
* `list`. Every node before the midpoint -- including the head -- is never
* passed to the callback.
*
* The list here is built by hand rather than with aksl_list_append so that this
* test fails only for the iterate defect, not for the separate append defect in
* TODO.md 2.1.1.
*
* Registered in AKSL_KNOWN_FAILING_TESTS (WILL_FAIL). When iterate is fixed,
* CTest reports this as unexpectedly passing -- move it into AKSL_TESTS then.
*/
#include "aksl_capture.h"
#define CHAIN_LEN 3
typedef struct VisitLog
{
int count;
aksl_ListNode *seen[CHAIN_LEN];
} VisitLog;
static akerr_ErrorContext AKERR_NOIGNORE *record_visit(aksl_ListNode *node, void *data)
{
VisitLog *log = NULL;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node");
FAIL_ZERO_RETURN(e, data, AKERR_NULLPOINTER, "data");
log = (VisitLog *)data;
if ( log->count < CHAIN_LEN ) {
log->seen[log->count] = node;
}
log->count += 1;
SUCCEED_RETURN(e);
}
static int test_iterate_visits_every_node_from_the_head(void)
{
aksl_ListNode node[CHAIN_LEN];
VisitLog log;
int i = 0;
memset((void *)node, 0x00, sizeof(node));
memset((void *)&log, 0x00, sizeof(log));
for ( i = 1; i < CHAIN_LEN; i++ ) {
node[i - 1].next = &node[i];
node[i].prev = &node[i - 1];
}
AKSL_CHECK_OK(aksl_list_iterate(&node[0], &record_visit, &log));
AKSL_CHECK(log.count == CHAIN_LEN);
for ( i = 0; i < CHAIN_LEN; i++ ) {
AKSL_CHECK(log.seen[i] == &node[i]);
}
return 0;
}
int main(void)
{
int failures = 0;
akerr_init();
AKSL_RUN(failures, test_iterate_visits_every_node_from_the_head);
AKSL_REPORT(failures);
}