Files
sx/tests/run_examples.sh
agra 26a04e49d0 tests: IR-snapshot harness — diff sx ir output when .ir present
run_examples.sh now supports an optional `tests/expected/<name>.ir`
sibling to `.txt`/`.exit`. When present, the runner also captures
`sx ir <file>` output, normalizes target-/host-specific noise
(module ID, target triple/datalayout, attribute groups, LLVM's
auto-suffixed %temp numbering), and diffs against the snapshot.
`--update` regenerates it alongside the runtime output.

Catches lowering changes that don't affect what the program prints
— exactly the shape Phase 1.5's selector interning will produce
(same runtime output, very different IR).

First snapshot: `ffi-objc-call-03-selector-sharing.ir`. Today the
test emits four `call ptr @sel_registerName(ptr @str.N)` lines for
its four call sites; after 1.5 we expect two static
`@OBJC_SELECTOR_REFERENCES_<sel>` globals + loads at each call
site. The diff between the two snapshots will be the visible
artifact of the optimization.
2026-05-19 13:01:28 +03:00

138 lines
3.7 KiB
Bash
Executable File

#!/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/<name>.ir exists,
# also diff `sx ir <file>` 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 ]]