diff options
Diffstat (limited to '')
| -rwxr-xr-x | scripts/tests/sendkeys.py (renamed from scripts/sendkeys.py) | 12 | ||||
| -rwxr-xr-x | scripts/tests/test-disk.sh | 67 | ||||
| -rwxr-xr-x | scripts/tests/test-fault.sh | 47 | ||||
| -rwxr-xr-x | scripts/tests/test-focus.py (renamed from scripts/test-focus.py) | 7 | ||||
| -rwxr-xr-x | scripts/tests/test-gfx.sh | 37 | ||||
| -rwxr-xr-x | scripts/tests/test-mouse.sh | 83 | ||||
| -rwxr-xr-x | scripts/tests/test-msg.sh | 98 |
7 files changed, 343 insertions, 8 deletions
diff --git a/scripts/sendkeys.py b/scripts/tests/sendkeys.py index a67903d..9300242 100755 --- a/scripts/sendkeys.py +++ b/scripts/tests/sendkeys.py | |||
| @@ -5,18 +5,20 @@ Each positional argument is one line: it is typed character by character | |||
| 5 | (US layout) followed by Enter. Use it to drive the rs-dos shell from a | 5 | (US layout) followed by Enter. Use it to drive the rs-dos shell from a |
| 6 | test script without a display. | 6 | test script without a display. |
| 7 | 7 | ||
| 8 | Usage: | 8 | Used by scripts/tests/test-*.sh, and manually against an interactive |
| 9 | sendkeys.py "ls /disk" "echo hi WRITING /disk/a.txt" "poweroff" | 9 | session started with `./scripts/run.sh --monitor`: |
| 10 | sendkeys.py --sock /tmp/qmon.sock "date" | ||
| 11 | 10 | ||
| 12 | Environment: QMON_SOCK (default: /tmp/qmon.sock) | 11 | ./scripts/tests/sendkeys.py "ls /disk" "date" "poweroff" |
| 12 | ./scripts/tests/sendkeys.py --sock /tmp/rsdos-monitor.sock "date" | ||
| 13 | |||
| 14 | Environment: QMON_SOCK (default: /tmp/rsdos-monitor.sock) | ||
| 13 | """ | 15 | """ |
| 14 | import os | 16 | import os |
| 15 | import socket | 17 | import socket |
| 16 | import sys | 18 | import sys |
| 17 | import time | 19 | import time |
| 18 | 20 | ||
| 19 | SOCK = os.environ.get("QMON_SOCK", "/tmp/qmon.sock") | 21 | SOCK = os.environ.get("QMON_SOCK", "/tmp/rsdos-monitor.sock") |
| 20 | 22 | ||
| 21 | # Unshifted punctuation needs QEMU's *named* keys — bare '-'/'.'/'/' are | 23 | # Unshifted punctuation needs QEMU's *named* keys — bare '-'/'.'/'/' are |
| 22 | # not valid sendkey names (and '-' is the modifier separator). | 24 | # not valid sendkey names (and '-' is the modifier separator). |
diff --git a/scripts/tests/test-disk.sh b/scripts/tests/test-disk.sh new file mode 100755 index 0000000..3836b6d --- /dev/null +++ b/scripts/tests/test-disk.sh | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/tests/test-disk.sh — full automated data-disk round trip. | ||
| 4 | # | ||
| 5 | # Builds, stages the system image, boots QEMU headless, drives the shell | ||
| 6 | # via the monitor (ls / mkdir / echo WRITING / cat / poweroff), then | ||
| 7 | # verifies the results with mtools and fsck. | ||
| 8 | # | ||
| 9 | # Usage: | ||
| 10 | # ./scripts/tests/test-disk.sh reuse the existing image | ||
| 11 | # ./scripts/tests/test-disk.sh --fresh reformat + reseed first | ||
| 12 | # | ||
| 13 | # Env: SYSTEM_IMG, OVMF, QEMU (see scripts/lib.sh) | ||
| 14 | # | ||
| 15 | set -euo pipefail | ||
| 16 | TNAME="disk" | ||
| 17 | # shellcheck source=../lib.sh | ||
| 18 | source "$(dirname "$0")/../lib.sh" | ||
| 19 | |||
| 20 | FRESH=0 | ||
| 21 | [ "${1:-}" = "--fresh" ] && FRESH=1 | ||
| 22 | STAMP="$(date +%s)" | ||
| 23 | |||
| 24 | echo "==> 1/6 build (kernel + bins)" | ||
| 25 | build_all | ||
| 26 | |||
| 27 | echo "==> 2/6 stage system image" | ||
| 28 | if [ "$FRESH" -eq 1 ]; then stage_image --force; else stage_image; fi | ||
| 29 | |||
| 30 | echo "==> 3/6 boot QEMU (headless)" | ||
| 31 | test_boot | ||
| 32 | sleep 1 | ||
| 33 | |||
| 34 | # Volumes mount under /disk/<UID>/ where UID = "LABEL-SERIAL" (e.g. | ||
| 35 | # RSDOS-5BD1E55F). Discover it from the boot log so the test works on | ||
| 36 | # any image without knowing the serial in advance. | ||
| 37 | VOL_UID="$(grep -a 'fat: mounted' "$LOG" | sed -n 's/.*at \/disk\/\([^ )]*\).*/\1/p' | head -1)" | ||
| 38 | [ -n "$VOL_UID" ] || fail "no FAT32 volume was mounted (look for 'fat: mounted' in the log)" | ||
| 39 | echo " mounted volume UID: $VOL_UID" | ||
| 40 | |||
| 41 | echo "==> 4/6 drive the shell (ls / mkdir / echo WRITING / cat / poweroff)" | ||
| 42 | # The filename is deliberately >13 chars so the FAT LFN path gets | ||
| 43 | # multi-entry entries (they're stored last-first on disk). | ||
| 44 | TESTFILE="disk-test-$STAMP.txt" | ||
| 45 | sendkeys \ | ||
| 46 | "ls /disk" \ | ||
| 47 | "mkdir /disk/$VOL_UID/sub" \ | ||
| 48 | "echo disk-test-$STAMP WRITING /disk/$VOL_UID/sub/$TESTFILE" \ | ||
| 49 | "cat /disk/$VOL_UID/sub/$TESTFILE" \ | ||
| 50 | "poweroff" | ||
| 51 | |||
| 52 | echo "==> 5/6 wait for shutdown" | ||
| 53 | wait_qemu_exit 30 || fail "QEMU did not exit after poweroff" | ||
| 54 | |||
| 55 | echo "==> 6/6 verify" | ||
| 56 | grep -a "disk-test-$STAMP" "$LOG" >/dev/null || fail "cat output missing from serial log" | ||
| 57 | if ! fsck.fat -n "$SYSTEM_IMG" >/tmp/rsdos-fsck.out 2>&1; then | ||
| 58 | fail "fsck.fat reports problems: $(tail -3 /tmp/rsdos-fsck.out)" | ||
| 59 | fi | ||
| 60 | MT="$(mtype -i "$SYSTEM_IMG" "::/sub/$TESTFILE" 2>/dev/null || true)" | ||
| 61 | [ "$MT" = "disk-test-$STAMP" ] || fail "mtype mismatch: got '$MT'" | ||
| 62 | |||
| 63 | echo "" | ||
| 64 | echo "PASS — disk round trip OK (fsck clean, file survived poweroff)" | ||
| 65 | echo " image: $SYSTEM_IMG" | ||
| 66 | echo " volume: /disk/$VOL_UID" | ||
| 67 | echo " written: /disk/$VOL_UID/sub/$TESTFILE = \"disk-test-$STAMP\"" | ||
diff --git a/scripts/tests/test-fault.sh b/scripts/tests/test-fault.sh new file mode 100755 index 0000000..0c39cde --- /dev/null +++ b/scripts/tests/test-fault.sh | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/tests/test-fault.sh — fault-diagnostics verification. | ||
| 4 | # | ||
| 5 | # Proves the fault-dump path: every CPU exception prints a white-on-red | ||
| 6 | # band to the framebuffer and a parseable serial line before halting. | ||
| 7 | # Boots with a dedicated CRASHLOG FAT stick on ide.1 so the crash band | ||
| 8 | # has somewhere to write. | ||
| 9 | # | ||
| 10 | # Usage: ./scripts/tests/test-fault.sh | ||
| 11 | # Env: SYSTEM_IMG, OVMF, QEMU (see scripts/lib.sh) | ||
| 12 | # | ||
| 13 | set -euo pipefail | ||
| 14 | TNAME="fault" | ||
| 15 | # shellcheck source=../lib.sh | ||
| 16 | source "$(dirname "$0")/../lib.sh" | ||
| 17 | |||
| 18 | echo "==> 1/5 build (kernel + bins)" | ||
| 19 | build_all | ||
| 20 | |||
| 21 | echo "==> 2/5 stage system image" | ||
| 22 | stage_image --force | ||
| 23 | |||
| 24 | echo "==> 3/5 boot QEMU (headless, + dedicated CRASHLOG stick)" | ||
| 25 | CRASH_IMG=/tmp/rsdos-crash.img | ||
| 26 | rm -f "$CRASH_IMG" | ||
| 27 | mkfs.fat -C -F 32 -n CRASHLOG "$CRASH_IMG" 65536 >/dev/null | ||
| 28 | test_boot \ | ||
| 29 | -drive "file=$CRASH_IMG,format=raw,if=none,id=crash" \ | ||
| 30 | -device ide-hd,drive=crash,bus=ide.1 | ||
| 31 | |||
| 32 | echo "==> 4/5 boot diagnostics present" | ||
| 33 | grep -aq "gop: fb base=" "$LOG" || fail "fb geometry diagnostic missing" | ||
| 34 | grep -aq "gop: mode .* selected" "$LOG" || fail "mode-selection diagnostic missing" | ||
| 35 | grep -aq "CRASHLOG" "$LOG" || fail "CRASHLOG stick not mounted" | ||
| 36 | |||
| 37 | echo "==> 5/5 inject a page fault (fault command)" | ||
| 38 | sendkeys "clear" "fault" | ||
| 39 | sleep 2 | ||
| 40 | |||
| 41 | grep -aq "!!! page fault vec=14 .*rip=" "$LOG" || fail "page-fault dump line missing" | ||
| 42 | grep -aq "cr2=0x400000000000" "$LOG" || fail "CR2 not reported (faulting address)" | ||
| 43 | grep -aq "task=0 \"shell\"" "$LOG" || fail "faulting task not identified" | ||
| 44 | grep -aq "!!! halting" "$LOG" || fail "no halt message" | ||
| 45 | |||
| 46 | echo "" | ||
| 47 | echo "PASS — fault dump verified" | ||
diff --git a/scripts/test-focus.py b/scripts/tests/test-focus.py index e1bbe2d..cfa317a 100755 --- a/scripts/test-focus.py +++ b/scripts/tests/test-focus.py | |||
| @@ -2,16 +2,17 @@ | |||
| 2 | """Drive the focus-routing gate: 3 windows, type markers, verify routing. | 2 | """Drive the focus-routing gate: 3 windows, type markers, verify routing. |
| 3 | 3 | ||
| 4 | Connects to the QEMU monitor once and keeps the socket open (the per-call | 4 | Connects to the QEMU monitor once and keeps the socket open (the per-call |
| 5 | scripts/sendkeys.py pattern loses the socket when the monitor resets). | 5 | sendkeys.py pattern loses the socket when the monitor resets). |
| 6 | 6 | ||
| 7 | Usage: python3 scripts/test-focus.py /tmp/qmon.sock | 7 | Usage: start the guest with `./scripts/run.sh --monitor`, then |
| 8 | python3 scripts/tests/test-focus.py [/tmp/rsdos-monitor.sock] | ||
| 8 | """ | 9 | """ |
| 9 | import socket | 10 | import socket |
| 10 | import sys | 11 | import sys |
| 11 | import time | 12 | import time |
| 12 | 13 | ||
| 13 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 14 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
| 14 | sock.connect(sys.argv[1] if len(sys.argv) > 1 else "/tmp/sysboot-mon.sock") | 15 | sock.connect(sys.argv[1] if len(sys.argv) > 1 else "/tmp/rsdos-monitor.sock") |
| 15 | 16 | ||
| 16 | 17 | ||
| 17 | def sk(k, d=0.35): | 18 | def sk(k, d=0.35): |
diff --git a/scripts/tests/test-gfx.sh b/scripts/tests/test-gfx.sh new file mode 100755 index 0000000..613318f --- /dev/null +++ b/scripts/tests/test-gfx.sh | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/tests/test-gfx.sh — automated graphics round-trip test. | ||
| 4 | # | ||
| 5 | # Builds, boots headless, runs the in-kernel gfx self-test and the | ||
| 6 | # status-bar demo, then powers off. | ||
| 7 | # | ||
| 8 | # Usage: ./scripts/tests/test-gfx.sh | ||
| 9 | # Env: SYSTEM_IMG, OVMF, QEMU (see scripts/lib.sh) | ||
| 10 | # | ||
| 11 | set -euo pipefail | ||
| 12 | TNAME="gfx" | ||
| 13 | # shellcheck source=../lib.sh | ||
| 14 | source "$(dirname "$0")/../lib.sh" | ||
| 15 | |||
| 16 | echo "==> 1/5 build (kernel + bins)" | ||
| 17 | build_all | ||
| 18 | |||
| 19 | echo "==> 2/5 stage system image" | ||
| 20 | stage_image --force | ||
| 21 | |||
| 22 | echo "==> 3/5 boot QEMU (headless)" | ||
| 23 | test_boot | ||
| 24 | wait_log "$LOG" "cursor: task " 10 || fail "cursor task did not spawn" | ||
| 25 | |||
| 26 | echo "==> 4/5 in-kernel pixel self-test (gfx check)" | ||
| 27 | sendkeys "gfx check" | ||
| 28 | wait_log "$LOG" "gfx check: PASS" 30 || fail "gfx check did not run" | ||
| 29 | |||
| 30 | echo "==> 5/5 gfx demo (draw scene)" | ||
| 31 | sendkeys "gfx" "poweroff" | ||
| 32 | wait_qemu_exit 30 || fail "QEMU did not exit after poweroff" | ||
| 33 | |||
| 34 | grep -aq "gfx: drew status bar" "$LOG" || fail "gfx demo did not draw" | ||
| 35 | |||
| 36 | echo "" | ||
| 37 | echo "PASS — gfx round trip" | ||
diff --git a/scripts/tests/test-mouse.sh b/scripts/tests/test-mouse.sh new file mode 100755 index 0000000..bb835be --- /dev/null +++ b/scripts/tests/test-mouse.sh | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/tests/test-mouse.sh — automated PS/2 mouse round-trip test. | ||
| 4 | # | ||
| 5 | # Boots QEMU headless, injects mouse movement via the QEMU monitor, | ||
| 6 | # verifies the on-screen cursor tracks it, and tests the mouse stream | ||
| 7 | # routing command. | ||
| 8 | # | ||
| 9 | # Usage: ./scripts/tests/test-mouse.sh | ||
| 10 | # Env: SYSTEM_IMG, OVMF, QEMU (see scripts/lib.sh) | ||
| 11 | # | ||
| 12 | set -euo pipefail | ||
| 13 | TNAME="mouse" | ||
| 14 | # shellcheck source=../lib.sh | ||
| 15 | source "$(dirname "$0")/../lib.sh" | ||
| 16 | |||
| 17 | # cursor_state — one-shot `cursor` command, parsed to "x y buttons". | ||
| 18 | cursor_state() { | ||
| 19 | sendkeys "cursor" | ||
| 20 | sleep 0.5 | ||
| 21 | sed -n 's/.*cursor: x=\([0-9]*\) y=\([0-9]*\) buttons=\([A-Za-z-]*\).*/\1 \2 \3/p' "$LOG" | tail -1 | ||
| 22 | } | ||
| 23 | |||
| 24 | echo "==> 1/6 build (kernel + bins)" | ||
| 25 | build_all | ||
| 26 | |||
| 27 | echo "==> 2/6 stage system image" | ||
| 28 | stage_image --force | ||
| 29 | |||
| 30 | echo "==> 3/6 boot QEMU (headless)" | ||
| 31 | test_boot | ||
| 32 | grep -aq "ps2: mouse ready" "$LOG" || fail "mouse driver did not come up at boot" | ||
| 33 | wait_log "$LOG" "cursor: task " 10 || fail "cursor task did not spawn" | ||
| 34 | |||
| 35 | echo "==> 4/6 cursor position tracks injected movement" | ||
| 36 | INIT=$(cursor_state) | ||
| 37 | [ -n "$INIT" ] || fail "no initial cursor position" | ||
| 38 | IX=$(echo "$INIT" | awk '{print $1}') | ||
| 39 | IY=$(echo "$INIT" | awk '{print $2}') | ||
| 40 | IB=$(echo "$INIT" | awk '{print $3}') | ||
| 41 | echo " initial: x=$IX y=$IY buttons=$IB" | ||
| 42 | [ "$IB" = "---" ] || fail "expected no buttons at boot, got $IB" | ||
| 43 | |||
| 44 | qemu_hmp "mouse_move 20 7" | ||
| 45 | S1=$(cursor_state) | ||
| 46 | X1=$(echo "$S1" | awk '{print $1}') | ||
| 47 | Y1=$(echo "$S1" | awk '{print $2}') | ||
| 48 | [ "$X1" -eq $((IX + 20)) ] || fail "x did not move +20 (was $IX, now $X1)" | ||
| 49 | [ "$Y1" -eq $((IY + 7)) ] || fail "y did not move +7 (was $IY, now $Y1)" | ||
| 50 | echo " moved: x=$X1 y=$Y1 (delta +20,+7)" | ||
| 51 | |||
| 52 | echo "==> 5/6 left button edge detection" | ||
| 53 | qemu_hmp "mouse_button 1" | ||
| 54 | S2=$(cursor_state) | ||
| 55 | IB2=$(echo "$S2" | awk '{print $3}') | ||
| 56 | [ "$IB2" = "L--" ] || fail "expected L--, got $IB2" | ||
| 57 | |||
| 58 | qemu_hmp "mouse_button 0" | ||
| 59 | S3=$(cursor_state) | ||
| 60 | IB3=$(echo "$S3" | awk '{print $3}') | ||
| 61 | [ "$IB3" = "---" ] || fail "expected --- after release, got $IB3" | ||
| 62 | echo " button: $IB2 → $IB3" | ||
| 63 | |||
| 64 | echo "==> 6/6 mouse stream routing to msgrecv's task" | ||
| 65 | # The spawned task id shifts with the task table (wm-input took slot 3) — | ||
| 66 | # parse it rather than hardcoding. | ||
| 67 | sendkeys "msgrecv" | ||
| 68 | sleep 1 | ||
| 69 | MID="$(sed -n 's/.*msgrecv: task \([0-9][0-9]*\) listening.*/\1/p' "$LOG" | tail -1)" | ||
| 70 | [ -n "$MID" ] || fail "msgrecv did not spawn" | ||
| 71 | echo " msgrecv task id: $MID" | ||
| 72 | sendkeys "mouse $MID" | ||
| 73 | qemu_hmp "mouse_move 20 7" | ||
| 74 | qemu_hmp "mouse_button 1" | ||
| 75 | sleep 0.5 | ||
| 76 | qemu_hmp "mouse_button 0" | ||
| 77 | sleep 0.5 | ||
| 78 | sendkeys "ctrl-c" | ||
| 79 | sleep 0.5 | ||
| 80 | grep -aq "Mouse(+20,+7" "$LOG" || fail "routed mouse move not logged" | ||
| 81 | |||
| 82 | echo "" | ||
| 83 | echo "PASS — mouse round trip" | ||
diff --git a/scripts/tests/test-msg.sh b/scripts/tests/test-msg.sh new file mode 100755 index 0000000..7786beb --- /dev/null +++ b/scripts/tests/test-msg.sh | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/tests/test-msg.sh — automated scheduler message-queue round trip. | ||
| 4 | # | ||
| 5 | # Tests all message types (user, timer, key, mouse), real-input routing, | ||
| 6 | # and the queue-full boundary. | ||
| 7 | # | ||
| 8 | # Usage: ./scripts/tests/test-msg.sh | ||
| 9 | # Env: SYSTEM_IMG, OVMF, QEMU (see scripts/lib.sh) | ||
| 10 | # | ||
| 11 | set -euo pipefail | ||
| 12 | TNAME="msg" | ||
| 13 | # shellcheck source=../lib.sh | ||
| 14 | source "$(dirname "$0")/../lib.sh" | ||
| 15 | |||
| 16 | echo "==> 1/5 build (kernel + bins)" | ||
| 17 | build_all | ||
| 18 | |||
| 19 | echo "==> 2/5 stage system image" | ||
| 20 | stage_image --force | ||
| 21 | |||
| 22 | echo "==> 3/5 boot QEMU (headless)" | ||
| 23 | test_boot | ||
| 24 | |||
| 25 | echo "==> 4/5 drive the shell (msgrecv / send / mouse routing / queue-full)" | ||
| 26 | # msgrecv's task id is assigned at spawn time and shifts when the task | ||
| 27 | # table grows (the Windowed GUI added wm-input at slot 3) — read it from | ||
| 28 | # the log instead of hardcoding. | ||
| 29 | sendkeys "msgrecv" | ||
| 30 | sleep 1 | ||
| 31 | MID="$(sed -n 's/.*msgrecv: task \([0-9][0-9]*\) listening.*/\1/p' "$LOG" | tail -1)" | ||
| 32 | [ -n "$MID" ] || fail "msgrecv did not spawn (no 'msgrecv: task N listening' in the log)" | ||
| 33 | echo " msgrecv task id: $MID" | ||
| 34 | |||
| 35 | sendkeys \ | ||
| 36 | "send $MID user 42" \ | ||
| 37 | "send $MID timer" \ | ||
| 38 | "send $MID key enter" \ | ||
| 39 | "send $MID key ctrl-c" \ | ||
| 40 | "send $MID key f3" \ | ||
| 41 | "send $MID mouse 5 -3 3" \ | ||
| 42 | "sleep 800" \ | ||
| 43 | "jobs" | ||
| 44 | |||
| 45 | sendkeys "mouse $MID" | ||
| 46 | qemu_hmp "mouse_move 20 7" | ||
| 47 | qemu_hmp "mouse_button 1" | ||
| 48 | qemu_hmp "mouse_move -5 -3" | ||
| 49 | qemu_hmp "mouse_button 0" | ||
| 50 | qemu_hmp "sendkey ctrl-c" | ||
| 51 | |||
| 52 | # Queue-full probe: overflow a queue that nobody drains. The shell used | ||
| 53 | # to be that task, but its exec pump now consumes its own User messages | ||
| 54 | # as completion codes (q=0 at the prompt), and msgrecv consumes on wake — | ||
| 55 | # the cursor task never calls recv, so its 16-slot ring fills for real. | ||
| 56 | CID="$(awk '$2 == "cursor" { print $1 }' "$LOG" | tail -1)" | ||
| 57 | [ -n "$CID" ] || fail "no cursor task in jobs output (cannot probe queue-full)" | ||
| 58 | echo " queue-full target: task $CID (cursor, never recv'd)" | ||
| 59 | |||
| 60 | sendkeys \ | ||
| 61 | "send $CID user 1" \ | ||
| 62 | "send $CID user 2" \ | ||
| 63 | "send $CID user 3" \ | ||
| 64 | "send $CID user 4" \ | ||
| 65 | "send $CID user 5" \ | ||
| 66 | "send $CID user 6" \ | ||
| 67 | "send $CID user 7" \ | ||
| 68 | "send $CID user 8" \ | ||
| 69 | "send $CID user 9" \ | ||
| 70 | "send $CID user 10" \ | ||
| 71 | "send $CID user 11" \ | ||
| 72 | "send $CID user 12" \ | ||
| 73 | "send $CID user 13" \ | ||
| 74 | "send $CID user 14" \ | ||
| 75 | "send $CID user 15" \ | ||
| 76 | "send $CID user 16" \ | ||
| 77 | "send $CID user 17" \ | ||
| 78 | "sleep 800" \ | ||
| 79 | "jobs" \ | ||
| 80 | "poweroff" | ||
| 81 | |||
| 82 | echo "==> 5/5 wait for shutdown + verify" | ||
| 83 | wait_qemu_exit 30 || fail "QEMU did not exit after poweroff" | ||
| 84 | |||
| 85 | grep -aq "msgrecv: task $MID listening" "$LOG" || fail "msgrecv did not spawn" | ||
| 86 | grep -aq "msgrecv\[$MID\]: User(42)" "$LOG" || fail "user message missing" | ||
| 87 | grep -aq "msgrecv\[$MID\]: Timer" "$LOG" || fail "timer message missing" | ||
| 88 | grep -aq "msgrecv\[$MID\]: Key(Enter)" "$LOG" || fail "key enter missing" | ||
| 89 | grep -aq "msgrecv\[$MID\]: Key(Ctrl-c)" "$LOG" || fail "key ctrl-c missing" | ||
| 90 | grep -aq "msgrecv\[$MID\]: Key(F3)" "$LOG" || fail "key f3 missing" | ||
| 91 | grep -aq "msgrecv\[$MID\]: Mouse(+5,-3 L R)" "$LOG" || fail "synthesized mouse message missing" | ||
| 92 | grep -aq "msgrecv\[$MID\]: Mouse(+20,+7" "$LOG" || fail "real mouse move 20,7 not routed to queue" | ||
| 93 | grep -aq "msgrecv\[$MID\]: Mouse(-5,-3 L" "$LOG" || fail "held-button routed move missing" | ||
| 94 | grep -aq "queue full" "$LOG" || fail "17th send did not report queue full" | ||
| 95 | grep -aq "send: unknown type" "$LOG" && fail "a bad send slipped through (expected none)" | ||
| 96 | |||
| 97 | echo "" | ||
| 98 | echo "PASS — message queues OK" | ||
