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.
138 lines
3.7 KiB
Bash
Executable File
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 ]]
|