screen-locker/screen_locker/tests/_tk_mocks.py
Krzysztof kuhy Rudnicki 76a4c6a0a7 Finish the Python side: scripts, conftest, and the last test modules
verify_lock_popup_safety.py (318) -> _xvfb_reexec.py      shared re-exec
                                       _popup_form_check.py HostileLock, ticks,
                                                            the real-form check
  verify_screen_fits.py (266)       -> uses _xvfb_reexec too
  conftest.py (329)                 -> _tk_mocks.py         the tk mock + the
                                                            module patch list
                                       _opt_in_fixtures.py  requestable fixtures
  three test modules                -> _part4/_part5/_part15 siblings

conftest keeps every autouse fixture. pytest applies autouse fixtures from
conftest only, so one moved to a sibling would silently stop running and take
its safety guard with it -- verified by asserting the fixtures are still
listed, not just that the suite is green. The five near-identical _isolate_*
fixtures collapse into one table-driven _isolate_state_files, which also picks
up the _shutdown_sick_state binding an earlier split created.

scripts/ becomes a real package so the shared helpers are importable. Both
gates now run as `python3 -m scripts.<name>`, including CI -- the path form put
scripts/ on sys.path instead of the repo root, and the alternative was a
try/except ImportError that this repo's no-silent-failures hook rightly
rejects. Each gate was run in its exact invocation form.

Note: verify_lock_popup_safety still fails on
`RecoveryLoop.__init__() takes 3 positional arguments but 6 were given`. That
is pre-existing -- confirmed by running it at HEAD in a clean worktree before
touching it -- a gatelock API drift, not this refactor. Left as found.

978 tests at 100% branch coverage. Gate: 41 -> 22 files, all remaining ones
Dart plus DESIGN_AUDIT_TODO.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EveB813BQ8abAP68VSqZ5Y
2026-08-15 20:57:29 +02:00

59 lines
2.2 KiB
Python

"""The tkinter mock and the list of modules it must be patched into.
Split out of ``conftest.py`` to keep every file under the 250-line cap.
Only this scaffolding moved: every ``autouse`` fixture stays in ``conftest.py``,
because pytest collects autouse fixtures from conftest only -- one moved here
would simply stop running, and the suite would still pass while the guard it
provides was gone.
When a new module imports ``tkinter as tk`` and calls it, add it to
``_TK_MODULES`` below or its widgets are built on a real display.
"""
from __future__ import annotations
import tkinter as tk
from unittest.mock import MagicMock
# Every module that imports ``tkinter as tk`` and calls it directly. The UI is
# split across these, so each ``tk`` must be patched — so no test touches a real
# display and a ``mock_tk`` holder sees widgets made on that same mock.
_TK_MODULES = (
"screen_locker.screen_lock",
"screen_locker._sick_dialog",
"screen_locker._manual_workout_dialog",
"screen_locker._manual_workout_sport_fields",
"screen_locker._manual_workout_widgets",
"screen_locker._ui_form_fields",
"screen_locker._ui_widgets",
"screen_locker._window_setup",
"screen_locker.status_view",
"screen_locker._heat_skip",
"screen_locker._surface_group",
# The scroll viewport the lock surfaces are built from lives in gatelock
# and imports tkinter independently, so it needs the same mock -- otherwise
# container.first is a *real* tk.Frame whose winfo_children() cannot be
# stubbed, and every surface assertion breaks.
"gatelock._scrollable",
)
_VT_SHUTIL = "gatelock._vt.shutil"
_VT_SUBPROCESS = "gatelock._vt.subprocess"
def _make_mock_tk() -> MagicMock:
"""Build a MagicMock that stands in for the ``tkinter`` module."""
mock = MagicMock()
mock_root = MagicMock()
mock_root.winfo_screenwidth.return_value = 1920
mock_root.winfo_screenheight.return_value = 1080
mock.Tk.return_value = mock_root
mock_frame = MagicMock()
mock_frame.winfo_children.return_value = []
mock.Frame.return_value = mock_frame
# Keep real TclError so ``except tk.TclError`` still works.
mock.TclError = tk.TclError
return mock