#!/bin/bash set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Parse arguments AUTO_MODE=false for arg in "$@"; do case $arg in --auto) AUTO_MODE=true shift ;; esac done PROJECT_ROOT="$SCRIPT_DIR/.." VM_DIR="$PROJECT_ROOT/.nogit/vm" ISO_PATH="$PROJECT_ROOT/.nogit/iso/ecoos.iso" DISK_PATH="$VM_DIR/test-disk.qcow2" MONITOR_SOCK="$VM_DIR/qemu-monitor.sock" SERIAL_SOCK="$VM_DIR/serial.sock" SERIAL_LOG="$VM_DIR/serial.log" PID_FILE="$VM_DIR/qemu.pid" # Create VM directory if not exists mkdir -p "$VM_DIR" # Check if ISO exists if [ ! -f "$ISO_PATH" ]; then echo "ERROR: ISO not found at $ISO_PATH" echo "Run 'pnpm run build' first to create the ISO" exit 1 fi # Create test disk if not exists if [ ! -f "$DISK_PATH" ]; then echo "Creating test disk (20GB)..." qemu-img create -f qcow2 "$DISK_PATH" 20G fi # Check if already running if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if kill -0 "$PID" 2>/dev/null; then echo "QEMU already running (PID: $PID)" echo "Run 'pnpm run test:stop' to stop it first" exit 1 fi fi echo "Starting QEMU with EcoOS ISO..." # Check if KVM is available KVM_OPTS="" if [ -e /dev/kvm ] && [ -r /dev/kvm ] && [ -w /dev/kvm ]; then KVM_OPTS="-enable-kvm -cpu host" echo "Using KVM acceleration" else echo "KVM not available, using software emulation (slower)" fi # Cleanup function cleanup() { echo "" echo "Shutting down..." if [ -n "$SCREENSHOT_LOOP_PID" ] && kill -0 "$SCREENSHOT_LOOP_PID" 2>/dev/null; then kill "$SCREENSHOT_LOOP_PID" 2>/dev/null || true fi if [ -n "$ENABLE_PID" ] && kill -0 "$ENABLE_PID" 2>/dev/null; then kill "$ENABLE_PID" 2>/dev/null || true fi if [ -n "$VIEWER_PID" ] && kill -0 "$VIEWER_PID" 2>/dev/null; then kill "$VIEWER_PID" 2>/dev/null || true fi if [ -n "$TWM_PID" ] && kill -0 "$TWM_PID" 2>/dev/null; then kill "$TWM_PID" 2>/dev/null || true fi if [ -n "$XORG_PID" ] && kill -0 "$XORG_PID" 2>/dev/null; then kill "$XORG_PID" 2>/dev/null || true fi if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if kill -0 "$PID" 2>/dev/null; then kill "$PID" 2>/dev/null || true fi rm -f "$PID_FILE" fi echo "Done" } trap cleanup EXIT INT TERM # Start QEMU with virtio-gpu multi-head (3 outputs) > "$SERIAL_LOG" # Clear old log qemu-system-x86_64 \ $KVM_OPTS \ -m 4G \ -smp 4 \ -bios /usr/share/qemu/OVMF.fd \ -drive file="$ISO_PATH",media=cdrom \ -drive file="$DISK_PATH",format=qcow2,if=virtio \ -device qxl-vga,id=video0,ram_size=67108864,vram_size=67108864,vgamem_mb=64 \ -device qxl,id=video1,ram_size=67108864,vram_size=67108864,vgamem_mb=64 \ -device qxl,id=video2,ram_size=67108864,vram_size=67108864,vgamem_mb=64 \ -display none \ -spice port=5930,disable-ticketing=on \ -device virtio-serial-pci \ -chardev spicevmc,id=vdagent,name=vdagent \ -device virtserialport,chardev=vdagent,name=com.redhat.spice.0 \ -serial unix:"$SERIAL_SOCK",server,nowait \ -monitor unix:"$MONITOR_SOCK",server,nowait \ -nic user,model=virtio-net-pci,hostfwd=tcp::3006-:3006,hostfwd=tcp::2222-:22 \ -pidfile "$PID_FILE" & QEMU_PID=$! echo "" echo "=== EcoOS Test VM Started ===" echo "QEMU PID: $QEMU_PID" echo "Management UI: http://localhost:3006" echo "" # Wait for QEMU to start and SPICE to be ready echo "Waiting for SPICE server..." sleep 3 # Check if remote-viewer is available if ! command -v remote-viewer &> /dev/null; then echo "WARNING: remote-viewer not installed" echo "Install with: sudo apt install virt-viewer" echo "" echo "Running without display viewer. Press Ctrl-C to stop." wait $QEMU_PID exit 0 fi # Set up virt-viewer settings for multi-display VIRT_VIEWER_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/virt-viewer" mkdir -p "$VIRT_VIEWER_CONFIG_DIR" if [ -f "$SCRIPT_DIR/virt-viewer-settings" ]; then cp "$SCRIPT_DIR/virt-viewer-settings" "$VIRT_VIEWER_CONFIG_DIR/settings" echo "Configured virt-viewer for 3 displays" fi # Detect DISPLAY if not set if [ -z "$DISPLAY" ]; then # Try to find an active X display if [ -S /tmp/.X11-unix/X0 ]; then export DISPLAY=:0 elif [ -S /tmp/.X11-unix/X1 ]; then export DISPLAY=:1 fi fi # Detect WAYLAND_DISPLAY if not set if [ -z "$WAYLAND_DISPLAY" ] && [ -z "$DISPLAY" ]; then # Try common Wayland sockets if [ -S "$XDG_RUNTIME_DIR/wayland-0" ]; then export WAYLAND_DISPLAY=wayland-0 elif [ -S "/run/user/$(id -u)/wayland-0" ]; then export XDG_RUNTIME_DIR="/run/user/$(id -u)" export WAYLAND_DISPLAY=wayland-0 fi fi # Launch remote-viewer - use dummy X server with 3 monitors if no display available if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ]; then echo "No display found, starting headless X server with 3 virtual monitors..." # Find an available display number XDISPLAY=99 while [ -S "/tmp/.X11-unix/X$XDISPLAY" ]; do XDISPLAY=$((XDISPLAY + 1)) done # Start Xorg with dummy driver config for 3 monitors XORG_CONFIG="$SCRIPT_DIR/xorg-dummy.conf" Xorg :$XDISPLAY -config "$XORG_CONFIG" -noreset +extension GLX +extension RANDR +extension RENDER & XORG_PID=$! sleep 2 export DISPLAY=:$XDISPLAY # Configure 3 virtual monitors using xrandr # Add mode to disconnected DUMMY outputs and position them xrandr --newmode "1920x1080" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync 2>/dev/null || true # Add mode to DUMMY1 and DUMMY2, then enable them xrandr --addmode DUMMY1 "1920x1080" 2>/dev/null || true xrandr --addmode DUMMY2 "1920x1080" 2>/dev/null || true # Position the outputs side by side xrandr --output DUMMY0 --mode 1920x1080 --pos 0x0 --primary xrandr --output DUMMY1 --mode 1920x1080 --pos 1920x0 2>/dev/null || true xrandr --output DUMMY2 --mode 1920x1080 --pos 3840x0 2>/dev/null || true echo "Headless X server started on :$XDISPLAY" # Launch remote-viewer in fullscreen to request all monitors remote-viewer --full-screen spice://localhost:5930 & VIEWER_PID=$! echo "remote-viewer running headlessly (PID: $VIEWER_PID)" else echo "Launching remote-viewer with fullscreen for multi-display (DISPLAY=$DISPLAY, WAYLAND_DISPLAY=$WAYLAND_DISPLAY)..." remote-viewer --full-screen spice://localhost:5930 & VIEWER_PID=$! fi echo "" echo "=== Press Ctrl-C to stop ===" echo "" # Enable all 3 displays via SPICE protocol (waits for agent automatically) # Using 300s timeout since ISO boot can take several minutes if [ -f "$SCRIPT_DIR/enable-displays.py" ]; then echo "Enabling displays (waiting for SPICE agent, up to 5 minutes)..." python3 "$SCRIPT_DIR/enable-displays.py" --timeout 300 2>&1 & ENABLE_PID=$! fi echo "Tips:" echo " - http://localhost:3006 - Management UI" echo " - socat - UNIX-CONNECT:.nogit/vm/serial.sock - Serial console (login: ecouser/ecouser)" echo "" # Start screenshot loop in background (takes screenshots every 5 seconds) echo "Starting screenshot loop..." (while true; do "$SCRIPT_DIR/screenshot.sh" 2>/dev/null; sleep 5; done) & SCREENSHOT_LOOP_PID=$! if [ "$AUTO_MODE" = true ]; then echo "=== Auto mode: waiting for display setup ===" # Wait for enable-displays.py to complete if [ -n "$ENABLE_PID" ]; then wait $ENABLE_PID ENABLE_EXIT=$? if [ $ENABLE_EXIT -ne 0 ]; then echo "FAIL: Could not enable displays (exit code: $ENABLE_EXIT)" exit 1 fi fi # Take screenshot echo "Taking screenshot..." "$SCRIPT_DIR/screenshot.sh" # Verify screenshot dimensions (should be 5760x1080 for 3 displays) SCREENSHOT="$PROJECT_ROOT/.nogit/screenshots/latest.png" if [ -f "$SCREENSHOT" ]; then WIDTH=$(identify -format "%w" "$SCREENSHOT" 2>/dev/null || echo "0") if [ "$WIDTH" -ge 5760 ]; then echo "SUCCESS: Multi-display test passed (width: ${WIDTH}px)" exit 0 else echo "FAIL: Screenshot width is ${WIDTH}px, expected >= 5760px" exit 1 fi else echo "FAIL: Screenshot not found" exit 1 fi else # Interactive mode - wait for QEMU to exit wait $QEMU_PID 2>/dev/null || true fi