Skip to content

feat(simu): unify UI automation harness - #7668

Open
bultodepapas wants to merge 22 commits into
EdgeTX:mainfrom
bultodepapas:docs/simu-ui-automation-consolidation
Open

bultodepapas wants to merge 22 commits into
EdgeTX:mainfrom
bultodepapas:docs/simu-ui-automation-consolidation

Conversation

@bultodepapas

@bultodepapas bultodepapas commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

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

  • Activate with --automation-stdio and an explicit existing output directory.
    Stdout carries protocol records; diagnostics use stderr.
  • Use bounded UTF-8 requests, monotonic IDs, strict argument/JSON validation and
    correlated terminal responses. Stop prevents further command execution and
    rejects already queued requests with session_stopping.
  • Execute immediate inputs on the SDL thread. Telemetry and Lua reload cross a
    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.
  • Capture a strictly newer RGB565 framebuffer, including invalidation of static
    LVGL screens. A worker publishes PPM files without overwriting existing files;
    the host independently verifies PPM/PNG data and hashes.
  • Bound host writes, request deadlines, queues and diagnostic memory. Stream
    complete protocol evidence to JSONL. Reap processes and reader/writer threads
    on normal stop, timeout, crash, terminate and kill paths.
  • Keep automation behind the native-only SIMU_AUTOMATION build 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 cf16d8f582 incorporates upstream 1fb31843ad. The LVGL conflict is resolved
by 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 path
    conversion errors instead of letting them terminate the simulator.
  • 7d9f2a81c6: reject internal spaces in ordinary arguments; only capture
    accepts a single remainder argument.

Validation

Validation below refers to cf16d8f582 and 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 and
    ping/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_t load in frsky_sport.cpp:262 (the FrSkyDCells test).
A real Linux smoke with that instrumented simulator stops at an unaligned
lu_int32 load in Lua's ltable.c:779. Both source files, and the S.Port test
file, 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:

python tools/ui-harness/edgetx-ui smoke --build-dir build/ui-harness/tx16s

For a prebuilt simulator, edgetx-ui harden <simulator> runs the full lifecycle
and visual hardening gate. With Windows SDL's dummy video backend, also set
SDL_RENDER_DRIVER=software and SDL_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

  1. Protocol bounds, response ownership, stop and EOF behavior.
  2. SDL/firmware thread ownership and restart epochs.
  3. Frame freshness, capture publication and trusted-root contract.
  4. Host deadlines, cleanup, evidence bounds and declarative flow validation.
  5. Native-only build isolation and the dedicated CI workflow.

@bultodepapas

bultodepapas commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor Author

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 5139415071686414f19fa6227e9bdd0835da8080: one pure bounded protocol/state module plus focused tests, with no SDL transport or runtime activation yet. Local validation passed 122/122 tests with ASan, and the complete GitHub test/build/documentation/packaging matrix is green.

@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.

@bultodepapas bultodepapas changed the title docs(simu): plan unified UI automation harness feat(simu): unify UI automation harness Aug 17, 2026
@bultodepapas

Copy link
Copy Markdown
Contributor Author

Phase 2 is now published at c1be4bd5fb on this same branch and draft PR.

This slice adds the opt-in cross-platform stdio transport and SDL-loop activation while keeping the scope narrow: ping and stop work now; input, telemetry, Lua, restart, and framebuffer capture stay for later phases.

Validation completed:

  • 129/129 gtests-radio tests passed with ASan in the official Linux environment;
  • native Linux simulator built and passed real-pipe probes, including clean JSON-only stdout, EOF, malformed input, queue limits, paths with spaces, and dormant normal mode;
  • parser/protocol/transport compiled on Windows with both Clang and MinGW under -Wall -Wextra -Werror;
  • POSIX and Windows protocol vectors produced the same byte hash;
  • strict documentation build and git diff --check passed.

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.

@bultodepapas

bultodepapas commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Phase 3 slice 3A is now published at 49f2e3db9b on this same branch and draft PR.

This keeps the scope intentionally narrow: a dependency-free, binary-pipe Python session and minimal edgetx-ui probe for the native commands that exist today (ping and stop). It adds strict ID/JSON validation, separate bounded stderr draining, command deadlines, crash detection, and guaranteed process/thread cleanup without select or startup sleeps.

