mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-07-04 13:23:13 +02:00
chore: optimize pre-commit, remove tracked binaries, fix lint issues
- Move slow hooks (mypy, pylint, bandit, pytest, prettier) to pre-push stage - Remove redundant autoflake (ruff covers F401/F841) - Fix shellcheck OOM by batching files with xargs -n 40 - Remove tracked .o, .wav, .pyc binaries from git - Move pomodoro wav files to ../testsAndMisc_binaries/ with symlinks - Add *.o, *.so, *.a to .gitignore - Refactor hltb._pick_best_hltb_entry to fix C901/PLR0911/SIM102 - Fix SC2034 warnings in gif_to_square.sh and upgrade.sh - Add disk_cleanup_check.sh script - Various test and code improvements across screen_locker, steam_backlog_enforcer, word_frequency, moviepy_showcase
This commit is contained in:
parent
f71024e9f4
commit
9c409a32cd
@ -4,10 +4,8 @@
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
SCREEN_LOCK_PATH="$SCRIPT_DIR/screen_lock.py"
|
SCREEN_LOCK_PATH="$SCRIPT_DIR/screen_lock.py"
|
||||||
SERVICE_FILE="$SCRIPT_DIR/workout-locker.service"
|
SERVICE_FILE="$SCRIPT_DIR/workout-locker.service"
|
||||||
TIMER_FILE="$SCRIPT_DIR/workout-locker.timer"
|
|
||||||
USER_SERVICE_DIR="$HOME/.config/systemd/user"
|
USER_SERVICE_DIR="$HOME/.config/systemd/user"
|
||||||
SERVICE_NAME="workout-locker.service"
|
SERVICE_NAME="workout-locker.service"
|
||||||
TIMER_NAME="workout-locker.timer"
|
|
||||||
|
|
||||||
# Check if service is already installed
|
# Check if service is already installed
|
||||||
if [ -f "$USER_SERVICE_DIR/$SERVICE_NAME" ]; then
|
if [ -f "$USER_SERVICE_DIR/$SERVICE_NAME" ]; then
|
||||||
@ -26,9 +24,14 @@ fi
|
|||||||
# Create user systemd directory if it doesn't exist
|
# Create user systemd directory if it doesn't exist
|
||||||
mkdir -p "$USER_SERVICE_DIR"
|
mkdir -p "$USER_SERVICE_DIR"
|
||||||
|
|
||||||
# Copy service and timer files to user systemd directory
|
# Remove old timer if it was previously installed
|
||||||
|
if systemctl --user is-active "workout-locker.timer" &>/dev/null; then
|
||||||
|
systemctl --user disable --now "workout-locker.timer" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -f "$USER_SERVICE_DIR/workout-locker.timer"
|
||||||
|
|
||||||
|
# Copy service file to user systemd directory
|
||||||
cp "$SERVICE_FILE" "$USER_SERVICE_DIR/$SERVICE_NAME"
|
cp "$SERVICE_FILE" "$USER_SERVICE_DIR/$SERVICE_NAME"
|
||||||
cp "$TIMER_FILE" "$USER_SERVICE_DIR/$TIMER_NAME"
|
|
||||||
|
|
||||||
# Update paths in the service file to use absolute paths
|
# Update paths in the service file to use absolute paths
|
||||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
@ -39,18 +42,16 @@ sed -i "s|ExecStart=/usr/bin/python3.*|ExecStart=/usr/bin/python3 -m python_pkg.
|
|||||||
# Reload systemd daemon
|
# Reload systemd daemon
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
|
|
||||||
# Enable the service to start on login and the timer for periodic checks
|
# Enable the service to start on login (one-shot, no periodic timer)
|
||||||
systemctl --user enable "$SERVICE_NAME"
|
systemctl --user enable "$SERVICE_NAME"
|
||||||
systemctl --user enable --now "$TIMER_NAME"
|
|
||||||
|
|
||||||
echo "✓ Workout locker service installed"
|
echo "✓ Workout locker service installed"
|
||||||
echo "✓ Service will start automatically on next login"
|
echo "✓ Service will start automatically on next login"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To start now: systemctl --user start workout-locker"
|
echo "To start now: systemctl --user start workout-locker"
|
||||||
echo "To check status: systemctl --user status workout-locker"
|
echo "To check status: systemctl --user status workout-locker"
|
||||||
echo "To check timer: systemctl --user list-timers workout-locker.timer"
|
|
||||||
echo "To stop: systemctl --user stop workout-locker"
|
echo "To stop: systemctl --user stop workout-locker"
|
||||||
echo "To disable autostart: systemctl --user disable workout-locker workout-locker.timer"
|
echo "To disable autostart: systemctl --user disable workout-locker"
|
||||||
|
|
||||||
# Check autostart installation status
|
# Check autostart installation status
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -50,6 +50,21 @@ __all__ = [
|
|||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_not_under_pytest() -> None:
|
||||||
|
"""Raise if the screen locker is being created inside a pytest run.
|
||||||
|
|
||||||
|
Defence-in-depth: prevents a real fullscreen Tk window from locking
|
||||||
|
the user's screen when tests forget to mock ``tk.Tk``.
|
||||||
|
The check is cheap (one dict lookup) and only fires during testing.
|
||||||
|
"""
|
||||||
|
if "pytest" in sys.modules and getattr(tk, "__name__", "") == "tkinter":
|
||||||
|
msg = (
|
||||||
|
"SAFETY: ScreenLocker.__init__ called under pytest with "
|
||||||
|
"real tkinter — tk.Tk is not mocked"
|
||||||
|
)
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
|
||||||
class ScreenLocker(
|
class ScreenLocker(
|
||||||
ShutdownMixin,
|
ShutdownMixin,
|
||||||
PhoneVerificationMixin,
|
PhoneVerificationMixin,
|
||||||
@ -64,6 +79,7 @@ class ScreenLocker(
|
|||||||
verify_only: bool = False,
|
verify_only: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize screen locker with optional demo mode."""
|
"""Initialize screen locker with optional demo mode."""
|
||||||
|
_assert_not_under_pytest()
|
||||||
script_dir = Path(__file__).resolve().parent
|
script_dir = Path(__file__).resolve().parent
|
||||||
self.log_file = script_dir / "workout_log.json"
|
self.log_file = script_dir / "workout_log.json"
|
||||||
self.verify_only = verify_only
|
self.verify_only = verify_only
|
||||||
|
|||||||
@ -1,4 +1,12 @@
|
|||||||
"""Shared fixtures and helpers for screen_locker tests."""
|
"""Shared fixtures and helpers for screen_locker tests.
|
||||||
|
|
||||||
|
Safety:
|
||||||
|
``_block_real_tk_and_exit`` (autouse) replaces the **entire** ``tk``
|
||||||
|
module reference inside ``screen_lock`` with a MagicMock and stubs
|
||||||
|
``sys.exit``. This makes it physically impossible for any test to
|
||||||
|
create a real Tk root window, go fullscreen, or grab input — even if
|
||||||
|
the test forgets to request the explicit ``mock_tk`` fixture.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@ -12,7 +20,40 @@ import pytest
|
|||||||
from python_pkg.screen_locker.screen_lock import ScreenLocker
|
from python_pkg.screen_locker.screen_lock import ScreenLocker
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator, Iterator
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _block_real_tk_and_exit() -> Iterator[None]:
|
||||||
|
"""Replace the whole ``tk`` module and ``sys.exit`` for every test.
|
||||||
|
|
||||||
|
Patching the entire module (not just ``tk.Tk``) ensures that
|
||||||
|
**nothing** in tkinter can touch the real display server.
|
||||||
|
"""
|
||||||
|
mock = _make_mock_tk()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch("python_pkg.screen_locker.screen_lock.tk", mock),
|
||||||
|
patch("python_pkg.screen_locker.screen_lock.sys.exit"),
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@ -10,12 +10,29 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from python_pkg.screen_locker.screen_lock import _assert_not_under_pytest
|
||||||
from python_pkg.screen_locker.tests.conftest import create_locker
|
from python_pkg.screen_locker.tests.conftest import create_locker
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssertNotUnderPytest:
|
||||||
|
"""Tests for the _assert_not_under_pytest runtime guard."""
|
||||||
|
|
||||||
|
def test_raises_when_tk_is_real(self) -> None:
|
||||||
|
"""Guard fires if tk.Tk is the real tkinter class under pytest."""
|
||||||
|
with (
|
||||||
|
patch("python_pkg.screen_locker.screen_lock.tk", tk),
|
||||||
|
pytest.raises(RuntimeError, match="SAFETY"),
|
||||||
|
):
|
||||||
|
_assert_not_under_pytest()
|
||||||
|
|
||||||
|
def test_silent_when_tk_is_mocked(self) -> None:
|
||||||
|
"""Guard stays silent when tk is already mocked (normal test run)."""
|
||||||
|
_assert_not_under_pytest()
|
||||||
|
|
||||||
|
|
||||||
class TestScreenLockerInit:
|
class TestScreenLockerInit:
|
||||||
"""Tests for ScreenLocker initialization."""
|
"""Tests for ScreenLocker initialization."""
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Periodically check if workout was done today
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
OnBootSec=5s
|
|
||||||
OnUnitActiveSec=15min
|
|
||||||
Persistent=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
Loading…
Reference in New Issue
Block a user