From 7e71b83c619119a0c30e82df5300e01d0f8093ed Mon Sep 17 00:00:00 2001 From: Matzman Date: Mon, 5 Oct 2026 21:36:22 +0200 Subject: [PATCH] Design: threads and test strategy; high-level architecture done D29: only the MQTT client runs in its own thread; OpenVR and the HA layer stay in the main thread. D30: four-level test strategy. Co-Authored-By: Claude Opus 5.5 --- docs/DESIGN.md | 53 +++++++++++++++++++++++++++++++++++++++++++++--- docs/PROGRESS.md | 31 ++++++++++++++-------------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 52d588a..2cefc59 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -1,14 +1,41 @@ # Design Status: **in progress**, iteration 1, phase 2 (planning and design). High-level architecture -first, then the details for the MVP. Requirements and their decisions (D1–D15): +done; next: the details for the MVP. Requirements and their decisions (D1–D15): [REQUIREMENTS.md](REQUIREMENTS.md). Design decisions start at D16. ## Process and threads - **One process** started by SteamVR does everything: HA connection, sensors, services, all overlays (D16). -- Several threads inside it for concurrent work. Which work runs in which thread is still open. +- Several threads inside it for concurrent work (D16). **A thread is added only where work can + block** and would stall rendering and SteamVR events (D29): + + | Thread | Runs | + |---|---| + | main | Qt event loop, QML, off-screen rendering of all surfaces, SteamVR event polling and texture submission, **all OpenVR calls** (I/O wrappers), HA device and entities, managers | + | MQTT | the MQTT client (libmosquitto): DNS lookup, TCP and TLS connect block for seconds when the broker is unreachable | + + - OpenVR stays in the main thread: it isn't documented as thread-safe, and all events come + through one queue. + - The boundary is three signals with value arguments (queued connections): + + ``` + main thread MQTT thread + HA device ── publish(topic, payload) ──▶ MQTT client + HA device ◀── messageReceived(topic, payload) ── MQTT client + HA device ◀── connectionChanged(state, error) ── MQTT client + ``` + + - The MQTT client lives in its thread via `moveToThread()`; it creates its socket notifiers + and timers there, after the move. + - Not needed as own threads: WebEngine (own processes), Qt Multimedia and QtNetwork + (asynchronous), later D-Bus and `QProcess` for network/audio (asynchronous). Rendering stays + in the main thread as long as NFR-3 holds. + - Rules: no direct calls on objects of another thread (signals or + `QMetaObject::invokeMethod` with `Qt::QueuedConnection`); lambdas connected across threads + always get a context object; no `BlockingQueuedConnection`; only values, no shared + pointers, cross the boundary. ## Layers @@ -160,7 +187,25 @@ Notes for later: ## Open -1. High-level points 3–7 (see [PROGRESS.md](PROGRESS.md), "Next steps"). +1. MVP details (see [PROGRESS.md](PROGRESS.md), "Next steps"). + +## Test strategy + +Four levels (D30), implementing NFR-8: + +| Level | What | How | Where | +|---|---|---|---| +| 1. Unit tests | configuration (CFG-2), discovery payloads, throttling (SEN-14), availability logic (SEN-15), entities, notification manager | QtTest with **fakes at the boundaries**: a fake MQTT client for the three signals (D29), fake I/O wrappers instead of OpenVR | host and test container, `ctest` | +| 2. Integration tests with a real broker | CON-1–4: connect, last will when killed, reconnect after broker restart, login/TLS | the test starts a local `mosquitto` on a free port (host: installed; test container: add the package) | host and test container, `ctest` | +| 3. UI and rendering | main tab and notification cards, transparency | existing tools: `--snapshot`, `tst_render`, desktop modes | host | +| 4. Manual | against the user's HA: entities appear, no deprecation warnings (NFR-7). On the Frame: sensors, notification in VR and in a game, DSH-1, premultiplied alpha, NFR-3 | checklist per iteration, results documented in `docs/` | user's HA, the Frame | + +- OpenVR wrappers stay thin; logic goes into the testable parts behind them. The wrappers + themselves are only checked on the device (level 4). +- HA itself is tested manually for now (an HA container would be possible but costly); + revisit later (NFR-8 allows adjusting the strategy). +- The NFR-3 measurement method is a detail (idea: compare SteamVR compositor statistics, e.g. + missed frames, with and without FrameAssistant). ## Appendix A: notification UI in VR (research) @@ -222,3 +267,5 @@ Collected 2026-10-05 for the notification design. | D25 | 2026-10-05 | Two overlay kinds in the platform: dashboard tabs and world overlays (MVP: one placement, not interactive). Set `VROverlayFlags_IsPremultiplied`. Runtime surfaces and interactive world overlays later | Notifications need world overlays; Qt Quick renders premultiplied alpha | | D26 | 2026-10-05 | One world overlay for all notifications; QML lays out the cards | No runtime surfaces, fewer resources; per-notification placement isn't required | | D27 | 2026-10-05 | MVP notification placement: spawn in front (about 1 m, slightly below the line of sight), then stay | Research favours non-head-locked placement; no continuous tracking needed | +| D29 | 2026-10-05 | Threads: only the MQTT client runs in its own thread; everything else, incl. all OpenVR calls and the HA layer, in the main thread. New threads only for work that can block | Blocking connect must not stall rendering; OpenVR isn't documented as thread-safe; the narrowest possible thread boundary | +| D30 | 2026-10-05 | Test strategy in four levels: unit tests with fakes at the boundaries, integration tests against a local mosquitto, UI/render tools, manual checklist (user's HA, the Frame) | Covers NFR-8; the narrow boundaries (D29, I/O wrappers) make fakes cheap; HA automation deferred | diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index d9749c5..dda534c 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -5,27 +5,24 @@ CLAUDE.md ("Working rules"). ## Current state -- **Iteration 1, phase 2 (planning and design) started.** Phase 1 is approved (D13); - [REQUIREMENTS.md](REQUIREMENTS.md) is the baseline. +- **Iteration 1, phase 2 (planning and design).** Phase 1 is approved (D13); + [REQUIREMENTS.md](REQUIREMENTS.md) is the baseline. High-level architecture done + ([DESIGN.md](DESIGN.md)); MVP details next. ## Next steps -Phase 2: first the high-level architecture, then the details for the MVP (D8, plus DSH-1 by -D28). High-level -questions, in this order: +Phase 2: the high-level architecture is done (D16–D30, [DESIGN.md](DESIGN.md)). Next, the +details for the MVP (D8, plus DSH-1 by D28), in this order: -1. ~~Process model~~: one process, several threads (D16). -2. ~~Layers~~: presentation, business logic, I/O (D17). -3. ~~How features plug in~~: object classes (D18), configuration injection (D19), one class - per HA entity type with native types preferred (D23). -4. ~~HA channels~~: MQTT for the device, own channel for the dashboard later (D24). -5. Overlay kinds: dashboard tabs and world overlays (D25), one notification overlay (D26), - MVP placement (D27); DSH-1 web UI test in the MVP (D28). -6. Runtime model: which work runs in which thread (D16: several threads). -7. Test strategy at a high level. +1. Topics and payloads: topic scheme, device and entity ids, discovery content, MVP entity + list, retain and availability. +2. MVP notification: command format and title (MQTT `notify` drops the title). +3. Configuration file format, incl. TLS/CA settings. +4. Shipping libmosquitto (build, package, licence text). +5. NFR-3 measurement method. -Details afterwards (MQTT library done: D14, D15): topics and payloads, basic notification, -configuration, packaging of libmosquitto. +Then the platform changes (start-up hook D21, desktop modes D22, world overlay D25, WebEngine +initialisation for DSH-1) and the implementation plan for iteration 1. ## Log @@ -42,6 +39,8 @@ configuration, packaging of libmosquitto. present (relevant for Q2/Q3). - libmosquitto ABI check: Debian's 2.0.21 needs at most `OPENSSL_3.0.0`/`GLIBC_2.34`; the Frame has OpenSSL 3.2.1 and glibc 2.39. No libcjson dependency. D14 holds. +- Test strategy in four levels (D30). High-level architecture done. +- Threads: MQTT client in its own thread, everything else incl. OpenVR in the main thread (D29). - DSH-1 split: web UI out-of-the-box test in the MVP, full dashboard as DSH-2 (D28). - Overlays: two kinds (D25), one overlay for all notifications (D26), MVP placement "spawn in front, then stay" (D27). Researched notification UI in VR (DESIGN.md, Appendix A). Found: