run-tests.sh (9666B)
1 #!/bin/sh 2 ## run-tests.sh — unified test runner for the m1pp, p1, and scheme1 suites. 3 ## 4 ## Each suite is a directory of `<name>.<ext>` fixtures with sibling 5 ## `<name>.expected` files. The runner builds each fixture, runs it inside 6 ## the matching busybox container (`boot2-busybox:<arch>`), and diffs 7 ## actual against expected output. Filenames starting with `_` are skipped 8 ## (parked, ad-hoc debugging). 9 ## 10 ## Suites: 11 ## m1pp tests/M1pp/<name>.M1 — P1 program built via build-p1.sh 12 ## for each requested arch, run in 13 ## container, stdout diffed. 14 ## tests/M1pp/<name>.M1pp — m1pp expander parity test: per-arch 15 ## m1pp binary consumes the fixture 16 ## and writes <out>; diffed. 17 ## p1 tests/P1/<name>.P1pp — P1pp program built via build-p1pp.sh 18 ## for each requested arch, run in 19 ## container, stdout diffed. 20 ## scheme1 tests/scheme1/<name>.scm — Scheme source run by the per-arch 21 ## scheme1 binary. stdout diffed 22 ## against <name>.expected (default 23 ## empty); exit code diffed against 24 ## <name>.expected-exit (default 0). 25 ## 26 ## All three arches by default; --arch restricts to one. 27 ## 28 ## Usage: scripts/run-tests.sh --suite <m1pp|p1|scheme1> [--arch ARCH] [name ...] 29 30 set -eu 31 32 SUITE= 33 ARCH= 34 NAMES= 35 36 while [ "$#" -gt 0 ]; do 37 case "$1" in 38 --suite) shift; SUITE=$1 ;; 39 --suite=*) SUITE=${1#--suite=} ;; 40 --arch) shift; ARCH=$1 ;; 41 --arch=*) ARCH=${1#--arch=} ;; 42 --) shift; while [ "$#" -gt 0 ]; do NAMES="$NAMES $1"; shift; done; break ;; 43 -*) echo "$0: unknown flag '$1'" >&2; exit 2 ;; 44 *) NAMES="$NAMES $1" ;; 45 esac 46 shift 47 done 48 49 case "$SUITE" in 50 m1pp|p1|scheme1) ;; 51 "") echo "$0: --suite required (m1pp | p1 | scheme1)" >&2; exit 2 ;; 52 *) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;; 53 esac 54 55 REPO=$(cd "$(dirname "$0")/.." && pwd) 56 cd "$REPO" 57 58 platform_of() { 59 case "$1" in 60 aarch64) echo linux/arm64 ;; 61 amd64) echo linux/amd64 ;; 62 riscv64) echo linux/riscv64 ;; 63 *) echo "$0: unknown arch '$1'" >&2; return 1 ;; 64 esac 65 } 66 67 run_in_container() { 68 arch=$1; shift 69 podman run --rm --pull=never --platform "$(platform_of "$arch")" \ 70 --tmpfs /tmp:size=512M \ 71 -e "ARCH=$arch" \ 72 -v "$REPO":/work -w /work \ 73 "boot2-busybox:$arch" "$@" 74 } 75 76 discover() { 77 dir=$1; ext=$2 78 ls "$dir" 2>/dev/null \ 79 | sed -n "s/^\\([^_][^.]*\\)\\.$ext\$/\\1/p" \ 80 | sort -u 81 } 82 83 PASS=0 84 FAIL=0 85 86 report() { 87 label=$1; status=$2 88 case "$status" in 89 PASS) PASS=$((PASS + 1));; 90 FAIL) FAIL=$((FAIL + 1));; 91 esac 92 echo " $status $label" 93 } 94 95 show_diff() { 96 expected=$1; actual=$2 97 echo " --- expected ---" 98 printf '%s\n' "$expected" | sed 's/^/ /' 99 echo " --- actual ---" 100 printf '%s\n' "$actual" | sed 's/^/ /' 101 } 102 103 ## --- m1pp suite --------------------------------------------------------- 104 ## 105 ## Caller (Make) ensures build/<arch>/m1pp expanders, build/<arch>/tools/M0, 106 ## the per-arch image, and P1/P1-<arch>.M1 tables exist before this runs. 107 ## .M1 fixtures are still built per-fixture inline via boot-build-p1.sh 108 ## (per-fixture build is test work, not infrastructure). 109 110 run_m1pp_suite() { 111 if [ -z "$ARCH" ]; then 112 ARCHES="aarch64 amd64 riscv64" 113 else 114 ARCHES=$ARCH 115 fi 116 if [ -z "$NAMES" ]; then 117 m1=$(discover tests/M1pp M1) 118 m1pp=$(discover tests/M1pp M1pp) 119 NAMES=$(printf '%s\n%s\n' "$m1" "$m1pp" | sort -u | tr '\n' ' ') 120 fi 121 for name in $NAMES; do 122 expected=tests/M1pp/$name.expected 123 m1_src=tests/M1pp/$name.M1 124 m1pp_src=tests/M1pp/$name.M1pp 125 126 if [ ! -e "$expected" ]; then 127 echo " SKIP $name (no .expected)" 128 continue 129 fi 130 expected_content=$(cat "$expected") 131 132 for arch in $ARCHES; do 133 label="[$arch] $name" 134 if [ -e "$m1pp_src" ]; then 135 outfile=build/$arch/m1pp-out/$name 136 mkdir -p "$(dirname "$outfile")" 137 rm -f "$outfile" 138 run_in_container "$arch" "./build/$arch/m1pp" "$m1pp_src" "$outfile" \ 139 >/dev/null 2>&1 || true 140 actual=$([ -e "$outfile" ] && cat "$outfile" || echo "") 141 elif [ -e "$m1_src" ]; then 142 bin=build/$arch/m1pp-tests/$name 143 if ! ARCH=$arch sh scripts/lint.sh "$m1_src" >/dev/null 2>&1 \ 144 || ! run_in_container "$arch" sh scripts/boot-build-p1.sh "$m1_src" "$bin" \ 145 >/dev/null 2>&1; then 146 report "$label" FAIL 147 ARCH=$arch sh scripts/lint.sh "$m1_src" 2>&1 | sed 's/^/ /' >&2 || true 148 run_in_container "$arch" sh scripts/boot-build-p1.sh "$m1_src" "$bin" \ 149 2>&1 | sed 's/^/ /' >&2 || true 150 continue 151 fi 152 actual=$(run_in_container "$arch" "./$bin" 2>&1 || true) 153 else 154 echo " SKIP $name (no .M1 or .M1pp)" 155 break 156 fi 157 158 if [ "$actual" = "$expected_content" ]; then 159 report "$label" PASS 160 else 161 report "$label" FAIL 162 show_diff "$expected_content" "$actual" 163 fi 164 done 165 done 166 } 167 168 ## --- p1 suite ----------------------------------------------------------- 169 170 run_p1_suite() { 171 if [ -z "$ARCH" ]; then 172 ARCHES="aarch64 amd64 riscv64" 173 else 174 ARCHES=$ARCH 175 fi 176 if [ -z "$NAMES" ]; then 177 NAMES=$(discover tests/P1 P1pp) 178 fi 179 for name in $NAMES; do 180 fixture=tests/P1/$name.P1pp 181 expected=tests/P1/$name.expected 182 if [ ! -e "$fixture" ]; then 183 echo " SKIP $name (no .P1pp)"; continue 184 fi 185 if [ ! -e "$expected" ]; then 186 echo " SKIP $name (no .expected)"; continue 187 fi 188 expected_content=$(cat "$expected") 189 190 for arch in $ARCHES; do 191 label="[$arch] $name" 192 bin=build/$arch/p1-tests/$name 193 if ! run_in_container "$arch" sh scripts/boot-build-p1pp.sh "$fixture" "$bin" \ 194 >/dev/null 2>&1; then 195 report "$label" FAIL 196 run_in_container "$arch" sh scripts/boot-build-p1pp.sh "$fixture" "$bin" \ 197 2>&1 | sed 's/^/ /' >&2 || true 198 continue 199 fi 200 actual=$(run_in_container "$arch" "./$bin" 2>&1 || true) 201 if [ "$actual" = "$expected_content" ]; then 202 report "$label" PASS 203 else 204 report "$label" FAIL 205 show_diff "$expected_content" "$actual" 206 fi 207 done 208 done 209 } 210 211 ## --- scheme1 suite ------------------------------------------------------ 212 ## 213 ## Caller (Make) ensures build/<arch>/scheme1 already exists. The runner 214 ## just invokes that binary against each .scm fixture, capturing stdout 215 ## and the exit status. stdout is diffed against <name>.expected (defaults 216 ## to empty if absent); the exit status is diffed against 217 ## <name>.expected-exit (defaults to 0 if absent). 218 219 run_scheme1_suite() { 220 if [ -z "$ARCH" ]; then 221 ARCHES="aarch64 amd64 riscv64" 222 else 223 ARCHES=$ARCH 224 fi 225 if [ -z "$NAMES" ]; then 226 NAMES=$(discover tests/scheme1 scm) 227 fi 228 for name in $NAMES; do 229 fixture=tests/scheme1/$name.scm 230 expected_stdout_file=tests/scheme1/$name.expected 231 expected_exit_file=tests/scheme1/$name.expected-exit 232 233 if [ ! -e "$fixture" ]; then 234 echo " SKIP $name (no .scm)"; continue 235 fi 236 if [ -e "$expected_stdout_file" ]; then 237 expected_stdout=$(cat "$expected_stdout_file") 238 else 239 expected_stdout= 240 fi 241 if [ -e "$expected_exit_file" ]; then 242 expected_exit=$(cat "$expected_exit_file") 243 else 244 expected_exit=0 245 fi 246 247 for arch in $ARCHES; do 248 label="[$arch] $name" 249 bin=build/$arch/scheme1 250 if [ ! -x "$bin" ]; then 251 report "$label" FAIL 252 echo " (missing $bin -- run 'make scheme1 ARCH=$arch')" >&2 253 continue 254 fi 255 256 tmp_stdout=$(mktemp) 257 if run_in_container "$arch" "./$bin" "$fixture" >"$tmp_stdout" 2>&1; then 258 actual_exit=0 259 else 260 actual_exit=$? 261 fi 262 actual_stdout=$(cat "$tmp_stdout") 263 rm -f "$tmp_stdout" 264 265 if [ "$actual_stdout" = "$expected_stdout" ] \ 266 && [ "$actual_exit" = "$expected_exit" ]; then 267 report "$label" PASS 268 else 269 report "$label" FAIL 270 if [ "$actual_stdout" != "$expected_stdout" ]; then 271 show_diff "$expected_stdout" "$actual_stdout" 272 fi 273 if [ "$actual_exit" != "$expected_exit" ]; then 274 echo " exit: expected $expected_exit, got $actual_exit" 275 fi 276 fi 277 done 278 done 279 } 280 281 case "$SUITE" in 282 m1pp) run_m1pp_suite ;; 283 p1) run_p1_suite ;; 284 scheme1) run_scheme1_suite ;; 285 esac 286 287 echo "$PASS passed, $FAIL failed" 288 [ "$FAIL" -eq 0 ]