#!/usr/bin/env bash # Break the code on purpose and confirm a named test notices. # # A green test proves nothing on its own. The only evidence that a test guards # a behaviour is watching it fail when that behaviour is removed -- and doing # that by hand goes wrong in ways that all look like success: # # the edit never applied a stale anchor, or one that matched twice, so # the "mutation" run was the unmutated code # the filter matched nothing `cargo test ` reports `0 failed` when it # ran no tests at all # the mutation was left behind every later run is then against broken code # # Each of those is checked here rather than eyeballed. Usage: # # scripts/mutate.sh -- # scripts/mutate.sh a_handle_repair -- python3 /tmp/break-the-lock.py # # The tree must be clean, so that restoring is exact rather than a guess about # which changes were the mutation's. set -uo pipefail cd "$(dirname "$0")/.." filter=${1:-} shift || true [ "${1:-}" = "--" ] && shift if [ -z "$filter" ] || [ $# -eq 0 ]; then sed -n '2,20p' "$0" exit 2 fi if [ -n "$(git status --porcelain)" ]; then echo "refusing: the tree has changes, so restoring afterwards could not be exact" >&2 echo "commit or set aside your work first; mutation testing runs against committed code" >&2 exit 2 fi restore() { git checkout -- . 2>/dev/null if [ -n "$(git status --porcelain)" ]; then echo "STILL DIRTY after restore -- fix this before running anything else:" >&2 git status --short >&2 exit 3 fi } trap restore EXIT echo "== applying the mutation" if ! "$@"; then echo "the edit command failed" >&2 exit 1 fi # It has to have changed something. An anchor that no longer matches, or that # matched somewhere else, produces a run against unmutated code that passes # and means nothing. if [ -z "$(git status --porcelain)" ]; then echo "FAIL: the edit changed no tracked file, so nothing was mutated" >&2 exit 1 fi echo " $(git diff --shortstat)" echo "== running $filter" out=$(CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS:-2} nice -n 19 cargo test "$filter" 2>&1) echo "$out" | grep -E "^test result" | sed 's/^/ /' # `cargo test` exits 0 when the filter matched nothing, which reads exactly # like a passing suite. ran=$(echo "$out" | grep -oE "[0-9]+ passed; [0-9]+ failed" | awk -F'[ ;]' '{p+=$1; f+=$4} END{print p+f}') if [ -z "$ran" ] || [ "$ran" -eq 0 ]; then echo "FAIL: $filter matched no tests, so nothing was proved" >&2 exit 1 fi failed=$(echo "$out" | grep -oE "[0-9]+ failed" | awk '{s+=$1} END{print s+0}') if [ "$failed" -eq 0 ]; then echo "FAIL: the mutation was applied and all $ran test(s) still passed" >&2 echo " the test does not guard what it claims to" >&2 exit 1 fi echo "CAUGHT: $failed of $ran test(s) failed with the mutation in place"