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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#!/usr/bin/env bash
#
# scripts/test-msg.sh — automated scheduler message-queue round trip.
#
# Builds the kernel, boots QEMU headless, and verifies inter-task
# messaging end to end:
# - `msgrecv` spawns a task that blocks on recv_blocking
# - `send <id> <type>` delivers every message type in order
# - `mouse <id>` routes *real* IRQ12 mouse events into the queue
# (the window-manager input path)
# - queue-full is a clean error (16-cap; tested against the shell's
# own never-consumed queue)
# - jobs shows q=0 after everything drains, task back to blocked
#
# Usage:
# ./scripts/test-msg.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-msg-qpid
SOCK=/tmp/rsdos-msg-qmon.sock
LOG=/tmp/rsdos-msg-serial.log
QLOG=/tmp/rsdos-msg-qemu.log
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() {
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() {
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() {
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"
echo "==> 4/5 drive the shell (msgrecv / send / mouse routing / queue-full)"
python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
"msgrecv" \
"send 2 user 42" \
"send 2 timer" \
"send 2 key enter" \
"send 2 key ctrl-c" \
"send 2 key f3" \
"send 2 mouse 5 -3 3" \
"sleep 800" \
"jobs"
# Real IRQ12 events through the queue: start routing to task 2, inject
# moves + a left press, stop.
python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "mouse 2"
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"
# Queue-full boundary: 16 sends fit, the 17th must fail cleanly. The
# shell never consumes its own queue, so nothing races the drain here.
python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
"send 0 user 1" \
"send 0 user 2" \
"send 0 user 3" \
"send 0 user 4" \
"send 0 user 5" \
"send 0 user 6" \
"send 0 user 7" \
"send 0 user 8" \
"send 0 user 9" \
"send 0 user 10" \
"send 0 user 11" \
"send 0 user 12" \
"send 0 user 13" \
"send 0 user 14" \
"send 0 user 15" \
"send 0 user 16" \
"send 0 user 17" \
"sleep 800" \
"jobs" \
"poweroff"
echo "==> 5/5 wait for shutdown + verify"
wait_qemu_exit 30 || fail "QEMU did not exit after poweroff"
grep -aq "msgrecv: task 2 listening" "$LOG" || fail "msgrecv did not spawn"
grep -aq "msgrecv\[2\]: User(42)" "$LOG" || fail "user message missing"
grep -aq "msgrecv\[2\]: Timer" "$LOG" || fail "timer message missing"
grep -aq "msgrecv\[2\]: Key(Enter)" "$LOG" || fail "key enter missing"
grep -aq "msgrecv\[2\]: Key(Ctrl-c)" "$LOG" || fail "key ctrl-c missing"
grep -aq "msgrecv\[2\]: Key(F3)" "$LOG" || fail "key f3 missing"
grep -aq "msgrecv\[2\]: Mouse(+5,-3 L R)" "$LOG" || fail "synthesized mouse message missing"
# Real IRQ12 events routed through the queue (button held across the move).
grep -aq "msgrecv\[2\]: Mouse(+20,+7" "$LOG" || fail "real mouse move 20,7 not routed to queue"
grep -aq "msgrecv\[2\]: Mouse(-5,-3 L" "$LOG" || fail "held-button routed move missing"
grep -aq "queue full" "$LOG" || fail "17th send did not report queue full"
grep -aq "send: unknown type" "$LOG" && fail "a bad send slipped through (expected none)"
echo ""
echo "PASS — message queues OK (all types, ordering, real-input routing, queue-full)"
echo " sent: user · timer · key(enter,ctrl-c,f3) · mouse · 16+1 queue-full"
echo " routed: mouse_move 20,7 → queue → msgrecv[2]"
|