Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ codecov:
# some notification settings.
notify:
wait_for_ci: true
after_n_builds: 1
# Both flags upload from the same job as separate steps; wait for
# both before posting so the comment never reflects half the tree.
after_n_builds: 2

coverage:
precision: 2
Expand All @@ -46,21 +48,23 @@ comment:
require_changes: true
layout: "diff,flags,files"

# Flag membership comes from the upload itself: the coverage job emits
# coverage-cxx.xml (gcovr) and coverage-py.xml (coverage.py) and uploads
# each under its own flag. Do NOT re-filter by `paths` here.
#
# A stale `paths` list silently DROPS files from the report rather than
# merely un-flagging them: the previous list named LiverMarkups/ (module
# deleted) and Modeling/ (never existed), placed LiverResectionsLib/ at
# the wrong level, pointed the py flag at **/Testing/Python/ (which the
# ignore list below discards anyway), and omitted Liver/,
# LiverSegmentation/ and SlicerLiverInteractionLib/ entirely -- so those
# three modules were absent from the whole-tree percentage even though
# the coverage job measured them.
flags:
cxx:
paths:
- LiverResections/
- LiverResectionsLib/
- LiverMarkups/
- VascularTerritories/
- LiverVolumetry/
- Modeling/
carryforward: false
py:
paths:
- LiverResections/Testing/Python/
- LiverResectionsLib/Testing/Python/
- VascularTerritories/
- LiverVolumetry/
carryforward: false

# Exclude generated, test-only, and third-party code from both
# coverage % and PR comment annotations. These paths are uninteresting
Expand Down
9 changes: 9 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,12 @@ liverresectionslib =
liversegmentationlib =
LiverSegmentation/LiverSegmentationLib/
*/qt-scripted-modules/LiverSegmentationLib/
livervolumetrylib =
LiverVolumetry/LiverVolumetryLib/
*/qt-scripted-modules/LiverVolumetryLib/
slicerliverinteractionlib =
SlicerLiverInteractionLib/
*/qt-scripted-modules/SlicerLiverInteractionLib/
vascularterritorieslib =
VascularTerritories/VascularTerritoriesLib/
*/qt-scripted-modules/VascularTerritoriesLib/
22 changes: 19 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ jobs:
--cov=LiverSegmentation \
--cov=LiverResections/LiverResectionsLib \
--cov=LiverVolumetry \
--cov=SlicerLiverInteractionLib \
--cov=VascularTerritories \
--cov-report= \
|| true
Expand Down Expand Up @@ -721,12 +722,18 @@ jobs:
XML
}

- name: Upload coverage to Codecov
# Two uploads, one per flag. A single step with
# ``files: a,b`` + ``flags: cxx,py`` produces ONE session carrying
# BOTH flags, so neither flag isolates its own language and the
# per-flag figures on the Codecov UI are meaningless (the symptom
# is ``sessions: 1`` on every commit while two flags exist).
# Splitting the step is what makes the cxx/py split real.
- name: Upload C++ coverage to Codecov
if: github.event_name != 'pull_request' || steps.changes.outputs.any_changed == 'true'
uses: codecov/codecov-action@v5
with:
files: ./coverage-cxx.xml,./coverage-py.xml
flags: cxx,py
files: ./coverage-cxx.xml
flags: cxx
# Non-blocking: per ADR-0021 the job's pass/fail is not a
# branch-protection signal. An upload failure (Codecov
# outage, OAuth not yet configured by the maintainer, etc.)
Expand All @@ -745,3 +752,12 @@ jobs:
# ``CODECOV_TOKEN`` secret. Rotating the token is a
# maintainer-only step on the Codecov side.
token: ${{ secrets.CODECOV_TOKEN }}

- name: Upload Python coverage to Codecov
if: github.event_name != 'pull_request' || steps.changes.outputs.any_changed == 'true'
uses: codecov/codecov-action@v5
with:
files: ./coverage-py.xml
flags: py
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
28 changes: 28 additions & 0 deletions Docs/adr/0021-coverage-measurement.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,34 @@ absolute-% threshold; ratchet pressure is per-PR.
PR comment; tests still run.
- Coverage % drift on rebases can produce noisy PR comments.

## Configuration traps (learned 2026-09-11)

The whole-tree percentage is only as honest as the upload wiring, and
two settings can corrupt it silently -- no warning, no failed job, just
a wrong number:

- **`flags.<flag>.paths` in `.codecov.yml` DROPS files, it does not
merely un-flag them.** A path list that has fallen behind the module
layout removes those modules from the reported percentage entirely.
This is the trap that hid `Liver/`, `LiverSegmentation/` and
`SlicerLiverInteractionLib/` while the coverage job was measuring
them. Flag membership should come from the upload, not from a second
filter that has to be kept in sync with the tree.
- **One `codecov-action` step with `files: a,b` + `flags: x,y` is ONE
session carrying BOTH flags**, so neither flag isolates its language.
The tell is `sessions: 1` on a repo with two flags. Upload once per
flag.

Because both faults change the *denominator*, they also make the
percentage non-comparable across commits: a docs-only commit can appear
to move coverage by several points. When a coverage swing has no
plausible cause in the diff, check the file and line totals before
reading it as a test-quality regression.

Any new Python sub-package staged into `qt-scripted-modules/` needs a
`[paths]` alias in `.coveragerc`, or its launched-leg records stay on
build-tree paths and never merge onto the source file.

## References

- [ADR-0003][adr-0003] — Testability invariant.
Expand Down
Loading