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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,22 @@ jobs:
- name: Run tests
run: uv run --no-sync python -m pytest -q

- name: Measure coverage against the production source layout
run: |
set -euo pipefail
uv run --no-sync python -m coverage run -m pytest -q
uv run --no-sync python -m coverage report --show-missing
uv run --no-sync python - <<'PY'
from coverage import CoverageData

data = CoverageData(basename=".coverage")
data.read()
files = sorted(data.measured_files())
if not files:
raise SystemExit("coverage measured no files")
for path in files:
print(path)
PY

- name: Build package
run: uv run --no-sync python -m build
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,12 @@ ignore = ["E701", "E702", "E741", "F841", "F821", "B008", "F401", "F601", "E402"

[tool.coverage.run]
branch = true
source = ["src"]
omit = ["tests/*", "*/external/*"]
source = ["."]
omit = ["tests/*", "*/external/*", ".venv/*", "build/*", "dist/*"]

[tool.coverage.report]
show_missing = true
include_namespace_packages = true

[tool.uv]
override-dependencies = [
Expand Down
164 changes: 164 additions & 0 deletions tests/test_coverage_source_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
"""Verify the coverage source layout measures shipped production code.

The denominator is the repository tree minus explicitly excluded categories.
A hermetic synthetic tree proves that unexecuted in-scope lines and files
remain in the denominator while excluded test/external/build code cannot
inflate the measurement.
"""

from __future__ import annotations

from pathlib import Path
import re
import subprocess
import sys
import tempfile
import tomllib
import unittest


ROOT = Path(__file__).resolve().parents[1]

SYNTHETIC_MODULE = (
"def executed():\n"
" return 1\n"
"\n"
"\n"
"def never_executed():\n"
" value = 1\n"
" return value\n"
)

CANONICAL_OMIT_CATEGORIES = ("tests/", "external/", ".venv/", "build/", "dist/")


def pyproject():
return tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))


def coverage_run_config():
return pyproject()["tool"]["coverage"]["run"]


def coverage_report_config():
return pyproject()["tool"]["coverage"]["report"]


def declared_production_sources():
data = pyproject()
modules = data["tool"]["setuptools"]["py-modules"]
include = data["tool"]["setuptools"]["packages"]["find"]["include"]
packages = sorted({p.name for pattern in include for p in ROOT.glob(pattern) if p.is_dir()})
return modules, packages


def matches_any_omit(path, omit_patterns):
for pattern in omit_patterns:
regex = "".join(".*" if char == "*" else re.escape(char) for char in pattern)
if re.fullmatch(regex, path):
return True
return False


class CoverageSourceLayoutTests(unittest.TestCase):
def test_source_is_repository_root_with_explicit_category_exclusions(self):
run = coverage_run_config()
report = coverage_report_config()
self.assertEqual(run["source"], ["."])
self.assertTrue(report.get("include_namespace_packages"))
self.assertFalse((ROOT / "src").exists())
self.assertNotIn("fail_under", str(pyproject()["tool"]["coverage"]))
omit = " ".join(run["omit"])
for category in CANONICAL_OMIT_CATEGORIES:
self.assertIn(category, omit)

def test_declared_production_paths_are_measured(self):
modules, packages = declared_production_sources()
omit = coverage_run_config()["omit"]
for module in modules:
relative = f"{module}.py"
self.assertTrue((ROOT / relative).exists(), relative)
self.assertFalse(matches_any_omit(relative, omit), relative)
for package in packages:
self.assertFalse(matches_any_omit(package, omit), package)

def test_unexecuted_in_scope_files_remain_in_denominator(self):
modules, packages = declared_production_sources()
run = coverage_run_config()
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
for module in modules:
(root / f"{module}.py").write_text(SYNTHETIC_MODULE, encoding="utf-8")
for package in packages:
(root / package).mkdir()
(root / packages[0] / "synthetic_service.py").write_text(
SYNTHETIC_MODULE, encoding="utf-8"
)
excluded = "def never_executed():\n value = 1\n return value\n"
for directory in ("tests", "external", ".venv", "build", "dist"):
(root / directory).mkdir(parents=True)
(root / directory / "excluded_module.py").write_text(excluded, encoding="utf-8")
imported = modules[:-1]
imports = [
f"importlib.import_module({module!r}).executed()" for module in imported
]
(root / "runner.py").write_text(
"import importlib\n" + "\n".join(imports) + "\nprint('done')\n",
encoding="utf-8",
)
(root / ".coveragerc").write_text(
"[run]\n"
"branch = true\n"
"source =\n"
+ "".join(f" {entry}\n" for entry in run["source"])
+ "omit =\n"
+ "".join(f" {pattern}\n" for pattern in run["omit"])
+ "[report]\n"
f"include_namespace_packages = {coverage_report_config().get('include_namespace_packages', False)}\n",
encoding="utf-8",
)
collected = subprocess.run(
[sys.executable, "-m", "coverage", "run", "runner.py"],
cwd=root,
capture_output=True,
text=True,
timeout=60,
check=False,
)
self.assertEqual(collected.returncode, 0, collected.stderr)
report = subprocess.run(
[sys.executable, "-m", "coverage", "report", "--show-missing"],
cwd=root,
capture_output=True,
text=True,
timeout=60,
check=False,
)
self.assertEqual(report.returncode, 0, report.stderr)
output = report.stdout
self.assertNotIn("No data to report", output)
lines = {
candidate.split()[0]: candidate
for candidate in output.splitlines()
if candidate and not candidate.startswith(("Name", "-", "TOTAL"))
}
for module in imported:
self.assertIn(module + ".py", lines)
self.assertIn("6-7", lines[module + ".py"])
never_imported = modules[-1]
self.assertIn(never_imported + ".py", lines)
self.assertIn("0%", lines[never_imported + ".py"])
service_path = f"{packages[0]}/synthetic_service.py"
self.assertIn(service_path, lines)
self.assertIn("0%", lines[service_path])
for directory in ("tests", "external", ".venv", "build", "dist"):
self.assertNotIn(f"{directory}/excluded_module.py", output, directory)

def test_ci_measurement_step_runs_the_suite_instrumented(self):
workflow = (ROOT / ".github/workflows/ci.yml").read_text(encoding="utf-8")
self.assertIn("coverage run -m pytest", workflow)
self.assertIn("coverage report --show-missing", workflow)


if __name__ == "__main__":
unittest.main()
Loading