mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-11 12:27:45 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Lending the X grab to the workout app stops recovery and releases it with the claim still live, so gatelock 0.11 held_grabs (leisure-watch C2) read "held" for the whole workout. release() now reports held=False before grab_release; reacquire()'s grab_input() sets it true once the grab lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
119 lines
3.9 KiB
Python
119 lines
3.9 KiB
Python
"""Tests for lending the lock screen's grab to the workout app.
|
|
|
|
The invariant: the grab moves, nothing else does. VT switching stays disabled
|
|
and the arbiter claim stays held, because ``close()`` would undo both and hand
|
|
the user Ctrl+Alt+F2 out of the obligation the lock exists to enforce.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tkinter as tk
|
|
|
|
from screen_locker._workout_handoff import lock_grab_handoff, start_workout
|
|
|
|
|
|
class _FakeRecovery:
|
|
"""Stands in for gatelock's watchdog, which re-grabs every 1000ms."""
|
|
|
|
def __init__(self) -> None:
|
|
self.running = True
|
|
self.events: list[str] = []
|
|
|
|
def stop(self) -> None:
|
|
self.running = False
|
|
self.events.append("stop")
|
|
|
|
|
|
class _FakeLock:
|
|
"""A lock window that records the order of grab operations."""
|
|
|
|
def __init__(self, *, release_raises: bool = False) -> None:
|
|
self._recovery = _FakeRecovery()
|
|
self.events: list[str] = []
|
|
self.closed = False
|
|
self._release_raises = release_raises
|
|
outer = self
|
|
|
|
class _Root:
|
|
def grab_release(self) -> None:
|
|
outer.events.append("grab_release")
|
|
if outer._release_raises:
|
|
msg = "no grab to release"
|
|
raise tk.TclError(msg)
|
|
|
|
self.root = _Root()
|
|
|
|
def report_grab_held(self, *, held: bool) -> None:
|
|
"""gatelock 0.11: what leisure-watch reads via ``held_grabs``."""
|
|
self.events.append(f"held={held}")
|
|
|
|
def grab_input(self) -> None:
|
|
self.events.append("grab_input")
|
|
self._recovery.running = True
|
|
|
|
def close(self) -> None:
|
|
self.closed = True
|
|
|
|
|
|
class TestLockGrabHandoff:
|
|
"""Releasing and re-taking, in the only safe order."""
|
|
|
|
def test_stops_the_watchdog_before_releasing(self) -> None:
|
|
"""A tick between release and the app's own grab would steal it back."""
|
|
lock = _FakeLock()
|
|
lock_grab_handoff(lock).release()
|
|
assert lock._recovery.events == ["stop"]
|
|
assert lock.events == ["held=False", "grab_release"]
|
|
assert not lock._recovery.running
|
|
|
|
def test_reacquire_restores_the_grab_and_the_watchdog(self) -> None:
|
|
handoff = lock_grab_handoff(lock := _FakeLock())
|
|
handoff.release()
|
|
handoff.reacquire()
|
|
assert lock.events == ["held=False", "grab_release", "grab_input"]
|
|
assert lock._recovery.running
|
|
|
|
def test_never_closes_the_lock_window(self) -> None:
|
|
"""close() also restores VT switching -- that is the escape hatch."""
|
|
handoff = lock_grab_handoff(lock := _FakeLock())
|
|
handoff.release()
|
|
handoff.reacquire()
|
|
assert not lock.closed
|
|
|
|
def test_tolerates_having_no_grab_to_release(self) -> None:
|
|
"""Tk raises when releasing a grab we do not hold; not fatal."""
|
|
handoff = lock_grab_handoff(lock := _FakeLock(release_raises=True))
|
|
handoff.release()
|
|
handoff.reacquire()
|
|
assert lock.events == ["held=False", "grab_release", "grab_input"]
|
|
|
|
|
|
class TestStartWorkout:
|
|
"""The button handler never raises onto a screen with no way out."""
|
|
|
|
def test_reports_a_failed_launch_as_a_message(self, monkeypatch: object) -> None:
|
|
from screen_locker import _workout_handoff
|
|
|
|
class _Result:
|
|
launched = False
|
|
reason = "not built"
|
|
|
|
monkeypatch.setattr(
|
|
_workout_handoff, "launch_workout_app", lambda _h: _Result()
|
|
)
|
|
message = start_workout(_FakeLock())
|
|
assert "Could not start the workout" in message
|
|
assert "not built" in message
|
|
|
|
def test_reports_a_finished_run(self, monkeypatch: object) -> None:
|
|
from screen_locker import _workout_handoff
|
|
|
|
class _Result:
|
|
launched = True
|
|
reason = "exited with code 0"
|
|
|
|
monkeypatch.setattr(
|
|
_workout_handoff, "launch_workout_app", lambda _h: _Result()
|
|
)
|
|
assert "checking whether it counted" in start_workout(_FakeLock())
|