joeylib2/scripts/verify-amiga-input.sh

175 lines
6.8 KiB
Bash
Executable file

#!/usr/bin/env bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# verify-amiga-input.sh - Typed-character acceptance gate for the Amiga
# port. Boots the Keys example off a directory hard drive in FS-UAE
# under Xvfb, types the RetroNet definition-of-done string with xdotool
# (real X key events, so ':' goes through the emulated shift and
# keymap.library's MapRawKey), then presses Escape. On exit Keys logs
# its full received-character history as hex to joeylog.txt on the boot
# volume -- which is the staged host directory -- and the harness
# compares that against the typed ASCII.
#
# Before the typing, the named keys the Amiga gives a second key for: the
# keypad's 8, 2, 4, and 6 as the arrows, its Enter as Return, and the right
# Alt as Alt, each held alone, and Up and the keypad's 8 held together with
# either let go first, which must leave Up down (scripts/lib/keysGate.sh) --
# read off KEYS's cells in FS-UAE's screenshots (tests/gate/keysCells.c),
# the stage found by its still empty verification row
# (tests/gate/keysRow.c).
#
# Usage: scripts/verify-amiga-input.sh
# Requires: fs-uae, Xvfb, xdotool; build/amiga/bin/Keys (make amiga);
# the Kickstart (install.sh install emulator:amiga).
set -euo pipefail
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
source "$repo/scripts/lib/joeyEnv.sh"
joeyNeed FSUAE fsuae
source "$repo/scripts/lib/amigaGate.sh"
source "$repo/scripts/lib/keysGate.sh"
amigaMachine
bin_dir=$repo/build/amiga/bin
target="192.168.1.10:6510"
shotKey=F11
[ -f "$bin_dir/Keys" ] || { echo "verify-amiga-input: build Keys first (make amiga)" >&2; exit 2; }
work=$(mktemp -d -t joeylib-amiga-input.XXXXXX)
display_num=$((RANDOM % 500 + 300))
cleanup() {
joeyStop "${fsuae_pid:-}"
[ -n "${xvfb_pid:-}" ] && kill "$xvfb_pid" 2>/dev/null
rm -rf "$work"
}
trap cleanup EXIT
mkdir -p "$work/hd/s" "$work/shots"
cp "$bin_dir/Keys" "$work/hd/"
echo ":Keys" > "$work/hd/s/startup-sequence"
Xvfb ":$display_num" -screen 0 1024x768x24 >/dev/null 2>&1 &
xvfb_pid=$!
sleep 1
# No keyboard joystick in port 1 and no modifier key: by default FS-UAE
# takes the cursor keys and the right Ctrl and Alt for a joystick, and the
# left Alt for its own shortcuts, and the Amiga never sees them.
# --fullscreen=1 is REQUIRED under a WM-less Xvfb: SDL only accepts
# keyboard input once its window has input focus, no window manager
# ever grants it to a plain window, and xdotool's windowfocus/
# XSendEvent routes are ignored by SDL. A fullscreen SDL window takes
# focus itself, after which plain XTEST typing (xdotool without
# --window) lands in the emulated Amiga.
DISPLAY=":$display_num" "$FSUAE" \
"${AMIGA_MACHINE_ARGS[@]}" \
--fast_memory=2048 \
--hard_drive_0="$work/hd" \
--hard_drive_0_label=JOEYLIB \
--fullscreen=1 \
--initial_input_grab=1 \
--floppy_drive_speed=800 \
--joystick_port_1=none \
--modifier_key=none \
--keyboard_key_${shotKey,,}=action_screenshot \
--screenshots_output_dir="$work/shots" \
>/dev/null 2>&1 &
fsuae_pid=$!
# Boot AmigaDOS off the directory HD and let startup-sequence launch
# Keys. No sentinel channel here, so give it a generous fixed wait.
sleep 28
# XTEST keyboard events land in whatever window HAS focus, and under
# a WM-less Xvfb the fullscreen SDL window latches it on its own
# schedule -- so wait until getwindowfocus actually reports the FS-UAE
# window before typing, nudging with windowfocus while waiting.
win=$(DISPLAY=":$display_num" xdotool search --name -- "FS-UAE" | head -1)
for i in $(seq 1 30); do
focus=$(DISPLAY=":$display_num" xdotool getwindowfocus 2>/dev/null || true)
[ -n "$win" ] && [ "$focus" = "$win" ] && break
[ -n "$win" ] && DISPLAY=":$display_num" xdotool windowfocus --sync "$win" 2>/dev/null || true
sleep 1
[ -z "$win" ] && win=$(DISPLAY=":$display_num" xdotool search --name -- "FS-UAE" | head -1)
done
if ! amigaShot ":$display_num" "$shotKey" "$work/shots" "$work/stage.png"; then
echo "verify-amiga-input: FAIL - FS-UAE took no screenshot" >&2
exit 1
fi
found=$("$(joeyTool keysRow)" "$work/stage.png" "") || true
if [[ ! "$found" =~ row\ at\ \(([0-9]+),([0-9]+)\)\ at\ ([0-9]+)x ]]; then
echo "verify-amiga-input: FAIL - KEYS's stage is not on the screen: $found" >&2
exit 1
fi
scale=${BASH_REMATCH[3]}
stageAt="${BASH_REMATCH[1]},$((BASH_REMATCH[2] - 198 * scale))"
# X's names for the keys (scripts/lib/keysGate.sh's).
declare -A xKey=([up]=Up [kp8]=KP_Up [kp2]=KP_Down [kp4]=KP_Left [kp6]=KP_Right [kpenter]=KP_Enter [ralt]=Alt_R)
KEYS_LABEL=verify-amiga-input
keysPress() {
DISPLAY=":$display_num" xdotool keydown "${xKey[$1]}"
}
keysRelease() {
DISPLAY=":$display_num" xdotool keyup "${xKey[$1]}"
}
keysRead() {
if amigaShot ":$display_num" "$shotKey" "$work/shots" "$work/cells.png"; then
"$(joeyTool keysCells)" "$work/cells.png" --at "$stageAt" --scale "$scale"
else
echo "no screenshot"
fi
}
keysRunSteps kp8 kp2 kp4 kp6 kpenter ralt
if [ -n "$KEYS_FAILS" ]; then
echo "verify-amiga-input: FAIL - $KEYS_FAILS" >&2
exit 1
fi
# Up to three attempts: type, press Escape, then look for the typed=
# line Keys logs on exit. The verdict compares the history's SUFFIX
# against the target, so a partially-delivered early attempt can't
# poison a complete later one -- the proof is that the final 17
# characters arrived in order through the queue.
log="$work/hd/joeylog.txt"
delivered=""
for attempt in 1 2 3; do
DISPLAY=":$display_num" xdotool type --delay 200 -- "$target"
sleep 2
# Hold Escape across several emulated frames: an instantaneous
# down+up can land inside ONE drainMessages pass, where set-then-
# clear in the same poll is invisible to the edge predicate.
DISPLAY=":$display_num" xdotool keydown Escape
sleep 0.5
DISPLAY=":$display_num" xdotool keyup Escape
for i in $(seq 1 10); do
if [ -f "$log" ] && grep -q "typed=" "$log"; then
delivered=1
break 2
fi
sleep 1
done
done
if [ -z "$delivered" ]; then
echo "verify-amiga-input: FAIL - no typed= line in joeylog.txt (keystrokes never landed?)" >&2
ls -la "$work/hd" >&2
exit 1
fi
typedHex=$(grep -o "typed=[0-9A-Fa-f]*" "$log" | tail -1 | cut -d= -f2)
expect=$(printf '%s' "$target" | od -A n -t x1 | tr -d ' \n' | tr 'a-f' 'A-F')
echo "VERIFY-AMIGA-INPUT typed=$typedHex"
case "$typedHex" in
*"$expect")
echo "verify-amiga-input: PASS (typed \"$target\", all ${#target} chars arrived via jlInputGetChar; $KEYS_STEPS named-key steps: the keypad's arrows and Enter, right Alt, Up held with its second key)"
;;
*)
echo "verify-amiga-input: FAIL" >&2
echo " expected suffix: $expect" >&2
echo " got: $typedHex" >&2
exit 1
;;
esac