#!/bin/bash # Example/issue regression test runner. # Usage: ./tests/run_examples.sh [--update] # --update: regenerate expected output (.exit/.stdout/.stderr, and .ir where present) # # Layout (per CLAUDE.md): expected output lives in an `expected/` dir that # sits NEXT TO the test file, with three streams split out: # /.sx # /expected/.exit # process exit code # /expected/.stdout # normalized stdout # /expected/.stderr # normalized stderr # /expected/.ir # optional `sx ir` snapshot # A test is any .sx that has an /expected/.exit marker. # Roots scanned: examples/ and issues/. set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" SX="$ROOT_DIR/zig-out/bin/sx" ROOTS=("$ROOT_DIR/examples" "$ROOT_DIR/issues") PASS=0 FAIL=0 SKIP=0 TIMEOUT_COUNT=0 UPDATE=0 TIMEOUT=10 if [[ "${1:-}" == "--update" ]]; then UPDATE=1 fi normalize() { sed 's/0x[0-9a-f]\{4,\}/0xADDR/g' } # Normalize `sx ir` output for snapshot diffing (host-specific noise + LLVM # auto-suffixed temporaries). normalize_ir() { sed -E \ -e '/^; ModuleID =/d' \ -e '/^source_filename =/d' \ -e '/^target datalayout =/d' \ -e '/^target triple =/d' \ -e '/^attributes #[0-9]+ = \{/d' \ -e 's/%([a-z]+)[0-9]+/%\1N/g' } TMP_ERR="$(mktemp)" trap 'rm -f "$TMP_ERR"' EXIT for root in "${ROOTS[@]}"; do expected_dir="$root/expected" [[ -d "$expected_dir" ]] || continue for exit_file in "$expected_dir"/*.exit; do [[ -e "$exit_file" ]] || continue name=$(basename "$exit_file" .exit) sx_file="$root/${name}.sx" out_file="$expected_dir/${name}.stdout" err_file="$expected_dir/${name}.stderr" ir_file="$expected_dir/${name}.ir" if [[ ! -f "$sx_file" ]]; then SKIP=$((SKIP + 1)) continue fi printf " %-48s" "$name" actual_out=$(timeout "$TIMEOUT" "$SX" run "$sx_file" 2>"$TMP_ERR" | normalize) actual_exit=${PIPESTATUS[0]} actual_err=$(normalize < "$TMP_ERR") if [[ $actual_exit -eq 124 ]]; then TIMEOUT_COUNT=$((TIMEOUT_COUNT + 1)) echo "TIMEOUT (>${TIMEOUT}s)" continue fi has_ir=false [[ -f "$ir_file" ]] && has_ir=true actual_ir="" if $has_ir; then actual_ir=$("$SX" ir "$sx_file" 2>&1 | normalize_ir) fi if [[ $UPDATE -eq 1 ]]; then echo "$actual_out" > "$out_file" echo "$actual_err" > "$err_file" echo "$actual_exit" > "$exit_file" $has_ir && echo "$actual_ir" > "$ir_file" echo "updated (exit=$actual_exit)" continue fi expected_out=$(normalize < "$out_file" 2>/dev/null) expected_err=$(normalize < "$err_file" 2>/dev/null) expected_exit=$(cat "$exit_file") expected_ir="" $has_ir && expected_ir=$(normalize_ir < "$ir_file") out_ok=true; err_ok=true; exit_ok=true; ir_ok=true [[ "$actual_out" == "$expected_out" ]] || out_ok=false [[ "$actual_err" == "$expected_err" ]] || err_ok=false [[ "$actual_exit" == "$expected_exit" ]] || exit_ok=false if $has_ir && [[ "$actual_ir" != "$expected_ir" ]]; then ir_ok=false; fi if $out_ok && $err_ok && $exit_ok && $ir_ok; then PASS=$((PASS + 1)) echo "ok" else FAIL=$((FAIL + 1)) echo "FAIL" $out_ok || { echo " --- stdout diff ---"; diff <(echo "$expected_out") <(echo "$actual_out") || true; } $err_ok || { echo " --- stderr diff ---"; diff <(echo "$expected_err") <(echo "$actual_err") || true; } $exit_ok || echo " exit code: expected=$expected_exit actual=$actual_exit" $ir_ok || { echo " --- IR diff ---"; diff <(echo "$expected_ir") <(echo "$actual_ir") || true; } fi done 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 ]]