From 7e7c34a0731495c86167b70d42fc719428517392 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 21 Aug 2026 13:00:20 +0200 Subject: [PATCH] gatelock: make fit/scroll geometry tests immune to a real WM Under i3 (not Xvfb), a managed tk.Tk() toplevel's geometry() request was silently rewritten to the tiled pane size instead of the requested WxH, so the content never actually overflowed and every assertion built on "does this overflow" failed -- 11 tests across test_fitcheck.py/test_scrollable.py. Fix: overrideredirect the throwaway root, matching how every production lock window is already built and for the identical reason (a WM must not be able to rewrite geometry a fit/scroll check depends on). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018M5gKyYzxWG6QkCaj1wWfk --- gatelock/gatelock/_fitcheck.py | 7 +++++++ gatelock/gatelock/tests/test_scrollable.py | 9 +++++++++ gatelock/pyproject.toml | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/gatelock/gatelock/_fitcheck.py b/gatelock/gatelock/_fitcheck.py index e5b2196..ed40a19 100644 --- a/gatelock/gatelock/_fitcheck.py +++ b/gatelock/gatelock/_fitcheck.py @@ -109,6 +109,13 @@ def _measure( """Render the screen once and read its height back.""" root = tk.Tk() try: + # A managed window has its geometry rewritten wholesale by a real + # window manager -- confirmed under i3, which tiled this to the pane + # size instead of the requested width/height, silently measuring the + # wrong resolution. Production lock windows are overrideredirect for + # exactly this reason; the throwaway root here must match so the + # measurement is honest regardless of what WM is running the check. + root.overrideredirect(boolean=True) root.geometry(f"{width}x{height}+0+0") root.update_idletasks() surface = ScrollableSurface(root, config or LockConfig(), center_when_fits=True) diff --git a/gatelock/gatelock/tests/test_scrollable.py b/gatelock/gatelock/tests/test_scrollable.py index 6964207..cd17439 100644 --- a/gatelock/gatelock/tests/test_scrollable.py +++ b/gatelock/gatelock/tests/test_scrollable.py @@ -35,6 +35,12 @@ _TALL = 60 def surface() -> tk.Misc: """A 400x200 viewport holding 20 stacked entries -- deliberately taller.""" root = tk.Tk() + # A managed window has its geometry rewritten wholesale by a real window + # manager (i3 confirmed: a plain geometry() request came back tiled to + # the pane size instead of 400x200, so nothing ever overflowed and every + # scroll assertion in this file failed against a real WM session) -- + # exactly the reason production lock windows are overrideredirect too. + root.overrideredirect(boolean=True) root.geometry(f"400x{_VIEWPORT_H}+0+0") # No window manager under Xvfb, so the toplevel must claim X input focus # before any synthetic key event is delivered anywhere. @@ -203,6 +209,9 @@ def test_a_surface_without_a_scrollbar_still_scrolls() -> None: """``show_scrollbar=False`` removes the affordance, not the mechanism.""" root = tk.Tk() try: + # See the `surface` fixture above for why this must be + # overrideredirect under a real WM. + root.overrideredirect(boolean=True) root.geometry(f"400x{_VIEWPORT_H}+0+0") root.focus_force() root.update_idletasks() diff --git a/gatelock/pyproject.toml b/gatelock/pyproject.toml index 1ceade6..93608e9 100644 --- a/gatelock/pyproject.toml +++ b/gatelock/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "gatelock" -version = "0.7.0" +version = "0.7.1" description = "Shared per-output lock-window + HMAC log-integrity backend for blocking-overlay apps" requires-python = ">=3.10" dependencies = [] # pure stdlib — tkinter is bundled with Python