Files
kaptan/firmware/scripts/report-test-coverage.sh
Onur Özkan 62b18d5b2e build: replace argument dispatch and retain source-bound verification
Replace argument recovery with direct build targets. Run host, target,
board, coverage, Miri and selected Kani checks while retaining source
identities, logs and artifacts, including failed attempts.

Keep the PID proof diagnostic exception explicit and match its exact
identity alongside passing contract assertions and covers.

Validation: final-source verification matrix, assembled workflow tests
and documentation build.
2026-11-07 18:12:11 +03:00

139 lines
5.9 KiB
Bash
Executable File

#!/usr/bin/env sh
# Host workspace coverage. The four strict-core packages require 100% source
# lines and branch outcomes; all other package totals are informational.
# Run from firmware/ in the locked Nix shell (cargo-llvm-cov 0.8.7).
# Install the separate coverage compiler with:
# rustup toolchain install nightly-2025-08-01 --profile minimal --component llvm-tools-preview
set -eu
export RUSTUP_TOOLCHAIN=nightly-2025-08-01
export CARGO_BUILD_JOBS=2
if [ "$(cargo llvm-cov --version)" != "cargo-llvm-cov 0.8.7" ]; then
echo "Use cargo-llvm-cov 0.8.7 from nix develop (flake.lock)." >&2
exit 127
fi
if [ -n "${RUSTC_BOOTSTRAP:-}" ] || [ -n "${RUSTC:-}" ] || [ -n "${RUSTDOC:-}" ]; then
echo "Coverage requires the pinned compiler without RUSTC_BOOTSTRAP/RUSTC/RUSTDOC overrides." >&2
exit 2
fi
sysroot=$(rustc --print sysroot)
host=$(rustc -vV | awk '/^host:/ { print $2 }')
export LLVM_COV="$sysroot/lib/rustlib/$host/bin/llvm-cov"
export LLVM_PROFDATA="$sysroot/lib/rustlib/$host/bin/llvm-profdata"
rustc -vV
cargo llvm-cov --version
"$LLVM_COV" --version
"$LLVM_PROFDATA" --version
echo "Coverage profile: dev; compiler and LLVM tools: $RUSTUP_TOOLCHAIN"
python3 scripts/test-summarize-test-coverage.py
# Keep coverage out of ordinary build artifacts. Strict-core invocations also
# need separate object directories to avoid unexecuted dependency duplicates.
: "${CARGO_TARGET_DIR:=$PWD/target/test-coverage}"
coverage_root=$CARGO_TARGET_DIR
mkdir -p "$coverage_root"
# Discover current workspace members, including host platform contracts and
# proc-macro runtime harnesses. Only bare-metal boards are excluded.
# Put the strict core first so unrelated test failures cannot hide its gaps.
cargo metadata --no-deps --format-version 1 --locked --offline >"$coverage_root/metadata.json"
python3 - "$coverage_root/metadata.json" >"$coverage_root/packages.txt" <<'PY'
import json
from pathlib import Path
import sys
metadata = json.loads(Path(sys.argv[1]).read_text())
excluded = {"kaptan-geprc-f722-v2", "kaptan-kakute-f7-mini-v3"}
core = {"kaptan-math", "kaptan-navigation", "kaptan-control", "kaptan-safety"}
packages = [p for p in metadata["packages"] if p["id"] in metadata["workspace_members"] and p["name"] not in excluded]
if not core <= {p["name"] for p in packages}:
raise ValueError("missing strict-core package")
for package in sorted(packages, key=lambda p: (p["name"] not in core, p["name"])):
source = Path(package["manifest_path"]).parent.relative_to(metadata["workspace_root"])
print(package["name"], f"/{source}/")
PY
report_package() {
package=$1
source_path=$2
shift 2
lcov_report="$coverage_root/$package.lcov"
case "$package" in
kaptan-math | kaptan-navigation | kaptan-control | kaptan-safety)
package_target_dir="$coverage_root/$package"
json_report="$coverage_root/$package.json"
report_format=strict-core
;;
*)
package_target_dir="$coverage_root/other"
report_format=lcov
;;
esac
printf '\nCoverage package: %s; extra arguments: %s\n' "$package" "$*"
CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov --profile dev \
--locked --offline --branch --no-report -p "$package" "$@" || return 1
CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov report --profile dev \
--locked --offline --lcov --output-path "$lcov_report" || return 1
case "$report_format" in
strict-core)
# LCOV DA defines source lines. JSON spans identify branch outcomes
# across generic instances. Both exports use the same compiler/profile.
CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov report --profile dev \
--locked --offline --json --output-path "$json_report" || return 1
totals=$(python3 scripts/summarize-test-coverage.py \
"$lcov_report" "$json_report" "$source_path") || return 1
;;
lcov)
totals=$(awk -v source_path="$source_path" '
/^SF:/ { selected = index($0, source_path) != 0 }
selected && /^LF:/ { lines_found += substr($0, 4) }
selected && /^LH:/ { lines_hit += substr($0, 4) }
selected && /^BRF:/ { branches_found += substr($0, 5) }
selected && /^BRH:/ { branches_hit += substr($0, 5) }
END {
printf "%d %d %d %d", lines_hit, lines_found, branches_hit, branches_found
}
' "$lcov_report") || return 1
;;
esac
# Both summarizers emit exactly four space-separated integer counters.
# shellcheck disable=SC2086
set -- $totals
printf '%-30s lines %s/%s, branches %s/%s\n' "$package" "$1" "$2" "$3" "$4"
if [ "$report_format" = strict-core ]; then
if [ "$2" -eq 0 ] || [ "$4" -eq 0 ] || [ "$1" -ne "$2" ] || [ "$3" -ne "$4" ]; then
echo "$package: FAIL strict 100% line/branch gate" >&2
return 1
fi
elif [ "$2" -eq 0 ]; then
echo "$package: no instrumented host source; coverage unavailable"
fi
}
echo "Host test coverage (reports: $coverage_root; strict core unions LCOV DA lines and JSON branches)"
echo "UI/compile-fail suites run under Rust 1.90.0 in make test, not under coverage nightly."
echo "Proc-macro runtime-harness totals do not establish compiler-phase macro coverage."
incomplete=0
while read -r package source_path; do
set --
case "$package" in
kaptan-navigation) set -- --features sensor-snapshots ;;
kaptan-mavlink) set -- --features sensor-telemetry ;;
# Diagnostic snapshots describe the production compiler's output. Keep
# those suites in stable make test; instrument the runtime harnesses here.
kaptan-config-derive) set -- --lib --test section ;;
kaptan-err-reportable-macros) set -- --lib ;;
esac
if ! report_package "$package" "$source_path" "$@"; then
echo "$package: coverage/test failure (see diagnostics above)" >&2
incomplete=1
fi
done <"$coverage_root/packages.txt"
echo "kaptan-geprc-f722-v2, kaptan-kakute-f7-mini-v3: target-only; make boards validates release images"
if [ "$incomplete" -ne 0 ]; then
echo "Coverage gate failed. Strict core gaps and test/report failures are listed above." >&2
exit 1
fi