big-code-analysis runs cargo-mutants on a quarterly schedule
against the highest-leverage modules: every metric implementation
under src/metrics/, plus big-code-analysis-ast/src/checker.rs and big-code-analysis-ast/src/getter.rs.
Mutation testing complements the regular test suite by mechanically
mutating production code (e.g. flipping > to >=, replacing a
function body with Default::default()) and re-running the tests.
Any mutant that survives is a gap in test coverage — the suite did
not catch a deliberate bug.
A full mutants run on src/metrics/ takes tens of minutes per file
on a GitHub-hosted runner. Gating per-PR CI on it would either burn
hours of runner time on every change or force aggressive scoping that
defeats the point. Instead the workflow runs four times a year and
files an issue when escapes are detected, so the project gets a
quarterly health check without slowing day-to-day work.
.github/workflows/mutation-test.yml runs:
- On
cron: '0 7 1 1,4,7,10 *'— 07:00 UTC on the 1st of January, April, July, and October. - On
workflow_dispatchfor ad-hoc runs from the Actions tab.
The job:
- Checks out the repo with submodules.
- Installs
cargo-mutantsviataiki-e/install-action@v2. - Runs
cargo mutantsagainstsrc/metrics/,big-code-analysis-ast/src/checker.rs, andbig-code-analysis-ast/src/getter.rs. - Uploads
target/mutants/as thecargo-mutants-reportartifact (90-day retention). - On non-zero exit, opens a GitHub issue labelled
mutation-testingwith the missed / timed-out counts and a link back to the run.
The job has issues: write permission and uses GITHUB_TOKEN; no
extra secrets are required.
Install cargo-mutants once:
cargo install cargo-mutants --lockedThe repo ships a cargo mutants alias (.cargo/config.toml) that
pins the package and mutation order (--package big-code-analysis --no-shuffle --in-place); CI additionally passes -j 2 --minimum-test-timeout 120 --output target/mutants. To exercise a
single metric file (the typical local case):
cargo mutants -f src/metrics/cognitive.rsTo exercise the same surface as CI:
cargo mutants --package big-code-analysis --package big-code-analysis-ast \
-f src/metrics/ -f big-code-analysis-ast/src/checker.rs \
-f big-code-analysis-ast/src/getter.rsPlan on tens of minutes per file on a laptop. Use -j N to bound
parallelism if the run is starving other work; the CI workflow uses
-j 2.
cargo-mutants writes its results to target/mutants/ in CI (via
--output); a local run with the alias writes to the default
mutants.out/ in the repo root:
| File | Meaning |
|---|---|
missed.txt |
Mutants that survived — the suite did not catch them |
timeout.txt |
Tests that hit the timeout (often infinite loops, occasionally real gaps) |
caught.txt |
Mutants the suite correctly killed |
unviable.txt |
Mutants that did not compile (no signal either way) |
mutants.log |
Full stdout from the run |
Each line in missed.txt looks like:
src/metrics/cognitive.rs:142:9: replace > with >= in compute
Triage:
- Read the mutant location and the surrounding code.
- If the mutation produces observably different output (different metric value, different control flow), add a test that pins the correct behaviour. This is the common case and the whole point.
- If the mutation is genuinely behaviour-equivalent (e.g. a
matcharm that is provably unreachable, an unused branch in a helper that always early-returns), document why and add the location to--exclude-rein a follow-up. Do this sparingly — most "looks equivalent" mutants are real coverage gaps. - Timeouts often mean a mutation introduced an infinite loop. The
--minimum-test-timeout 120in the CI workflow guards against spurious timeouts on slow runners; if a specific test case is chronically slow, prefer fixing the test.
The auto-filed issue is the canonical entry point. It contains the
missed / timed-out counts, a link to the run, and the first 50
missed lines for quick scanning. Download the
cargo-mutants-report artifact from the run for the full list, then
follow the triage steps above.
Close the issue once every escape has either:
- A new test that kills the mutant, or
- An explicit
--exclude-reentry with a justification commented in.github/workflows/mutation-test.ymlor the relevant source file.