Candidate on8e2c5c2d0: 4 files, tree91503e62b8. 6 of 6 mutants killed, 3 controls pass, cli suite and every scripts/test-*.sh green. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
54 lines
3.3 KiB
Bash
Executable File
54 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Row 142 (#1636): mosaic talk prints only the reply to its own message.
|
||
# Each check must fail on a mutant. For each mutant this runs one named test file against the
|
||
# real source (control) and against the source with one exact-once text edit, in the given worktree,
|
||
# one test file at a time, and restores every source file on exit.
|
||
# Usage: mutants.sh <worktree> <outdir> (MUTANT_DRY=1 only proves every edit matches exactly once)
|
||
set -u
|
||
WT=$(cd "$1" && pwd); OUT=$(mkdir -p "$2" && cd "$2" && pwd)
|
||
cd "$WT" || exit 2
|
||
BAK=$(mktemp -d "$OUT/.bak.XXXXXX")
|
||
declare -A SAVED
|
||
save() { [ -n "${SAVED[$1]:-}" ] || { mkdir -p "$BAK/$(dirname "$1")"; cp "$1" "$BAK/$1"; SAVED[$1]=1; }; }
|
||
restore() { for f in "${!SAVED[@]}"; do cp "$BAK/$f" "$f"; done; rm -rf "$BAK"; }
|
||
trap restore EXIT
|
||
edit() { # file old new: exact-once replace
|
||
save "$1"
|
||
node -e '
|
||
const fs = require("fs"); const [f, a, b] = process.argv.slice(1);
|
||
const s = fs.readFileSync(f, "utf8"); const n = s.split(a).length - 1;
|
||
if (n !== 1) { console.error("expected one match, found " + n); process.exit(3); }
|
||
fs.writeFileSync(f, s.replace(a, () => b));
|
||
' "$1" "$2" "$3"
|
||
}
|
||
run() { # name testfile
|
||
if [ -n "${MUTANT_DRY:-}" ]; then echo "$1: edit ok (dry)"; return; fi
|
||
setsid -w timeout -k 10 400 node --test "$2" > "$OUT/$1.out" 2>&1; echo "$?" > "$OUT/$1.rc"
|
||
echo "$1: exit $(cat "$OUT/$1.rc"); $(grep -E '^ℹ (tests|pass|fail)' "$OUT/$1.out" | tr '\n' ' ')"
|
||
grep -E '^✖' "$OUT/$1.out" | sort -u | sed 's/ ([0-9.]*ms)$//' | sed "s/^/ failed: /"
|
||
}
|
||
mutant() { # name testfile file old new
|
||
edit "$3" "$4" "$5" || { echo "$1: edit failed"; return; }
|
||
diff -u "$BAK/$3" "$3" > "$OUT/$1.patch"
|
||
run "$1" "$2"
|
||
cp "$BAK/$3" "$3"
|
||
}
|
||
C=packages/cli/src/cli.mjs; VT=packages/cli/tests/verbs.test.mjs; FT=packages/cli/tests/follow.test.mjs; TT=packages/cli/tests/tester.test.mjs
|
||
for t in "$VT" "$FT" "$TT"; do run "control-$(basename "$t" .test.mjs)" "$t"; done
|
||
|
||
# m1: no filter, so talk prints and reads everything again
|
||
mutant m1-filter-removed "$VT" "$C" ' if (!follow && m.in_reply_to !== sent.id) continue;
|
||
' ''
|
||
# m2: the filter also applies under --follow and follow, which must keep receiving everything
|
||
mutant m2-filter-under-follow "$FT" "$C" 'if (!follow && m.in_reply_to !== sent.id) continue;' 'if (m.in_reply_to !== sent?.id) continue;'
|
||
# m3: the filter is inverted, so only the other messages are printed
|
||
mutant m3-filter-inverted "$VT" "$C" 'if (!follow && m.in_reply_to !== sent.id) continue;' 'if (!follow && m.in_reply_to === sent.id) continue;'
|
||
# m4: only the reply is printed, but every message is marked read
|
||
mutant m4-read-all "$VT" "$C" ' if (!follow && m.in_reply_to !== sent.id) continue;
|
||
io.stdout.write(formatMessage(m));' ' if (follow || m.in_reply_to === sent.id) io.stdout.write(formatMessage(m));'
|
||
# m5: the timeout message goes back to claiming the next talk shows a late reply
|
||
mutant m5-timeout-message "$VT" "$C" 'it may still come, and mosaic inbox shows it' 'it may still come, and the next mosaic talk shows it'
|
||
# m6: any reply is printed, not just the reply to this message, so a late reply to an earlier one shows up
|
||
mutant m6-any-reply "$VT" "$C" 'if (!follow && m.in_reply_to !== sent.id) continue;' 'if (!follow && !m.in_reply_to) continue;'
|
||
echo "mutants finished"
|