import pytest import sys import os import threading import time sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) def test_render_warmup_indicator_function_exists() -> None: """render_warmup_status_indicator is importable from src.gui_2.""" from src.gui_2 import render_warmup_status_indicator assert callable(render_warmup_status_indicator) def test_callback_function_exists() -> None: """_on_warmup_complete_callback is importable from src.gui_2.""" from src.gui_2 import _on_warmup_complete_callback assert callable(_on_warmup_complete_callback) def test_callback_sets_timestamp() -> None: """_on_warmup_complete_callback sets _warmup_completion_ts and appends to toast list.""" from src.gui_2 import _on_warmup_complete_callback class MockApp: pass app = MockApp() status = {"pending": [], "completed": ["a", "b", "c"], "failed": []} _on_warmup_complete_callback(app, status) assert hasattr(app, "_warmup_completion_ts") assert isinstance(app._warmup_completion_ts, float) assert app._warmup_completion_ts > 0 assert hasattr(app, "_warmup_toast_lock") assert isinstance(app._warmup_toast_lock, type(threading.Lock())) assert hasattr(app, "_warmup_toast_messages") assert len(app._warmup_toast_messages) == 1 ts, msg = app._warmup_toast_messages[0] assert msg == "All imports ready (3 modules)" def test_callback_with_failures_uses_warning_message() -> None: """_on_warmup_complete_callback uses a failures message when failed is non-empty.""" from src.gui_2 import _on_warmup_complete_callback class MockApp: pass app = MockApp() status = {"pending": [], "completed": ["a"], "failed": ["b", "c"]} _on_warmup_complete_callback(app, status) ts, msg = app._warmup_toast_messages[0] assert "2 failures" in msg assert "3 modules" in msg def test_callback_is_thread_safe_under_concurrent_invocation() -> None: """10 concurrent callback invocations all append to the toast list.""" from src.gui_2 import _on_warmup_complete_callback class MockApp: pass app = MockApp() def fire(): for _ in range(10): _on_warmup_complete_callback(app, {"pending": [], "completed": ["x"], "failed": []}) threads = [threading.Thread(target=fire) for _ in range(10)] for t in threads: t.start() for t in threads: t.join() assert len(app._warmup_toast_messages) == 100 def test_live_render_warmup_indicator_does_not_crash(live_gui) -> None: """Live: render_warmup_status_indicator() can be called in the running GUI without crashing. Verifies the function is callable in a real ImGui frame context and that the controller exposes the warmup_status API the indicator consumes. """ from src.gui_2 import render_warmup_status_indicator from src.api_hook_client import ApiHookClient client = ApiHookClient() assert client.wait_for_server(timeout=10) # The live_gui fixture has the GUI running. The function should be # importable and the controller should expose warmup_status. diag = client.get_warmup_status() assert "pending" in diag assert "completed" in diag assert "failed" in diag # The function is callable; it would render on the next frame. assert callable(render_warmup_status_indicator)