ref:main

End-to-end tests

These tests run the real pipeline: a QEMU VM with no guest agent, Sunshine with capture = qemu, and a headless Moonlight client that pairs, streams, decodes, and checks the picture. They are not part of test_sunshine; tests/CMakeLists.txt excludes this directory.

Layout

Path What it is
qemu/guest/pattern.S 512-byte boot sector: VGA mode 13h with red, green, blue, white quadrants and a blinking square
qemu/guest/build_guest.sh Builds the guest disk image with GNU as/ld (no binaries are committed)
qemu/run_vm.sh Starts a private dbus-daemon and QEMU with -display dbus,addr=... and -audiodev dbus
qemu/e2e_stream.sh The end-to-end test (REQ-E2E-001), also used for latency numbers (REQ-NFR-001)
moonlight_client/ Headless client: GameStream pairing/launch plus moonlight-common-c and FFmpeg decoding

Requirements

  • A Sunshine build with SUNSHINE_ENABLE_QEMU=ON.
  • QEMU with D-Bus display (qemu-system-x86_64 -display help | grep dbus). Both paths are tested: QEMU 8.2 (Ubuntu 24.04) sends pixel copies over D-Bus; QEMU 11.1 uses the shared memory map (org.qemu.Display1.Listener.Unix.Map).
  • dbus-daemon, gdbus, curl, GNU binutils, and for the client: libcurl, OpenSSL, FFmpeg (libavcodec, libswscale) development packages.
  • /dev/kvm is optional; without it the VM runs under TCG (set a longer E2E_TIMEOUT).

Running

cmake -B cmake-build-debug -G Ninja -DSUNSHINE_ENABLE_QEMU=ON -DBUILD_TESTS=ON
ninja -C cmake-build-debug sunshine
cmake -S tests/e2e/moonlight_client -B cmake-build-e2e-client -G Ninja
ninja -C cmake-build-e2e-client
tests/e2e/qemu/e2e_stream.sh

On success it prints e2e: PASS and a JSON summary:

{"matched":true,"decoded_frames":31,"seconds":1.82,"host_latency_ms":{"samples":31,"p50":17.2,"p95":32.9},
"quadrants":[[253,0,0],[0,254,0],[0,0,254],[255,255,255]],"terminated":false}

host_latency_ms is the frame processing latency Sunshine reports in each video frame header: the time from receiving the QEMU display call to sending the encoded frame.

On failure, or when E2E_ARTIFACTS is set, the Sunshine, QEMU, dbus-daemon and client logs, the last decoded frame (last_frame.ppm) and summary.json are kept. See the header of qemu/e2e_stream.sh for all environment variables (binaries, port, stream mode, frame count).

Latency runs

SUNSHINE_BIN=cmake-build-release/sunshine E2E_WIDTH=1920 E2E_HEIGHT=1080 E2E_FPS=60 E2E_FRAMES=600 \
E2E_ARTIFACTS=/tmp/latency tests/e2e/qemu/e2e_stream.sh

Set E2E_ENCODER=nvenc (a Sunshine build with CUDA) or E2E_ENCODER=vaapi to measure a hardware encoder instead of software x264. NVENC also works under WSL2; VAAPI and DMABUF need a DRM render node, which WSL2 doesn’t have.