testsAndMisc/python_pkg/focus_policy/tests/test_model_policy.py
Krzysztof kuhy Rudnicki b60cb4972a Clear five python_pkg packages under the cap
android_ui, app_icons, token_audit, focus_policy and random_jpg are now
entirely under 250 lines. Only wsg_grabber's six test modules remain in
python_pkg.

android_ui/driver.py 411 -> 205 took two cuts and a class split: the
element and error types plus the XML parser to _elements.py, and the
text-entry methods to a TextEntryMixin, using the ABC-with-abstract-
methods shape because the tests call them as bound attributes.
app_icons/glyphs.py 291 -> 93 by moving the twelve path fragments out.

Three intermediate failures, all module-level globals that travelled
with their code (_logger, re, _SAME_FIELD_X_TOLERANCE_PX) - the same
class of miss as _make_verifier, which ruff cannot flag.

Counts unchanged: 81, 137, 14, 44, 57.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 18:14:36 +02:00

169 lines
6.7 KiB
Python

"""Tests for the FocusPolicy aggregate."""
from __future__ import annotations
from datetime import time
import pytest
from python_pkg.focus_policy.model import (
CurfewWindow,
FocusPolicy,
HomeLocation,
PolicyError,
)
HOME = HomeLocation(latitude=52.2297, longitude=21.0122)
METRES_PER_DEGREE_LAT = 111_320.0
def _policy(
*,
allowed_packages: frozenset[str] | None = None,
night_allowed_packages: frozenset[str] | None = None,
curfew: CurfewWindow | None = None,
launcher_package: str | None = None,
allowed_prefixes: tuple[str, ...] = (),
night_allowed_prefixes: tuple[str, ...] = (),
) -> FocusPolicy:
"""Build a small policy, overriding individual fields per test."""
return FocusPolicy(
home=HOME,
allowed_packages=(
frozenset({"com.good", "pl.mbank", "com.launcher"})
if allowed_packages is None
else allowed_packages
),
night_allowed_packages=(
frozenset({"pl.mbank"})
if night_allowed_packages is None
else night_allowed_packages
),
never_disable_prefixes=("com.android.providers", "com.android.settings"),
curfew=curfew,
launcher_package=launcher_package,
allowed_prefixes=allowed_prefixes,
night_allowed_prefixes=night_allowed_prefixes,
)
class TestFocusPolicy:
"""Allow/deny decisions and the invariants that keep the phone usable."""
def test_rejects_launcher_outside_allowlist(self) -> None:
"""A policy that blocks its own launcher would leave no home screen."""
with pytest.raises(PolicyError, match="no home screen"):
_policy(launcher_package="com.missing")
def test_accepts_launcher_inside_allowlist(self) -> None:
"""The guard permits a launcher that is actually allowed."""
assert _policy(launcher_package="com.launcher").launcher_package
def test_rejects_night_packages_absent_from_day_list(self) -> None:
"""A night allowance for a day-blocked package is contradictory."""
with pytest.raises(PolicyError, match="subset"):
_policy(night_allowed_packages=frozenset({"com.unknown"}))
@pytest.mark.parametrize(
("package", "expected"),
[
("com.android.settings", True),
("com.android.providers", True),
("com.android.providers.telephony", True),
("com.android.providersomething", False),
("com.good", False),
],
)
def test_protection_matches_whole_labels_only(
self,
package: str,
*,
expected: bool,
) -> None:
"""Prefix protection covers subpackages but not lookalike names.
``com.android.providers.telephony`` must be protected; the unrelated
``com.android.providersomething`` must not be swept in by a bare
string prefix test.
"""
assert _policy().is_protected(package) is expected
def test_protected_packages_are_allowed_even_during_curfew(self) -> None:
"""Core system packages survive the strictest state."""
policy = _policy()
assert policy.is_allowed("com.android.settings", during_curfew=True)
def test_curfew_narrows_the_allowlist(self) -> None:
"""Day-allowed apps can still be blocked at night."""
policy = _policy()
assert policy.is_allowed("com.good")
assert not policy.is_allowed("com.good", during_curfew=True)
assert policy.is_allowed("pl.mbank", during_curfew=True)
def test_unknown_package_is_blocked(self) -> None:
"""Anything not explicitly allowed is denied."""
assert not _policy().is_allowed("com.evil")
def test_allowed_prefix_covers_a_family_of_packages(self) -> None:
"""Tachiyomi ships every source as its own apk.
An exact list goes stale the moment a new extension is installed, which
looks exactly like a bug from the phone.
"""
policy = _policy(allowed_prefixes=("eu.kanade.tachiyomi",))
assert policy.is_allowed("eu.kanade.tachiyomi")
assert policy.is_allowed("eu.kanade.tachiyomi.sy")
assert policy.is_allowed("eu.kanade.tachiyomi.extension.all.mangadex")
def test_allowed_prefix_matches_whole_labels_only(self) -> None:
"""A bare string prefix would allow an unrelated package."""
policy = _policy(allowed_prefixes=("eu.kanade.tachiyomi",))
assert not policy.is_allowed("eu.kanade.tachiyomisomething")
def test_a_day_only_prefix_is_blocked_during_curfew(self) -> None:
"""The night list narrows the prefixes too, not just the packages."""
policy = _policy(allowed_prefixes=("eu.kanade.tachiyomi",))
assert policy.is_allowed("eu.kanade.tachiyomi.sy")
assert not policy.is_allowed("eu.kanade.tachiyomi.sy", during_curfew=True)
def test_a_night_prefix_survives_the_curfew(self) -> None:
"""Manga is deliberately available at night (chosen 2026-08-14)."""
policy = _policy(
allowed_prefixes=("eu.kanade.tachiyomi",),
night_allowed_prefixes=("eu.kanade.tachiyomi",),
)
assert policy.is_allowed("eu.kanade.tachiyomi.sy", during_curfew=True)
assert policy.is_allowed(
"eu.kanade.tachiyomi.extension.all.mangadex",
during_curfew=True,
)
def test_night_prefixes_must_be_a_subset_of_day_prefixes(self) -> None:
"""The curfew tightens the day policy; it must never widen it."""
with pytest.raises(PolicyError, match="night_allowed_prefixes"):
_policy(night_allowed_prefixes=("eu.kanade.tachiyomi",))
def test_is_curfew_active_without_window_is_false(self) -> None:
"""A policy with no curfew is never in curfew."""
assert not _policy().is_curfew_active(time(23, 30))
def test_is_curfew_active_with_window(self) -> None:
"""A configured window drives the curfew state."""
policy = _policy(curfew=CurfewWindow(start=time(23, 0), end=time(5, 0)))
assert policy.is_curfew_active(time(23, 30))
assert not policy.is_curfew_active(time(12, 0))
def test_packages_to_block_ignores_uninstalled(self) -> None:
"""Only packages present on the device can be blocked."""
policy = _policy()
installed = frozenset({"com.good", "com.evil", "com.android.settings"})
assert policy.packages_to_block(installed) == frozenset({"com.evil"})
def test_packages_to_block_during_curfew(self) -> None:
"""The curfew list is applied when the curfew flag is set."""
policy = _policy()
installed = frozenset({"com.good", "pl.mbank"})
assert policy.packages_to_block(installed, during_curfew=True) == frozenset(
{"com.good"},
)