#!/usr/bin/env bash # check_the_checker for scripts/checkers.sh. Every case injects a fault that # a bare `./scripts/X-test.sh` cannot see, and asserts the runner does. # See context/test-evidence/tooling.md. set -uo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" RUNNER="$here/checkers.sh" work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT pass=0; fail=0 ok() { printf ' ok %s\n' "$1"; pass=$((pass+1)); } bad() { printf ' FAIL %s (%s)\n' "$1" "$2"; fail=$((fail+1)); } # A sandbox is a copy of the runner beside fixture suites, so `$HERE` inside it # is the sandbox: the shebang scan and the suite list are both the fixtures'. sandbox() { local d="$work/$1" mkdir -p "$d" cp "$RUNNER" "$d/checkers.sh" chmod +x "$d/checkers.sh" printf '%s' "$d" } # A suite is only discovered when the thing it checks is present, so every # fixture gets a stub subject beside it. suite() { local d="$1" name="$2" cat > "$d/$name-test.sh" chmod +x "$d/$name-test.sh" printf '#!/usr/bin/env bash\nexit 0\n' > "$d/$name.sh" chmod +x "$d/$name.sh" } # $1 name, $2 expected exit, rest: the command. Nothing follows the runner in # its own pipeline, which is the rule its own header states. expect() { local what="$1" want="$2"; shift 2 "$@" > "$work/out" 2>&1 local got=$? [ "$got" = "$want" ] && ok "$what" || bad "$what" "exit $got, wanted $want" } # --------------------------------------------------------------------------- # 1. The fault this runner exists for. `nix develop` exports `name`; a suite # that reads an unset variable under `set -u` is therefore SILENT in the dev # shell and fatal in CI. That is improvise-f9qf, reproduced exactly. d="$(sandbox setu)" suite "$d" masked <<'EOF' #!/usr/bin/env bash set -euo pipefail echo "read: $name" EOF export name=nix-shell-env expect "a suite reading an inherited dev-shell variable fails under the runner" \ 1 "$d/checkers.sh" masked # The same suite run the way the old gate ran it PASSES, which is what makes # the case above discriminating rather than a test of `set -u`. "$d/masked-test.sh" > "$work/out" 2>&1 direct=$? [ "$direct" = 0 ] \ && ok "and passes when run directly with that variable exported" \ || bad "and passes when run directly with that variable exported" \ "exit $direct, wanted 0" unset name # --------------------------------------------------------------------------- # 2. The shebang rule. `#!/bin/sh` is dash in CI and bash-in-sh-mode on macOS. d="$(sandbox shebang)" suite "$d" fine <<'EOF' #!/usr/bin/env bash exit 0 EOF printf '#!/bin/sh\nexit 0\n' > "$d/legacy.sh" chmod +x "$d/legacy.sh" expect "a #!/bin/sh script in the scripts directory is refused" 1 \ "$d/checkers.sh" fine grep -q 'wanted #!/usr/bin/env bash' "$work/out" \ && ok "and the refusal names the shebang it wanted" \ || bad "and the refusal names the shebang it wanted" "no such message" # Without the injected file the same sandbox passes, so the case above is not # passing on some other complaint. rm -f "$d/legacy.sh" expect "and the same sandbox passes once it is removed" 0 "$d/checkers.sh" fine # --------------------------------------------------------------------------- # 3. A failing suite is reported, and a passing one is not. d="$(sandbox rc)" suite "$d" red <<'EOF' #!/usr/bin/env bash exit 1 EOF expect "a failing suite fails the runner" 1 "$d/checkers.sh" red d="$(sandbox green)" suite "$d" blue <<'EOF' #!/usr/bin/env bash exit 0 EOF expect "a passing suite passes the runner" 0 "$d/checkers.sh" blue # --------------------------------------------------------------------------- # 4. A named suite that does not exist is a usage error, not a silent no-op. d="$(sandbox missing)" suite "$d" present <<'EOF' #!/usr/bin/env bash exit 0 EOF expect "an unknown suite name is exit 4" 4 "$d/checkers.sh" absent # --------------------------------------------------------------------------- # 5. --list names every suite in the directory and nothing else. d="$(sandbox listing)" suite "$d" alpha <<'EOF' #!/usr/bin/env bash exit 0 EOF suite "$d" beta <<'EOF' #!/usr/bin/env bash exit 0 EOF "$d/checkers.sh" --list > "$work/out" 2>&1 listed="$(tr '\n' ' ' < "$work/out")" [ "$listed" = "alpha beta " ] && ok "--list names exactly the suites present" \ || bad "--list names exactly the suites present" "got [$listed]" # A test RUNNER also ends in -test.sh. It is not a suite, and running it as # one is how `scripts/node-test.sh` printed its usage inside a green gate. printf '#!/usr/bin/env bash\nexit 4\n' > "$d/gamma-test.sh" chmod +x "$d/gamma-test.sh" "$d/checkers.sh" --list > "$work/out" 2>&1 listed="$(tr '\n' ' ' < "$work/out")" [ "$listed" = "alpha beta " ] \ && ok "an X-test.sh with no X beside it is not a suite" \ || bad "an X-test.sh with no X beside it is not a suite" "got [$listed]" printf '\ncheckers-test: %d passed, %d failed\n' "$pass" "$fail" [ "$fail" -eq 0 ]