Files

69 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Row 156 (#1651) mutant driver. Slot work: run only on Sage's go.
# mutants.sh <worktree> <outdir> <manifest>
# Verifies the candidate against the manifest, runs each named test file once as a control (all must
# pass), then per mutant: back up the file, apply, run its test files, restore, re-verify the manifest.
# A mutant is killed when its test run exits non-zero. MUTANT_DRY=1 lists the plan; MUTANT_ONLY=NAME
# runs one mutant. Exit 0 only if every mutant was killed.
set -uo pipefail
WT=$(realpath -e "${1:?worktree}") || exit 2
OUT=$(realpath -m "${2:?outdir}")
MANIFEST=$(realpath -e "${3:?manifest}") || exit 2
SPEC=$(realpath -e "$(dirname "$0")/mutants.mjs") || exit 2
export TMPDIR=$HOME/dewey-scratch/tmp/lt
mkdir -p "$TMPDIR" "$OUT" || { echo "abort: cannot create $TMPDIR or $OUT"; exit 2; }
[ -d "$OUT" ] && [ -w "$OUT" ] || { echo "abort: $OUT not writable"; exit 2; }
cd "$WT" || exit 2
BACKUP=""; TARGET=""
restore() {
if [ -n "$BACKUP" ] && [ -n "$TARGET" ]; then
cp -p "$BACKUP" "$TARGET" || echo "RESTORE FAILED: $TARGET from $BACKUP"
BACKUP=""; TARGET=""
fi
sha256sum --quiet -c "$MANIFEST" || echo "MANIFEST MISMATCH after restore"
}
trap restore EXIT
sha256sum --quiet -c "$MANIFEST" || { echo "abort: candidate differs from $MANIFEST"; exit 2; }
node "$SPEC" check > "$OUT/check.txt" || { cat "$OUT/check.txt"; echo "abort: spec check failed"; exit 2; }
PLAN=$(node "$SPEC" list) || { echo "abort: spec list failed"; exit 2; }
if [ -n "${MUTANT_ONLY:-}" ]; then
PLAN=$(printf '%s\n' "$PLAN" | awk -F'\t' -v n="$MUTANT_ONLY" '$1==n')
[ -n "$PLAN" ] || { echo "abort: no mutant $MUTANT_ONLY"; exit 2; }
fi
if [ "${MUTANT_DRY:-0}" = 1 ]; then printf '%s\n' "$PLAN"; exit 0; fi
run() { # run <log> <test files...>
local log=$1; shift
setsid -w timeout -k 10 600 node --test --test-reporter=spec "$@" > "$log" 2>&1
}
# Controls: each test file the plan names, alone, on the unmutated candidate.
CONTROLS=$(printf '%s\n' "$PLAN" | cut -f3 | tr ' ' '\n' | sort -u)
for f in $CONTROLS; do
log="$OUT/control-$(echo "$f" | tr / _).log"
if run "$log" "$f"; then echo "control ok $f"; else echo "abort: control failed $f (rc $?), see $log"; exit 2; fi
done
killed=0; survived=0; timedout=0
while IFS=$'\t' read -r name file tests; do
[ -n "$name" ] || continue
TARGET="$WT/$file"; BACKUP="$OUT/$name.orig"
cp -p "$TARGET" "$BACKUP" && cmp -s "$TARGET" "$BACKUP" || { echo "abort: backup failed for $file"; BACKUP=""; TARGET=""; exit 2; }
node "$SPEC" apply "$name" || { echo "abort: apply $name failed"; exit 2; }
cmp -s "$TARGET" "$BACKUP" && { echo "abort: $name changed nothing"; exit 2; }
# shellcheck disable=SC2086
run "$OUT/$name.log" $tests; rc=$?
cp -p "$BACKUP" "$TARGET" || { echo "abort: restore failed for $file"; exit 2; }
BACKUP=""; TARGET=""
sha256sum --quiet -c "$MANIFEST" || { echo "abort: manifest mismatch after $name"; exit 2; }
if [ "$rc" -eq 124 ] || [ "$rc" -eq 137 ]; then timedout=$((timedout+1)); echo "TIMEOUT $name rc=$rc"
elif [ "$rc" -ne 0 ]; then killed=$((killed+1)); echo "killed $name rc=$rc"
else survived=$((survived+1)); echo "SURVIVED $name"; fi
done <<< "$PLAN"
echo "killed $killed survived $survived timeout $timedout"
[ "$survived" -eq 0 ] && [ "$timedout" -eq 0 ]