Private
Public Access
0
0

refactor(test): wrap live_gui subprocess in _LiveGuiHandle class

This commit is contained in:
2026-06-09 15:37:47 -04:00
parent 30c04860c7
commit 16bd3d3a47
3 changed files with 56 additions and 5 deletions
+49 -2
View File
@@ -1,5 +1,6 @@
import pytest
import subprocess
import threading
import time
import requests
import os
@@ -396,8 +397,54 @@ def app_instance() -> Generator[App, None, None]:
if hasattr(app, 'shutdown'):
app.shutdown()
class _LiveGuiHandle:
def __init__(self, process: subprocess.Popen, gui_script: str) -> None:
"""[SDM: tests/conftest.py:_LiveGuiHandle] [C: tests/conftest.py:live_gui fixture]"""
self._process = process
self._gui_script = gui_script
self._lock = threading.Lock()
self._respawn_count = 0
def __iter__(self):
"""Support tuple unpacking: `process, gui_script = handle` (backward compat)."""
return iter((self._process, self._gui_script))
def __getitem__(self, index: int):
"""Support indexing: `handle[0]` returns process, `handle[1]` returns gui_script."""
if index == 0:
return self._process
if index == 1:
return self._gui_script
raise IndexError(index)
@property
def process(self) -> subprocess.Popen:
"""[M: tests/conftest.py:live_gui fixture]"""
return self._process
@property
def gui_script(self) -> str:
"""[M: tests/conftest.py:live_gui fixture]"""
return self._gui_script
def is_alive(self) -> bool:
"""Returns True if the subprocess is running."""
return self._process is not None and self._process.poll() is None
def ensure_alive(self) -> None:
"""No-op stub for Phase 2: the live_gui fixture is session-scoped, so we cannot respawn the subprocess in-place. The handle is respawned between test sessions. If the process died, the counter is incremented for diagnostics."""
with self._lock:
if not self.is_alive():
self._respawn_count += 1
@property
def respawn_count(self) -> int:
"""[M: tests/conftest.py:_LiveGuiHandle.ensure_alive]"""
return self._respawn_count
@pytest.fixture(scope="session")
def live_gui() -> Generator[tuple[subprocess.Popen, str], None, None]:
def live_gui() -> Generator["_LiveGuiHandle", None, None]:
"""
@@ -561,7 +608,7 @@ def live_gui() -> Generator[tuple[subprocess.Popen, str], None, None]:
diag.finalize("Live GUI Startup Telemetry", "PASS", "Hook server successfully initialized.")
try:
yield process, gui_script
yield _LiveGuiHandle(process, gui_script)
finally:
print(f"\n[Fixture] Finally block triggered: Shutting down {gui_script}...")
# Reset the GUI state before shutting down
+4 -2
View File
@@ -17,14 +17,16 @@ from src.api_hook_client import ApiHookClient
# Session-wide storage for comparing metrics
_shared_metrics = {}
def test_performance_benchmarking(live_gui: tuple) -> None:
def test_performance_benchmarking(live_gui) -> None:
"""
Collects performance metrics for the current GUI script over a 5-second window.
Ensures the application does not lock up and can report its internal state.
"""
process, gui_script = live_gui
handle = live_gui
process = handle.process
gui_script = handle.gui_script
client = ApiHookClient()
# Wait for app to stabilize and render some frames
time.sleep(3.0)
+3 -1
View File
@@ -34,7 +34,9 @@ def test_live_gui_project_settings_opens_without_filedialog_crash(live_gui) -> N
5. Verifies no AttributeError was logged (the bug would print to
the GUI's stderr, which the live_gui fixture captures to a log)
"""
process, gui_script = live_gui
handle = live_gui
process = handle.process
gui_script = handle.gui_script
client = ApiHookClient()
log_path = Path(f"logs/{Path(gui_script).name.replace('.', '_')}_test.log")