import argparse
import json
import sys
from pathlib import Path
AREAS = [
("src/diff/", "diff/ - the engine"),
("src/code/", "code/ - parsing, metadata"),
("src/tui/", "tui/ - viewer, headless"),
("src/stats/", "stats/ - sampling, git"),
("src/test/", "test/ - fixture helpers"),
("src/bin/", "bin/ - dev tools"),
]
def area_of(path: str) -> str:
best = ""
label = "other"
for prefix, name in AREAS:
if prefix in path and len(prefix) > len(best):
best, label = prefix, name
return label
def badge_color(percent: float) -> str:
for floor, color in (
(90, "brightgreen"),
(80, "green"),
(70, "yellowgreen"),
(60, "yellow"),
):
if percent >= floor:
return color
return "orange"
def write_badge(path: Path, product: list[int], everything: list[int]) -> None:
lib = 100.0 * product[0] / product[1] if product[1] else 0.0
total = 100.0 * everything[0] / everything[1] if everything[1] else 0.0
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(
{
"schemaVersion": 1,
"label": "coverage",
"message": f"{lib:.0f}% lib ยท {total:.0f}% all",
"color": badge_color(lib),
},
indent=2,
)
+ "\n"
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"--badge",
type=Path,
metavar="PATH",
help="also write a shields.io endpoint JSON here, for the README badge",
)
args = parser.parse_args()
report = json.load(sys.stdin)
files = report["data"][0]["files"]
totals: dict[str, list[int]] = {}
worst: list[tuple[int, str, float]] = []
for entry in files:
lines = entry["summary"]["lines"]
covered, count = lines["covered"], lines["count"]
if count == 0:
continue
bucket = totals.setdefault(area_of(entry["filename"]), [0, 0])
bucket[0] += covered
bucket[1] += count
worst.append((count - covered, entry["filename"], lines["percent"]))
def row(label: str, covered: int, count: int) -> str:
percent = 100.0 * covered / count if count else 0.0
return f" {label:<26} {covered:>6}/{count:<6} lines {percent:5.1f}%"
print("Line coverage by area")
product = [0, 0]
for _, label in AREAS:
if label not in totals:
continue
covered, count = totals[label]
print(row(label, covered, count))
if not label.startswith("bin/"):
product[0] += covered
product[1] += count
print()
print(row("PRODUCT (everything but bin/)", *product))
everything = [
sum(v[0] for v in totals.values()),
sum(v[1] for v in totals.values()),
]
print(row("EVERYTHING", *everything))
if args.badge:
write_badge(args.badge, product, everything)
print(f"\nBadge written to {args.badge}")
worst.sort(reverse=True)
print("\nMost uncovered lines")
for missed, filename, percent in worst[:10]:
short = filename.split("/src/", 1)[-1]
print(f" {missed:>5} missed {percent:5.1f}% {short}")
return 0
if __name__ == "__main__":
sys.exit(main())