makepad/tools/run/fixture/quit_clean.sh
Admin b5706d8e9b makepad run: sysv64/win64 ABIs, clean quits, boundary gate, Android and Windows fixes
Explicit extern "sysv64" and "win64" (and their -unwind forms) follow
rustc's x86_64 conventions, with rustc's E0570 on other targets. A quit is
clean at any time (signal, window close, remote quit, during boot); the boot
window moves, resizes and closes while the app boots, and a boot probe
reports stalls. `makepad check --boundary` audits every provided crate the
loader binds: JIT code reaches makepad-hal, script, shader and Linux
capture state only through #[relax_api] ops. Domains keep their package
names and threads; constants are copied per domain through their pointer
words; a read binding owns its snapshot on the reading thread. Android:
zone arenas reserve 8 GB, inspect shows thread stacks, opening an app from
the phone WM no longer hangs. Windows: the source watcher ends at quit and
the boot window's shaders compile for D3D11. Tests that build or drop a JIT
unit run alone in a child process. The load screen follows the WM's
dark/light live, has a close button and drags from anywhere.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:24:22 +02:00

123 lines
5.4 KiB
Bash
Executable file

#!/bin/sh
# Regression test: a quit of `makepad run` is clean at any time. Each case starts the app hidden and
# muted, ends it, and requires exit status 0 and no crash, fault or leak line in its output:
# boot-<s> SIGTERM <s> seconds after the start (the boot window, the app compiling or opening)
# present SIGTERM 0.5 s after the first frame
# swap-<s> SIGTERM <s> seconds after the compiler-zone swap (settle)
# pages clicks CLICKS (page/tab switches, "x,y;x,y;..."), then SIGTERM
# remote-quit `makepad-remote app <pid> quit` after the swap
# script-cold the HAL script `wait_frame 1` (MAKEPAD_HAL_SCRIPT) on a cold cache of its own: the
# app is asked to quit right after its first frame, while a cold start is still
# bringing things up; it must exit by itself (a run still alive 120 s after its
# first frame is reported hung, then stopped)
# Run from the workspace root after `cargo build --release -p makepad-run -p makepad-remote`.
# PACKAGES (default makepad-app-calculator), OFFSETS (swap, default "0.5 2 5"), BOOT (default
# "0.5 2 4"), CLICKS (the pages case's clicks; no pages case without), TARGET (default
# target/release). Logs stay in a scratch directory under TMPDIR. The first case of a package
# compiles cold (a fresh cache).
T=${TARGET:-target/release}
R=$T/makepad-remote
dir=$(mktemp -d "${TMPDIR:-/tmp}/quit_clean.XXXXXX") || exit 1
fails=0
cases=0
# run_case <package> <name> <when> <offset> [clicks]: when = start | present | swap | script
run_case() {
pkg=$1; name=$2; when=$3; off=$4; clicks=$5
d=$dir/$pkg-$name
mkdir -p "$d/home"
if [ "$when" = script ]; then
printf 'wait_frame 1\n' > "$d/quit.hal"
MAKEPAD_HIDE_WINDOWS=1 MAKEPAD_MUTE=1 SANDBOX_MUTE=1 MUSIC_MUTE=1 MAKEPAD_REMOTE=1 MAKEPAD_HOME="$d/home" \
MAKEPAD_RUN_HOME="$dir/runhome" MAKEPAD_RUN_CACHE_DIR="$d/cache" MAKEPAD_WINDOW=1280x800@1 \
MAKEPAD_HAL_SCRIPT="$d/quit.hal" "$T/makepad" run "$pkg" > "$d/run.log" 2>&1 &
pid=$!
n=0
until grep -q "first frame at" "$d/run.log"; do
n=$((n + 1)); [ $n -ge 3000 ] && break
kill -0 $pid 2>/dev/null || break
sleep 0.2
done
n=0
while kill -0 $pid 2>/dev/null; do
n=$((n + 1))
if [ $n -ge 1200 ]; then
echo "quit_clean: $pkg $name: still running 120 s after its first frame: hung" >> "$d/run.log"
kill -TERM $pid
break
fi
sleep 0.1
done
wait $pid
rc=$?
grep -q "hung$" "$d/run.log" && rc=124
report_case
return
fi
MAKEPAD_HIDE_WINDOWS=1 MAKEPAD_MUTE=1 SANDBOX_MUTE=1 MUSIC_MUTE=1 MAKEPAD_REMOTE=1 MAKEPAD_HOME="$d/home" \
MAKEPAD_RUN_HOME="$dir/runhome" MAKEPAD_RUN_CACHE_DIR="$dir/cache-$pkg" MAKEPAD_WINDOW=1280x800@1 \
"$T/makepad" run "$pkg" > "$d/run.log" 2>&1 &
pid=$!
if [ "$when" != start ]; then
n=0
until "$R" app $pid grab out="$d/g.png" > "$d/remote.log" 2>&1 && [ -s "$d/g.png" ]; do
n=$((n + 1)); [ $n -ge 1200 ] && break
kill -0 $pid 2>/dev/null || break
sleep 0.5
done
fi
if [ "$when" = swap ]; then
n=0
until grep -q "makepad-run zone: swap " "$d/run.log"; do
n=$((n + 1)); [ $n -ge 1200 ] && break
kill -0 $pid 2>/dev/null || break
sleep 0.1
done
fi
sleep "$off"
if [ -n "$clicks" ]; then
rest=$clicks
while [ -n "$rest" ]; do
c=${rest%%;*}
[ "$c" = "$rest" ] && rest="" || rest=${rest#*;}
"$R" app $pid click x=${c%,*} y=${c#*,} wait=1 >> "$d/remote.log" 2>&1
sleep 0.7
done
fi
if [ "$name" = remote-quit ]; then
"$R" app $pid quit >> "$d/remote.log" 2>&1
else
kill -TERM $pid
fi
wait $pid
rc=$?
report_case
}
# report_case: the verdict of the case in $pkg $name $d $rc
report_case() {
bad=$(grep -aE "use after compiler-zone|zone: fault|panicked|Segmentation|Bus error|SIGSEGV|SIGBUS|Aborted|exiting may race" "$d/run.log" | head -3)
# an app thread still busy when the stop gave up (bounded work: a kernel compile, a probe):
# not a failure, said so the slow quit is visible
leaked=$(grep -ao "leaked [0-9]* threads: .*" "$d/run.log" | cut -c1-160)
swapped=$(grep -c "makepad-run zone: swap " "$d/run.log")
cases=$((cases + 1))
if [ $rc -eq 0 ] && [ -z "$bad" ]; then
echo "quit_clean: $pkg $name: ok (swaps $swapped)${leaked:+; $leaked}"
else
fails=$((fails + 1))
echo "quit_clean: $pkg $name: FAILED exit $rc (swaps $swapped) log $d/run.log"
[ -n "$bad" ] && echo "$bad" | cut -c1-240
fi
}
for pkg in ${PACKAGES:-makepad-app-calculator}; do
run_case $pkg script-cold script 0
# warms the cache too: the later cases boot warm, as a user's relaunch does
run_case $pkg present present 0.5
for s in ${BOOT:-0.5 2 4}; do run_case $pkg boot-$s start $s; done
for s in ${OFFSETS:-0.5 2 5}; do run_case $pkg swap-$s swap $s; done
[ -n "$CLICKS" ] && run_case $pkg pages present 1 "$CLICKS"
run_case $pkg remote-quit swap 1
done
if [ $fails -eq 0 ]; then echo "quit_clean: ok ($cases cases; logs in $dir)"; else echo "quit_clean: FAILED ($fails of $cases cases; logs in $dir)"; exit 1; fi