Private
Public Access
0
0

docs(testing): add Known Gotchas section (live_gui non-determinism + early-render C crash)

This commit is contained in:
2026-06-05 18:21:24 -04:00
parent 2f0c1eb3cc
commit 9a6bcb2f34
+37
View File
@@ -579,6 +579,43 @@ The `live_gui` session fixture runs once at the start of the test session and te
---
## Known Gotchas (2026-06-05)
### Live_gui Non-Determinism
`live_gui` is a **session-scoped** fixture. All tests in a session share the same `sloppy.py` subprocess. As a result:
- A test that triggers deep render paths early in the session "warms up" ImGui state (Fonts, DisplaySize, internal caches) for later tests. The same test run in a different order or in isolation may fail with a crash that wouldn't happen in the full run.
- `time.sleep(1)` between test operations is **not** enough for ImGui to stabilize in the first few render frames. Reproducers that need stable render state should sleep 3+ seconds before triggering the call.
- Bisect by running the failing test both **with** and **without** the rest of the suite to distinguish "needs warmup" from "real bug".
- When writing a new live_gui test, do NOT assume the first render frame is "ready". Use a `wait_for_event` or multi-poll pattern that tolerates the first few frames being broken.
### Early-Render C-Level Crashes (Defer-Not-Catch Pattern)
`imgui.save_ini_settings_to_memory()` (and similar raw imgui calls that read internal state) will **crash the Python process at the C level** (`0xc0000005` access violation) if called before ImGui's internal state is fully initialized. This is **not catchable from Python**`try/except Exception` cannot intercept native access violations.
Symptoms:
- The `sloppy.py` subprocess disappears without a Python traceback.
- The pytest output shows `pytest.fail("Hook server did not start in 15s")` (the subprocess died during startup).
- Windows Event Viewer shows `Faulting module: _imgui_bundle.cp311-win_amd64.pyd` with exception code `0xc0000005`.
**Fix pattern: defer-not-catch.** Track a one-shot "ready" flag in the instance state; return early on the first call, only invoking the C function on subsequent calls:
```python
def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile:
if not getattr(self, "_ini_capture_ready", False):
self._ini_capture_ready = True
return models.WorkspaceProfile(name=name, docking_layout=b"", ...)
ini = imgui.save_ini_settings_to_memory()
return models.WorkspaceProfile(name=name, docking_layout=ini.encode("utf-8") if isinstance(ini, str) else ini, ...)
```
The first call (during initial startup) returns a safe empty profile and flips the flag; subsequent calls (when the user actually clicks "Save Profile") invoke the C function. The user's workflow is unaffected because the first call is non-blocking and the user cannot have clicked "Save Profile" before the GUI was fully rendered.
See `src/gui_2.py:601-606` for the canonical implementation. This pattern unblocks 4-5 live_gui tests that were crashing the GUI subprocess during the first render frames after `_capture_workspace_profile` was invoked by the test (typically via a `save_workspace_profile` Hook API callback).
---
## See Also
- **[guide_simulations.md](guide_simulations.md)** — Older guide focused on the Puppeteer pattern; still relevant for the test scenarios it documents