Validation:

  • 22/22 focused tests pass with warnings treated as errors on Windows Python 3.11 and official-image Linux Python 3.10;
  • the suite includes malformed/partial stdout, wrong/repeated IDs, stderr flooding, command errors, crash, timeout, terminate fallback, CLI failure exit, and 100 subprocess cycles on each host;
  • the real Linux TX16S simulator built and completed 100 start/ping/stop cycles with exit code zero and no live reader thread;
  • the exact CLI also passed end-to-end with SDL offscreen and paths containing spaces;
  • strict docs and git diff --check pass.

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 status/frame/capability readiness. @onliner10 and @pfeerick are invited to review this concrete host boundary before those pieces are added.

CI update: the complete GitHub test, firmware-build, documentation, and packaging matrix passed for this head (45/45).

@bultodepapas

bultodepapas commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor Author

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:

  • 29/29 host tests pass with warnings as errors on Windows Python 3.11 and official-image Linux Python 3.10, including 100 lifecycle cycles on each host and forced kill-then-wait cleanup;
  • 131/131 native TX16S tests pass;
  • the real Linux simulator completes 100 readiness/stop cycles without an orphan;
  • the exact current Windows simulator build completes ping/describe/first-frame-ready/stop with paths containing spaces and exit code zero;
  • git diff --check passes.

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.

bultodepapas added a commit to bultodepapas/edgetx-GR that referenced this pull request Aug 18, 2026
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.
@bultodepapas

Copy link
Copy Markdown
Contributor Author

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 Co-authored-by trailer. I will not rewrite commit history or use an email address without your approval; if you would like that attribution, please confirm the GitHub-associated email you prefer (a GitHub noreply address is completely fine).

No pressure either way — your review and feedback would already be greatly appreciated.

@bultodepapas

Copy link
Copy Markdown
Contributor Author

Phase 5 is now published at bfd59db236 on this same branch and draft PR.

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:

  • 34/34 focused native tests pass under AddressSanitizer;
  • 49/49 host tests pass;
  • the Windows TX16S simulator compiles, links, and captures through real binary pipes;
  • 20 static captures are byte-identical with increasing display sequences;
  • an ENTER interaction produces a different framebuffer hash;
  • Unicode-and-space paths work, metadata hashes match, and no temporary artifacts remain;
  • the complete current-head GitHub matrix passes 45/45.

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.

@bultodepapas

bultodepapas commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Phase 6 is now published at ada9972144 on this same branch and draft PR. This slice completes the state/lifecycle layer without adding another control surface: discovered switches and analogs use the existing simulator drivers, firmware-owned telemetry and Lua work cross a bounded SPSC mailbox, warm restart completes on the first frame of a new epoch, and cold restart returns a new process backed by fresh fixture copies. Automation uses deterministic no-splash startup, while normal simulator behavior and physical/WASM builds remain outside the change. Final local validation: - 38/38 focused native SimuAutomation* tests pass under AddressSanitizer; - 57/57 host protocol/session tests pass; - the Windows TX16S simulator compiles and links; - a real run passed switch and analog control, telemetry create/update, generation-observed Lua reload, warm restart, and cold restart with a new PID and a fully reaped old child; - formatting, compileall, and git diff --check pass. Thank you again, @onliner10, for the reusable harness/session direction established in #7337. Phase 6 deliberately keeps that direction while integrating the state-injection and restart goals from #7646. I would genuinely value your review of the host API, mailbox boundary, and restart semantics, and any simplification or correction you would like to propose. @pfeerick, review of the firmware/UI hook placement would also be very welcome. Current-head GitHub Actions, firmware builds, documentation, packaging, and CodeRabbit passed 45/45. G0 remains open, so the PR stays draft.

@pfeerick

Copy link
Copy Markdown
Member

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.
@bultodepapas
bultodepapas force-pushed the docs/simu-ui-automation-consolidation branch 2 times, most recently from 651220c to c6afd45 Compare August 31, 2026 21:11
@bultodepapas
bultodepapas marked this pull request as ready for review September 1, 2026 14:55
Copilot AI lite review requested due to automatic review settings September 1, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread tools/ui-harness/edgetx_ui/protocol.py Resolved

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants