#!/usr/bin/env bash # Row 125 (#1619) mutant driver. Slot work: run only on Sage's go. # mutants.sh # 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 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 ]