Add a real logic-test gate so future pure-sx game logic fails the build on a bad assertion: - tests/test.sx: `expect(cond, msg)` assert helper — prints a greppable `FAIL <file>:<line>: <msg>` and exits non-zero via process.exit on failure. - tools/run_tests.sh: snapshot runner mirroring sx/tests/run_examples.sh; runs each tests/<name>.sx and diffs stdout + exit code against tests/expected/. Exits 0 iff all tests pass. - tests/arith.sx (+ expected snapshots): seed passing sanity test. - README.md: document both halves of the gate — logic runner and the reproducible ios-sim build/launch sequence (with device discovery).
113 lines
3.4 KiB
Bash
Executable File
113 lines
3.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# m3te logic-test gate. Mirrors sx/tests/run_examples.sh: run each sx logic
|
|
# test, diff its stdout + exit code against committed expectations. Exits 0 iff
|
|
# ALL tests pass, NON-ZERO otherwise (so a broken assertion fails the build gate).
|
|
#
|
|
# Usage: bash tools/run_tests.sh [--update]
|
|
# --update: regenerate the expected .stdout/.exit from current output.
|
|
#
|
|
# Layout (a test is any tests/<name>.sx with a tests/expected/<name>.exit marker;
|
|
# helper modules such as tests/test.sx have no marker, so they are not run):
|
|
# tests/<name>.sx
|
|
# tests/expected/<name>.stdout # normalized stdout
|
|
# tests/expected/<name>.exit # process exit code
|
|
#
|
|
# SX binary: override with the SX env var; defaults to the dev build.
|
|
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
SX="${SX:-/Users/agra/projects/sx/zig-out/bin/sx}"
|
|
TESTS_DIR="$ROOT_DIR/tests"
|
|
EXPECTED_DIR="$TESTS_DIR/expected"
|
|
TIMEOUT=30
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
SKIP=0
|
|
TIMEOUT_COUNT=0
|
|
UPDATE=0
|
|
|
|
if [[ "${1:-}" == "--update" ]]; then
|
|
UPDATE=1
|
|
fi
|
|
|
|
if [[ ! -x "$SX" ]]; then
|
|
echo "sx binary not found/executable at: $SX (override with SX=/path/to/sx)" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# Collapse the absolute test-file path back to its repo-relative form so the
|
|
# `FAIL <file>:<line>` lines printed by tests/test.sx are checkout-location
|
|
# independent. Applied identically to expected and actual, so it can only
|
|
# reconcile location noise, never desync an otherwise-matching pair.
|
|
normalize() {
|
|
sed -E -e "s#${ROOT_DIR}/##g"
|
|
}
|
|
|
|
TMP_ERR="$(mktemp)"
|
|
trap 'rm -f "$TMP_ERR"' EXIT
|
|
|
|
shopt -s nullglob
|
|
markers=("$EXPECTED_DIR"/*.exit)
|
|
if [[ ${#markers[@]} -eq 0 ]]; then
|
|
echo "no tests found under $EXPECTED_DIR (expected <name>.exit markers)" >&2
|
|
exit 2
|
|
fi
|
|
|
|
for exit_file in "${markers[@]}"; do
|
|
name=$(basename "$exit_file" .exit)
|
|
sx_file="$TESTS_DIR/${name}.sx"
|
|
out_file="$EXPECTED_DIR/${name}.stdout"
|
|
|
|
if [[ ! -f "$sx_file" ]]; then
|
|
printf " %-40s SKIP (missing %s)\n" "$name" "tests/${name}.sx"
|
|
SKIP=$((SKIP + 1))
|
|
continue
|
|
fi
|
|
|
|
printf " %-40s" "$name"
|
|
actual_out=$(timeout "$TIMEOUT" "$SX" run "$sx_file" 2>"$TMP_ERR" | normalize)
|
|
actual_exit=${PIPESTATUS[0]}
|
|
|
|
if [[ $actual_exit -eq 124 ]]; then
|
|
TIMEOUT_COUNT=$((TIMEOUT_COUNT + 1))
|
|
echo "TIMEOUT (>${TIMEOUT}s)"
|
|
continue
|
|
fi
|
|
|
|
if [[ $UPDATE -eq 1 ]]; then
|
|
printf '%s\n' "$actual_out" > "$out_file"
|
|
printf '%s\n' "$actual_exit" > "$exit_file"
|
|
echo "updated (exit=$actual_exit)"
|
|
continue
|
|
fi
|
|
|
|
expected_out=$(normalize < "$out_file" 2>/dev/null)
|
|
expected_exit=$(cat "$exit_file")
|
|
|
|
out_ok=true; exit_ok=true
|
|
[[ "$actual_out" == "$expected_out" ]] || out_ok=false
|
|
[[ "$actual_exit" == "$expected_exit" ]] || exit_ok=false
|
|
|
|
if $out_ok && $exit_ok; then
|
|
PASS=$((PASS + 1))
|
|
echo "ok"
|
|
else
|
|
FAIL=$((FAIL + 1))
|
|
echo "FAIL"
|
|
$out_ok || { echo " --- stdout diff (expected vs actual) ---"; diff <(printf '%s\n' "$expected_out") <(printf '%s\n' "$actual_out") || true; }
|
|
$exit_ok || echo " exit code: expected=$expected_exit actual=$actual_exit"
|
|
[[ -s "$TMP_ERR" ]] && { echo " --- stderr ---"; cat "$TMP_ERR"; }
|
|
fi
|
|
done
|
|
|
|
if [[ $UPDATE -eq 1 ]]; then
|
|
echo "Updated all expected output files."
|
|
exit 0
|
|
fi
|
|
|
|
echo "$PASS passed, $FAIL failed, $SKIP skipped, $TIMEOUT_COUNT timed out"
|
|
[[ $FAIL -eq 0 && $TIMEOUT_COUNT -eq 0 ]]
|