makepad/tools/remote_smoke.sh
Admin 9d2e17e3b9 chore: README, workspace, agent docs, and the box-driving scripts
- README: a build quick-start for macOS and Windows, the honest Linux
    story, what CUDA is for and how to install the separation model.
  - AGENTS.md: the `--remote` control surface protocol, so the harness it
    documents is usable without reading platform/src/remote.rs.
  - Cargo.toml: workspace membership for the crates this series adds and
    removes.
  - tools/: the Windows box scripts (wincmd, winps and friends, winrun) and
    remote_smoke.sh — how a build gets driven on a remote machine.
  - apps/asset-server: the standalone server binary and its README.
  - Small follow-ups in libs/{windows,apple_sys,makepad_test,mbtile_reader,
    converse} and apps/route, plus .gitignore and makepad.splash.
2026-08-23 01:34:36 +02:00

196 lines
7.2 KiB
Bash
Executable file

#!/bin/bash
# End-to-end proof of the `--remote` control surface (see AGENTS.md).
#
# Launches two example apps with `--remote`, drives them purely over HTTP, and
# checks that: the port line is printed, /s lists windows, /g writes a real PNG,
# an injected click changes app state, text/key input reaches a TextInput, a
# two-window app can be grabbed and driven per window, and /gq leaves nothing
# running.
#
# tools/remote_smoke.sh # builds what it needs, then runs
# SKIP_BUILD=1 tools/remote_smoke.sh
set -uo pipefail
cd "$(dirname "$0")/.." || exit 1
FAILED=0
PIDS=()
cleanup() {
# Belt and braces: /gq should already have quit everything.
for pid in "${PIDS[@]:-}"; do
[ -n "$pid" ] && kill "$pid" 2>/dev/null
done
}
trap cleanup EXIT
ok() { echo " ok $1"; }
bad() { echo " FAIL $1"; FAILED=1; }
check() { # check <description> <haystack> <needle>
case "$2" in
*"$3"*) ok "$1" ;;
*) bad "$1 -- got: $2" ;;
esac
}
launch() { # launch <binary> <logfile> -> echoes port
local bin=$1 log=$2 port=""
rm -f "$log"
"./target/release/$bin" --remote >"$log" 2>&1 &
PIDS+=("$!")
for _ in $(seq 1 100); do
port=$(grep -o 'listening on 127.0.0.1:[0-9]*' "$log" 2>/dev/null | grep -o '[0-9]*$')
[ -n "$port" ] && break
sleep 0.2
done
# give the first frame time to land
sleep 2
echo "$port"
}
if [ -z "${SKIP_BUILD:-}" ]; then
echo "building examples..."
cargo build --release \
-p makepad-example-splash \
-p makepad-example-text-input \
-p makepad-example-floating-panel >/dev/null 2>&1 || {
echo "build failed"
exit 1
}
fi
# ---------------------------------------------------------------- splash: grab + click
echo "splash (grab, snapshot, click through the real event path)"
PORT=$(launch makepad-example-splash /tmp/remote-smoke-splash.log)
if [ -z "$PORT" ]; then
bad "no [makepad-remote] port line"
else
ok "startup line printed port $PORT"
STATUS=$(curl -s "http://127.0.0.1:$PORT/s")
check "/s reports the window" "$STATUS" '"i":0'
HELP=$(curl -s "http://127.0.0.1:$PORT/")
check "/ serves the cheat sheet" "$HELP" "makepad-remote"
GRAB=$(curl -s "http://127.0.0.1:$PORT/g?scale=0.5")
PNG=$(echo "$GRAB" | sed -n 's/.*"png":"\([^"]*\)".*/\1/p')
if [ -f "$PNG" ] && [ "$(head -c 4 "$PNG" | od -An -tx1 | tr -d ' \n')" = "89504e47" ]; then
ok "/g wrote a real PNG ($(wc -c <"$PNG" | tr -d ' ') bytes) at $PNG"
else
bad "/g did not produce a PNG: $GRAB"
fi
# find a button by name, click its centre, watch the app's own label change
curl -s "http://127.0.0.1:$PORT/snap?q=buttons_tab" >/tmp/remote-smoke-tab.json
TAB=$(python3 -c "
import json
w=json.load(open('/tmp/remote-smoke-tab.json'))['s'][0]['r']
print(int(w[0]+w[2]/2), int(w[1]+w[3]/2))
" 2>/dev/null)
if [ -n "$TAB" ]; then
curl -s "http://127.0.0.1:$PORT/click?x=${TAB% *}&y=${TAB#* }&wait=1" >/dev/null
BEFORE=$(curl -s "http://127.0.0.1:$PORT/snap?q=press_status")
BTN=$(curl -s "http://127.0.0.1:$PORT/snap?q=press_demo_button" | python3 -c "
import json,sys
w=json.load(sys.stdin)['s'][0]['r']
print(int(w[0]+w[2]/2), int(w[1]+w[3]/2))
" 2>/dev/null)
curl -s "http://127.0.0.1:$PORT/click?x=${BTN% *}&y=${BTN#* }&wait=1" >/dev/null
AFTER=$(curl -s "http://127.0.0.1:$PORT/snap?q=press_status")
check "click before: label idle" "$BEFORE" "Last press: none"
check "click after: app state changed" "$AFTER" "Last press: Run on_press"
else
bad "/snap?q= found no buttons_tab"
fi
check "/g on a dead window 404s clearly" \
"$(curl -s "http://127.0.0.1:$PORT/g?w=9")" '"err":"no window 9"'
QUIT=$(curl -s "http://127.0.0.1:$PORT/gq?scale=0.25")
check "/gq grabbed and quit" "$QUIT" '"quit":1'
sleep 2
if pgrep -f 'target/release/makepad-example-splash' >/dev/null; then
bad "/gq left the app running"
else
ok "/gq left nothing running"
fi
fi
# ---------------------------------------------------------------- text_input: typing
echo "text_input (IME text path and key codes)"
PORT=$(launch makepad-example-text-input /tmp/remote-smoke-text.log)
if [ -z "$PORT" ]; then
bad "no port line"
else
BOX=$(curl -s "http://127.0.0.1:$PORT/snap?q=input_singleline" | python3 -c "
import json,sys
w=json.load(sys.stdin)['s'][0]['r']
print(int(w[0]+w[2]/2), int(w[1]+w[3]/2))
" 2>/dev/null)
curl -s "http://127.0.0.1:$PORT/click?x=${BOX% *}&y=${BOX#* }&wait=1" >/dev/null
curl -s "http://127.0.0.1:$PORT/k?t=hello%20remote&wait=1" >/dev/null
check "/k?t= typed into the focused TextInput" \
"$(curl -s "http://127.0.0.1:$PORT/snap?q=input_singleline")" '"val":"hello remote"'
curl -s "http://127.0.0.1:$PORT/k?k=press&c=Backspace&wait=1" >/dev/null
check "/k?c=Backspace deleted a character" \
"$(curl -s "http://127.0.0.1:$PORT/snap?q=input_singleline")" '"val":"hello remot"'
curl -s "http://127.0.0.1:$PORT/gq" >/dev/null
sleep 2
fi
# ---------------------------------------------------------------- floating_panel: two windows
echo "floating_panel (two windows, per-window grab and input)"
PORT=$(launch makepad-example-floating-panel /tmp/remote-smoke-panel.log)
if [ -z "$PORT" ]; then
bad "no port line"
else
STATUS=$(curl -s "http://127.0.0.1:$PORT/s")
check "/s lists window 0" "$STATUS" '"i":0'
check "/s lists window 1" "$STATUS" '"i":1'
G0=$(curl -s "http://127.0.0.1:$PORT/g?w=0&scale=0.5")
G1=$(curl -s "http://127.0.0.1:$PORT/g?w=1&scale=0.5")
P0=$(echo "$G0" | sed -n 's/.*"png":"\([^"]*\)".*/\1/p')
P1=$(echo "$G1" | sed -n 's/.*"png":"\([^"]*\)".*/\1/p')
S0=$(echo "$G0" | sed -n 's/.*"sz":\[\([0-9]*\).*/\1/p')
S1=$(echo "$G1" | sed -n 's/.*"sz":\[\([0-9]*\).*/\1/p')
if [ -f "$P0" ] && [ -f "$P1" ] && [ "$S0" != "$S1" ]; then
ok "each window grabbed separately (${S0}px wide vs ${S1}px wide)"
else
bad "per-window grab failed: $G0 / $G1"
fi
# type into window 1; window 0's label must react
BOX=$(curl -s "http://127.0.0.1:$PORT/snap?w=1&q=panel_input" | python3 -c "
import json,sys
w=json.load(sys.stdin)['s'][0]['r']
print(int(w[0]+w[2]/2), int(w[1]+w[3]/2))
" 2>/dev/null)
curl -s "http://127.0.0.1:$PORT/click?w=1&x=${BOX% *}&y=${BOX#* }&wait=1" >/dev/null
curl -s "http://127.0.0.1:$PORT/k?w=1&t=cross%20window&wait=1" >/dev/null
curl -s "http://127.0.0.1:$PORT/k?w=1&k=press&c=enter&wait=1" >/dev/null
check "input to window 1 updated window 0" \
"$(curl -s "http://127.0.0.1:$PORT/snap?w=0&q=status_label")" "Panel submitted"
curl -s "http://127.0.0.1:$PORT/close?w=1" >/dev/null
sleep 1
check "/close?w=1 removed the window" "$(curl -s "http://127.0.0.1:$PORT/s")" '"i":0'
check "/close is logged" "$(cat /tmp/remote-smoke-panel.log)" "remote closed window 1"
QUIT=$(curl -s "http://127.0.0.1:$PORT/gq")
check "/gq grabbed and quit" "$QUIT" '"quit":1'
sleep 2
if pgrep -f 'target/release/makepad-example-floating-panel' >/dev/null; then
bad "/gq left the app running"
else
ok "/gq left nothing running"
fi
fi
echo
if [ "$FAILED" = 0 ]; then
echo "remote smoke: PASS"
else
echo "remote smoke: FAIL"
fi
exit "$FAILED"