#!/usr/bin/env bash # # scripts/test-disk.sh — full automated data-disk round trip. # # Builds the kernel, ensures the FAT32 image exists (with --fresh it is # re-formatted and re-seeded), boots QEMU headless, drives the shell via # the monitor, then verifies the results with mtools and fsck. # # Usage: # ./scripts/test-disk.sh # reuse existing image # ./scripts/test-disk.sh --fresh # re-format the image first # # Environment: # DISK_IMG data disk path (default: /tmp/disk.img) # 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" FRESH=0 [ "${1:-}" = "--fresh" ] && FRESH=1 OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" IMG="${SYSTEM_IMG:-/tmp/system.img}" QPID=/tmp/rsdos-test-qpid SOCK=/tmp/rsdos-test-qmon.sock LOG=/tmp/rsdos-test-serial.log QLOG=/tmp/rsdos-test-qemu.log STAMP="$(date +%s)" # Never leave a QEMU behind (an orphan holding our stdout would hang any # caller that pipes our output). 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 — poll a file for a regex. 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 — poll until the pid is gone. 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 } echo "==> 1/6 build kernel (debug)" cargo build --release --package rsdos --target x86_64-unknown-uefi echo "==> 2/6 ensure system image" if [ "$FRESH" -eq 1 ]; then "$SCRIPT_DIR/mksystem.sh" --force "$IMG" else "$SCRIPT_DIR/mksystem.sh" "$IMG" fi echo "==> 3/6 boot QEMU (headless)" kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true sleep 1 rm -f "$SOCK" "$LOG" # Throwaway OVMF varstore: a stale/poisoned NvVars (e.g. left by the old # vvfat setup) makes the firmware skip the \EFI\BOOT fallback entirely. NV="/tmp/rsdos-test-ovmf-vars.fd" rm -f "$NV" cp /usr/share/edk2/x64/OVMF.4m.fd "$NV" "$QEMU" -bios "$NV" \ -drive file="$IMG",format=raw,if=none,id=disk \ -device ide-hd,drive=disk,bus=ide.0 \ -serial file:"$LOG" \ -monitor unix:"$SOCK",server,nowait \ -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ >"$QLOG" 2>&1 & wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" sleep 1 # Volumes mount under /disk// where UID = "LABEL-SERIAL" (e.g. # RSDOS-5BD1E55F). Discover it from the boot log so the test works on # any image without knowing the serial in advance. VOL_UID="$(grep -a 'fat: mounted' "$LOG" | sed -n 's/.*at \/disk\/\([^ )]*\).*/\1/p' | head -1)" [ -n "$VOL_UID" ] || fail "no FAT32 volume was mounted (look for 'fat: mounted' in the log)" echo " mounted volume UID: $VOL_UID" echo "==> 4/6 drive the shell (ls / mkdir / echo WRITING / cat / poweroff)" # The filename is deliberately >13 chars so the FAT LFN path gets # multi-entry entries (they're stored last-first on disk). TESTFILE="disk-test-$STAMP.txt" python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ "ls /disk" \ "mkdir /disk/$VOL_UID/sub" \ "echo disk-test-$STAMP WRITING /disk/$VOL_UID/sub/$TESTFILE" \ "cat /disk/$VOL_UID/sub/$TESTFILE" \ "poweroff" echo "==> 5/6 wait for shutdown" wait_qemu_exit 30 || fail "QEMU did not exit after poweroff" echo "==> 6/6 verify" grep -a "disk-test-$STAMP" "$LOG" >/dev/null || fail "cat output missing from serial log" if ! fsck.fat -n "$IMG" >/tmp/rsdos-fsck.out 2>&1; then fail "fsck.fat reports problems: $(cat /tmp/rsdos-fsck.out | tail -3)" fi MT="$(mtype -i "$IMG" "::/sub/$TESTFILE" 2>/dev/null || true)" [ "$MT" = "disk-test-$STAMP" ] || fail "mtype mismatch: got '$MT'" echo "" echo "PASS — disk round trip OK (fsck clean, file survived poweroff)" echo " image: $IMG" echo " volume: /disk/$VOL_UID" echo " written: /disk/$VOL_UID/sub/$TESTFILE = \"disk-test-$STAMP\""