Skip to main content
Path matching is where coverage integrations silently fail. mehen sees workspace paths (src/app.py); a report contains whatever the producing tool decided to write. When the two disagree, a naive map lookup returns nothing for 100% of files — and every function suddenly “reads” 0% covered. Both cargo-crap and grcov document this as the central hazard of coverage ingestion; mehen treats it as a first-class layer with pinned regression tests.

What reports actually contain

Note the direction flips: JaCoCo paths are a suffix of the workspace path, while Go and CI-absolute paths contain the workspace path as their suffix. Any fixed prefix-stripping rule handles one direction and breaks the other.

The matching algorithm

Report paths are normalized once (forward slashes, ./.. segments resolved lexically — so ./-spelled variants of one file merge instead of racing), then each analyzed file resolves in two levels:
  1. Canonical identity. A report path spelled absolute that exists on this machine canonicalizes to an on-disk identity; a query resolving to the same file matches exactly. Same-machine runs — the common local case — always take this precise path.
  2. Component-suffix match. Otherwise the query and a report entry match when one’s component list is a trailing subsequence of the other’s — components, never bytes, so /foo/bar.rs can never match oofoo/bar.rs. The longest suffix wins; exact component equality outranks partial consumption (src/lib.rs prefers the src/lib.rs entry over vendor/dep/src/lib.rs).
  3. Root-relative retry. When an absolute query matches nothing — a local checkout being compared against a report written on a CI machine, where neither absolute spelling contains the other — the query is re-spelled relative to its repository root and resolved again. This is grcov’s --prefix-dir idea, automated and scoped to the repository boundary rather than applied as a blanket basename match (which would happily hand tests/util.py the coverage of src/util.py).
Two rules are deliberately conservative:
  • Relative report paths are never resolved against the working directory. Doing so would silently bind them to whatever happens to exist under the directory mehen was launched from — cargo-crap pins the same invariant with a dedicated regression test, and so does mehen.
  • A genuine tie is unmeasured, not a coin flip. When two distinct report entries match a query with equal specificity (a/sub/mod.rs vs b/sub/mod.rs for the query sub/mod.rs), mehen logs the ambiguity and treats the file as unmeasured rather than resolving by map order. Deterministic and honest beats accidentally-right.

Failure visibility

Because unmeasured files publish nothing (never 0% — see the overview), a path-matching failure cannot fabricate gate violations; it surfaces instead as:
  • n/a scores in top-offenders output and absent coverage families in metrics JSON,
  • an info log naming how many ingested report entries the file was checked against,
  • a warning on ambiguous matches naming the candidate count.
If a report that should match reads as unmeasured, check the three usual suspects: the report was generated in a different checkout layout (regenerate, or use explicit --coverage=<path> from the matching root), the workspace file is spelled through a symlinked directory (canonicalization handles most of this), or two same-named files genuinely tie (make the report paths more specific — e.g. configure the tool to emit paths relative to the repo root).

References