148 lines
10 KiB
Bash
148 lines
10 KiB
Bash
#!/bin/bash
|
||
# Row 302 (#1809, Vikunja 588) gate. Plan: agents/gizmoduck/work/queue-302/BUILD.md d3 "Run plan", d4, d5.
|
||
# 0. Preflight: worktree at TIP and clean, the five test blobs from d5. TIP is the local merge of 64f6558ee
|
||
# onto origin/refactor 1ac655005 (sage-3289); the 12 row paths are unchanged in BASE..1ac655005.
|
||
# 1. Red (mos-1415): f799e8b15 (final tests, unfixed product). idle-stop-race and the runner.test land case
|
||
# must fail. The bus files are run and recorded. A green forcing case here is a STOP.
|
||
# 2. On TIP: idle-stop-race alone 600 s, runner.test whole 900 s, compactions-launch whole 600 s,
|
||
# bus 1200 s, harness 1200 s, cli 2100 s, then the scripts/test-*.sh at 900 s each.
|
||
# 3. M1-M5, M7, M8, M9 (mutants.py). Each kill-listed file runs whole; every listed test name must fail.
|
||
# After each: git checkout, sha256 equal to the candidate's, tree clean. A survivor is a STOP to Sage.
|
||
# mos-1272: a first-run red in a new test is placed and reported after; any other red stops the run.
|
||
set -o pipefail
|
||
WT=/mnt/storage/scratch/gizmo-302
|
||
# Run 2 (BUILD d6): FIX is the d6 tip; TIP is a fresh merge of it onto ONTO, set when the run is cleared.
|
||
TIP=${TIP:?set TIP to the run-2 merge sha}
|
||
FIX=5b63ea3236d82e14956594b41a00cbd7f7dc5f71
|
||
ONTO=${ONTO:?set ONTO to origin/refactor at the merge}
|
||
BR=gizmoduck/row302-gate
|
||
RED=f799e8b15561ea2c0f0735e5328ba149c7a1a7b5
|
||
BASE=0e30a85fa
|
||
OUT=/mnt/storage/scratch/gizmo-302-out/gate
|
||
PY=/mnt/storage/scratch/gizmo-302-out/mutants.py
|
||
cd "$WT" || exit 9
|
||
[ "$(git rev-parse HEAD)" = "$TIP" ] || { echo "not on the gate commit"; exit 9; }
|
||
[ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "dirty"; exit 9; }
|
||
TESTS=""
|
||
while read -r blob f; do
|
||
[ "$(git rev-parse "$TIP:$f")" = "$blob" ] && [ "$(git rev-parse "$FIX:$f")" = "$blob" ] || { echo "blob mismatch $f"; exit 9; }
|
||
TESTS="$TESTS $f"
|
||
done <<'EOF'
|
||
52343932b231691ba2b4fc117547ea7f6f504704 packages/bus/tests/land.test.mjs
|
||
6fb6e15c2fd34f742f9aab718ff654ab2704e198 packages/bus/tests/launcher-released.test.mjs
|
||
27193a745c4ed8d4936de96c1768ab010a572856 packages/bus/tests/threads.test.mjs
|
||
9dfacfea15a487c4e7056ebae3e311cb514b0467 packages/cli/tests/idle-stop-race.test.mjs
|
||
0792431b60314a17ed74b3df0c5b3748dc3c1286 packages/harness/tests/runner.test.mjs
|
||
EOF
|
||
echo "blobs: the five final test blobs (d5, launcher-released from d6) match in $FIX and $TIP"
|
||
[ -L node_modules ] || ln -s /mnt/storage/src/mosaic-stack/node_modules node_modules
|
||
[ -L node_modules ] || { echo "no node_modules link"; exit 8; }
|
||
[ "$(git rev-parse $TIP^1) $(git rev-parse $TIP^2)" = "$ONTO $FIX" ] || { echo "merge parents wrong"; exit 9; }
|
||
paths=$(git diff --name-only $BASE $FIX)
|
||
[ "$(echo "$paths" | wc -l)" -eq 12 ] || { echo "row paths not 12"; exit 9; }
|
||
over=$(git diff --name-only $BASE $ONTO -- $paths)
|
||
[ -z "$over" ] || { echo "BASE..ONTO touches row paths:"; echo "$over"; exit 9; }
|
||
git diff --quiet $FIX $TIP -- $paths || { echo "merge changed a row path"; exit 9; }
|
||
echo "merge: $TIP = $FIX onto $ONTO; 12 row paths equal to $FIX, none touched in $BASE..$ONTO"
|
||
[ -e "$OUT" ] && { echo "$OUT exists; move it first"; exit 9; }
|
||
mkdir -p "$OUT/red" "$OUT/suite" "$OUT/mut"
|
||
newtmp() { export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/gXXX); echo " TMPDIR $TMPDIR"; }
|
||
echo "TIP $TIP (merge) FIX $FIX RED $RED base $BASE onto $ONTO origin/refactor $(git rev-parse origin/refactor) start $(date -u +%FT%TZ)"; echo "loadavg: $(cat /proc/loadavg)"
|
||
T0=$SECONDS; t0=$SECONDS; lap() { echo " wall $((SECONDS - t0)) s"; t0=$SECONDS; }
|
||
counts() { grep -E '^ℹ (tests|pass|fail|cancelled|skipped|todo) ' "$1" | tr '\n' ' '; echo; }
|
||
timed() { # log dir limit args... ; sets rc
|
||
local log=$1 dir=$2 lim=$3; shift 3
|
||
newtmp
|
||
(cd "$dir" && timeout -k 10 "$lim" "$@") > "$log" 2>&1
|
||
rc=$?
|
||
if [ $rc -eq 124 ] || [ $rc -eq 137 ]; then
|
||
echo "TIMEOUT after $lim s: $(basename "$log" .log). To Sage."
|
||
case $(basename "$log") in test-*.log) echo "SHELL-PHASE TIMEOUT (mos-1275): to Mos unplaced";; esac
|
||
fi
|
||
echo "$(basename "$log" .log) rc=$rc $(counts "$log")"
|
||
}
|
||
green() { # log dir limit args... ; any red stops
|
||
timed "$@"
|
||
if [ $rc -ne 0 ]; then
|
||
echo "RED: $(basename "$1" .log), stop at $(date -u +%FT%TZ), wall $((SECONDS - T0)) s"
|
||
grep -nE '^not ok|✖|AssertionError|Error:' "$1" | head -40
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
echo "== 1. red first: $RED product with the final test files from $FIX checked out on top"
|
||
git checkout -q --detach "$RED" || exit 9
|
||
git checkout -q "$FIX" -- $TESTS || exit 9
|
||
while read -r f; do [ "$(git hash-object "$f")" = "$(git rev-parse "$FIX:$f")" ] || { echo "red tree blob wrong $f"; exit 9; }; done < <(echo $TESTS | tr ' ' '\n')
|
||
[ -z "$(git diff --name-only HEAD -- 'packages/*/src/*')" ] || exit 9
|
||
echo " red tree: product of $RED, tests of $FIX: $(git diff --cached --name-only HEAD | tr '\n' ' ')"
|
||
[ -z "$(git diff --name-only $BASE HEAD -- 'packages/*/src/*')" ] || { echo "red commit changes product files"; git checkout -q $BR; exit 9; }
|
||
timed "$OUT/red/idle-stop-race.log" "$WT/packages/cli" 600 node --test tests/idle-stop-race.test.mjs; r1=$rc
|
||
timed "$OUT/red/runner-land.log" "$WT/packages/harness" 600 node --test --test-name-pattern="^a run in a landing turn isn't released for a second held decision" tests/runner.test.mjs; r2=$rc
|
||
timed "$OUT/red/bus.log" "$WT/packages/bus" 600 node --test tests/launcher-released.test.mjs tests/threads.test.mjs tests/land.test.mjs
|
||
grep -E '✖ ' "$OUT/red/bus.log" | sort -u | sed 's/^/ /' | head -20
|
||
git reset -q --hard "$RED" && git checkout -q $BR || exit 9
|
||
[ "$(git rev-parse HEAD)" = "$TIP" ] && [ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "not back on TIP clean"; exit 9; }
|
||
red_ok=1
|
||
[ $r1 -ne 0 ] && grep -qF "✖ row 302 REQ-THREAD-2 an idle-released run" "$OUT/red/idle-stop-race.log" || { echo " idle-stop-race NOT red on $RED"; red_ok=0; }
|
||
[ $r2 -ne 0 ] && grep -qF "✖ a run in a landing turn isn't released for a second held decision" "$OUT/red/runner-land.log" || { echo " runner.test land case NOT red on $RED"; red_ok=0; }
|
||
grep -nE 'AssertionError|actual|expected' "$OUT/red/idle-stop-race.log" | head -12 | sed 's/^/ /'
|
||
grep -nE 'AssertionError|actual|expected' "$OUT/red/runner-land.log" | head -8 | sed 's/^/ /'
|
||
# The four ruled pins (mos-1414/1415/1416). :772 sits after :771 in one test, so :771 stops it first;
|
||
# :771's message is out.stderr, so the log shows the old "stopped working: not-holder" line and no
|
||
# "released by the launcher" line, which is the line :772 now pins.
|
||
grep -qF "✖ REQ-THREAD-2 REQ-LAUNCH-1: threadIdle releases only a thread with nothing waiting" "$OUT/red/bus.log" || { echo " threads.test threadIdle case NOT red"; red_ok=0; }
|
||
grep -qF "✖ landRelease releases the holding run by land-relaunch" "$OUT/red/bus.log" || { echo " land.test landRelease case NOT red"; red_ok=0; }
|
||
for pin in "threads.test.mjs:394:" "land.test.mjs:464:"; do
|
||
grep -qF "$pin" "$OUT/red/bus.log" && echo " pin red at $pin" || { echo " pin $pin not in the failure stacks"; red_ok=0; }
|
||
done
|
||
grep -qF "runner.test.mjs:771:" "$OUT/red/runner-land.log" && echo " pin red at runner.test.mjs:771:" || { echo " pin runner.test.mjs:771 not in the failure stack"; red_ok=0; }
|
||
grep -qF "session stopped working: not-holder" "$OUT/red/runner-land.log" && ! grep -qF "session released by the launcher" "$OUT/red/runner-land.log" \
|
||
&& echo " :772 pattern absent from stderr (old not-holder line present)" || { echo " :772 evidence missing"; red_ok=0; }
|
||
echo " every red on $RED:"; cat "$OUT"/red/*.log | grep -E '^✖ |^ ✖ |✖ ' | sed 's/^ *//' | sort -u | sed 's/^/ /'
|
||
[ $red_ok -eq 1 ] && echo " red stage: both forcing cases and the four pins red" || { echo "STOP: red stage failed, to Sage"; exit 3; }
|
||
lap
|
||
|
||
echo "== 2. green on $TIP"
|
||
green "$OUT/suite/idle-stop-race.log" "$WT/packages/cli" 600 node --test tests/idle-stop-race.test.mjs; lap
|
||
green "$OUT/suite/runner.log" "$WT/packages/harness" 900 node --test tests/runner.test.mjs; lap
|
||
green "$OUT/suite/compactions-launch.log" "$WT/packages/cli" 600 node --test tests/compactions-launch.test.mjs; lap
|
||
green "$OUT/suite/bus.log" "$WT/packages/bus" 1200 node --test tests/*.test.mjs; lap
|
||
green "$OUT/suite/harness.log" "$WT/packages/harness" 1200 node --test tests/*.test.mjs; lap
|
||
green "$OUT/suite/cli.log" "$WT/packages/cli" 2100 node --test tests/*.test.mjs; lap
|
||
for s in "$WT"/scripts/test-*.sh; do
|
||
n=$(basename "$s" .sh)
|
||
green "$OUT/suite/$n.log" "$WT" 900 bash "$s"
|
||
tail -2 "$OUT/suite/$n.log" | sed 's/^/ /'; lap
|
||
done
|
||
[ -z "$(git status --porcelain --untracked-files=no)" ] && echo "after tests: clean" || { echo "after tests: DIRTY"; exit 1; }
|
||
echo "== skips"
|
||
grep -HE '^ℹ skipped [1-9]|^ℹ todo [1-9]' "$OUT"/suite/*.log | sed "s#$OUT/##"
|
||
grep -HiE '^(skip|# skip|SKIP)|skipped' "$OUT"/suite/test-*.log | grep -v '^ℹ' | sed "s#$OUT/##" | head -40
|
||
|
||
echo "== 3. mutants"
|
||
limit() { case $1 in *cli/*) echo 600;; *runner.test*) echo 900;; *) echo 600;; esac; }
|
||
for m in $(python3 -B "$PY" list); do
|
||
src=$(python3 -B "$PY" file "$m")
|
||
sum=$(git show "HEAD:$src" | sha256sum | cut -c1-64)
|
||
python3 -B "$PY" apply "$m" >/dev/null || { echo "$m apply-failed"; git checkout -- "$src"; exit 8; }
|
||
git diff > "$OUT/mut/$m.diff"
|
||
ok=1
|
||
for f in $(python3 -B "$PY" kills "$m" | cut -f1 | sort -u); do
|
||
pkg=${f%%/tests/*}; rel=tests/${f#*/tests/}
|
||
log="$OUT/mut/$m.$(basename "$f" .test.mjs).log"
|
||
timed "$log" "$WT/$pkg" "$(limit "$f")" node --test "$rel"
|
||
[ $rc -ne 0 ] || { echo " $m survived in $f"; ok=0; }
|
||
while IFS=$'\t' read -r kf name; do
|
||
[ "$kf" = "$f" ] || continue
|
||
grep -qF "✖ $name" "$log" && echo " killed: $name" || { echo " NOT failing: $name"; ok=0; }
|
||
done < <(python3 -B "$PY" kills "$m")
|
||
done
|
||
git checkout -- "$src"
|
||
[ "$(sha256sum "$WT/$src" | cut -c1-64)" = "$sum" ] && [ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "$m restore-failed"; exit 7; }
|
||
if [ $ok -eq 1 ]; then echo "$m KILLED by rule"; else echo "STOP: $m kill rule not met, to Sage"; exit 4; fi
|
||
lap
|
||
done
|
||
echo "end $(date -u +%FT%TZ) wall $((SECONDS - T0)) s"
|
||
cd /; echo "node with cwd in the worktree: $(for p in $(pgrep -x node); do readlink /proc/$p/cwd; done 2>/dev/null | grep -c "$WT")"
|