Skip to content

feat(coverage): report per-line execution hit counts #856

Description

@Chemaclass

🎯 Goal

Surface per-line execution hit counts (gcov-style: how many times each line ran) in coverage reports, instead of only boolean covered/not-covered. The raw data to do this largely exists already — this is mostly a reporting change.

📍 Where

  • Raw hit records are produced by bashunit::coverage::record_line into _BASHUNIT_COVERAGE_DATA_FILE / _BASHUNIT_COVERAGE_TEST_HITS_FILE (src/coverage.sh:294-301, flush at :336-339).
  • Report readers collapse to a covered set today: src/coverage.sh:575 (grep … | sort -u), :688, :743.
  • Report writers: src/reports.sh (HTML/JUnit/etc.) and the console coverage summary.

⚠️ Interaction with the dedup issue (read first)

The per-line dedup issue proposes collapsing repeated hits at source (one record per file:line). If that lands, hit counts are lost at the source. Two options — the implementer must pick and document:

  • (A) Count in memory: keep dedup, but maintain a small in-memory file:line → count tally in record_line and flush counts (not repeated records). Preserves counts and the I/O win. Preferred.
  • (B) Order the work: land hit-counts first (parse repeated raw records → counts at report time), then make dedup opt-out when counts are requested.

Coordinate so the two issues don't contradict. (A) is the clean end state.

🔧 Approach (assuming option A)

  1. In record_line, when a (file,line) is first seen, record it; on repeat, increment an in-memory counter keyed file:line rather than appending a duplicate record.
  2. On flush, write file:line:count (extend the record format; keep backward-compatible parsing or bump the data-file format guarded by a version marker).
  3. Report layer: aggregate counts across the run (and across --parallel per-$$ files at merge), expose per-line counts in HTML (heat/number column) and optionally a --coverage-mode flag to show counts in the console.
  4. Keep the existing boolean summary (coverage %) unchanged — counts are additive detail.

✅ Acceptance criteria

  • Reports can show, per line: run count (0 = uncovered).
  • --parallel merge sums counts across worker files correctly.
  • Coverage percentage and the covered-line set are unchanged vs. current output.
  • Data-file format change (if any) is versioned and parsed safely.

🧪 TDD / measurement

  • RED: fixture with a line executed a known number of times (e.g. a 5-iteration loop) → assert the report attributes count 5 to that line; a once-run line → 1; an unrun line → 0.
  • --parallel: two workers each hitting the same shared-source line → summed count.
  • Gate: ./bashunit tests/, ./bashunit --parallel --simple --strict tests/, make sa, make lint. Regenerate coverage HTML/report snapshots if any.

⛓️ Constraints

  • Bash 3.0+ — in-memory count tally via the existing string-cache pattern (bounded by distinct lines); no ${var//} over big strings (perf-fork-budget.md).
  • Don't regress the dedup I/O win — option (A) keeps both.
  • If it materially adds record_line overhead, gate counts behind a flag (e.g. BASHUNIT_COVERAGE_COUNTS=true) so default coverage stays lean.

📈 Impact

Turns coverage from "covered?" into "how hot?" — useful for spotting under-exercised branches and dead-ish code. Depends on / must be sequenced with the per-line dedup issue.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions