fix: mutants-protocol invocation

This commit is contained in:
Roman 2026-05-28 21:27:48 +08:00
parent 1da53a9566
commit ee7b3b0f69
No known key found for this signature in database
GPG Key ID: 583BDF43C238B83E
2 changed files with 69 additions and 17 deletions

View File

@ -165,21 +165,37 @@ jobs:
cargo fuzz build "${target}" cargo fuzz build "${target}"
done done
# cargo-mutants is invoked from the LEZ workspace (sibling directory). # cargo-mutants >=24 dropped --test-command; intercept "cargo test" with a
# FUZZ_REPO is exported so the wrapper script can locate the corpus and # fake cargo wrapper that runs the corpus oracle instead. cargo-mutants is
# fuzz directory without relying on relative paths. # called as a direct binary (not through `cargo`) so the CARGO env var we
# set is respected rather than being overridden by cargo's process launch.
- name: Run mutation tests against LEZ (nssa + common) - name: Run mutation tests against LEZ (nssa + common)
env:
FUZZ_REPO: ${{ github.workspace }}
working-directory: logos-execution-zone
run: | run: |
cargo mutants \ REAL_CARGO="$(command -v cargo)"
--package nssa \ FAKE_CARGO="$(mktemp /tmp/fake-cargo-XXXXXX)"
--package common \ # Intercept the test *execution* phase only; forward the build phase
--in-place \ # (cargo test --no-run) to the real cargo so mutants are compiled.
--test-command "${{ github.workspace }}/scripts/mutants-corpus-test.sh" \ # cargo-mutants uses:
--output "${{ github.workspace }}/mutants-protocol.out" \ # Build phase: cargo test --no-run --verbose --package=...
--timeout-multiplier 5.0 # Test phase: cargo test --verbose --package=...
printf '#!/bin/bash\n_has_no_run=false\nfor _a in "$@"; do [ "$_a" = "--no-run" ] && _has_no_run=true && break; done\nif [ "${1:-}" = "test" ] && [ "$_has_no_run" = "false" ]; then\n FUZZ_REPO="%s" exec "%s"\nelse\n exec "%s" "$@"\nfi\n' \
"${{ github.workspace }}" \
"${{ github.workspace }}/scripts/mutants-corpus-test.sh" \
"$REAL_CARGO" > "$FAKE_CARGO"
chmod +x "$FAKE_CARGO"
# cargo install places cargo-mutants next to cargo in the same bin dir.
MUTANTS_BIN="$(command -v cargo-mutants 2>/dev/null || echo "$(dirname "$REAL_CARGO")/cargo-mutants")"
cd "${{ github.workspace }}/logos-execution-zone"
# cargo-mutants is a Cargo plugin; when invoked directly (not via
# `cargo mutants`) we must supply "mutants" as argv[1] ourselves.
CARGO="$FAKE_CARGO" \
"$MUTANTS_BIN" mutants \
--package nssa \
--package common \
--in-place \
--output "${{ github.workspace }}/mutants-protocol.out" \
--timeout-multiplier 5.0
rm -f "$FAKE_CARGO"
- name: Upload mutants report - name: Upload mutants report
if: always() if: always()

View File

@ -722,14 +722,50 @@ mutants-protocol PACKAGES="nssa common":
# --in-place is required because LEZ depends on path crates outside its own # --in-place is required because LEZ depends on path crates outside its own
# directory (e.g. the Rust standard toolchain); without it cargo-mutants copies # directory (e.g. the Rust standard toolchain); without it cargo-mutants copies
# the workspace to a temp dir where those relative paths would not resolve. # the workspace to a temp dir where those relative paths would not resolve.
#
# cargo-mutants >=24 dropped --test-command and only supports --test-tool cargo|nextest.
# Work around: create a fake `cargo` wrapper that intercepts `cargo test` and
# runs the corpus oracle instead; every other sub-command is delegated to the
# real cargo. We call the cargo-mutants binary directly so that cargo's own
# process launch doesn't override the CARGO env var back to the real binary.
REAL_CARGO="$(command -v cargo)"
FAKE_CARGO=$(mktemp /tmp/fake-cargo-XXXXXX)
FAKE_CARGO_LOG=$(mktemp /tmp/fake-cargo-log-XXXXXX.txt)
trap 'rm -f "$FAKE_CARGO" "$FAKE_CARGO_LOG"' EXIT
# The fake cargo intercepts the test *execution* phase only.
# cargo-mutants drives two kinds of "cargo test" invocations:
# Build phase: cargo test --no-run --verbose --package=... (compile only)
# Test phase: cargo test --verbose --package=... (run tests)
# The oracle must only replace the test execution phase; the build phase
# must be forwarded to the real cargo so mutants are actually compiled.
printf '#!/bin/bash\necho "FAKE_CARGO: $*" >> "%s"\n_has_no_run=false\nfor _a in "$@"; do [ "$_a" = "--no-run" ] && _has_no_run=true && break; done\nif [ "${1:-}" = "test" ] && [ "$_has_no_run" = "false" ]; then\n FUZZ_REPO="%s" exec "%s"\nelse\n exec "%s" "$@"\nfi\n' \
"$FAKE_CARGO_LOG" \
"$REPO_DIR" \
"${REPO_DIR}/scripts/mutants-corpus-test.sh" \
"$REAL_CARGO" > "$FAKE_CARGO"
chmod +x "$FAKE_CARGO"
# Locate the cargo-mutants binary (installed by `cargo install cargo-mutants`).
MUTANTS_BIN="$(command -v cargo-mutants 2>/dev/null || true)"
if [ -z "$MUTANTS_BIN" ]; then
MUTANTS_BIN="$(dirname "$REAL_CARGO")/cargo-mutants"
fi
if [ ! -x "$MUTANTS_BIN" ]; then
echo "ERROR: cargo-mutants not found. Install with: cargo install cargo-mutants --locked"
exit 1
fi
# cargo-mutants is a Cargo plugin. When invoked via `cargo mutants`, Cargo
# automatically prepends "mutants" as argv[1]. When we invoke the binary
# directly (to keep our CARGO env override alive), we must supply it ourselves.
cd "$LEZ_DIR" cd "$LEZ_DIR"
FUZZ_REPO="$REPO_DIR" \ CARGO="$FAKE_CARGO" \
cargo mutants \ "$MUTANTS_BIN" mutants \
"${PKG_FLAGS[@]}" \ "${PKG_FLAGS[@]}" \
--in-place \ --in-place \
--test-command "${REPO_DIR}/scripts/mutants-corpus-test.sh" \
--output "${REPO_DIR}/mutants-protocol.out" \ --output "${REPO_DIR}/mutants-protocol.out" \
--timeout-multiplier 5.0 --timeout-multiplier 5.0 \
|| { echo "--- fake-cargo invocations ---"; cat "$FAKE_CARGO_LOG"; exit 1; }
echo "" echo ""
echo "=== Mutation report summary ===" echo "=== Mutation report summary ==="