Repository navigation
feat(simu): unify UI automation harness - #7668
bultodepapas wants to merge 22 commits into
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.
Preserve native automation framebuffer invalidation alongside the new voice-control Enter injection in the LVGL loop.
Fixes: N/A
Summary
This adds one opt-in UI automation interface for the native EdgeTX simulator.
It turns a manual reproduction into a repeatable flow: start from a known
fixture, discover the target, drive the real firmware, wait for LCD updates,
capture the framebuffer, and retain evidence of success or failure.
The native simulator owns input and firmware execution. A standard-library-only
Python harness owns scenarios, deadlines, process cleanup, PNG conversion and
artifact verification. Normal simulator use keeps its existing startup path.
Implementation and review boundaries
--automation-stdioand an explicit existing output directory.Stdout carries protocol records; diagnostics use stderr.
correlated terminal responses. Stop prevents further command execution and
rejects already queued requests with
session_stopping.bounded mailbox into firmware context. Warm restart completes on a new epoch's
first frame; cold restart creates a new process and fresh fixture copies.
LVGL screens. A worker publishes PPM files without overwriting existing files;
the host independently verifies PPM/PNG data and hashes.
complete protocol evidence to JSONL. Reap processes and reader/writer threads
on normal stop, timeout, crash, terminate and kill paths.
SIMU_AUTOMATIONbuild boundary.Add Windows/Linux host CI, native ASan/UBSan tests and WASI isolation checks.
TX16S is the first declarative-flow profile. Other targets need an explicit
profile, fixture and representative flow before the declarative runner accepts
them. The generic native discovery/probe interface also works independently of
that profile list.
Integration and review fixes
Head
cf16d8f582incorporates upstream1fb31843ad. The LVGL conflict is resolvedby preserving both automation framebuffer invalidation and upstream's voice
Enter injection under their respective compile guards, before
lv_timer_handler.The combined configuration was built and probed on the V16 simulator with
CI1302_SENSOR=ON.Both prior inline review findings are addressed:
e274501cb7: reject invalid UTF-8 capture paths and handle filesystem pathconversion errors instead of letting them terminate the simulator.
7d9f2a81c6: reject internal spaces in ordinary arguments; onlycaptureaccepts a single remainder argument.
Validation
Validation below refers to
cf16d8f582and its source tree:Windows host suite: 94/94, including a local run with warnings as errors.
Ubuntu host suite: 94/94 in GitHub Actions.
Focused native automation suite: 53/53 with ASan and UBSan in GitHub Actions.
Windows TX16S simulator builds and passes the real smoke flow.
Full real Windows hardening passes: 100 process lifecycle cycles, 10,000
correlated pings, 20 Lua reloads, 20 warm restarts and 20 static captures,
plus deliberate visual mutation. Fixtures remain unchanged, no temporary
artifacts remain, and all child processes and threads are reaped.
Windows V16 with
CI1302_SENSOR=ON: simulator build andping/describe/first-frame-ready/stop probe pass with graceful exit 0.
Whitespace validation against the updated upstream base passes.
All 44 currently reported repository checks pass, including firmware builds,
packaging, documentation and WASI isolation. See the
automation run and
radio test/build run.
The same 53 focused automation tests also pass locally in the pinned Linux
development image with ASan and UBSan.
Additional validation limit: running the entire native suite with UBSan stops
at an unaligned
uint16_tload infrsky_sport.cpp:262(theFrSkyDCellstest).A real Linux smoke with that instrumented simulator stops at an unaligned
lu_int32load in Lua'sltable.c:779. Both source files, and the S.Port testfile, are byte-identical to upstream
1fb31843ad; this PR does not change them.Neither run is claimed as passing, and no sanitizer suppression was added.
The successful dedicated sanitizer gate and the normal repository test matrix
should be read separately from these broader UBSan limitations.
Reproduce the reference flow:
For a prebuilt simulator,
edgetx-ui harden <simulator>runs the full lifecycleand visual hardening gate. With Windows SDL's dummy video backend, also set
SDL_RENDER_DRIVER=softwareandSDL_AUDIODRIVER=dummy.Scope and remaining validation limits
This does not redesign a screen or widget, change model storage, introduce a
Lua automation API or network listener, define a golden-image approval policy,
or make warm restart equivalent to a fresh process. Capture currently requires
RGB565.
The output root is a trusted session-owned directory, not an OS sandbox against
another same-user process concurrently replacing filesystem entries.
The evidence does not establish exact Linux-versus-Windows RGB equality or
include a separately built and inspected Emscripten artifact. Native-only
compile guards cover Emscripten, but that artifact claim remains unverified.
Passing checks do not replace maintainer acceptance of the protocol and
integration boundaries.
Provenance
This consolidates the directions explored in #7337 and #7646.
Mateusz Urban (
onliner10) established the reusable host-harness direction in#7337: CLI, declarative flows, target-aware interaction, fixture-based startup
and host framebuffer tooling. The TX16S fixture here is derived and migrated
from that work. Thank you to Mateusz for that foundation.
#7646 explored Windows-native transport, input/state injection, Lua reload,
restart and capture after redraw. This implementation substantially redesigns
those boundaries into one protocol and host lifecycle; it does not carry forward
the command-file transport, simulator-only Lua control API, native PNG encoder
or direct cross-thread mutation.
Suggested review order