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.
139 lines
5.9 KiB
Bash
Executable File
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
|