Record the mutation score after the exit-status fix
Some checks failed
libakerror CI Build / cmake_build (push) Successful in 2m48s
libakerror CI Build / coverage (push) Successful in 2m47s
libakerror CI Build / thread_sanitizer (push) Failing after 2m50s
libakerror CI Build / mutation_test (push) Successful in 39m13s

src/error.c now scores 81.4%: 245 of 301 mutants killed, 211 by a
failing test, 24 by failing to compile, and 10 by hanging the suite.
The population grew by 8 with akerr_exit(), and all 8 die, as do the 4
in akerr_default_handler_unhandled_error() -- no survivor anywhere in
either function.

Two of those are worth naming. Mutating the guard to status < 1 (the
sentinel-for-zero behaviour this library deliberately does not have) and
mutating the NULL-context exit(1) to exit(0) both die, so the tests pin
the two ways an exit could start claiming success rather than just the
one that was broken.

That moves the default handler out of the "needs a subprocess test"
survivor category noted here: it has one now. The default *logger* is
still in it -- every other test swaps in the capturing logger, so
nothing watches what the real one writes to stderr.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 07:57:33 -04:00
parent 5eaa956f50
commit 076b80c846

View File

@@ -94,8 +94,8 @@ Re-run after adding tests and confirm the score went up.
## Current status
`src/error.c` scores 81.2% — 238 of 293 mutants killed (204 by a failing test,
24 by failing to compile, 10 by hanging the suite), 55 surviving. The CI gate is
`src/error.c` scores 81.4% — 245 of 301 mutants killed (211 by a failing test,
24 by failing to compile, 10 by hanging the suite), 56 surviving. The CI gate is
set to 65% for headroom.
The ten timeout kills are all in the locking: deleting `akerr_mutex_init()` or
@@ -123,10 +123,16 @@ The remaining survivors are dominated by:
it never sees that. Deleting an `akerr_init()` call survives for a duller
reason: something else has always initialized the library by the time that
line runs.
* **Default logger / handler internals** (`vfprintf`, `va_end`, the
`errctx == NULL` branch, `exit(1)`): killing these needs a subprocess-based
test that captures a child's stderr and exit code, rather than the in-process
capturing logger the other tests use.
* **The default logger** (`vfprintf`, `va_end`, and the `return` in the
no-stdlib branch): the other tests replace `akerr_log_method` with the
in-process capturing logger, so nothing observes what the default one writes
to a real stderr. Killing these needs a test that captures a child's stderr.
The *handler* internals next to them are no longer in this category:
`tests/err_unhandled_null.c` and `tests/err_exit_status.c` read a forked
child's exit code, which kills every mutant in `akerr_exit()` and in
`akerr_default_handler_unhandled_error()` — all twelve of them, including the
`status < 0``status < 1` variant that only a test asserting
`akerr_exit(0)` exits 0 can distinguish.
* **Static assertions** (`akerr_assert_name_slots_pow2` and the occupancy cap
it guards): a mutated compile-time assertion that still compiles has no
runtime behavior to observe. Unkillable by construction — the assertion is