run.sh (7346B)
1 #!/bin/sh 2 ## tests/run.sh — host-side dispatcher for the unified test runner. 3 ## 4 ## All build/run/diff work happens inside the container via 5 ## tests/run-suite.sh: this script just starts one podman 6 ## process per requested arch and aggregates the per-arch 7 ## PASS/FAIL totals. Prior versions of this runner re-entered the 8 ## container per fixture (and per build/run within a fixture); now 9 ## a whole arch's suite is one podman invocation. 10 ## 11 ## The one bit of work that stays on the host is the lint preflight 12 ## for the m1pp and p1 suites: tools/lint.sh runs python, which the 13 ## busybox container doesn't carry. Names that fail lint are reported 14 ## here (FAIL + diagnostic) and excluded from the in-container batch. 15 ## 16 ## Suites: 17 ## m1pp tests/M1pp/<name>.M1pp — m1pp expander parity test. 18 ## p1 tests/P1/<name>.P1pp — built via tests/build-p1pp.sh 19 ## and run; stdout diffed. 20 ## tests/P1/<name>.P1 — raw P1, built via 21 ## tests/build-p1.sh (no expander). 22 ## scheme1 tests/scheme1/<name>.scm — run by per-arch scheme1. 23 ## cc-util tests/cc-util/<name>.scm — scheme1 prelude+util byte-diff. 24 ## cc-lex tests/cc-lex/<name>.c — lex pipeline byte-diff. 25 ## cc-pp tests/cc-pp/<name>.c — pp pipeline byte-diff (+ .scm). 26 ## cc-cg tests/cc-cg/<name>.scm — cg emit -> P1pp -> ELF -> run. 27 ## cc tests/cc/<name>.c — cc -> P1pp -> ELF -> run. 28 ## cc-ext vendor/c-testsuite/single-exec/<name>.c — broad subset 29 ## coverage from the upstream 30 ## c-testsuite. needs-libc tests 31 ## link the mes-libc chain (same 32 ## as cc-libc); plain tests use 33 ## the bare cc pipeline. Any 34 ## compile/assemble/runtime error 35 ## counts as FAIL. 36 ## tcc-cc tests/cc/<name>.c — tcc-boot2 -> ELF -> run. 37 ## tcc-libc tests/cc-libc/<name>.c — tcc-boot2 builds mes-libc into 38 ## libc.o, then compiles + links 39 ## each fixture against it -> run. 40 ## 41 ## Core and cc.scm suites run on all four bootstrap arches by default; 42 ## suites consuming boot3+ TCC/libc artifacts retain three targets. 43 ## 44 ## Usage: tests/run.sh --suite <suite> [--arch ARCH] [name ...] 45 46 set -eu 47 48 SUITE= 49 ARCH= 50 NAMES= 51 STAGE= 52 53 while [ "$#" -gt 0 ]; do 54 case "$1" in 55 --suite) shift; SUITE=$1 ;; 56 --suite=*) SUITE=${1#--suite=} ;; 57 --arch) shift; ARCH=$1 ;; 58 --arch=*) ARCH=${1#--arch=} ;; 59 --stage) shift; STAGE=$1 ;; 60 --stage=*) STAGE=${1#--stage=} ;; 61 --) shift; while [ "$#" -gt 0 ]; do NAMES="$NAMES $1"; shift; done; break ;; 62 -*) echo "$0: unknown flag '$1'" >&2; exit 2 ;; 63 *) NAMES="$NAMES $1" ;; 64 esac 65 shift 66 done 67 68 case "$SUITE" in 69 m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc|cc-ext|tcc-cc|tcc-libc) ;; 70 "") echo "$0: --suite required (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc | cc-libc | cc-ext | tcc-cc | tcc-libc)" >&2; exit 2 ;; 71 *) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;; 72 esac 73 74 REPO=$(cd "$(dirname "$0")/.." && pwd) 75 cd "$REPO" 76 77 platform_of() { 78 case "$1" in 79 aarch64) echo linux/arm64 ;; 80 amd64) echo linux/amd64 ;; 81 riscv64) echo linux/riscv64 ;; 82 riscv32) 83 native=$(podman info --format '{{.Host.Arch}}') 84 case "$native" in aarch64) native=arm64 ;; x86_64) native=amd64 ;; esac 85 echo "linux/$native" 86 ;; 87 *) echo "$0: unknown arch '$1'" >&2; return 1 ;; 88 esac 89 } 90 91 run_in_container() { 92 arch=$1; shift 93 podman run --rm --pull=never --platform "$(platform_of "$arch")" \ 94 --tmpfs /tmp:size=512M \ 95 -e "ARCH=$arch" \ 96 -e "CC_TRACE_EMIT=${CC_TRACE_EMIT:-0}" \ 97 -e "CC_DEBUG=${CC_DEBUG:-0}" \ 98 -e "STAGE=${STAGE:-}" \ 99 -v "$REPO":/work -w /work \ 100 "boot2-busybox-test:$arch" "$@" 101 } 102 103 if [ -z "$ARCH" ]; then 104 case "$SUITE" in 105 cc-libc|cc-ext|tcc-cc|tcc-libc) 106 ARCHES="aarch64 amd64 riscv64" 107 ;; 108 *) ARCHES="aarch64 amd64 riscv64 riscv32" ;; 109 esac 110 else 111 ARCHES=$ARCH 112 fi 113 114 case "$SUITE:$ARCHES" in 115 cc-libc:riscv32|cc-ext:riscv32|tcc-cc:riscv32|tcc-libc:riscv32) 116 echo "$SUITE is unavailable for riscv32: it consumes the intentionally unimplemented TCC/libc stages" >&2 117 exit 2 118 ;; 119 esac 120 121 PASS=0 122 FAIL=0 123 124 # Lint preflight: lint.sh uses python (host-only). Discover the 125 # fixture set if --names was empty, lint each raw fixture (.M1pp / .P1), 126 # emit a host-side FAIL + diagnostic for any miss, write the kept name 127 # list to $keep_file. FAIL line goes to stdout to interleave with the 128 # container's PASS/FAIL output; the FAIL counter updates in-scope. 129 # 130 # Suite layout: 131 # m1pp: tests/M1pp/<name>.M1pp (no raw .M1 fixtures any more) 132 # p1: tests/P1/<name>.P1pp (lint skipped — expander output) 133 # tests/P1/<name>.P1 (lint runs) 134 lint_preflight() { 135 arch=$1; keep_file=$2; dir=$3; raw_ext=$4; pp_ext=$5 136 : > "$keep_file" 137 if [ -z "$NAMES" ]; then 138 raw=$(ls "$dir" 2>/dev/null \ 139 | sed -n "s/^\([^_][^.]*\)\.${raw_ext}\$/\1/p") 140 pp=$(ls "$dir" 2>/dev/null \ 141 | sed -n "s/^\([^_][^.]*\)\.${pp_ext}\$/\1/p") 142 all=$(printf '%s\n%s\n' "$raw" "$pp" | sort -u | tr '\n' ' ') 143 else 144 all=$NAMES 145 fi 146 for name in $all; do 147 raw_src=$dir/$name.$raw_ext 148 if [ -e "$raw_src" ] \ 149 && ! ARCH=$arch sh tools/lint.sh "$raw_src" >/dev/null 2>&1; then 150 echo " FAIL [$arch] $name" 151 ARCH=$arch sh tools/lint.sh "$raw_src" 2>&1 \ 152 | sed 's/^/ /' >&2 || true 153 FAIL=$((FAIL + 1)) 154 else 155 printf '%s ' "$name" >> "$keep_file" 156 fi 157 done 158 } 159 160 for arch in $ARCHES; do 161 case "$SUITE" in 162 m1pp) preflight_args="tests/M1pp M1 M1pp" ;; 163 p1) preflight_args="tests/P1 P1 P1pp" ;; 164 *) preflight_args= ;; 165 esac 166 if [ -n "$preflight_args" ]; then 167 keep_file=$(mktemp) 168 # shellcheck disable=SC2086 # $preflight_args is intentionally word-split. 169 lint_preflight "$arch" "$keep_file" $preflight_args 170 names=$(cat "$keep_file") 171 rm -f "$keep_file" 172 # Skip the container call only when the user gave names AND 173 # all of them failed lint. With no names, an empty kept set 174 # would mean nothing to run anyway. 175 names_trimmed=$(echo "$names" | tr -d ' \t\n') 176 if [ -z "$names_trimmed" ]; then 177 continue 178 fi 179 else 180 names=$NAMES 181 fi 182 183 out=$(mktemp) 184 # shellcheck disable=SC2086 # $names is intentionally word-split. 185 run_in_container "$arch" sh tests/run-suite.sh \ 186 --suite="$SUITE" $names | tee "$out" 187 p=$(grep -c '^ PASS ' "$out" 2>/dev/null || true) 188 f=$(grep -c '^ FAIL ' "$out" 2>/dev/null || true) 189 PASS=$((PASS + ${p:-0})) 190 FAIL=$((FAIL + ${f:-0})) 191 rm -f "$out" 192 done 193 194 echo "$PASS passed, $FAIL failed" 195 [ "$FAIL" -eq 0 ]