coverage.sh

2.7 KB · raw

1#!/bin/bash2# Run the full Haven test suite and enforce physical source-line coverage.3set -euo pipefail45REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"6cd "${REPO_ROOT}"78workdir="$(mktemp -d "${TMPDIR:-/tmp}/haven-coverage.XXXXXX")"9cleanup() { trash "${workdir}"; }10trap cleanup EXIT1112swift test --enable-code-coverage1314binary=".build/debug/Haven"15test_profile=".build/debug/codecov/default.profdata"16smoke_profile="${workdir}/smoke.profraw"17merged_profile="${workdir}/merged.profdata"1819LLVM_PROFILE_FILE="${smoke_profile}" HAVEN_COVERAGE_SMOKE=1 "${binary}"20test -s "${smoke_profile}"21xcrun llvm-profdata merge -sparse "${test_profile}" "${smoke_profile}" -o "${merged_profile}"2223printf '\nLLVM aggregate report (compiler-generated mappings included):\n'24xcrun llvm-cov report "${binary}" \25  -instr-profile="${merged_profile}" \26  --ignore-filename-regex='(Tests|\\.build)'2728xcrun llvm-cov export "${binary}" \29  -instr-profile="${merged_profile}" \30  -format=text > "${workdir}/coverage.json"3132REPO_ROOT="${REPO_ROOT}" COVERAGE_JSON="${workdir}/coverage.json" python3 <<'PY'33import json34import os35from pathlib import Path3637repo_root = Path(os.environ["REPO_ROOT"]).resolve()38repo_prefix = f"{repo_root.as_posix().rstrip('/')}/"39coverage = json.loads(Path(os.environ["COVERAGE_JSON"]).read_text())4041covered = 042total = 043missed = []4445for entry in coverage["data"][0]["files"]:46    source_path = Path(entry["filename"]).resolve()47    source_text = source_path.as_posix()48    if not source_text.casefold().startswith(repo_prefix.casefold()):49        continue50    relative_path = Path(source_text[len(repo_prefix) :])51    if relative_path.parts[:2] != ("Sources", "Haven"):52        continue5354    source_lines = source_path.read_text().splitlines()55    line_counts = {}56    for segment in entry["segments"]:57        line, _, count, has_count, *_ = segment58        if has_count:59            line_counts[line] = max(line_counts.get(line, 0), count)6061    for line, count in sorted(line_counts.items()):62        if line > len(source_lines):63            continue64        text = source_lines[line - 1].strip()65        if (66            not text67            or text.startswith("//")68            or text.startswith("/*")69            or text.startswith("*")70            or text.startswith("*/")71            or not any(character.isalnum() for character in text)72        ):73            continue74        total += 175        if count > 0:76            covered += 177        else:78            missed.append(f"{relative_path}:{line}: {text}")7980print(f"\nPhysical repository source lines: {covered}/{total} covered")81if missed:82    print("Uncovered physical source lines:")83    print("\n".join(missed))84    raise SystemExit(1)85PY