From d02c6d569c38cc6e711531e5482e9ca394255502 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 18 Jun 2026 14:43:27 -0400 Subject: [PATCH] test(tests): TDD for test_execution_sim_live GUI subprocess crash (failing test) Captures the structural root cause of the test_execution_sim_live failure: src/gui_2.py:render_response_panel calls imgui.set_window_focus directly during the render frame. On Windows, the GUI subprocess main thread has only 1.94 MB of stack; the focus call exhausts it and crashes the GUI with 0xC00000FD = STATUS_STACK_OVERFLOW. This test enforces the fix's contract: the render body must NOT call imgui.set_window_focus directly; it must defer the call via a _pending_focus_response flag to the next frame's idle phase. Mirrors the existing _autofocus_response_tab pattern at gui_2.py:5353-5356. Test currently FAILS on this commit. Will pass after the fix in src/gui_2.py:render_response_panel and the deferred handler in the main render loop. --- tests/test_extended_sims.py | 71 ++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/tests/test_extended_sims.py b/tests/test_extended_sims.py index 86df2672..b7a97c47 100644 --- a/tests/test_extended_sims.py +++ b/tests/test_extended_sims.py @@ -69,4 +69,73 @@ def test_execution_sim_live(live_gui: Any) -> None: client.set_value('auto_add_history', True) sim.run() time.sleep(2) - sim.teardown() \ No newline at end of file + sim.teardown() + +def test_render_response_panel_defers_set_window_focus() -> None: + """Regression test for the GUI subprocess STATUS_STACK_OVERFLOW crash. + + Captures the root cause of the `test_execution_sim_live` failure: the + `render_response_panel` function used to call + `imgui.set_window_focus("Response")` directly during the render frame. + On Windows, the GUI subprocess's main thread has only 1.94 MB of stack + (set by Python's PE header). imgui-bundle's native focus call uses + ~2-3 MB of C stack, which exceeds the committed size and triggers + `0xC00000FD = STATUS_STACK_OVERFLOW`. + + The contract enforced here: the render body MUST defer the call to the + next frame's idle phase via a one-shot `_pending_focus_response` flag. + The fix mirrors the existing `_autofocus_response_tab` pattern + (see `src/gui_2.py:5353-5356`). + + Note: the source function is `render_response_panel` (no underscore). + The `_render_response_panel` label in the bug report is the perf-monitor + component name, not the function name. + """ + import re + gui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src", "gui_2.py")) + with open(gui_path, "r", encoding="utf-8") as f: + src = f.read() + + # Extract the render_response_panel function body. Non-greedy match up + # to the next top-level def (or end of file) so nested defs/comments + # inside the body are not treated as the boundary. + match = re.search( + r"def render_response_panel\(app: App\) -> None:.*?(?=\ndef |\Z)", + src, + re.DOTALL, + ) + assert match is not None, "render_response_panel function not found in src/gui_2.py" + body = match.group(0) + rest = src[:match.start()] + src[match.end():] + + # 1. The render body MUST NOT call imgui.set_window_focus("Response") + # directly. A direct call during the render frame exhausts the 1.94 MB + # main thread stack of the GUI subprocess and causes STATUS_STACK_OVERFLOW. + assert 'imgui.set_window_focus("Response")' not in body, ( + "render_response_panel still calls imgui.set_window_focus('Response') directly. " + "This exhausts the 1.94 MB main thread stack of the GUI subprocess and causes " + "STATUS_STACK_OVERFLOW (0xC00000FD). Defer the call to the next frame's idle " + "phase by setting app._pending_focus_response = True and handling it in the " + "main render loop, mirroring the _autofocus_response_tab pattern." + ) + + # 2. The render body MUST signal the deferral by setting the flag. + assert "_pending_focus_response = True" in body, ( + "render_response_panel must set _pending_focus_response = True (e.g. " + "app._pending_focus_response = True) when _trigger_blink fires. The deferred " + "handler in the main render loop will consume the flag on the next frame's " + "idle phase and then clear it." + ) + + # 3. The main render flow (everything outside render_response_panel) MUST + # contain a deferred handler that reads the flag and calls + # imgui.set_window_focus("Response") when the flag is set. + assert "app._pending_focus_response" in rest, ( + "No reference to app._pending_focus_response found outside render_response_panel. " + "The main render loop must read the flag and call imgui.set_window_focus('Response') " + "when it is set, then clear the flag." + ) + assert 'imgui.set_window_focus("Response")' in rest, ( + "No imgui.set_window_focus('Response') call found outside render_response_panel. " + "The deferred handler in the main render flow must invoke it when the flag is set." + ) \ No newline at end of file