Proves 100% line coverage from a stale subset plus the full-run baseline.
mix qlover --eligible
mix test --stale --cover --export-coverage .qlover_fresh
mix qlover
mix qlover --write-baselineThe baseline records beam hashes from the last green full run, a hash of
the non-test gate inputs (priv/repo, config, mix.exs, mix.lock),
and a snapshot of the test files (content hashes plus traced module
references). A stale run satisfies the gate when the non-test inputs are
unchanged: every test that references a changed beam reruns under
--stale, so fresh 100% line coverage for the changed beams plus the
untouched baseline equals a full run. Test-only edits gate incrementally
too: changed tests re-prove the modules they could have covered (tracked
via the reference snapshot) instead of forcing a full run. Anything else
must fall back to mix test --no-stale --cover.
Prefer mix test.qlover, which runs the whole flow (eligible → stale →
gate, else full → snapshot) in one command.
Options
The task is normally driven by the cover.sh wrapper, but the paths can
be overridden for testing or custom layouts:
--baseline PATH- baseline file (default:cover/.qlover_baseline)--export NAME- fresh export name under the cover output dir (default:cover/.qlover_fresh.coverdata)--expansion-export PATH- second scratch export for the focused expansion run (default:cover/.qlover_expansion.coverdata)
A successful gate deletes the scratch exports so a later
mix test.coverage never unions a stale partial into a full report.
Sharing baselines across worktrees
Every snapshot is also written through to a shared content-addressed cache, and a missing or invalid local baseline is fetched from it:
QLOVER_CACHE_DIR=~/.cache/qlover # the default (XDG-aware)The cache key is derived from the beam hashes, gate hash, and test
hashes, so a hit means byte-identical content: the gate re-verifies the
fetched baseline against the local tree exactly as if it were local, so
a cache hit can never pass where a local baseline would fail. Set
QLOVER_CACHE_DIR to another directory to share across checkouts, to
"" to disable the cache, or keep the default for a personal
cross-worktree cache. Tracer records merge the same way (per-file
content keys; last-writer-wins races only ever degrade to a full run).
Test attribution
Per-test-file attribution needs reference data from the compiler tracer
(Qlover.Tracer). Enable it in the host project:
# mix.exs
def project do
[...,
elixirc_options: [tracers: [Qlover.Tracer]],
test_elixirc_options: [tracers: [Qlover.Tracer]]]
endWithout tracer data every test edit falls back to the full suite, exactly like before — attribution is purely additive and can never pass where the old gate would fail.