TravelRouter documentation Experimental
Image boot and client tests
The image job boots the compressed disk image produced by the build. Successful configuration parsing alone cannot establish that the hotspot, resolver and operations panel start together. The boot test exercises their real service integration and blocks artifact/release publication when it fails.
What runs
The test extracts the kernel, initramfs and Pi 4 device tree from the finished
image and starts QEMU's raspi4b machine. Its writable qcow2 layer leaves the
base image unchanged. Alpine restores the image's apkovl and installs its
embedded APK packages through the normal diskless initramfs path.
The extracted test device tree declares the emulator's upper 1 GiB RAM bank:
QEMU 11.0.3 otherwise advertises only its lower 960 MiB to Linux despite allocating
2 GiB. The reserved VideoCore region stays excluded. Original/test device-tree
hashes and this fixup are recorded in result.json; the release image, kernel
and initramfs are unchanged.
The copied device tree also disables the absent Bluetooth device on the emulated
serial controller, exposing its terminal device for the test harness.
A CI-only initial shell loads mac80211_hwsim and creates three simulated radios:
two for the router and one in a separate client network namespace. It runs the
shipped OpenRC sysinit, boot and default runlevels. Production service files,
application code and package contents are not replaced with mocks.
The client checks:
- WPA2 association with the actual hostapd process.
- A DHCP lease with the expected gateway, DNS address and subnet.
- Local/captive DNS responses, including redirected external DNS traffic.
- HTTP page, JavaScript, stylesheet and font responses through the firewall.
- Captive redirects, Host/Origin restrictions and authenticated API access.
- Mandatory first setup, CSRF rejection and credential rotation.
- Configuration persistence across a cold VM restart, followed by a second association, DHCP exchange, login, metrics request and logs request.
- Denial of management access from synthetic untrusted ingress and forwarding while the router is offline.
Firewall denial checks first require successful HTTP over the same network path with narrowly scoped temporary test allowances. After removing those allowances, fresh connections must hit passive counters immediately before the shipped drop policy. A timeout caused by a broken route cannot pass this check. The temporary rules exist only in the disposable VM and are cleaned up after each measurement.
Generic UEFI targets
The AMD64 and ARM64 jobs boot their installer ISO through QEMU's corresponding UEFI firmware and GRUB. A disposable ISO copy changes only the external GRUB configuration to select a serial console and the test's initial shell. The installer script, embedded runtime image, bootloader code and Alpine packages remain the shipped versions.
The test runs the actual installer against an empty file-backed virtual disk, provides its explicit disk/erase confirmations, and requires its complete read-back verification. After stopping that VM, the host independently hashes the whole written image region and compares it with the built runtime image.
That installed disk then boots through UEFI and GRUB for both runtime test phases. Only its copied external GRUB configuration receives the test console and initial-shell arguments. The same simulated-radio, OpenRC, DHCP, DNS, HTTP, firewall and cold-reboot checks run against each architecture. Original ISO/image hashes, test-copy hashes and firmware hashes appear in the diagnostics. The released images contain no VM-specific serial argument or test account.
Boundaries
This is a Pi 4-compatible image boot and service integration test. QEMU does not currently provide a CM5/BCM2712 machine. It does not validate the physical CM5 firmware, RP1, carrier board, radio firmware, USB adapter or RF behavior. The test's initial shell also replaces the usual PID 1 entry point so it can inject simulated hardware; normal init process signal/reaping behavior is not qualified by that instrumentation.
HTTP client checks do not execute browser JavaScript. Browser layout and interaction checks are separate from the image test. Public captive networks, external VPN/proxy providers and real tunnel-failure packet tests remain in the hardware qualification checklist.
Running and diagnosing
The normal quality/image workflow runs this test after each new image build, before uploading the release image. Publication is restricted to tag pushes; all release gates, including the image boot test, must succeed.
For a reported problem with an existing release, manually run the workflow on
a branch and set image_release to a semantic tag such as v0.1.0. That mode
downloads the published image and verifies its checksum instead of rebuilding
it. It never publishes a release and is rejected on tag refs.
Inside the pinned aarch64 Alpine build container:
sh image/e2e/run.sh dist/travelrouter-cm5-aarch64.img.gz
This requires no host block device, privileged container, KVM or physical Wi-Fi
adapter. QEMU uses software emulation. The harness uses a fresh scratch directory
under build/e2e-vm; guest writes are disposable.
GitHub uploads image-boot-diagnostics even after a test failure. The compact
results under build/e2e-results contain image hashes, the failed stage, serial
boot logs, OpenRC status, interfaces/routes, listeners, firewall rules and service
logs when the guest is responsive. A host preparation or early boot failure may
only have the result summary and console output. Missing readiness, an assertion
failure or a timeout exits nonzero; it is never counted as a pass.
References: QEMU Raspberry Pi models and Linux mac80211_hwsim.