mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-11 22:13:48 +01:00
Dormant until earned_time.leisure_active(gaming day) -- the switch file written by `leisure-watch go-live`. From that day: - enforced `seconds` are billed by the shadow rule (lock pause, fail-closed, once per wall clock); budget from plain resolve(); warnings and the cutoff say "Leisure". - at 0, beside the unchanged Steam path, _leisure_kill SIGTERMs (SIGKILL 30 s later, per process) every focus/media pid in now.json except browsers, every failclosed browser pid, and -- with now.json untrusted -- every browser, media player and wsg-grabber reviewer. Root/system, lockers and the enforcer's own chain are never signalled. Kills go to budget.jsonl. - tests: an autouse fixture redirects both switch readers, so no test reads /etc/leisure-watch/leisure_from; existing tests unchanged. Re-pins earned-time to v0.15.0 (leisure_active). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
53 lines
1.8 KiB
Python
53 lines
1.8 KiB
Python
"""Autouse isolation of the Phase-2 leisure switch and the leisure kill scan.
|
|
|
|
The switch file ``/etc/leisure-watch/leisure_from`` is read twice per tick:
|
|
by ``earned_time`` itself (``is_leisure``, looked up at call time from
|
|
``earned_time._leisure``) and by the enforcer's gate, which passes its own
|
|
constant explicitly. Both point at an absent file under ``tmp_path``, so no
|
|
test ever reads the real one and every test starts dormant. A test turns
|
|
Phase 2 on by writing a date to :func:`switch_file`.
|
|
|
|
The leisure kill scan reads ``/proc`` through its own constant; it gets an
|
|
empty directory (:func:`leisure_proc`), so no test can see -- or signal -- a
|
|
live browser or player.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
from pathlib import Path
|
|
|
|
_SWITCH_NAME = "leisure_from"
|
|
_PROC_NAME = "leisure_proc"
|
|
|
|
|
|
def switch_file(tmp_path: Path) -> Path:
|
|
"""The redirected switch file for this test (absent until written)."""
|
|
return tmp_path / "etc-leisure-watch" / _SWITCH_NAME
|
|
|
|
|
|
def leisure_proc(tmp_path: Path) -> Path:
|
|
"""The redirected ``/proc`` the leisure kill scan reads for this test."""
|
|
return tmp_path / _PROC_NAME
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate_leisure(tmp_path: Path) -> Iterator[None]:
|
|
"""Point both switch readers and the kill scan at ``tmp_path``."""
|
|
switch = switch_file(tmp_path)
|
|
switch.parent.mkdir(exist_ok=True)
|
|
proc = leisure_proc(tmp_path)
|
|
proc.mkdir(exist_ok=True)
|
|
with (
|
|
patch("earned_time._leisure.LEISURE_FROM_PATH", switch),
|
|
patch("steam_backlog_enforcer._leisure_mode.LEISURE_FROM_PATH", switch),
|
|
patch("steam_backlog_enforcer._leisure_procs._PROC", proc),
|
|
):
|
|
yield
|