Runs the test suite with incremental line-coverage gating in one command:
mix test.qloverPlain mix test is left untouched, so single-file runs and ad-hoc flags
keep working exactly as before. mix test.qlover decides by itself:
- Baseline exists and non-test gate inputs (
priv/repo/,config/,mix.exs,mix.lock) match: runs exactly the affected test files with coverage, then gates (mix test --no-stale <files> --cover --export-coverage .qlover_fresh+mix qlover). Test-only edits stay incremental through per-test attribution. - Otherwise — first run, missing/invalid baseline, or changed
non-test inputs — runs the full suite with coverage and snapshots a
new baseline (
mix test --no-stale --cover+mix qlover --write-baseline). An info message says which path was taken.
There is no stale manifest involved: the file list comes from qlover's
own content-keyed reference graph, so selection is deterministic across
worktrees and machines, and a hostile host test alias injecting
--stale cannot shrink it (--no-stale is passed first, which wins the
duplicate-flag resolution).
Extra arguments are appended to the selection (mix test.qlover --seed 0); extra file arguments widen it, which stays sound because all
coverage still comes from one code version. The flags --stale,
--no-stale, --cover, --no-cover, --export-coverage, --failed,
--partitions, --dry-run, --no-compile are managed by the task and
rejected when passed explicitly.
Path overrides (mirroring mix qlover):
--baseline PATH- baseline file (default:cover/.qlover_baseline)--export PATH- scratch export (default:cover/.qlover_fresh.coverdata); custom paths must live under thetest_coverageoutput dir with a.coverdatasuffix.--expansion-export PATH- second scratch export accepted by the gate (default:cover/.qlover_expansion.coverdata).
Test failures abort before gating and never update the baseline. Scratch
exports are deleted after gating (and any leftovers removed before each
run), so a later mix test.coverage never unions a stale partial into a
full report. When test files change but reference data is available, only
the affected tests rerun in the focused run instead of the full
suite (see Mix.Tasks.Qlover).
Each invocation reports qlover: ran X tests; didn't run Y tests. Counts
come from ExUnit (including generated tests and doctests); unchanged files
reuse counts stored in the baseline and shared cache. ExUnit skips and
exclusions are included in Y and reported separately. Failed runs also
print the summary. If a run aborts without reporting counts, unknown
values are labelled rather than estimated. Older baselines receive one
full run to collect counts.
Coverage percentages are rounded down to two decimals so incomplete
coverage never displays as 100.00%. Threshold checks use exact counts.
The --qlover flag spelling
Mix does not let dependencies add flags to mix test, but the same
spelling is one alias away in your own mix.exs:
defp aliases do
[
test: fn
args ->
if "--qlover" in args do
Mix.Task.run("test.qlover", args -- ["--qlover"])
else
Mix.Tasks.Test.run(args)
end
end
]
endWith that, mix test --qlover gates while bare mix test (including
mix test test/foo_test.exs) stays vanilla.