6.8 KiB
Graphics and recovery verification
Run from the checkout. Tools are temporary Nix development dependencies; no
persistent host installation is required. The two-guest smoke test needs access
to /dev/kvm and creates its own headless Wayland display, VM disks and sockets
under a fresh /tmp/hydra-smoke-* directory. It never uses the inherited display,
active TTY, existing guests or existing disks. Evidence remains in that directory;
the script stops only its own verified QEMU instances on exit.
python3 -m unittest discover -s tests -v
nix-build dev/tools.nix -A viewer --no-out-link
nix-build dev/tools.nix -A qemu --no-out-link
nix-shell dev/tools.nix --run 'python3 dev/smoke.py'
The smoke test builds a private input helper with an ordinary evdev/pc105 US
keyboard map. It boots two tiny counter workloads from separate scratch qcow2
disks, connects two fullscreen viewers, and checks reserved switching/recovery,
manual USB selection, explicit reconnect and independent QMP lifecycles.
After each confirmed workspace switch, the hidden guest is sampled across a
half-second interval with no focus changes, switches or reconnects; the check is
then repeated with the other guest hidden. result.json records both intervals.
Guest memory counters must advance and QEMU PIDs remain unchanged through
switching. Viewer protocol logs must show no reconnection during switching.
A test-only ToggleKeybinds action and F12 marker establish native labwc
keybinding inhibition before checking the production reserved bindings. Those
extra controls are absent from the production session configuration. See the
labwc configuration manual for
overrideInhibition and the actions manual
for native ToggleKeybinds behavior.
USB acceptance checks the actual selector window title, holds the modifiers through opening, releases them inside the modal dialog, then asserts fresh keyboard leave/enter transitions for both Right and Left switches. QEMU's USB device list must stay unchanged without a selection. Both viewer USB filters deny automatic hotplug and connection redirection. This check does not attach a physical USB device or prove guest driver behavior.
The viewer adaptation handles only the configured manual USB accelerator before forwarding an event to the guest. Upstream keyboard grabs remove the GTK application accelerator group; without this adaptation, the configured USB shortcut reaches the viewer but does not open its selector.
The pinned labwc 0.20.2 runtime has no documented runtime XWayland-disable
option. This native-Wayland probe sets WLR_XWAYLAND=/bin/false so no X server
is launched, and explicitly disables activation-environment updates. It can
still log an occupied X socket while wlroots reserves a display number. Only
[xwayland/sockets.c] Failed to bind socket ... Address already in use is
classified as expected noise in result.json; any other compositor [ERROR]
fails the probe. Full logs remain available. This classification does not
suppress viewer errors or compiler/test diagnostics. The viewer build's upstream
Meson boolean-check and compiler-warning-option guidance are build noise; the four-test
result, compiler errors and process exit status remain the build acceptance gate.
Installed guest workflow
--iso is optional. Attach or detach media only while the VM is stopped:
bin/hydra add guest --iso /absolute/path/installer.iso
bin/hydra start guest
bin/hydra view guest
# Shut down from inside the guest, then check it is stopped.
bin/hydra status guest
bin/hydra media guest --detach
bin/hydra start guest
For an existing stopped guest, bin/hydra media guest --iso PATH attaches
installation/rescue media. A missing configured ISO prevents a new start with
an actionable error; it does not prevent listing profiles, status, stopping a
running guest, reconnecting a viewer or detaching the missing media. Media
updates share the VM lifecycle lock and preserve its qcow2 disk. Abrupt stopping
still requires bin/hydra stop guest --force and verified QMP identity.
Display preferences and EDID
New developer profiles use user networking, 1280×800 and refresh_millihz=0.
Zero omits the patched QEMU refresh property and remains compatible with stock
QEMU. Hydra's installed defaults can use this flat /etc/hydra/defaults.toml:
network = "bridge"
bridge = "hydrabr0"
width = 1920
height = 1080
refresh_millihz = 144000
Only add reads distribution defaults. Explicit --network, --bridge,
--width, --height and --refresh-millihz options win; existing profiles do
not change. --defaults PATH selects another preferences file for development.
Missing defaults use developer preferences; unknown or invalid fields fail
before creating a profile. Dimensions are integers from 1 to 16384, refresh is
zero for automatic refresh or an integer from 50000 to 255000 mHz, matching
the vertical range descriptor limits. This bound does not establish every
resolution/refresh combination; pixel-clock and horizontal timing limits are
mode-dependent and the verified target remains 1920×1080/144000 mHz. Nonzero
refresh requires the tracked patched QEMU provided by dev/tools.nix and distribution packaging.
The patch keeps the requested refresh through frontend resizing and extends EDID monitor ranges to include the generated preferred mode. To verify the actual patched QEMU generator with the pinned source archive:
qemu_source=$(nix-build dev/tools.nix -A qemu.src --no-out-link)
nix-shell dev/tools.nix --run "python3 dev/check-edid.py '$qemu_source'"
The probe applies the tracked patch to four relevant source files in a temporary directory, compiles the actual EDID and virtio GPU generator functions with a small dependency harness, and decodes the result. It checks 60/75 Hz fallback, 144 Hz configuration, a conflicting 60 Hz frontend request, resized dimensions, checksums and monitor ranges. At 1920×1080/144000 mHz the preferred timing is 143.997231 Hz after EDID clock rounding. This is an advertised guest mode, not measured frame delivery or proof of a 144 Hz physical output.
Still requiring a dedicated physical session
- Physical reserved chords, brightness/backlight permissions and touchpad input.
- Physical held keys/modifiers across guest switching, grabs and modal dialogs; guest key-up delivery, pointer grab release and absence of stuck modifiers.
- Physical USB selection/redirection and guest drivers; no automatic redirection.
- Toolbar behavior at physical screen edges and actual recovery shell usability.
- ASUS output selection at 1920×1080/144 Hz, guest-reported display mode, delivered frame rate, hidden guest graphics continuity, tearing and switching latency.
Headless checks establish CPU workload continuity, protocol focus transitions and the exercised compositor actions. They cannot prove physical input or scanout.