page-unit.ts and native-bridge.ts run the page's script outside a browser, so the page's coverage counted none of what they test, and native.ts read as mostly untested. In a coverage run they now run the script from a file of its own, named as the browser names it, under NODE_V8_COVERAGE (scripts/coverage-node.sh, before the browser suite in npm run coverage:page and coverage.sh api), and the report's teardown takes their record in, mapped to web/src by the build's source map: the page's lines went from 73.3% to 80.0% of 3,043, native.ts to 93 of 120. The cargo tests in coverage.sh's container run on Postgres, as production does, when IPX_TEST_DATABASE_URL names the _test database, from the shell or else /src/.envrc (that variable only); on SQLite without it. The integration tests' own files are left out of the totals; a crate's inline mod tests cannot be on stable Rust. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
128 lines
5.8 KiB
Bash
128 lines
5.8 KiB
Bash
#!/bin/sh
|
|
# Which lines of the Rust the tests reach (#390, #394), inside the limits builds are held to.
|
|
#
|
|
# sh scripts/coverage.sh the cargo tests, all three crates, then their report
|
|
# sh scripts/coverage.sh ipx-feed one crate's cargo tests, then the report of what has run
|
|
# sh scripts/coverage.sh api the browser suite against an instrumented ipx and
|
|
# ipx-directory: what the API and the daemon run (and the
|
|
# page's coverage, which the same run measures)
|
|
#
|
|
# A coverage build instruments every crate, dependencies included, in a target of its own: a whole
|
|
# rebuild, the kind that took Tower down on 2026-11-04. So it builds in a container under the same
|
|
# cap as scripts/build-image.sh's builder (6 GB, six threads), past which the kernel kills it and
|
|
# production is left alone; and, as CLAUDE.md asks of tests, one crate at a time. If the cap kills
|
|
# it, say so and leave the cap be. The browser suite runs here, as it always does, its servers the
|
|
# container's build: the image is Ubuntu's, as code-server is, so they start here.
|
|
#
|
|
# The cargo tests run on Postgres, production's database, when IPX_TEST_DATABASE_URL names the
|
|
# _test one (#407), from the shell or else /src/.envrc; on SQLite without it, whose paths are then
|
|
# the only ones measured. The integration tests' own files (tests/) are left out of the totals;
|
|
# a crate's inline `mod tests` cannot be, on stable Rust.
|
|
#
|
|
# Reports: target/coverage/cargo and target/coverage/api (summary.txt, a line a file; lcov.info;
|
|
# html/index.html), target/coverage/page (npm run coverage:page's, or api's). Each run ends by
|
|
# writing the numbers for Grafana (scripts/coverage-metrics.ts).
|
|
set -eu
|
|
|
|
MEM=6g
|
|
CPUS=0-5
|
|
NEED_KB=$((9 * 1024 * 1024))
|
|
MAX_LOAD=12
|
|
IMAGE=192.168.1.130:5000/ipx-coverage:latest
|
|
# The Docker daemon is Tower's, so its paths are Tower's: this tree is /mnt/user/vscode/ipx there,
|
|
# and target/ is a link to target-real on /mnt/fast (builds through /mnt/user's FUSE took the load
|
|
# to 248, CLAUDE.md). code-server sees /mnt/fast at the same path, so the instrumented binaries
|
|
# and the profiles they write are at the same place for both.
|
|
SRC=/mnt/user/vscode/ipx
|
|
TARGET=/mnt/fast/vscode/ipx/target-real
|
|
REGISTRY=$TARGET/coverage-registry
|
|
LLVM_TARGET=$TARGET/llvm-cov-target
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
# Only the one variable, not the whole file: it holds keys the tests are not to see.
|
|
if [ -z "${IPX_TEST_DATABASE_URL:-}" ] && [ -f /src/.envrc ]; then
|
|
IPX_TEST_DATABASE_URL=$(sed -n 's/^export IPX_TEST_DATABASE_URL=//p' /src/.envrc | tr -d "\"'")
|
|
fi
|
|
export IPX_TEST_DATABASE_URL="${IPX_TEST_DATABASE_URL:-}"
|
|
[ -n "$IPX_TEST_DATABASE_URL" ] && echo "the cargo tests run on Postgres (IPX_TEST_DATABASE_URL)"
|
|
|
|
room() {
|
|
avail=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
|
|
load=$(cut -d' ' -f1 /proc/loadavg)
|
|
echo "MemAvailable $((avail / 1024)) MB, load $load"
|
|
if [ "$avail" -lt "$NEED_KB" ]; then
|
|
echo "refusing: under $((NEED_KB / 1024 / 1024)) GB available. Wait, or find what is using it." >&2
|
|
exit 1
|
|
fi
|
|
if awk -v l="$load" -v m="$MAX_LOAD" 'BEGIN { exit !(l > m) }'; then
|
|
echo "refusing: load over $MAX_LOAD. Wait for it to come down." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
run() {
|
|
# The source read only: nothing in the tree is the coverage run's to change. The registry is
|
|
# kept between runs, so the crates are downloaded once.
|
|
docker run --rm --memory "$MEM" --memory-swap "$MEM" --cpuset-cpus "$CPUS" \
|
|
-v "$SRC:/work:ro" -v "$TARGET:/target" -v "$REGISTRY:/usr/local/cargo/registry" \
|
|
-e CARGO_TARGET_DIR=/target/llvm-cov-target -e CARGO_LLVM_COV_TARGET_DIR=/target/llvm-cov-target \
|
|
-e IPX_TEST_DATABASE_URL \
|
|
"$IMAGE" "$@" || {
|
|
code=$?
|
|
[ "$code" -eq 137 ] && echo "killed at the ${MEM} memory cap: tell the user, do not raise it" >&2
|
|
exit "$code"
|
|
}
|
|
}
|
|
|
|
# The report of the profiles in the target, into target/coverage/$1, with the tree's own paths.
|
|
report() {
|
|
room
|
|
mkdir -p "$TARGET/coverage/$1"
|
|
skip="--ignore-filename-regex '(^|/)tests/'"
|
|
run sh -c "cargo llvm-cov report $skip --lcov --output-path /target/coverage/$1/lcov.info \
|
|
&& cargo llvm-cov report $skip --html --output-dir /target/coverage/$1 \
|
|
&& cargo llvm-cov report $skip --summary-only > /target/coverage/$1/summary.txt"
|
|
# The paths inside the container are /work/...; the tree's own, from here.
|
|
sed -i 's#/work/##' "$TARGET/coverage/$1/summary.txt" "$TARGET/coverage/$1/lcov.info"
|
|
tail -1 "$TARGET/coverage/$1/summary.txt"
|
|
}
|
|
|
|
docker pull -q "$IMAGE" >/dev/null
|
|
mkdir -p "$REGISTRY"
|
|
|
|
if [ "${1:-}" = api ]; then
|
|
# Built as cargo-llvm-cov builds for a test of its own, instrumented, with no tests: the
|
|
# binaries the browser suite starts. Profiles from an earlier run are not this one's.
|
|
run cargo llvm-cov clean --workspace
|
|
for p in ipx-directory ipx; do
|
|
room
|
|
echo "== build $p"
|
|
run sh -c "eval \"\$(cargo llvm-cov show-env --export-prefix)\" && cargo build --locked -p $p"
|
|
done
|
|
room
|
|
echo "== the browser suite"
|
|
# Not `set -e`'s to stop on: a failing test still ran what it ran, and the report says so; the
|
|
# suite's own exit code is this script's at the end.
|
|
suite=0
|
|
sh scripts/coverage-node.sh
|
|
IPX_API_COVERAGE=$LLVM_TARGET IPX_COVERAGE=1 IPX_SIGNALS_TESTS=1 LLVM_PROFILE_FILE="$LLVM_TARGET/ipx-browser-%p-%14m.profraw" \
|
|
npx playwright test || suite=$?
|
|
ls "$LLVM_TARGET"/ipx-browser-*.profraw >/dev/null 2>&1 \
|
|
|| { echo "no profiles were written: did the servers stop on SIGTERM?" >&2; exit 1; }
|
|
report api
|
|
node scripts/coverage-metrics.ts
|
|
exit "$suite"
|
|
fi
|
|
|
|
crates=${1:-ipx-feed ipx-directory ipx}
|
|
# A whole run starts from nothing; one crate adds to what has run.
|
|
[ $# -eq 0 ] && run cargo llvm-cov clean --workspace
|
|
for c in $crates; do
|
|
room
|
|
echo "== $c"
|
|
run cargo llvm-cov --no-report --locked -p "$c"
|
|
done
|
|
report cargo
|
|
node scripts/coverage-metrics.ts
|