"""Tests for the ``__main__`` entry-point helpers. These cover the bits that aren't exercised by the FastAPI test client — the signal-handler install path and the shutdown-state plumbing — so a regression in the launcher can't silently break the user's "stop targets on PC shutdown" guarantee. """ from __future__ import annotations import signal import threading from types import SimpleNamespace from ledgrab.__main__ import _install_signal_handlers, _request_shutdown def test_request_shutdown_sets_should_exit() -> None: server = SimpleNamespace(should_exit=False) _request_shutdown(server) assert server.should_exit is True def test_install_signal_handlers_installs_for_known_signals() -> None: """Tray path runs uvicorn on a background thread, so our handlers must actually survive — verify each catchable signal is replaced. """ server = SimpleNamespace(should_exit=False) previous = { name: signal.getsignal(getattr(signal, name)) for name in ("SIGINT",) if hasattr(signal, name) } try: _install_signal_handlers(server) for name in ("SIGINT", "SIGTERM", "SIGBREAK"): sig = getattr(signal, name, None) if sig is None: continue current = signal.getsignal(sig) # The handler is our local closure — its qualname starts with the function it's defined in. assert callable(current), f"{name} handler should be installed" assert getattr(current, "__qualname__", "").startswith( "_install_signal_handlers" ), f"{name} should be replaced by our handler, got {current!r}" finally: # Restore original handlers so the rest of the test suite isn't poisoned. for name, handler in previous.items(): signal.signal(getattr(signal, name), handler) def test_shutdown_state_is_shared_threading_event() -> None: """``__main__`` and ``main`` must share the same Event instance — if a fresh one is constructed on either side, WM_ENDSESSION waits forever. """ from ledgrab.shutdown_state import shutdown_complete as state_event assert isinstance(state_event, threading.Event) # If main.py is importable, confirm it re-exports the same object. try: from ledgrab.main import shutdown_complete as main_event except Exception: return # main.py needs full app state — fine to skip on a bare test run. assert main_event is state_event, "main.py must re-export the same Event, not create a new one"