Coverage Gate¶
The Coverage Gate fails a pull request when line coverage of the CPU build falls below a floor. Two required checks run it: Coverage Gate (hosted CPU build) and Coverage GPU (self-hosted GPU build). Both are defined in .github/workflows/tests-and-quality-gates.yml and listed in the Required Checks Aggregator (ADR-1297). The floors come from docs/principles.md §3; the ratchet rules are ADR-0922.
When you need this page¶
A PR shows Coverage Gate or Coverage GPU red, or you want to raise or lower a floor. To reproduce a failure, run the commands in Debugging a failing gate.
What the gate checks¶
scripts/ci/coverage-check.sh takes a gcovr JSON summary and two percentages:
- Overall line coverage, the unweighted average over every file in the summary, must reach the overall minimum. Exit code 1 on failure.
- Every security-critical file must reach the critical minimum, unless it has an entry in the
PER_FILE_MINmap of the script. Security-critical files match*core/src/dnn/*,*core/src/opt.cppand*core/src/read_json_model.cpp. A critical file with no executed lines is listed but not enforced.
The script defaults are 70 % overall and 90 % critical. The workflow passes its own values:
| Job | Invocation in the workflow | Overall | Critical |
|---|---|---|---|
Coverage Gate (coverage) | coverage-check.sh core/build-coverage/coverage.json 37 85 | 37 % | 85 % |
Coverage GPU (coverage-gpu) | coverage-check.sh core/build-coverage-gpu/coverage.json 70 85 | 70 % | 85 % |
The CPU job's 37 % overall floor tracks the measured value after the 2026-05-19 merge burst of new scaffold code, as the comment above the step records (ADR-0637). The per-file values in PER_FILE_MIN apply in both jobs.
The coverage job skips itself in-job when scripts/ci/plan-ci-impact.py (ADR-1140) reports no impacted source, so the check always reports and skipped is never its outcome.
Per-file overrides¶
Some files have a structural ceiling below the critical floor. They live in the PER_FILE_MIN map of coverage-check.sh, and each entry cites the ADR that justifies it.
| File | Floor | Justification |
|---|---|---|
core/src/dnn/ort_backend.c | 83 % | ADR-0114: the CUDA execution-provider success arm is unreachable on a CPU-only ONNX Runtime. |
core/src/dnn/dnn_api.c | 83 % | ADR-0114: same ceiling as ort_backend.c. |
core/src/dnn/tiny_extractor_template.h | 75 % | ADR-0881: template helpers are only instantiated by callers. |
Overrides only ratchet upward.
The per-PR delta script¶
scripts/ci/coverage-delta-check.sh compares a base and a head gcovr summary and fails when overall coverage, or a touched file's coverage, drops by more than a tolerance (0.5 percentage points by default). Exit code 1 means the overall drop, 2 a per-file drop, 3 a usage error. ADR-0922 introduced it, but no step of tests-and-quality-gates.yml calls it today, so it is a local tool, not a required check. Run it by hand:
scripts/ci/coverage-delta-check.sh \
--base-json /path/to/base-coverage.json \
--head-json core/build-coverage/coverage.json \
--changed-files "$(git diff --name-only origin/master...HEAD)"
The options are --base-json, --head-json, --changed-files, --max-overall-drop and --max-file-drop; read the header of the script for the exact input format.
Debugging a failing gate¶
The coverage-cpu and coverage-gpu artifacts of the failed run hold coverage.json, coverage.txt and coverage.xml. coverage.txt lists per-file numbers. To check a downloaded summary:
To rebuild what the Coverage Gate job builds (needs gcovr):
cd core
meson setup build-coverage --buildtype=debug \
-Db_coverage=true -Denable_cuda=false -Denable_sycl=false \
-Denable_float=true -Denable_avx512=true -Denable_dnn=enabled \
-Dc_args=-fprofile-update=atomic -Dcpp_args=-fprofile-update=atomic
ninja -C build-coverage
python3 ../scripts/ci/run_meson_test.py -- -C build-coverage --print-errorlogs --num-processes 1
gcovr --root .. \
--filter 'src/.*' \
--exclude '.*/test/.*' --exclude '.*/tests/.*' --exclude '.*/subprojects/.*' \
--gcov-ignore-parse-errors=negative_hits.warn \
--gcov-ignore-parse-errors=suspicious_hits.warn \
--json-summary build-coverage/coverage.json \
--txt build-coverage/coverage.txt \
build-coverage
../scripts/ci/coverage-check.sh build-coverage/coverage.json 37 85
-fprofile-update=atomic and --num-processes 1 are both needed so test binaries do not corrupt each other's counters in the shared libvmaf.so (ADR-0110). The CI job also runs a Python suite before collecting coverage; a local run without it measures lower on the DNN files.
make coverage-check runs the same recipe locally: it builds the instrumented tree in build-coverage/, runs the meson suite serially, writes the gcovr summary build-coverage/coverage.json and passes it to coverage-check.sh with the local floors COVERAGE_MIN_OVERALL (37) and COVERAGE_MIN_CRITICAL (85) from the Makefile. make coverage-html renders the gcovr HTML report. coverage-check.sh exits 2 on an input that is not a gcovr JSON summary, an lcov .info file included. Like the CI job without its Python suite, a local run measures the DNN files lower.
Raising a floor¶
Raising needs no ADR. Change the numbers in the Enforce coverage thresholds step of the workflow (or the value in PER_FILE_MIN), confirm that the latest master run is above the new floor, and leave a comment with the date and the measurement.
Lowering a floor¶
Lowering any floor needs a new ADR that supersedes ADR-0922 (and ADR-0114 for a per-file override):
- Allocate it with
scripts/adr/next-free.sh --claim <slug>. State why the lower floor is unavoidable and what mitigates the loss of coverage. - Cite the ADR inline at the changed number, for example
# ADR-NNNN — supersedes ADR-0922, lowers floor to X%. A bare number change is rejected in review. - Use
PER_FILE_MINfor a per-file exception. No other suppression mechanism exists.
Related ADRs¶
| ADR | Topic |
|---|---|
| ADR-0110 | Atomic gcov counters; foundation of the gate. |
| ADR-0111 | Move from lcov to gcovr, so per-file numbers are honest. |
| ADR-0114 | PER_FILE_MIN map and the structural-ceiling rationale. |
| ADR-0117 | gcovr suspicious-hits warning filter. |
| ADR-0637 | Floor tracks the measured value, ratcheted upward as tests land. |
| ADR-0881 | tiny_extractor_template.h floor rationale. |
| ADR-0922 | Ratchet rules and the delta script. |