mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-07-04 15:43:09 +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
6a9f137845
commit
c1cdd4535f
@ -15,6 +15,29 @@ import time
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Real Steam directory — used as a safety check to block destructive
|
||||||
|
# operations that leak through during testing.
|
||||||
|
_REAL_STEAMAPPS = Path("~/.local/share/Steam/steamapps").expanduser()
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_not_real_steam(path: Path) -> None:
|
||||||
|
"""Raise if *path* is inside the real Steam directory.
|
||||||
|
|
||||||
|
Defence-in-depth guard: even if test fixtures fail to
|
||||||
|
redirect ``STEAMAPPS_PATH``, destructive operations
|
||||||
|
(uninstall, rmtree, unlink) will refuse to touch real files.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
path.resolve().relative_to(_REAL_STEAMAPPS.resolve())
|
||||||
|
except ValueError:
|
||||||
|
return # path is NOT under real Steam — safe to proceed
|
||||||
|
if STEAMAPPS_PATH.resolve() == _REAL_STEAMAPPS.resolve():
|
||||||
|
msg = (
|
||||||
|
f"SAFETY: refusing destructive operation on real Steam path "
|
||||||
|
f"{path!s} — STEAMAPPS_PATH was not redirected by test fixtures"
|
||||||
|
)
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _echo(msg: str = "", *, end: str = "\n", flush: bool = False) -> None:
|
def _echo(msg: str = "", *, end: str = "\n", flush: bool = False) -> None:
|
||||||
"""Write user-facing CLI output to stdout.
|
"""Write user-facing CLI output to stdout.
|
||||||
@ -56,6 +79,7 @@ PROTECTED_APP_IDS = {
|
|||||||
1007020, # Proton EasyAntiCheat Runtime
|
1007020, # Proton EasyAntiCheat Runtime
|
||||||
# Games allowed to be installed anytime
|
# Games allowed to be installed anytime
|
||||||
3949040, # RV There Yet?
|
3949040, # RV There Yet?
|
||||||
|
2252570,
|
||||||
}
|
}
|
||||||
|
|
||||||
STEAMAPPS_PATH = Path("~/.local/share/Steam/steamapps").expanduser()
|
STEAMAPPS_PATH = Path("~/.local/share/Steam/steamapps").expanduser()
|
||||||
@ -312,6 +336,7 @@ def _remove_manifest(manifest: Path, game_name: str, app_id: int) -> bool:
|
|||||||
game_name: Human-readable game name for logging.
|
game_name: Human-readable game name for logging.
|
||||||
app_id: Steam application ID.
|
app_id: Steam application ID.
|
||||||
"""
|
"""
|
||||||
|
_assert_not_real_steam(manifest)
|
||||||
try:
|
try:
|
||||||
if manifest.exists():
|
if manifest.exists():
|
||||||
manifest.unlink()
|
manifest.unlink()
|
||||||
@ -333,6 +358,7 @@ def _remove_game_dirs(install_dir: Path | None, app_id: int) -> bool:
|
|||||||
"""
|
"""
|
||||||
success = True
|
success = True
|
||||||
if install_dir and install_dir.is_dir():
|
if install_dir and install_dir.is_dir():
|
||||||
|
_assert_not_real_steam(install_dir)
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(install_dir)
|
shutil.rmtree(install_dir)
|
||||||
logger.info("Removed game files: %s", install_dir)
|
logger.info("Removed game files: %s", install_dir)
|
||||||
@ -343,6 +369,7 @@ def _remove_game_dirs(install_dir: Path | None, app_id: int) -> bool:
|
|||||||
for subdir in ("shadercache", "compatdata"):
|
for subdir in ("shadercache", "compatdata"):
|
||||||
cache_path = STEAMAPPS_PATH / subdir / str(app_id)
|
cache_path = STEAMAPPS_PATH / subdir / str(app_id)
|
||||||
if cache_path.is_dir():
|
if cache_path.is_dir():
|
||||||
|
_assert_not_real_steam(cache_path)
|
||||||
with contextlib.suppress(OSError):
|
with contextlib.suppress(OSError):
|
||||||
shutil.rmtree(cache_path)
|
shutil.rmtree(cache_path)
|
||||||
logger.debug("Removed %s/%d", subdir, app_id)
|
logger.debug("Removed %s/%d", subdir, app_id)
|
||||||
|
|||||||
@ -154,6 +154,10 @@ def _pick_best_hltb_entry(
|
|||||||
When a short name like "FAITH" matches both "FAITH" (demo) and
|
When a short name like "FAITH" matches both "FAITH" (demo) and
|
||||||
"FAITH: The Unholy Trinity" (full game), prefer the full game
|
"FAITH: The Unholy Trinity" (full game), prefer the full game
|
||||||
since Steam often lists the full game under the shorter name.
|
since Steam often lists the full game under the shorter name.
|
||||||
|
|
||||||
|
When an exact match like "Timberman" (26 h) competes against an
|
||||||
|
unrelated subtitle entry like "Timberman: The Big Adventure" (2 h),
|
||||||
|
the exact match wins because it has more hours.
|
||||||
"""
|
"""
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return None
|
return None
|
||||||
@ -165,36 +169,72 @@ def _pick_best_hltb_entry(
|
|||||||
return usable[0]
|
return usable[0]
|
||||||
|
|
||||||
lower = search_name.lower()
|
lower = search_name.lower()
|
||||||
|
best_exact = _find_exact_match(usable, lower)
|
||||||
|
best_extended = _find_best_extended(usable, lower)
|
||||||
|
return _resolve_exact_vs_extended(best_exact, best_extended, usable)
|
||||||
|
|
||||||
|
|
||||||
|
def _find_exact_match(
|
||||||
|
usable: list[tuple[dict[str, Any], float]],
|
||||||
|
lower: str,
|
||||||
|
) -> tuple[dict[str, Any], float] | None:
|
||||||
|
"""Find best exact name/alias match (highest comp_100)."""
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
(e, s)
|
||||||
|
for e, s in sorted(
|
||||||
|
usable,
|
||||||
|
key=lambda x: x[0].get("comp_100", 0),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
if (e.get("game_name") or "").lower() == lower
|
||||||
|
or (e.get("game_alias") or "").lower() == lower
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _find_best_extended(
|
||||||
|
usable: list[tuple[dict[str, Any], float]],
|
||||||
|
lower: str,
|
||||||
|
) -> tuple[dict[str, Any], float] | None:
|
||||||
|
"""Find best extended entry ("Name: Subtitle" / "Name - Subtitle").
|
||||||
|
|
||||||
|
Skips subset entries (prologue, demo, etc.).
|
||||||
|
"""
|
||||||
|
best: tuple[dict[str, Any], float] | None = None
|
||||||
for entry, sim in usable:
|
for entry, sim in usable:
|
||||||
entry_name = (entry.get("game_name") or "").lower()
|
entry_name = (entry.get("game_name") or "").lower()
|
||||||
if entry_name.startswith((lower + ":", lower + " -")):
|
if entry_name.startswith((lower + ":", lower + " -")):
|
||||||
suffix = entry_name[len(lower) :].lstrip(" :-")
|
suffix = entry_name[len(lower) :].lstrip(" :-")
|
||||||
if not any(suffix.startswith(kw) for kw in _SUBSET_SUFFIXES):
|
if not any(suffix.startswith(kw) for kw in _SUBSET_SUFFIXES) and (
|
||||||
# Only prefer this extended entry when it has strictly more
|
best is None or entry.get("comp_100", 0) > best[0].get("comp_100", 0)
|
||||||
# comp_100 than any exact-name match. This prevents
|
):
|
||||||
# "Killing Floor: Toy Master" (1.2 h) from beating
|
best = (entry, sim)
|
||||||
# "Killing Floor" (296 h) while still letting
|
return best
|
||||||
# "FAITH: The Unholy Trinity" (7 h) beat "FAITH" (0.5 h demo).
|
|
||||||
extended_hours = entry.get("comp_100", 0)
|
|
||||||
best_exact = next(
|
def _resolve_exact_vs_extended(
|
||||||
(
|
best_exact: tuple[dict[str, Any], float] | None,
|
||||||
(e, s)
|
best_extended: tuple[dict[str, Any], float] | None,
|
||||||
for e, s in sorted(
|
usable: list[tuple[dict[str, Any], float]],
|
||||||
usable,
|
) -> tuple[dict[str, Any], float]:
|
||||||
key=lambda x: x[0].get("comp_100", 0),
|
"""Decide between exact match, extended entry, or highest similarity."""
|
||||||
reverse=True,
|
if best_exact is not None and best_extended is not None:
|
||||||
)
|
exact_hours = best_exact[0].get("comp_100", 0)
|
||||||
if (e.get("game_name") or "").lower() == lower
|
extended_hours = best_extended[0].get("comp_100", 0)
|
||||||
or (e.get("game_alias") or "").lower() == lower
|
# Prefer the extended entry only when it has strictly more hours
|
||||||
),
|
# than the exact match. This lets "FAITH: The Unholy Trinity"
|
||||||
None,
|
# (7 h) beat "FAITH" (0.5 h demo) while preventing
|
||||||
)
|
# "Timberman: The Big Adventure" (2 h) from beating
|
||||||
if (
|
# "Timberman" (26 h).
|
||||||
best_exact is not None
|
if extended_hours > exact_hours:
|
||||||
and best_exact[0].get("comp_100", 0) >= extended_hours
|
return best_extended
|
||||||
):
|
return best_exact
|
||||||
return best_exact
|
if best_exact is not None:
|
||||||
return entry, sim
|
return best_exact
|
||||||
|
if best_extended is not None:
|
||||||
|
return best_extended
|
||||||
|
|
||||||
# Fall back to highest similarity.
|
# Fall back to highest similarity.
|
||||||
return max(usable, key=lambda x: x[1])
|
return max(usable, key=lambda x: x[1])
|
||||||
|
|||||||
@ -7,12 +7,14 @@ to temporary directories. This stops tests from accidentally:
|
|||||||
user's current assignment)
|
user's current assignment)
|
||||||
- Reading real appmanifest files from ~/.local/share/Steam/steamapps
|
- Reading real appmanifest files from ~/.local/share/Steam/steamapps
|
||||||
- Modifying /etc/hosts via the store blocker
|
- Modifying /etc/hosts via the store blocker
|
||||||
|
- Corrupting the HLTB cache on disk
|
||||||
|
- Launching real Steam or calling real subprocess commands
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from unittest.mock import patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -57,6 +59,12 @@ def _isolate_filesystem(tmp_path: Path) -> Iterator[None]:
|
|||||||
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
|
"python_pkg.steam_backlog_enforcer.game_install.STEAMAPPS_PATH",
|
||||||
fake_steamapps,
|
fake_steamapps,
|
||||||
),
|
),
|
||||||
|
# HLTB cache file (computed at import time from CONFIG_DIR, so
|
||||||
|
# patching CONFIG_DIR alone does not redirect it)
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer._hltb_types.HLTB_CACHE_FILE",
|
||||||
|
fake_config / "hltb_cache.json",
|
||||||
|
),
|
||||||
# /etc/hosts (store blocker)
|
# /etc/hosts (store blocker)
|
||||||
patch(
|
patch(
|
||||||
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE",
|
"python_pkg.steam_backlog_enforcer.store_blocker.HOSTS_FILE",
|
||||||
@ -68,3 +76,43 @@ def _isolate_filesystem(tmp_path: Path) -> Iterator[None]:
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _block_real_subprocesses() -> Iterator[None]:
|
||||||
|
"""Block subprocess calls that could launch real Steam or modify system.
|
||||||
|
|
||||||
|
Individual tests that need to test subprocess behaviour should
|
||||||
|
patch the specific module's ``subprocess.run`` / ``subprocess.Popen``
|
||||||
|
themselves — their local patch will override this one.
|
||||||
|
"""
|
||||||
|
noop_run = MagicMock(return_value=MagicMock(returncode=1))
|
||||||
|
noop_popen = MagicMock()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.game_install.subprocess.run",
|
||||||
|
noop_run,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.game_install.subprocess.Popen",
|
||||||
|
noop_popen,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.enforcer.subprocess.run",
|
||||||
|
noop_run,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.store_blocker.subprocess.run",
|
||||||
|
noop_run,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.run",
|
||||||
|
noop_run,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.library_hider.subprocess.Popen",
|
||||||
|
noop_popen,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|||||||
@ -6,7 +6,10 @@ import os
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from python_pkg.steam_backlog_enforcer.game_install import (
|
from python_pkg.steam_backlog_enforcer.game_install import (
|
||||||
|
_assert_not_real_steam,
|
||||||
_echo,
|
_echo,
|
||||||
_ensure_steam_running,
|
_ensure_steam_running,
|
||||||
_get_real_user,
|
_get_real_user,
|
||||||
@ -22,7 +25,43 @@ from python_pkg.steam_backlog_enforcer.game_install import (
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
PKG = "python_pkg.steam_backlog_enforcer.game_install"
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssertNotRealSteam:
|
||||||
|
"""Tests for the _assert_not_real_steam safety guard."""
|
||||||
|
|
||||||
|
def test_allows_tmp_path(self, tmp_path: Path) -> None:
|
||||||
|
"""Non-Steam paths pass through without raising."""
|
||||||
|
_assert_not_real_steam(tmp_path / "appmanifest_440.acf")
|
||||||
|
|
||||||
|
def test_raises_when_real_steam_not_redirected(self, tmp_path: Path) -> None:
|
||||||
|
"""Raises when path is under real Steam and STEAMAPPS_PATH is real."""
|
||||||
|
real = tmp_path / "real_steam"
|
||||||
|
real.mkdir()
|
||||||
|
fake_manifest = real / "appmanifest_440.acf"
|
||||||
|
fake_manifest.touch()
|
||||||
|
with (
|
||||||
|
patch(f"{PKG}._REAL_STEAMAPPS", real),
|
||||||
|
patch(f"{PKG}.STEAMAPPS_PATH", real),
|
||||||
|
pytest.raises(RuntimeError, match="SAFETY"),
|
||||||
|
):
|
||||||
|
_assert_not_real_steam(fake_manifest)
|
||||||
|
|
||||||
|
def test_allows_when_steamapps_redirected(self, tmp_path: Path) -> None:
|
||||||
|
"""No raise when STEAMAPPS_PATH differs from _REAL_STEAMAPPS."""
|
||||||
|
real = tmp_path / "real_steam"
|
||||||
|
real.mkdir()
|
||||||
|
fake_manifest = real / "appmanifest_440.acf"
|
||||||
|
fake_manifest.touch()
|
||||||
|
redirected = tmp_path / "fake_steam"
|
||||||
|
redirected.mkdir()
|
||||||
|
with (
|
||||||
|
patch(f"{PKG}._REAL_STEAMAPPS", real),
|
||||||
|
patch(f"{PKG}.STEAMAPPS_PATH", redirected),
|
||||||
|
):
|
||||||
|
_assert_not_real_steam(fake_manifest)
|
||||||
|
|
||||||
|
|
||||||
class TestEcho:
|
class TestEcho:
|
||||||
|
|||||||
@ -345,3 +345,89 @@ class TestPickBestHltbEntry:
|
|||||||
)
|
)
|
||||||
assert result is not None
|
assert result is not None
|
||||||
assert result[0]["game_name"] == "NEEDY GIRL OVERDOSE"
|
assert result[0]["game_name"] == "NEEDY GIRL OVERDOSE"
|
||||||
|
|
||||||
|
def test_exact_match_beats_different_subtitled_game(self) -> None:
|
||||||
|
"""Exact 'Timberman' (26.5 h) must beat 'Timberman: The Big Adventure' (2 h).
|
||||||
|
|
||||||
|
Unlike FAITH where the short name is a demo, here the short name
|
||||||
|
is the real full game and the subtitled entry is a different, shorter
|
||||||
|
game. The exact match should win because it has more hours.
|
||||||
|
"""
|
||||||
|
base: dict[str, Any] = {
|
||||||
|
"game_name": "Timberman",
|
||||||
|
"comp_100": 95400, # 26.5 h
|
||||||
|
}
|
||||||
|
other: dict[str, Any] = {
|
||||||
|
"game_name": "Timberman: The Big Adventure",
|
||||||
|
"comp_100": 7200, # 2 h
|
||||||
|
}
|
||||||
|
timberman_vs: dict[str, Any] = {
|
||||||
|
"game_name": "Timberman VS",
|
||||||
|
"comp_100": 23400, # 6.5 h
|
||||||
|
}
|
||||||
|
result = _pick_best_hltb_entry(
|
||||||
|
"Timberman",
|
||||||
|
[(other, 0.49), (timberman_vs, 0.86), (base, 1.0)],
|
||||||
|
)
|
||||||
|
assert result is not None
|
||||||
|
assert result[0]["game_name"] == "Timberman"
|
||||||
|
|
||||||
|
def test_exact_match_wins_even_when_extended_appears_first(self) -> None:
|
||||||
|
"""Exact match wins regardless of candidate ordering."""
|
||||||
|
base: dict[str, Any] = {
|
||||||
|
"game_name": "Timberman",
|
||||||
|
"comp_100": 95400, # 26.5 h
|
||||||
|
}
|
||||||
|
other: dict[str, Any] = {
|
||||||
|
"game_name": "Timberman: The Big Adventure",
|
||||||
|
"comp_100": 7200, # 2 h
|
||||||
|
}
|
||||||
|
# Extended entry appears first in the list.
|
||||||
|
result = _pick_best_hltb_entry(
|
||||||
|
"Timberman",
|
||||||
|
[(other, 0.49), (base, 1.0)],
|
||||||
|
)
|
||||||
|
assert result is not None
|
||||||
|
assert result[0]["game_name"] == "Timberman"
|
||||||
|
|
||||||
|
def test_exact_only_no_extended(self) -> None:
|
||||||
|
"""Exact match returned when no extended entries exist at all."""
|
||||||
|
exact: dict[str, Any] = {
|
||||||
|
"game_name": "Celeste",
|
||||||
|
"comp_100": 180000, # 50 h
|
||||||
|
}
|
||||||
|
unrelated: dict[str, Any] = {
|
||||||
|
"game_name": "Unrelated Game",
|
||||||
|
"comp_100": 7200,
|
||||||
|
}
|
||||||
|
result = _pick_best_hltb_entry(
|
||||||
|
"Celeste",
|
||||||
|
[(exact, 1.0), (unrelated, 0.6)],
|
||||||
|
)
|
||||||
|
assert result is not None
|
||||||
|
assert result[0]["game_name"] == "Celeste"
|
||||||
|
|
||||||
|
def test_no_exact_no_extended_falls_back(self) -> None:
|
||||||
|
"""When no exact or extended match exists, fall to highest similarity."""
|
||||||
|
a: dict[str, Any] = {"game_name": "FooBar", "comp_100": 3600}
|
||||||
|
b: dict[str, Any] = {"game_name": "FooBaz", "comp_100": 7200}
|
||||||
|
result = _pick_best_hltb_entry("Foo", [(a, 0.7), (b, 0.8)])
|
||||||
|
assert result is not None
|
||||||
|
assert result[0]["game_name"] == "FooBaz"
|
||||||
|
|
||||||
|
def test_extended_only_no_exact(self) -> None:
|
||||||
|
"""Extended entry returned when no exact name match exists."""
|
||||||
|
extended: dict[str, Any] = {
|
||||||
|
"game_name": "Neon: Ultimate Edition",
|
||||||
|
"comp_100": 36000,
|
||||||
|
}
|
||||||
|
unrelated: dict[str, Any] = {
|
||||||
|
"game_name": "Neon Lights",
|
||||||
|
"comp_100": 3600,
|
||||||
|
}
|
||||||
|
result = _pick_best_hltb_entry(
|
||||||
|
"Neon",
|
||||||
|
[(extended, 0.6), (unrelated, 0.7)],
|
||||||
|
)
|
||||||
|
assert result is not None
|
||||||
|
assert result[0]["game_name"] == "Neon: Ultimate Edition"
|
||||||
|
|||||||
@ -20,8 +20,6 @@ from python_pkg.steam_backlog_enforcer.library_hider import (
|
|||||||
_wait_for_cdp_ready,
|
_wait_for_cdp_ready,
|
||||||
_wait_for_collections_ready,
|
_wait_for_collections_ready,
|
||||||
ensure_steam_debug_port,
|
ensure_steam_debug_port,
|
||||||
hide_other_games,
|
|
||||||
unhide_all_games,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -425,85 +423,3 @@ class TestEnsureSteamDebugPort:
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
ensure_steam_debug_port()
|
ensure_steam_debug_port()
|
||||||
|
|
||||||
|
|
||||||
class TestHideOtherGames:
|
|
||||||
"""Tests for hide_other_games."""
|
|
||||||
|
|
||||||
def test_hides(self) -> None:
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider.ensure_steam_debug_port",
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
|
||||||
return_value={
|
|
||||||
"result": {"result": {"value": '{"totalHidden": 5}'}},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
|
||||||
return_value='{"totalHidden": 5}',
|
|
||||||
),
|
|
||||||
):
|
|
||||||
count = hide_other_games([1, 2, 3], 1)
|
|
||||||
assert count == 5
|
|
||||||
|
|
||||||
def test_empty_list(self) -> None:
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider.ensure_steam_debug_port",
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
|
||||||
return_value={
|
|
||||||
"result": {"result": {"value": '{"totalHidden": 0}'}},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
|
||||||
return_value='{"totalHidden": 0}',
|
|
||||||
),
|
|
||||||
):
|
|
||||||
count = hide_other_games([1], 1)
|
|
||||||
assert count == 0
|
|
||||||
|
|
||||||
def test_no_allowed(self) -> None:
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider.ensure_steam_debug_port",
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
|
||||||
return_value={
|
|
||||||
"result": {"result": {"value": '{"totalHidden": 2}'}},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
|
||||||
return_value='{"totalHidden": 2}',
|
|
||||||
),
|
|
||||||
):
|
|
||||||
count = hide_other_games([1, 2], None)
|
|
||||||
assert count == 2
|
|
||||||
|
|
||||||
|
|
||||||
class TestUnhideAllGames:
|
|
||||||
"""Tests for unhide_all_games."""
|
|
||||||
|
|
||||||
def test_unhides(self) -> None:
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider.ensure_steam_debug_port",
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
|
||||||
return_value={"result": {"result": {"value": '{"count": 10}'}}},
|
|
||||||
),
|
|
||||||
patch(
|
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
|
||||||
return_value='{"count": 10}',
|
|
||||||
),
|
|
||||||
):
|
|
||||||
count = unhide_all_games([1, 2, 3])
|
|
||||||
assert count == 10
|
|
||||||
|
|||||||
@ -8,7 +8,9 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
from python_pkg.steam_backlog_enforcer.library_hider import (
|
from python_pkg.steam_backlog_enforcer.library_hider import (
|
||||||
_run_as_user,
|
_run_as_user,
|
||||||
|
hide_other_games,
|
||||||
restart_steam,
|
restart_steam,
|
||||||
|
unhide_all_games,
|
||||||
)
|
)
|
||||||
|
|
||||||
PKG = "python_pkg.steam_backlog_enforcer.library_hider"
|
PKG = "python_pkg.steam_backlog_enforcer.library_hider"
|
||||||
@ -116,3 +118,77 @@ class TestRestartSteam:
|
|||||||
patch(f"{PKG}._wait_for_cdp_ready", return_value=False),
|
patch(f"{PKG}._wait_for_cdp_ready", return_value=False),
|
||||||
):
|
):
|
||||||
restart_steam()
|
restart_steam()
|
||||||
|
|
||||||
|
|
||||||
|
class TestHideOtherGames:
|
||||||
|
"""Tests for hide_other_games."""
|
||||||
|
|
||||||
|
def test_hides(self) -> None:
|
||||||
|
with (
|
||||||
|
patch(f"{PKG}.ensure_steam_debug_port"),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._evaluate_js",
|
||||||
|
return_value={
|
||||||
|
"result": {"result": {"value": '{"totalHidden": 5}'}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._cdp_result_value",
|
||||||
|
return_value='{"totalHidden": 5}',
|
||||||
|
),
|
||||||
|
):
|
||||||
|
count = hide_other_games([1, 2, 3], 1)
|
||||||
|
assert count == 5
|
||||||
|
|
||||||
|
def test_empty_list(self) -> None:
|
||||||
|
with (
|
||||||
|
patch(f"{PKG}.ensure_steam_debug_port"),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._evaluate_js",
|
||||||
|
return_value={
|
||||||
|
"result": {"result": {"value": '{"totalHidden": 0}'}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._cdp_result_value",
|
||||||
|
return_value='{"totalHidden": 0}',
|
||||||
|
),
|
||||||
|
):
|
||||||
|
count = hide_other_games([1], 1)
|
||||||
|
assert count == 0
|
||||||
|
|
||||||
|
def test_no_allowed(self) -> None:
|
||||||
|
with (
|
||||||
|
patch(f"{PKG}.ensure_steam_debug_port"),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._evaluate_js",
|
||||||
|
return_value={
|
||||||
|
"result": {"result": {"value": '{"totalHidden": 2}'}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._cdp_result_value",
|
||||||
|
return_value='{"totalHidden": 2}',
|
||||||
|
),
|
||||||
|
):
|
||||||
|
count = hide_other_games([1, 2], None)
|
||||||
|
assert count == 2
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnhideAllGames:
|
||||||
|
"""Tests for unhide_all_games."""
|
||||||
|
|
||||||
|
def test_unhides(self) -> None:
|
||||||
|
with (
|
||||||
|
patch(f"{PKG}.ensure_steam_debug_port"),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._evaluate_js",
|
||||||
|
return_value={"result": {"result": {"value": '{"count": 10}'}}},
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
f"{PKG}._cdp_result_value",
|
||||||
|
return_value='{"count": 10}',
|
||||||
|
),
|
||||||
|
):
|
||||||
|
count = unhide_all_games([1, 2, 3])
|
||||||
|
assert count == 10
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user