packages feed

slackblocks-3.1.0: bin/quality

#!/usr/bin/env python3
"""Run all source, compiled API and handwritten coverage gates."""
import os
from pathlib import Path
import re
import subprocess
import sys
import xml.etree.ElementTree as ET

ROOT = Path(__file__).resolve().parents[1]
GENERATED = {"Slackblocks.Models", "Slackblocks.Internal.Schema"}


def run(*args, **kwargs):
    return subprocess.run(args, cwd=ROOT, check=True, **kwargs)


def coverage(xml, expected):
    tree = ET.fromstring(xml)
    modules = {}
    for module in tree.iter("module"):
        name = module.attrib["name"].split("/")[-1]
        if name in GENERATED:
            continue
        expressions = module.find("exprs")
        if expressions is None:
            raise ValueError(f"Missing expression coverage for {name}")
        if name in modules:
            raise ValueError(f"Duplicate module receipt: {name}")
        used, total = int(expressions.attrib["count"]), int(expressions.attrib["boxes"])
        if not 0 <= used <= total:
            raise ValueError(f"Invalid coverage counts: {name}")
        modules[name] = (used, total)
    if set(modules) != expected:
        raise ValueError(f"Missing/unexpected handwritten modules: {set(modules) ^ expected}")
    used = sum(value[0] for value in modules.values())
    total = sum(value[1] for value in modules.values())
    if not total or used * 100 < total * 95:
        raise ValueError(f"Handwritten expression coverage below 95%: {used}/{total}")
    print(f"Handwritten expressions: {used}/{total} ({100 * used / total:.2f}%)")
    return modules


def main():
    for command, expected in (("ormolu", "ormolu 0.7.7.0"), ("hlint", "HLint v3.8")):
        actual = run(command, "--version", capture_output=True, text=True).stdout.splitlines()[0]
        if actual.split(",", 1)[0] != expected:
            raise SystemExit(f"Expected {expected}; found {actual}")
    run(sys.executable, "generator/generate.py", "--check")
    sources = [p for folder in ('src', 'test', 'consumer', 'integrations/src') for p in sorted((ROOT / folder).rglob('*.hs'))]
    for source in sources:
        run("ormolu", "--mode", "check", str(source))
    # Generated code is checked by formatting, deterministic regeneration and
    # GHC warnings. HLint reviews every handwritten production/test module.
    handwritten = []
    for source in sources:
        module = ".".join(source.relative_to(ROOT / "src").with_suffix("").parts) if source.is_relative_to(ROOT / "src") else None
        if module not in GENERATED:
            handwritten.append(str(source))
    run("hlint", *handwritten)
    run(sys.executable, "bin/api-audit")
    build = Path(os.environ.get("SLACKBLOCKS_HASKELL_COVERAGE", "dist-coverage")).resolve()
    for tix in build.rglob("*.tix"):
        tix.unlink()  # Only this checker's dedicated coverage build tree.
    run("cabal", f"--builddir={build}", "test", "all", "--enable-coverage", "--disable-optimization", "--test-show-details=direct")
    cabal = (ROOT / "slackblocks.cabal").read_text(encoding="utf-8")
    suites = set(re.findall(r"^test-suite (\S+)", cabal, re.M))
    tixes = sorted(build.glob("build/*/*/slackblocks-*/t/*/noopt/hpc/vanilla/tix/*.tix"))
    if {p.stem for p in tixes} != suites or len(tixes) != len(suites):
        raise SystemExit("Incomplete or ambiguous native coverage suite inventory")
    mix = list(build.glob("build/*/*/slackblocks-*/noopt/build/extra-compilation-artifacts/hpc/vanilla/mix"))
    if len(mix) != 1:
        raise SystemExit("Ambiguous library coverage artifacts")
    expected = {".".join(p.relative_to(ROOT / "src").with_suffix("").parts) for p in (ROOT / "src").rglob("*.hs")} - GENERATED
    # A re-export-only module has no executable ticks, but must still have a
    # compiled Mix record. Any added code is automatically included by HPC.
    compiled = {p.stem for p in mix[0].rglob("*.mix")}
    if not expected <= compiled:
        raise SystemExit(f"Uninstrumented production modules: {expected - compiled}")
    empty = {p.stem for p in mix[0].rglob("*.mix") if p.read_text(encoding="utf-8").rstrip().endswith(" []")}
    expected -= empty
    output = build / "quality"
    output.mkdir(exist_ok=True)
    combined = output / "combined.tix"
    run("hpc", "sum", "--union", "--exclude=Main", "--exclude=Constructions", "--exclude=Paths_slackblocks", f"--output={combined}", *(str(p) for p in tixes))
    report = ["hpc", "report", str(combined), f"--hpcdir={mix[0]}", "--per-module", "--xml-output"]
    xml = run(*report, capture_output=True, text=True).stdout
    (output / "coverage.xml").write_text(xml, encoding="utf-8")
    coverage(xml, expected)
    # Mutate actual compiled receipts, not a fabricated percentage, and prove
    # the gate fails when execution disappears or a module is omitted.
    tree = ET.fromstring(xml)
    for exprs in tree.iter("exprs"):
        exprs.set("count", "0")
    for candidate, scope in ((ET.tostring(tree, encoding="unicode"), expected), (xml, expected | {"Slackblocks.Unmeasured"})):
        try:
            coverage(candidate, scope)
        except ValueError:
            pass
        else:
            raise SystemExit("Coverage guard accepted its negative control")
    print("Source formatting, lint, compiled API, 95% handwritten coverage and negative controls passed.")


if __name__ == "__main__":
    main()