diff --git a/tests/conftest.py b/tests/conftest.py index 6bee7ab7..8a5efe8d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -356,15 +356,30 @@ def live_gui() -> Generator[tuple[subprocess.Popen, str], None, None]: else: os.symlink(src_assets, temp_workspace / "assets") - # Check if already running (shouldn't be) + # Check if already running (shouldn't be). If stale, kill the old process + # before spawning a new one — otherwise the new subprocess fails to bind + # port 8999 and the wait loop connects to the stale process instead, + # leading to state pollution across batches. try: resp = requests.get("http://127.0.0.1:8999/status", timeout=0.5) if resp.status_code == 200: - print("[Fixture] WARNING: Hook Server already up on port 8999. Test state might be polluted.") - # Optionally try to reset it - try: requests.post("http://127.0.0.1:8999/api/gui", json={"action": "click", "item": "btn_reset"}, timeout=1) - except: pass - except: pass + print("[Fixture] WARNING: Hook Server already up on port 8999. Killing stale process...") + netstat = subprocess.run(["netstat", "-ano"], capture_output=True, text=True, timeout=5) + stale_pids: set[int] = set() + for line in netstat.stdout.splitlines(): + if ":8999" in line and "LISTENING" in line: + parts = line.split() + if parts: + try: stale_pids.add(int(parts[-1])) + except ValueError: pass + for pid in stale_pids: + try: + subprocess.run(["taskkill", "/F", "/PID", str(pid)], capture_output=True, timeout=5) + print(f"[Fixture] Killed stale PID {pid}") + except Exception: pass + time.sleep(1.0) + print("[Fixture] Proceeding with fresh sloppy.py spawn") + except Exception: pass print(f"\n[Fixture] Starting {gui_script} --enable-test-hooks in {temp_workspace}...") os.makedirs("logs", exist_ok=True)