#!/usr/bin/env bash # # coverage-gate.sh — T-iOS-16 per-package OWN-SOURCES coverage gate (>= 80%). # # Usage: coverage-gate.sh # e.g. coverage-gate.sh WireProtocol # Env: COVERAGE_THRESHOLD= # default 80 (red/green demo knob) # # WHY THIS EXISTS (plan §9 flaw, fix assigned to T-iOS-16): the raw §9 command # xcrun llvm-cov export -summary-only ... -ignore-filename-regex '(Tests|TestSupport|\.build)/' # | jq '.data[0].totals.lines.percent >= 80' # reads the export TOTALS, which include every STATICALLY-LINKED DEPENDENCY # source compiled into the test binary (e.g. WireProtocol sources inside # SessionCore's test run). That lets a package's own weak coverage hide behind a # well-covered dependency (or vice versa). The corrected filter below keeps ONLY # files under Packages/

/Sources/ (excluding *Placeholder* scaffolding) and # recomputes line coverage from the per-file summaries. # # Exit codes: 0 = gate passed; 1 = below threshold, filter matched no sources, # or any build/test failure (set -e). set -euo pipefail PKG="${1:?usage: coverage-gate.sh (WireProtocol|SessionCore|HostRegistry|APIClient)}" THRESHOLD="${COVERAGE_THRESHOLD:-80}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" PKG_DIR="$REPO_ROOT/ios/Packages/$PKG" if [ ! -d "$PKG_DIR" ]; then echo "coverage-gate($PKG): package dir not found: $PKG_DIR" >&2 exit 1 fi # 1) Run the package tests with coverage instrumentation (test failure => gate red). swift test --package-path "$PKG_DIR" --enable-code-coverage # 2) Locate the test binary and the merged profile (paths per plan §9). BIN="$(swift build --package-path "$PKG_DIR" --show-bin-path)/${PKG}PackageTests.xctest/Contents/MacOS/${PKG}PackageTests" PROF="$(dirname "$(swift test --package-path "$PKG_DIR" --show-codecov-path)")/default.profdata" # 3) Export per-file summaries and keep ONLY this package's own production # sources: path must contain /Packages//Sources/ and must not be a # *Placeholder* scaffold file. Dependency sources (other packages), Tests/, # TestSupport and .build shims all fail the path filter automatically. read -r COVERED TOTAL < <( xcrun llvm-cov export -summary-only "$BIN" -instr-profile "$PROF" \ | jq -r --arg pkg "$PKG" ' [.data[0].files[] | select(.filename | contains("/Packages/" + $pkg + "/Sources/")) | select(.filename | contains("Placeholder") | not) | .summary.lines] | "\(map(.covered) | add // 0) \(map(.count) | add // 0)"' ) if [ "$TOTAL" -eq 0 ]; then echo "coverage-gate($PKG): FAIL — filter matched no own-source files under" \ "Packages/$PKG/Sources/ (filter bug or empty package?)" >&2 exit 1 fi # LC_ALL=C on both awk calls: a comma-decimal locale would print "100,00", # which awk then compares as a STRING against the threshold (silent false FAIL). PCT="$(LC_ALL=C awk -v c="$COVERED" -v t="$TOTAL" 'BEGIN { printf "%.2f", c * 100 / t }')" echo "coverage-gate($PKG): own-sources line coverage ${PCT}% (${COVERED}/${TOTAL} lines, threshold ${THRESHOLD}%)" if ! LC_ALL=C awk -v p="$PCT" -v th="$THRESHOLD" 'BEGIN { exit !(p >= th) }'; then echo "coverage-gate($PKG): FAIL — below threshold ${THRESHOLD}%" >&2 exit 1 fi echo "coverage-gate($PKG): PASS"