173 lines
7.9 KiB
Bash
Executable File
173 lines
7.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Windows canvas smoke under Wine.
|
|
#
|
|
# Exercises the Windows gpu_surface software path (src/platform/windows/
|
|
# webview2_host.cpp: child HWND, WM_TIMER frame events, SetDIBitsToDevice
|
|
# blits) without Windows hardware: cross-compiles examples/ui-inbox for
|
|
# x86_64-windows-gnu, runs the .exe under Xvfb + Wine, and asserts against
|
|
# the automation snapshot:
|
|
#
|
|
# 1. snapshot ready=true (app booted, automation server live)
|
|
# 2. gpu_backend=software (the SetDIBitsToDevice path is active)
|
|
# 3. gpu_nonblank=true (real pixels were presented)
|
|
# 4. widget-click "Add task" -> '4 open' (automation input mutates state)
|
|
# 5. real X11 click + typing lands in the draft textbox (XTEST -> Wine ->
|
|
# WM_LBUTTONDOWN/WM_CHAR -> runtime)
|
|
#
|
|
# Step 5 (xdotool) is deliberately included: it is the only coverage of the
|
|
# Win32 pointer/keyboard input mapping in webview2_host.cpp. It is also the
|
|
# flakiest step (window lookup, focus without a window manager), so every
|
|
# failure path dumps the X window list, the snapshot, and the app log.
|
|
#
|
|
# Deliberately NOT `set -e`: grep exits 1 on zero matches inside the poll
|
|
# loops, and we want explicit, diagnosable failures instead of silent early
|
|
# exits. Every assertion goes through fail(), which dumps diagnostics.
|
|
set -u
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
app_dir="$repo_root/examples/ui-inbox"
|
|
snap="$app_dir/.zig-cache/native-sdk-automation/snapshot.txt"
|
|
cli="$repo_root/zig-out/bin/native"
|
|
app_log="${TMPDIR:-/tmp}/windows-canvas-smoke-app.log"
|
|
|
|
# Wine needs an X display; when none is present (CI), re-exec the whole
|
|
# script under a private Xvfb server so the app and xdotool share it. The
|
|
# explicit screen size beats xvfb-run's 640x480x8 default: the app window is
|
|
# 720x520 and Wine wants a 24-bit visual.
|
|
if [ -z "${DISPLAY:-}" ]; then
|
|
exec xvfb-run -a --server-args="-screen 0 1280x800x24" "$0" "$@"
|
|
fi
|
|
|
|
export WINEPREFIX="${WINEPREFIX:-$repo_root/.zig-cache/wineprefix}"
|
|
export WINEDEBUG="${WINEDEBUG:--all}"
|
|
|
|
app_pid=""
|
|
cleanup() {
|
|
[ -n "$app_pid" ] && kill "$app_pid" >/dev/null 2>&1
|
|
wineserver -k >/dev/null 2>&1
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
diagnostics() {
|
|
echo "---- diagnostics ----"
|
|
echo "-- X windows:"
|
|
xdotool search --name "." 2>/dev/null | while read -r w; do
|
|
echo " $w: $(xdotool getwindowname "$w" 2>/dev/null)"
|
|
done
|
|
echo "-- snapshot ($snap):"
|
|
if [ -f "$snap" ]; then tr '|' '\n' < "$snap" | sed 's/^/ /'; else echo " (missing)"; fi
|
|
echo "-- app log tail ($app_log):"
|
|
tail -40 "$app_log" 2>/dev/null | sed 's/^/ /'
|
|
echo "---------------------"
|
|
}
|
|
|
|
fail() {
|
|
echo "FAIL: $1"
|
|
diagnostics
|
|
exit 1
|
|
}
|
|
|
|
# poll <seconds> <pattern>: wait until $snap contains <pattern>.
|
|
poll() {
|
|
local deadline=$((SECONDS + $1))
|
|
while [ "$SECONDS" -lt "$deadline" ]; do
|
|
[ -f "$snap" ] && grep -q "$2" "$snap" && return 0
|
|
sleep 0.5
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# ---- build ----------------------------------------------------------------
|
|
(cd "$repo_root" && zig build) || fail "root zig build (CLI) failed"
|
|
(cd "$app_dir" && zig build -Dtarget=x86_64-windows-gnu -Dplatform=windows -Dweb-engine=system -Dautomation=true) \
|
|
|| fail "ui-inbox Windows cross-compile failed"
|
|
|
|
# ---- wineprefix -----------------------------------------------------------
|
|
# First run initializes the prefix (measured ~10-30s on CI-class machines);
|
|
# subsequent runs are instant, so no cache step is needed.
|
|
start=$SECONDS
|
|
wineboot --init >/dev/null 2>&1
|
|
wineserver --wait >/dev/null 2>&1
|
|
echo "== wineprefix ready in $((SECONDS - start))s ($WINEPREFIX)"
|
|
|
|
# ---- launch ---------------------------------------------------------------
|
|
cd "$app_dir" || fail "missing $app_dir"
|
|
rm -rf .zig-cache/native-sdk-automation
|
|
mkdir -p .zig-cache/native-sdk-automation
|
|
wine zig-out/bin/ui-inbox.exe > "$app_log" 2>&1 &
|
|
app_pid=$!
|
|
|
|
# ---- 1: automation snapshot becomes ready ---------------------------------
|
|
poll 180 'ready=true' || fail "snapshot never became ready"
|
|
echo "== ready: $(head -1 "$snap" | cut -d'|' -f1)"
|
|
|
|
# ---- 2 + 3: software backend presented non-blank pixels --------------------
|
|
poll 60 'gpu_nonblank=true' || fail "gpu_nonblank never became true"
|
|
grep -q 'gpu_backend=software' "$snap" || fail "gpu_backend is not software"
|
|
echo "== canvas: $(grep -o 'gpu_backend=[a-z]*' "$snap" | head -1)" \
|
|
"$(grep -o 'gpu_nonblank=[a-z]*' "$snap" | head -1)" \
|
|
"$(grep -o 'gpu_sample=0x[0-9a-f]*' "$snap" | head -1)" \
|
|
"$(grep -o 'gpu_present_mode=[a-z]*' "$snap" | head -1)"
|
|
|
|
# ---- 4: automation widget-click mutates the model --------------------------
|
|
echo "== open before click: $(grep -oE '[0-9]+ open' "$snap" | head -1)"
|
|
add_id=$(grep -o 'widget @w1/inbox-canvas#[0-9]* role=button name="Add task"' "$snap" \
|
|
| grep -o '#[0-9]*' | tr -d '#')
|
|
[ -n "$add_id" ] || fail "Add task button not found in snapshot"
|
|
"$cli" automate widget-click inbox-canvas "$add_id" || fail "CLI widget-click failed"
|
|
poll 30 '4 open' || fail "widget-click did not reach '4 open'"
|
|
echo "== open after click: $(grep -oE '[0-9]+ open' "$snap" | head -1)"
|
|
|
|
# ---- 5: real X11 input through the Win32 path ------------------------------
|
|
# Root-coordinate math. Hidden-titlebar windows keep the full overlapped
|
|
# frame and reclaim the caption band through WM_NCCALCSIZE, so the Win32
|
|
# client area starts at the very top of the window. Under a WM-less Wine
|
|
# the X11 driver still places (and reports) the client X window at the
|
|
# DEFAULT frame offset - one caption band lower - so the X origin sits a
|
|
# band BELOW where Win32 client coordinates actually map, and the reported
|
|
# X height is short by exactly that band (measured: X window 718x489 at
|
|
# y=30 for a 718x519 client whose clicks land at y=0). The snapshot knows
|
|
# the true client height, so the height shortfall IS the y correction; a
|
|
# standard-frame window reports matching heights and corrects by zero.
|
|
win=""
|
|
for w in $(xdotool search --name "." 2>/dev/null); do
|
|
case "$(xdotool getwindowname "$w" 2>/dev/null)" in
|
|
*[Ii]nbox*) win="$w" ;;
|
|
esac
|
|
done
|
|
[ -n "$win" ] || fail "app X window not found"
|
|
eval "$(xdotool getwindowgeometry --shell "$win")"
|
|
client_h=$(grep -o 'window @w1 "[^"]*" bounds=([^)]*)' "$snap" | head -1 \
|
|
| sed -n 's/.*x\([0-9]*\)[^x]*$/\1/p')
|
|
[ -n "$client_h" ] || client_h=$HEIGHT
|
|
y_off=$((client_h - HEIGHT))
|
|
[ "$y_off" -ge 0 ] 2>/dev/null || y_off=0
|
|
echo "== x window $win: pos=($X,$Y) size=${WIDTH}x${HEIGHT} client_h=$client_h y_off=$y_off"
|
|
xdotool windowactivate "$win" >/dev/null 2>&1 || xdotool windowfocus "$win" >/dev/null 2>&1
|
|
|
|
draft_line=$(grep -o 'widget @w1/inbox-canvas#[0-9]* role=textbox[^|]*' "$snap" | head -1)
|
|
[ -n "$draft_line" ] || fail "draft textbox not found in snapshot"
|
|
bounds=$(echo "$draft_line" | grep -o 'bounds=([^)]*)')
|
|
bx=$(echo "$bounds" | sed -n 's/bounds=(\([0-9.]*\),.*/\1/p')
|
|
by=$(echo "$bounds" | sed -n 's/bounds=([0-9.]*,\([0-9.]*\) .*/\1/p')
|
|
bw=$(echo "$bounds" | sed -n 's/.* \([0-9.]*\)x[0-9.]*).*/\1/p')
|
|
bh=$(echo "$bounds" | sed -n 's/.* [0-9.]*x\([0-9.]*\)).*/\1/p')
|
|
[ -n "$bx" ] && [ -n "$by" ] && [ -n "$bw" ] && [ -n "$bh" ] || fail "could not parse draft bounds: $draft_line"
|
|
cx=$(awk "BEGIN{printf \"%d\", $X + $bx + $bw / 2}")
|
|
cy=$(awk "BEGIN{printf \"%d\", $Y - $y_off + $by + $bh / 2}")
|
|
echo "== clicking draft field $bounds at ($cx,$cy)"
|
|
xdotool mousemove "$cx" "$cy" click 1
|
|
# The click must move widget focus into the textbox before any keys are
|
|
# sent: spaces in the typed string would otherwise activate whatever
|
|
# widget held focus (a button press adds a task and the real failure -
|
|
# input landing in the wrong widget - would read as missing text).
|
|
poll 10 'role=textbox[^|]*focused=true' || fail "draft textbox did not take focus from the click"
|
|
sleep 1
|
|
xdotool type --delay 120 "hi from wine"
|
|
poll 30 'hi from wine' || fail "typed text never appeared in the snapshot"
|
|
echo "== draft after typing: $(grep -o 'widget @w1/inbox-canvas#[0-9]* role=textbox[^|]*' "$snap" | head -1 | cut -c1-160)"
|
|
echo "== input latency: $(grep -o 'gpu_input_latency_ns=[0-9]*' "$snap" | head -1)"
|
|
|
|
echo "PASS: windows canvas smoke"
|
|
exit 0
|