feat(e2e): Meshtasticator radio-level validation harness for the adapter #2
Reference in New Issue
Block a user
Delete Branch "feat/meshtasticator-e2e"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Radio-level counterpart to the contract suite: an end-to-end harness that drives
hermes-meshtasticagainst live Meshtasticator simulated nodes (real MeshtasticD instances speaking the actual TCP/protobuf API on port 4404+), so the adapter is validated against the device-software/mesh layer without deploying or needing physical radios.Why
The contract tests (
plugin/tests/contract) guarantee adapter ↔ Hermes-gateway conformance but cannot exercise the radio path that previously failed at runtime. This fills that gap in the development loop:What's in the PR
scripts/meshtasticator_e2e/run_e2e.sh— boots N Meshtasticator nodes (Docker mode or Linux-native MeshtasticD via--program), waits for their TCP API ports, runs the suite, tears down (incl. docker container cleanup).scripts/meshtasticator_e2e/README.md— architecture, prerequisites, run instructions, status/open items.plugin/tests/e2e/test_meshtasticator_e2e.py— skip-gated tests (MESHTASTICATOR_E2E=1): adapter connects to node 0 and receives a peer-node broadcast as a real gatewayMessageEvent; adapter's >180-char send is chunked and the final chunk arrives at the peer.Ordinary unit/CI runs are unaffected: the e2e module skips unless explicitly enabled, and no new CI job is added yet (needs a docker-enabled runner + a slow first-run firmware build — tracked as an open item).
Validation status
MESHTASTICATOR_E2E=1+ real gateway + meshtastic lib it progresses to a genuineadapter.connect()attempt (fails only because no simulator is running here — no Docker/Linux MeshtasticD on this machine).out/nodeConfig.yaml), channel alignment if mirroring production's channel-1 setup, and per-node lifecycle control to exercise the adapter's watchdog reconnect.Run 10536 reached pytest but every connect was reset: the simulator died at import with 'Tkinter is needed' — lib/gui.py calls matplotlib.use("TkAgg") unconditionally (an explicit use() overrides MPLBACKEND=Agg), and the port probe passed instantly because it was accepting the localhost forwarders, not the nodes. - run_e2e.sh: patch meshtasticator lib/gui.py TkAgg->Agg before boot; probe readiness on the upstream host (docker daemon) when forwards are active; docker pull meshtastic/meshtasticd up front so the pull is off the simulator's node-boot path. - e2e tests: cancel leftover loop tasks (watchdog) at loop teardown.Progress summary — Meshtasticator e2e validation attempt
Status: the harness, CI job, and full simulator boot are working; inter-node RF delivery through Meshtasticator's interactive sim is not, and the loop has been paused pending a runtime decision. Nothing here is merged or deployed; all work is on this branch (
b306323, 9 commits overmain).What now works (proven on the CI runner, runs 10535–10542)
release.yaml), plugin installed with deps, Meshtasticator pinned at17ceb823, isolated sim venv, thenscripts/meshtasticator_e2e/run_e2e.sh --mode docker.run_e2e.sh:lib/gui.pymatplotlib.use("TkAgg")at import →"Agg"(crash: Tkinter is needed),move_figure()unconditionalcanvas.manager.windowaccess at Graph construction → guarded no-op (crash: FigureManagerBase has no attribute 'window'),cmdloopimmediately in CI → fed from a FIFO held open until teardown (exitis sent through it for a clean shutdown).DOCKER_HOST=tcp://docker.local:2375. Meshtasticator hardcodeslocalhostfor its node connections, sorun_e2e.shstarts onetcp_forward.py(localhost → daemon) per node port, and readiness is probed on the upstream daemon host — not on the forwarders (which would always pass).docker pull meshtastic/meshtasticd) so the pull is off the sim's boot path.The blocker
Neither direction delivers a message between two nodes: the adapter (node 0) never receives the peer's broadcast, and chunked sends never arrive at the peer. Findings so far:
Node.sendText→Interface.sendText) + deliveryport=4405) and node 0 (port=4404) — but no delivery--from-file)Root-cause candidates, in order of suspicion:
reconnect_nodes: close + reconnect every interface) is racy and sometimes strands the sim before subscriptions are live.meshtastic~=2.6.1inrequirements.txt) predates themeshtastic/meshtasticdimage; run 10541 only started observing TX after the sim venv was switched to the current client.firmware_harness(SimMesh, ships insidemeshtastic/tests/) — same SIMULATOR_APP bridge — so the concept is sound; the maintenance/robustness is the gap.Open decision (paused per request)
How to get a green e2e run:
firmware_harness.SimMesh(recommended): extractmeshtasticdfrom the already-pulled image, run it natively on the Linux runner, deterministic topology, no docker port games, no GUI, maintained upstream. Keep the pytest module, adapter-under-test, and CI job shape.Branch contents (all under
scripts/meshtasticator_e2e/,plugin/tests/e2e/,.gitea/workflows/meshtasticator-e2e.yaml)workflow_dispatch; PR trigger commented out until green),concurrencyguard.Validation of the contract harness (separate concern, already merged to
mainin310c024) is unaffected:8 passedlocally and in CI.Pull request closed