Gate mutation testing on a measured score, not an inherited one
All checks were successful
All checks were successful
The threshold had been 80 against src/stdlib.c alone. There are three more sources now and nobody had measured them, so that number was a guess carried forward. Measured: 72.3%, 188 of a 260-mutant sample from the 1701 the four sources generate. Gate set to 65 -- a ratchet with headroom for the runner and for the sample shifting as sources change, not a target. A sample rather than the whole set, because 1701 rebuilds and test runs is hours. --max-mutants samples by even index rather than at random, so the same 260 run every time and the gate stays reproducible; sampling all four files beats exhausting one of them, which is what this job did before. 72.3% against the 89.6% reported at 0.1.0 is a change in denominator, not a regression in the tests. That figure covered one 561-line file; this covers four totalling 1716 lines, and most of the added surface is argument validation whose mutants are frequently *equivalent* -- 12 of the 72 survivors are `errno = 0` deleted from a wrapper whose libc call always sets errno, which no test that could be written would catch. The README breaks all 72 down and says which are worth acting on; TODO.md 2.4 carries the three clusters that are. Two of them were real and are fixed here and in the previous commit: the right child's `depth + 1` in the depth-first walk, and aksl_tree_remove on an empty tree, which without its guard dereferences NULL. Neither had a test; both do now. That is what the harness is for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -907,6 +907,25 @@ static int test_tree_remove_all_three_cases(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removing from an empty tree. Found by mutation testing: deleting the
|
||||
* `FAIL_ZERO_RETURN(e, *root, ...)` guard survived the whole suite, because
|
||||
* nothing had ever called remove on a tree with no root -- which without the
|
||||
* guard walks straight into tree_replace and dereferences NULL.
|
||||
*/
|
||||
static int test_tree_remove_from_an_empty_tree(void)
|
||||
{
|
||||
int value = 1;
|
||||
aksl_TreeNode node;
|
||||
aksl_TreeNode *root = NULL;
|
||||
|
||||
AKSL_CHECK_OK(aksl_tree_node_init(&node, &value));
|
||||
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_tree_remove(&root, &node),
|
||||
AKERR_VALUE, "tree is empty");
|
||||
AKSL_CHECK(root == NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The mirror images of the cases above: a node that is its parent's *right*
|
||||
* child, and a node whose only child is on the left. Both take different
|
||||
@@ -1112,6 +1131,7 @@ int main(void)
|
||||
AKSL_RUN(failures, test_tree_remove_mirrored_shapes);
|
||||
AKSL_RUN(failures, test_tree_remove_with_a_distant_successor);
|
||||
AKSL_RUN(failures, test_tree_remove_the_only_node);
|
||||
AKSL_RUN(failures, test_tree_remove_from_an_empty_tree);
|
||||
AKSL_RUN(failures, test_tree_height_and_count);
|
||||
AKSL_RUN(failures, test_tree_free_all);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user