#!/bin/bash # Example regression test runner # Usage: ./tests/run_examples.sh [--update] # --update: regenerate expected output and exit code files set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" SX="$ROOT_DIR/zig-out/bin/sx" EXPECTED_DIR="$SCRIPT_DIR/expected" 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. Strips host-specific # noise so a snapshot taken on one macOS machine matches another: # - target triple / datalayout / module-id headers # - function-attribute groups (target-cpu, frame-pointer string) # - LLVM's auto-suffixed temporary names (%add1, %icmp29 → %add, %icmp) 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' } for expected_file in "$EXPECTED_DIR"/*.txt; do name=$(basename "$expected_file" .txt) sx_file="$ROOT_DIR/examples/${name}.sx" exit_file="$EXPECTED_DIR/${name}.exit" if [[ ! -f "$sx_file" ]]; then SKIP=$((SKIP + 1)) continue fi printf " %-30s" "$name" actual=$(timeout "$TIMEOUT" "$SX" run "$sx_file" 2>&1 | normalize) actual_exit=${PIPESTATUS[0]} if [[ $actual_exit -eq 124 ]]; then TIMEOUT_COUNT=$((TIMEOUT_COUNT + 1)) echo "TIMEOUT (>${TIMEOUT}s)" continue fi # Optional IR-shape snapshot. When tests/expected/.ir exists, # also diff `sx ir ` against it. Used to lock down lowering # changes that don't show up in runtime output (e.g., selector # interning trims sel_registerName calls without changing what # the program prints). ir_file="$EXPECTED_DIR/${name}.ir" actual_ir="" has_ir_snapshot=false if [[ -f "$ir_file" || ( $UPDATE -eq 1 && -f "$EXPECTED_DIR/${name}.ir" ) ]]; then has_ir_snapshot=true fi if $has_ir_snapshot; then actual_ir=$("$SX" ir "$sx_file" 2>&1 | normalize_ir) fi if [[ $UPDATE -eq 1 ]]; then echo "$actual" > "$expected_file" echo "$actual_exit" > "$exit_file" if $has_ir_snapshot; then echo "$actual_ir" > "$ir_file" fi echo " updated $name (exit=$actual_exit)" continue fi expected=$(cat "$expected_file" | normalize) expected_exit=0 if [[ -f "$exit_file" ]]; then expected_exit=$(cat "$exit_file") fi expected_ir="" if $has_ir_snapshot; then expected_ir=$(cat "$ir_file" | normalize_ir) fi output_ok=true exit_ok=true ir_ok=true if [[ "$actual" != "$expected" ]]; then output_ok=false fi if [[ "$actual_exit" != "$expected_exit" ]]; then exit_ok=false fi if $has_ir_snapshot && [[ "$actual_ir" != "$expected_ir" ]]; then ir_ok=false fi if $output_ok && $exit_ok && $ir_ok; then PASS=$((PASS + 1)) echo "ok" else FAIL=$((FAIL + 1)) echo "FAIL" if ! $output_ok; then diff <(echo "$expected") <(echo "$actual") || true fi if ! $exit_ok; then echo " exit code: expected=$expected_exit actual=$actual_exit" fi if ! $ir_ok; then echo " IR diff:" diff <(echo "$expected_ir") <(echo "$actual_ir") || true fi 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 ]]