#!/usr/bin/env sh # Per-workspace-crate line and branch coverage report. # # This is deliberately a report over every host-testable workspace package, # not a misleading aggregate. STM32F7 platform/board images are target-only: # their `panic-halt` handler cannot link into the host test harness, so they # are reported as N/A and must be covered by target checks plus bench # evidence instead. # # Prerequisites (kept out of the firmware dependency graph): # cargo install cargo-llvm-cov --locked # rustup toolchain install nightly --component llvm-tools-preview # python3 (standard library only, for strict source-level accounting) set -eu if ! cargo llvm-cov --version >/dev/null 2>&1; then echo "cargo-llvm-cov is required; install it with: cargo install cargo-llvm-cov --locked" >&2 exit 127 fi if ! rustup toolchain list | grep -q '^nightly'; then echo "nightly with llvm-tools-preview is required for branch coverage" >&2 exit 127 fi if ! python3 --version >/dev/null 2>&1; then echo "python3 is required for strict source-level coverage accounting" >&2 exit 127 fi python3 scripts/test-summarize-test-coverage.py # Keep instrumentation products outside a possibly shared target directory. : "${CARGO_TARGET_DIR:=/tmp/kaptan-test-coverage}" export CARGO_TARGET_DIR mkdir -p "$CARGO_TARGET_DIR" # `cargo llvm-cov` merges every instrumented object below `CARGO_TARGET_DIR`. # A shared target directory therefore makes a later strict-core report include # an unexecuted copy of a shared dependency built while testing an earlier # package. Keep reports together but isolate each strict-core invocation. The # non-core packages retain the shared directory: several intentionally # target-only crates have no host-instrumented object to export by themselves. coverage_root=$CARGO_TARGET_DIR core_incomplete=0 report_package() { package=$1 source_path=$2 shift 2 case "$package" in kaptan-math | kaptan-navigation | kaptan-control | kaptan-safety) package_target_dir="$coverage_root/$package" lcov_report="$coverage_root/$package.lcov" json_report="$coverage_root/$package.json" report_format=strict-core ;; *) package_target_dir=$coverage_root lcov_report="$coverage_root/$package.lcov" report_format=lcov ;; esac case "$report_format" in strict-core) # Keep the strict core isolated, then export both representations # from the same test run. LCOV `DA` defines source lines; JSON # coordinates define branch identities across generic instances. RUSTC_BOOTSTRAP=1 CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov \ --branch \ --no-report \ -p "$package" \ "$@" RUSTC_BOOTSTRAP=1 CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov report \ --lcov \ --output-path "$lcov_report" RUSTC_BOOTSTRAP=1 CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov report \ --json \ --output-path "$json_report" totals=$(python3 scripts/summarize-test-coverage.py \ "$lcov_report" "$json_report" "$source_path") ;; lcov) RUSTC_BOOTSTRAP=1 CARGO_TARGET_DIR="$package_target_dir" cargo llvm-cov \ --branch \ --lcov \ --output-path "$lcov_report" \ -p "$package" \ "$@" 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") ;; esac set -- $totals printf '%-26s lines %s/%s, branches %s/%s\n' "$package" "$1" "$2" "$3" "$4" case "$package" in kaptan-math | kaptan-navigation | kaptan-control | kaptan-safety) if [ "$1" -ne "$2" ] || [ "$3" -ne "$4" ]; then core_incomplete=1 fi ;; esac } echo "Host test coverage (reports: $CARGO_TARGET_DIR; strict core unions LCOV DA lines and JSON branches)" report_package kaptan-actuator /libs/kaptan-actuator/ report_package kaptan-control /libs/kaptan-control/ report_package kaptan-data-channel /libs/kaptan-data-channel/ report_package kaptan-dma /libs/kaptan-dma/ report_package kaptan-drivers /libs/kaptan-drivers/ report_package kaptan-i2c /libs/kaptan-i2c/ report_package kaptan-math /libs/kaptan-math/ report_package kaptan-mavlink /libs/kaptan-mavlink/ --features sensor-telemetry report_package kaptan-navigation /libs/kaptan-navigation/ --features sitl report_package kaptan-safety /libs/kaptan-safety/ report_package kaptan-sensor /libs/kaptan-sensor/ report_package kaptan-spi /libs/kaptan-spi/ report_package kaptan-time /libs/kaptan-time/ report_package kaptan-tx /libs/kaptan-tx/ report_package kaptan-sitl /tools/sitl/ echo "platform-stm32f7 target-only/N/A (run cargo check -p platform-stm32f7 --lib)" echo "kaptan-geprc-f722-v2 target-only/N/A (run its thumbv7em-none-eabihf check)" echo "kaptan-kakute-f7-mini-v3 target-only/N/A (run its thumbv7em-none-eabihf check)" if [ "$core_incomplete" -ne 0 ]; then echo "Strict core coverage is incomplete; reports above identify each crate." >&2 exit 1 fi