ref:4fc4de6411b78f59632afd18166d3cb897683bc2

test(e2e): stream a QEMU in p2p mode that Sunshine joins through QMP

run_vm.sh VM_DBUS=qmp starts QEMU with -display dbus,p2p=on, no dbus-daemon, and a second QMP socket for Sunshine; e2e_stream.sh E2E_TRANSPORT=qmp and multi_instance.sh E2E_VM2_TRANSPORT=qmp use it. Refs #6 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SHA: 4fc4de6411b78f59632afd18166d3cb897683bc2
Author: Cole Christensen <cole.christensen@gmail.com>
Date: 2026-09-13 01:15
Parents: 4ffcec4
2 files changed +56 -17
Type
tests/e2e/qemu/e2e_stream.sh +12 −1
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# @tag requirements: [REQ-E2E-001, REQ-NFR-001, REQ-CAP-004, REQ-AUD-001, REQ-INP-001, REQ-INP-002, REQ-INP-003]
# @tag requirements: [REQ-E2E-001, REQ-NFR-001, REQ-CAP-004, REQ-AUD-001, REQ-INP-001, REQ-INP-002, REQ-INP-003, REQ-DEP-002]
# End-to-end test (REQ-E2E-001): an unmodified Moonlight client streams a QEMU VM that has no guest agent.
#
# 1. start a private dbus-daemon and a QEMU guest that draws a known pattern (run_vm.sh)
@@ -46,5 +46,8 @@
# Checked by check_guest_input.py; the summary is written to <artifacts>/input.json.
# E2E_SESSIONS run the client this many times in a row against the same Sunshine and VM (default: 1); every
# stream must pass, so capture and audio have to come back after a client disconnects
# E2E_TRANSPORT how Sunshine reaches QEMU's display: bus (default: a private dbus-daemon, qemu_dbus_address =
# unix:path=...) or qmp (QEMU -display dbus,p2p=on without a bus; Sunshine adds itself as a
# peer-to-peer client through a QMP socket, qemu_dbus_address = qmp:<socket>) (REQ-DEP-002)
# E2E_KEEP set to 1 to keep the work directory
set -euo pipefail
@@ -96,6 +99,14 @@
echo "e2e: E2E_INPUT can't be combined with E2E_RESET_AFTER_FRAMES or E2E_SESSIONS" >&2
exit 2
fi
case "${E2E_TRANSPORT:-bus}" in
bus | qmp) export VM_DBUS="${E2E_TRANSPORT:-bus}" ;;
*)
echo "e2e: E2E_TRANSPORT must be bus or qmp" >&2
exit 2
;;
esac
if [[ "${E2E_GL:-0}" == 1 ]]; then
if ! compgen -G "/dev/dri/renderD*" > /dev/null; then
tests/e2e/qemu/run_vm.sh +44 −16
@@ -12,13 +12,17 @@
# VM_START_WAIT seconds to wait for org.qemu on the bus (default: 30)
# VM_GL set to 1 for a GL display: virtio-vga-gl with -display dbus,gl=on (DMABUF scanouts)
# VM_RENDERNODE render node for VM_GL=1 (default: QEMU's choice, the first /dev/dri/renderD*)
# VM_DBUS "bus" (default): a private dbus-daemon, prints its address; "qmp": QEMU's display in
# peer-to-peer mode (-display dbus,p2p=on) with no bus, and a second QMP socket for Sunshine
# (display.qmp), prints qmp:<work-dir>/display.qmp
# VM_INPUT guest input devices: "virtio" (default: virtio-tablet-pci and virtio-keyboard-pci, an absolute
# mouse), "ps2" (only the built-in PS/2 keyboard and mouse, with vmport=off so the mouse is
# relative) or "touch" (virtio plus virtio-multitouch-pci)
#
# Writes into <work-dir>:
# bus.sock the private bus socket; bus address is unix:path=<work-dir>/bus.sock
# dbus.pid dbus-daemon pid
# bus.sock the private bus socket; bus address is unix:path=<work-dir>/bus.sock (VM_DBUS=bus)
# dbus.pid dbus-daemon pid (VM_DBUS=bus)
# display.qmp QMP socket Sunshine adds its display client on (VM_DBUS=qmp)
# qemu.pid QEMU pid
# qmp.sock QMP socket
# qemu.log QEMU stdout/stderr
@@ -44,11 +48,26 @@
mkdir -p "${work}"
bus_socket="${work}/bus.sock"
bus_address="unix:path=${bus_socket}"
transport="${VM_DBUS:-bus}"
case "${transport}" in
bus)
display_target="addr=${bus_address}"
transport_args=()
;;
qmp)
display_target="p2p=on"
transport_args=(-qmp "unix:${work}/display.qmp,server=on,wait=off")
;;
*)
echo "unknown VM_DBUS=${transport}" >&2
exit 2
;;
esac
display_device=virtio-vga
display_opts="dbus,addr=${bus_address},audiodev=snd0"
display_opts="dbus,${display_target},audiodev=snd0"
if [[ "${VM_GL:-0}" == 1 ]]; then
display_device=virtio-vga-gl
display_opts="dbus,gl=on,addr=${bus_address},audiodev=snd0${VM_RENDERNODE:+,rendernode=${VM_RENDERNODE}}"
display_opts="dbus,gl=on,${display_target},audiodev=snd0${VM_RENDERNODE:+,rendernode=${VM_RENDERNODE}}"
fi
case "${VM_INPUT:-virtio}" in
virtio) input_args=(-device virtio-tablet-pci -device virtio-keyboard-pci) ;;
@@ -60,18 +79,20 @@
exit 2
;;
esac
rm -f "${bus_socket}" "${work}/qmp.sock"
rm -f "${bus_socket}" "${work}/qmp.sock" "${work}/display.qmp"
dbus-daemon --session --nofork --nopidfile --address="${bus_address}" > "${work}/dbus.log" 2>&1 &
echo $! > "${work}/dbus.pid"
if [[ "${transport}" == bus ]]; then
dbus-daemon --session --nofork --nopidfile --address="${bus_address}" > "${work}/dbus.log" 2>&1 &
echo $! > "${work}/dbus.pid"
for _ in $(seq 1 100); do
[[ -S "${bus_socket}" ]] && break
sleep 0.05
done
for _ in $(seq 1 100); do
[[ -S "${bus_socket}" ]] && break
sleep 0.05
done
if [[ ! -S "${bus_socket}" ]]; then
echo "dbus-daemon didn't create ${bus_socket}" >&2
exit 1
if [[ ! -S "${bus_socket}" ]]; then
echo "dbus-daemon didn't create ${bus_socket}" >&2
exit 1
fi
fi
"${qemu}" \
@@ -86,5 +107,6 @@
"${input_args[@]}" \
-drive file="${image}",format=raw,if=ide,snapshot=on \
-qmp unix:"${work}/qmp.sock",server=on,wait=off \
"${transport_args[@]}" \
"$@" > "${work}/qemu.log" 2>&1 &
echo $! > "${work}/qemu.pid"
@@ -95,7 +117,13 @@
cat "${work}/qemu.log" >&2
exit 1
fi
if [[ "${transport}" == qmp ]]; then
# QEMU creates the QMP socket once the machine is set up; the display waits for add_client
if [[ -S "${work}/display.qmp" ]]; then
echo "qmp:${work}/display.qmp"
exit 0
fi
elif gdbus call --address "${bus_address}" --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus \
if gdbus call --address "${bus_address}" --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus \
--method org.freedesktop.DBus.NameHasOwner org.qemu 2>/dev/null | grep -q true; then
echo "${bus_address}"
exit 0
@@ -103,5 +131,5 @@
sleep 0.1
done
echo "QEMU didn't own org.qemu (or create display.qmp) within ${wait_s}s" >&2
echo "QEMU didn't own org.qemu within ${wait_s}s" >&2
exit 1