feat(simu): unify UI automation harness - #7668
Conversation
|
Correction to the delivery rule above: this effort now stays deliberately simple. Implementation proceeds in this same draft PR and branch; there is no second implementation branch and no branch per phase. G0 remains an explicit review requirement before the PR leaves draft or is merged, but it does not block small implementation commits. Phase 1 is now concrete at @onliner10 and @pfeerick are invited to review this concrete slice and shape the next one. Credit and provenance from #7337 and #7646 remain explicit, and neither source PR needs to close until parity is demonstrated. |
|
Phase 2 is now published at This slice adds the opt-in cross-platform stdio transport and SDL-loop activation while keeping the scope narrow: Validation completed:
The implementation intentionally does not add a permanent Python client yet; a disposable probe was enough to validate this transport slice. @onliner10 and @pfeerick remain invited to review the contract and retained behavior before the draft is marked ready. |
|
Phase 3 slice 3A is now published at This keeps the scope intentionally narrow: a dependency-free, binary-pipe Python session and minimal Validation:
Credit: the reusable host-session and CLI direction comes from #7337 by @onliner10. I reviewed that implementation, but this protocol-v1/Windows-safe lifecycle is a substantial rewrite with no coherent source block copied; the provenance ledger records that distinction. A co-author trailer remains intentionally absent unless approved. Phase 3 remains open for a real Windows-simulator run, explicit kill-fallback injection, and CI update: the complete GitHub test, firmware-build, documentation, and packaging matrix passed for this head (45/45). |
|
Phase 3 is now complete at f6c104e on this same branch and draft PR. This final slice adds only the readiness boundary needed before inputs: bounded status/describe responses, target/LCD/command/capability validation, and startup completion after the simulator owns a real first LCD frame. It intentionally does not advertise or implement input, capture, telemetry, Lua, or restart yet. Validation completed:
Thank you again, @onliner10 and @pfeerick. This slice combines the reusable host-session direction from #7337 with the guarded epoch/first-frame model from #7646. Please review the resulting boundary and help shape Phase 4 inputs; G0 remains open before this draft can be marked ready. CI update: the complete current-head GitHub Actions and CodeRabbit matrix passed 45/45 for f6c104e. |
Implement Phase 4 of the consolidated simulator automation plan: target-filtered key, rotary, and touch primitives; an asynchronous real-LCD frame barrier; host-side timed composites; release cleanup; and focused native/Python coverage. This combines the portable host-side composition direction from EdgeTX#7337 with the direct simulator helpers and explicit touch transitions explored in EdgeTX#7646. Thanks to Mateusz Urban (@onliner10) for the original harness direction; review remains explicitly invited in EdgeTX#7668.
|
Hi @onliner10 — Phase 4 of the consolidated simulator UI automation work is now available in this PR. Thank you again for the work and direction you established in #7337, especially the reusable host-side harness and action model. Those ideas have materially shaped this consolidation, and I have tried to keep that provenance clear in both the plan and the PR history. I would genuinely value your review of the protocol, host API, and retained behavior before this draft is marked ready. Please feel free to challenge the design or suggest a simpler direction where appropriate. If you are comfortable with commit-level attribution for the parts substantially derived from your work, I would also be happy to add a No pressure either way — your review and feedback would already be greatly appreciated. |
|
Phase 5 is now published at This slice adds one focused capability: render-complete framebuffer capture. It combines the portable PPM and host-conversion direction from #7337 with the static-screen invalidation requirement explored in #7646. Native capture waits for a strictly newer RGB565 framebuffer, publishes without replacing an existing artifact, and keeps file I/O out of the LCD notification. The Python client validates the PPM, converts and independently decodes PNG, and writes deterministic SHA-256 metadata. Validation is complete:
Thank you, @onliner10, for the original harness and capture direction in #7337. I would especially value your review of the freshness boundary, artifact contract, and host API. @pfeerick, your review of the simulator/LVGL integration would also be very welcome. G0 remains open, so the PR stays draft. |
|
Phase 6 is now published at |
|
Just a friendly FYI ... tell your AI assistant to be less eager to tag and mention people... it's the quickest way to get yourself ignored if you are pinging people every couple of hours. Commits generally also speak for themselves if well written and described, so iteratively updating the initial PR body post would have been more than sufficient here, and better than individual progress comments with each commit, as the posts here also flow into things like discord channels, meaning there are will be several effectively "noise" posts there about this PR. |
Add a dependency-free binary-pipe session for the implemented start, ping, and stop lifecycle, with strict response correlation and bounded cleanup tests.\n\nThe reusable host-session and CLI direction builds on EdgeTX#7337 by Mateusz Urban. This implementation is substantially rewritten for protocol v1, Windows-compatible reader threads, and deterministic process cleanup; no coherent source block is imported.
Add bounded status and describe responses backed by the simulator session state, then validate discovery and poll first-frame readiness from the cross-platform host session. This consolidates the host lifecycle and discovery direction from EdgeTX#7337 with the guarded epoch and first-frame state model from EdgeTX#7646. Credit to @onliner10 and @pfeerick for the two approaches brought together here.
Implement Phase 4 of the consolidated simulator automation plan: target-filtered key, rotary, and touch primitives; an asynchronous real-LCD frame barrier; host-side timed composites; release cleanup; and focused native/Python coverage. This combines the portable host-side composition direction from EdgeTX#7337 with the direct simulator helpers and explicit touch transitions explored in EdgeTX#7646. Thanks to Mateusz Urban (@onliner10) for the original harness direction; review remains explicitly invited in EdgeTX#7668.
Capture a strictly newer RGB565 framebuffer after an LVGL invalidation, publish deterministic PPM artifacts without replacement, and convert them to verified PNG metadata in the host client. This combines the portable PPM and client direction from EdgeTX#7337 with the static-screen invalidation approach from EdgeTX#7646. Thanks to @onliner10 for the original harness and capture direction.
651220c to
c6afd45
Compare
The fixture layout and declarative scenario requirements derive from EdgeTX EdgeTX#7337. The implementation is rewritten for the consolidated v1 protocol.
There was a problem hiding this comment.
Pull request overview
This PR consolidates EdgeTX simulator UI automation into a single, cross-platform harness: a bounded stdio protocol implemented in the native simulator plus a dependency-free Python host runner for flows, artifact capture/verification, and hardening.
Changes:
- Adds a versioned simulator automation protocol over stdin/stdout (JSON responses/events) with capture, input injection, Lua reload, restart, and frame barriers.
- Introduces a dependency-free Python UI harness (CLI + strict flow runner + protocol/PPM/PNG tooling) with extensive unit and hardening tests.
- Wires automation into the SDL simulator build/args and adds CI to exercise host tests, native ASan/UBSan radio tests, and WASI isolation checks.
Reviewed changes
Copilot reviewed 44 out of 45 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/ui-harness/tests/test_protocol.py | Adds protocol encoding/decoding and schema validation tests (wire limits, duplicates, strict shapes). |
| tools/ui-harness/tests/test_ppm.py | Tests strict PPM parsing and deterministic, dependency-free PNG writing/reading. |
| tools/ui-harness/tests/test_hardening.py | Adds lifecycle/stress/hardening runner tests using a fake simulator fixture. |
| tools/ui-harness/tests/test_flow.py | Adds strict flow schema validation and manifest/protocol evidence tests. |
| tools/ui-harness/tests/fake_simulator.py | Provides a portable subprocess fixture for protocol/session lifecycle testing. |
| tools/ui-harness/README.md | Documents harness usage (smoke, harden, probe) and output/evidence contracts. |
| tools/ui-harness/flows/tx16s-smoke.json | Adds the checked-in TX16S smoke flow scenario. |
| tools/ui-harness/fixtures/tx16s/settings/RADIO/radio.yml | Adds TX16S radio settings fixture template (CRLF-managed via attributes). |
| tools/ui-harness/fixtures/tx16s/settings/MODELS/model1.yml | Adds a deterministic model fixture for simulator startup. |
| tools/ui-harness/fixtures/tx16s/settings/MODELS/labels.yml | Adds model label metadata fixture. |
| tools/ui-harness/fixtures/tx16s/sdcard/.gitkeep | Ensures an empty sdcard fixture directory is tracked. |
| tools/ui-harness/fixtures/README.md | Documents fixture provenance and immutability/copy semantics. |
| tools/ui-harness/fixtures/.gitattributes | Forces CRLF for fixture YAML to match simulator-produced checksums. |
| tools/ui-harness/edgetx-ui | Adds a Python entrypoint wrapper script for the harness CLI. |
| tools/ui-harness/edgetx_ui/protocol.py | Implements strict request encoding and message parsing/decoding for protocol v1. |
| tools/ui-harness/edgetx_ui/ppm.py | Implements strict PPM handling plus deterministic PNG encode/decode and hashing. |
| tools/ui-harness/edgetx_ui/cli.py | Adds CLI commands for probe/run-flow/smoke/harden and optional simulator build. |
| tools/ui-harness/edgetx_ui/init.py | Exposes the harness public API surface. |
| radio/src/tests/simu_automation_runtime.cpp | Adds gtests for firmware mailbox, telemetry edge cases, and tuple correctness. |
| radio/src/tests/simu_automation_capture.cpp | Adds gtests for capture path validation, writer behavior, cancellation, and stability. |
| radio/src/tests/simu_arg_parser.cpp | Adds gtests for automation argument parsing and stdout/stderr behavior. |
| radio/src/tests/CMakeLists.txt | Wires simulator automation sources into native gtests build with SIMU_AUTOMATION. |
| radio/src/targets/simu/sdl_simu.cpp | Integrates stdio automation, input/capture hooks, restarts, and analog override reads. |
| radio/src/targets/simu/CMakeLists.txt | Adds SIMU_AUTOMATION build gating (native-only) and sources for protocol/capture/stdio. |
| radio/src/targets/simu/automation_stdio.h | Declares the stdio automation transport/pump and request processing interface. |
| radio/src/targets/simu/automation_runtime.h | Declares firmware-periodic automation runtime mailbox and analog override APIs. |
| radio/src/targets/simu/automation_runtime.cpp | Implements firmware mailbox, telemetry/Lua operations, epochs/generations, and hooks. |
| radio/src/targets/simu/automation_protocol.h | Defines protocol constants, parsing/serialization structures, and session state model. |
| radio/src/targets/simu/automation_capture.h | Declares capture validation, snapshot/worker pipeline, and atomic cancellation contract. |
| radio/src/targets/simu/automation_capture.cpp | Implements capture path containment rules and canonical RGB565->PPM writer pipeline. |
| radio/src/targets/simu/arg_parser.h | Extends simulator args for --automation-stdio/--automation-output (SIMU_AUTOMATION). |
| radio/src/targets/simu/arg_parser.cpp | Implements automation option validation and stdout/stderr routing rules. |
| radio/src/main.cpp | Calls firmware-periodic automation hooks around UI execution paths. |
| radio/src/gui/colorlcd/LvglWrapper.cpp | Adds LVGL invalidation hook for deterministic capture of static screens. |
| mkdocs.yml | Adds simulator UI automation plan docs to the developer documentation nav. |
| .github/workflows/simulator_ui_automation.yml | Adds CI workflow for host tests, native ASan/UBSan automation tests, and WASI isolation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Fixes: N/A
Summary
This PR adds a unified, opt-in UI automation system for the native EdgeTX
simulator.
Its purpose is to turn a manual simulator reproduction into a repeatable flow:
start from a known fixture, discover what the selected target supports, drive
the real EdgeTX runtime, wait for firmware-rendered LCD frames, capture
framebuffer artifacts, and preserve enough evidence to reproduce or diagnose a
failure.
Normal simulator use remains unchanged unless automation is explicitly enabled.
The automation surface is excluded from physical firmware, WASI, and Emscripten
builds.
Why this is useful
UI validation currently depends heavily on opening the simulator, navigating
screens by hand, and capturing the host window. Those results can vary with
window size, scaling, focus, timing, and local radio state.
This harness works against the simulator runtime and framebuffer instead of
automating the host window. It provides:
This gives contributors and maintainers a reproducible foundation for UI
development, bug reproduction, and future regression testing.
What is included
Native simulator automation
--automation-stdioand an explicit artifact root.simulator and firmware ownership paths.
static LVGL screens and exclusive no-overwrite PPM publication.
Host harness
edgetx-uiCLI.validation through
describe.declarative run.
timeout, crash, terminate, and kill paths.
How it works
Timed actions such as presses, taps, and drags are composed by the host, so the
native command path does not sleep. Firmware-owned work crosses an explicit
mailbox. The LCD callback only publishes sequence and snapshot state and
performs no filesystem work.
While the response channel remains available, every admitted request owns
exactly one terminal result. Stop acts as an admission barrier and queued
requests that have not executed are completed with
session_stopping. Requestsizes, queues, asynchronous work, timeouts, scenarios, diagnostics, and retained
in-memory evidence are bounded.
Developer workflow
The reference TX16S flow can be configured, built, run, and verified with:
Every declarative execution receives a writable copy of the checked-in fixture
and creates a unique run directory containing settings and SD-card copies,
captures, metadata, hashes, a manifest, streamed protocol evidence, and bounded
simulator diagnostics.
Once execution has started, failures preserve the evidence observed before
failure and return a nonzero exit code. Pre-launch preparation failures remove
their incomplete run directory. Existing runs and artifacts are never silently
reused or overwritten.
TX16S is the first supported end-to-end profile. The native discovery model is
generic, but each additional target requires an explicit profile, fixture,
capability set, and representative flow before the declarative runner accepts
it.
Provenance and consolidation
This work consolidates the directions explored in EdgeTX PRs #7337 and #7646.
It is a substantial redesign rather than a mechanical merge.
PR #7337, developed by Mateusz Urban (
onliner10), established the reusablehost-harness direction: a CLI, declarative flows, fixture-based startup,
target-aware interaction, and host-side framebuffer tooling. The TX16S fixture
in this PR is derived from and migrated from that work.
PR #7646 explored Windows-native transport requirements, explicit input
transitions, switch/analog/telemetry injection, Lua reload, restart behavior,
capture after a real LCD refresh, and forced invalidation of static LVGL screens.
The consolidated implementation keeps one generic activation path, one bounded
protocol, and one cross-platform host lifecycle. It deliberately does not carry
forward the append-only command-file transport, a simulator-only Lua control
API, native PNG encoding, direct cross-thread mutation, or a second automation
protocol.
Thank you to Mateusz for the original harness direction and fixture foundation.
Validation
Validation for the current head includes:
pending or failed checks.
validation passing.
covered protocol correlation, lifecycle, deterministic static capture,
deliberate visual mutation, Lua generations, restart epochs, fixture
integrity, temporary files, and process/thread reaping.
The evidence above does not include exact Linux-versus-Windows RGB equality or
a separately built and inspected Emscripten artifact.
Scope and limits
This PR does not:
process modifying it concurrently.
Future value
This establishes one automation boundary that later work can extend without
creating another simulator control surface. Possible follow-ups include:
The implementation is ready for maintainer review. Passing checks do not
replace maintainer acceptance of the protocol and integration boundaries.
Suggested review order