summaryrefslogtreecommitdiffstats
path: root/scripts/test-mouse.sh
blob: ed4df4427fb6f2c0a74bfecc6940fa07290d8f36 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env bash
#
# scripts/test-mouse.sh — automated PS/2 mouse round trip.
#
# Builds the kernel, boots QEMU headless, injects mouse movement and
# button presses through the monitor's `mouse_move` / `mouse_button`
# commands, and verifies the guest decoded them via IRQ12 → ring →
# packet decoder.  Covers both surfaces:
#   - `cat /dev/mouse`  (VFS snapshot of pending events)
#   - `mouse`           (live streaming command, stopped by ^C)
#
# Usage:
#   ./scripts/test-mouse.sh
#
# Environment:
#   OVMF   firmware (default: /usr/share/edk2/x64/OVMF.4m.fd)
#   QEMU   qemu binary (default: qemu-system-x86_64)
#
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_DIR"

OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}"
QEMU="${QEMU:-qemu-system-x86_64}"
ESP_DIR="$PROJECT_DIR/esp"
QPID=/tmp/rsdos-mouse-qpid
SOCK=/tmp/rsdos-mouse-qmon.sock
LOG=/tmp/rsdos-mouse-serial.log
QLOG=/tmp/rsdos-mouse-qemu.log

# Never leave a QEMU behind.
trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT

fail() {
	echo "FAIL: $1"
	[ -f "$LOG" ] && tail -20 "$LOG"
	exit 1
}

# wait_log FILE PATTERN TIMEOUT-SECONDS
wait_log() {
	local i=0
	while [ "$i" -lt $((${3:-60} * 10)) ]; do
		grep -q "$2" "$1" 2>/dev/null && return 0
		sleep 0.1
		i=$((i + 1))
	done
	return 1
}

# wait_qemu_exit TIMEOUT-SECONDS
wait_qemu_exit() {
	local i=0
	while [ "$i" -lt $((${1:-30} * 10)) ]; do
		kill -0 "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || return 0
		sleep 0.1
		i=$((i + 1))
	done
	return 1
}

# qemu_hmp — send one raw HMP command to the monitor socket.
qemu_hmp() {
	python3 - "$SOCK" "$1" <<'EOF'
import socket, sys, time
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(sys.argv[1])
s.sendall((sys.argv[2] + "\n").encode())
time.sleep(0.4)
EOF
}

echo "==> 1/5 build kernel (debug)"
cargo build --package rsdos --target x86_64-unknown-uefi

echo "==> 2/5 deploy to ESP"
mkdir -p "$ESP_DIR/EFI/BOOT"
cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI"

echo "==> 3/5 boot QEMU (headless)"
kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true
sleep 1
rm -f "$SOCK" "$LOG"
"$QEMU" -bios "$OVMF" \
	-drive file=fat:rw:"$ESP_DIR",format=raw \
	-serial file:"$LOG" \
	-monitor unix:"$SOCK",server,nowait \
	-m 256M -machine q35 -cpu qemu64 -no-reboot -display none \
	>"$QLOG" 2>&1 &
echo $! >"$QPID"

wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell"
grep -aq "ps2: mouse ready" "$LOG" || fail "mouse driver did not come up at boot"

# Seed a move *before* any consumer runs, so `cat /dev/mouse` has a
# queued event to drain (exercises the VFS snapshot path).
qemu_hmp "mouse_move 8 4"

echo "==> 4/5 drive the shell (cat /dev/mouse, mouse stream, poweroff)"
python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
	"cat /dev/mouse" \
	"mouse"

# While the `mouse` stream is live: a move, a left-press, a move with the
# button held, then release — the button state must ride along on the
# movement packet.
qemu_hmp "mouse_move 20 7"
qemu_hmp "mouse_button 1"
qemu_hmp "mouse_move -5 -3"
qemu_hmp "mouse_button 0"
qemu_hmp "sendkey ctrl-c"

python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
	"poweroff"

echo "==> 5/5 wait for shutdown + verify"
wait_qemu_exit 30 || fail "QEMU did not exit after poweroff"

grep -aq "ps2: mouse ready (IRQ12, stream mode)" "$LOG" || fail "no mouse-ready boot line"
grep -aq "dx=+8 dy=+4 buttons=---" "$LOG" || fail "/dev/mouse did not drain the queued move (dx=+8 dy=+4)"
grep -aq "dx=+20 dy=+7 buttons=---" "$LOG" || fail "mouse stream missed mouse_move 20 7"
grep -aq "dx=+0 dy=+0 buttons=L--" "$LOG" || fail "left-button press did not decode"
grep -aq "dx=-5 dy=-3 buttons=L--" "$LOG" || fail "held-button movement wrong (dx=-5 dy=-3 L)"
grep -aq "mouse: stopped" "$LOG" || fail "^C did not stop the mouse stream"

echo ""
echo "PASS — PS/2 mouse round trip OK (IRQ12, /dev/mouse, live stream, buttons)"
echo "  events:  8,4 · 20,7 · L-press · -5,-3 held · release"