-
Notifications
You must be signed in to change notification settings - Fork 0
Coverage is measured on one CI version and held at 90% statements, 80% branches #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2224e93
12a03ba
db3a243
7e24a0b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # SPDX-FileCopyrightText: 2026 The CTRLRun contributors | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| """Hold the coverage floors CONTRIBUTING.md states, from the JSON `scripts/check.sh` writes. | ||
|
|
||
| python scripts/coverage_floor.py coverage.json --statements 90 --branches 80 | ||
|
|
||
| Two numbers rather than coverage.py's single blended one, because the blend hides which of the | ||
| two slipped and the floors are stated separately. Exit 1 names the one that did. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import json | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| def measure(report: Path) -> tuple[float, float]: | ||
| totals = json.loads(report.read_text(encoding="utf-8"))["totals"] | ||
| statements = 100.0 * totals["covered_lines"] / totals["num_statements"] | ||
| branches = 100.0 * totals["covered_branches"] / totals["num_branches"] | ||
| return statements, branches | ||
|
|
||
|
|
||
| def main(argv: list[str] | None = None) -> int: | ||
| parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) | ||
| parser.add_argument("report", type=Path) | ||
| parser.add_argument("--statements", type=float, required=True, help="floor, percent") | ||
| parser.add_argument("--branches", type=float, required=True, help="floor, percent") | ||
| arguments = parser.parse_args(argv) | ||
| statements, branches = measure(arguments.report) | ||
| print(f"statements {statements:6.2f}% floor {arguments.statements:g}%") | ||
| print(f"branches {branches:6.2f}% floor {arguments.branches:g}%") | ||
| below = [] | ||
| if statements < arguments.statements: | ||
| below.append("statements") | ||
| if branches < arguments.branches: | ||
| below.append("branches") | ||
| if below: | ||
| print(f"coverage_floor: below the floor: {', '.join(below)}", file=sys.stderr) | ||
| return 1 | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -626,6 +626,57 @@ def test_every_lock_a_workflow_installs_from_exists_and_is_hashed(): | |||||
| assert "--hash=" in lines[index + 1], f"{lock}: {line} carries no hash" | ||||||
|
|
||||||
|
|
||||||
| # --- coverage is measured and the floors are held --------------------------------------------- | ||||||
|
|
||||||
|
|
||||||
| def test_ci_measures_coverage_on_one_version_and_holds_the_floors(): | ||||||
| """CONTRIBUTING.md states 90% of statements and 80% of branches; this is the step that | ||||||
| holds them, on one version of the matrix, from the JSON `scripts/check.sh` writes.""" | ||||||
| workflow = yaml.safe_load((WORKFLOWS / "ci.yml").read_text(encoding="utf-8")) | ||||||
| steps = workflow["jobs"]["check"]["steps"] | ||||||
| check = next(s for s in steps if s.get("name") == "check") | ||||||
| assert "CTRLRUN_COVERAGE" in check["env"] | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Assert the coverage condition. The - assert "CTRLRUN_COVERAGE" in check["env"]
+ assert "matrix.python-version == '3.12'" in check["env"]["CTRLRUN_COVERAGE"]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| floors = next(s for s in steps if s.get("name") == "Coverage floors") | ||||||
| assert "scripts/coverage_floor.py coverage.json" in floors["run"] | ||||||
| assert "--statements 90" in floors["run"] and "--branches 80" in floors["run"] | ||||||
| assert floors["if"] == "matrix.python-version == '3.12'" | ||||||
|
|
||||||
| script = (REPO_ROOT / "scripts" / "check.sh").read_text(encoding="utf-8") | ||||||
| assert "--cov=ctrlrun --cov-branch" in script and "json:coverage.json" in script | ||||||
| contributing = (REPO_ROOT / "CONTRIBUTING.md").read_text(encoding="utf-8") | ||||||
| assert "90% of\nstatements and 80% of branches" in contributing | ||||||
|
|
||||||
|
|
||||||
| def test_coverage_floor_names_the_number_that_slipped(tmp_path): | ||||||
| report = tmp_path / "coverage.json" | ||||||
| report.write_text( | ||||||
| json.dumps( | ||||||
| { | ||||||
| "totals": { | ||||||
| "covered_lines": 91, | ||||||
| "num_statements": 100, | ||||||
| "covered_branches": 79, | ||||||
| "num_branches": 100, | ||||||
| } | ||||||
| } | ||||||
| ) | ||||||
| ) | ||||||
| script = REPO_ROOT / "scripts" / "coverage_floor.py" | ||||||
| held = subprocess.run( | ||||||
| [sys.executable, str(script), str(report), "--statements", "90", "--branches", "79"], | ||||||
| capture_output=True, | ||||||
| text=True, | ||||||
| ) | ||||||
| assert held.returncode == 0, held.stderr | ||||||
| slipped = subprocess.run( | ||||||
| [sys.executable, str(script), str(report), "--statements", "90", "--branches", "80"], | ||||||
| capture_output=True, | ||||||
| text=True, | ||||||
| ) | ||||||
| assert slipped.returncode == 1 | ||||||
| assert slipped.stderr.strip() == "coverage_floor: below the floor: branches" | ||||||
|
Comment on lines
+650
to
+677
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧰 Tools🪛 ast-grep (0.45.3)[info] 635-644: use jsonify instead of json.dumps for JSON output (use-jsonify) [error] 647-651: Command coming from incoming request (subprocess-from-request) [error] 653-657: Command coming from incoming request (subprocess-from-request) 🤖 Prompt for AI Agents |
||||||
|
|
||||||
|
|
||||||
| # --- a build anyone can repeat --------------------------------------------------------------- | ||||||
|
|
||||||
|
|
||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Configure subprocess coverage for pytest-cov 7.1.0.
When CI sets
CTRLRUN_COVERAGE,scripts/check.shruns pytest-cov and writescoverage.json. The test path reachesrun_attempts()insrc/ctrlrun/verify/scenarios.py, which startsctrlrun.verify.workerwithsubprocess.Popen. pytest-cov 7.1.0 does not instrument such child interpreters without coverage subprocess patching. The worker's execution can therefore be absent fromcoverage.json, and the coverage floors can fail.Add the supported coverage configuration:
🤖 Prompt for AI Agents