invisible_playwright/tests/test_profile.py
feder-cr a1b75d93e9 tests: drop font-sampling tests
Follows the invisible_core removal of the dead per-profile font-sampling:
delete the derive_font_prefs/derive_font_whitelist tests, the 'fonts' pin
tests, and the profile font_whitelist assertions. Suite green (444 passed).
2026-07-06 21:36:26 +02:00

296 lines
10 KiB
Python

"""Unit tests for `_fpforge/profile.py`.
Covers `_validate_pin_key`, `_apply_pins_to_raw`, and `generate_profile`.
Test cases derived via ECP/BVA/error guessing.
"""
from dataclasses import FrozenInstanceError
import pytest
from invisible_playwright._fpforge import generate_profile
from invisible_playwright._fpforge.profile import (
Profile,
_PIN_GROUPS,
_PIN_TO_RAW,
_apply_pins_to_raw,
_validate_pin_key,
)
# ─────────────────────────────────────────────────────────────────────
# _validate_pin_key
# ─────────────────────────────────────────────────────────────────────
@pytest.mark.unit
def test_validate_pin_key_top_level_dark_theme():
"""VK2 — `dark_theme` is a known top-level key."""
_validate_pin_key("dark_theme")
@pytest.mark.unit
def test_validate_pin_key_dotted_screen_width():
"""VK3 — valid dotted path `screen.width`."""
_validate_pin_key("screen.width")
@pytest.mark.unit
def test_validate_pin_key_dotted_gpu_renderer():
"""VK4 — valid dotted path `gpu.renderer`."""
_validate_pin_key("gpu.renderer")
@pytest.mark.unit
def test_validate_pin_key_dotted_webgl_msaa_samples():
"""VK5 — valid dotted path `webgl.msaa_samples`."""
_validate_pin_key("webgl.msaa_samples")
@pytest.mark.unit
def test_validate_pin_key_no_dot_not_top_level_raises():
"""VK6 — bare key not in top-level set raises with hint."""
with pytest.raises(ValueError, match="group.field"):
_validate_pin_key("bogus")
@pytest.mark.unit
def test_validate_pin_key_unknown_group_raises():
"""VK7 — unknown group prefix."""
with pytest.raises(ValueError, match="unknown group"):
_validate_pin_key("network.port")
@pytest.mark.unit
def test_validate_pin_key_unknown_field_in_valid_group_raises():
"""VK8 — known group, unknown field."""
with pytest.raises(ValueError, match="unknown field"):
_validate_pin_key("screen.brightness")
@pytest.mark.unit
def test_validate_pin_key_empty_string_raises():
"""VK9 — empty key fails the dotted-form check."""
with pytest.raises(ValueError):
_validate_pin_key("")
@pytest.mark.unit
@pytest.mark.parametrize("group,fields", sorted(_PIN_GROUPS.items()))
def test_validate_pin_key_all_groups_first_field(group, fields):
"""VK10 — every defined group accepts its sorted-first field."""
first = sorted(fields)[0]
_validate_pin_key(f"{group}.{first}")
# ─────────────────────────────────────────────────────────────────────
# _apply_pins_to_raw
# ─────────────────────────────────────────────────────────────────────
def _raw_baseline():
"""A minimal raw dict for pin tests — only the keys we care about."""
return {
"screen_w": 1920,
"screen_h": 1080,
"webgl_vendor": "Google Inc. (Intel)",
"webgl_renderer": "ANGLE (Intel)",
"dark_theme": 0,
}
@pytest.mark.unit
def test_apply_pins_to_raw_screen_width():
"""AP1 — `screen.width` rewrites `screen_w` in raw."""
out = _apply_pins_to_raw(_raw_baseline(), {"screen.width": 2560})
assert out["screen_w"] == 2560
@pytest.mark.unit
def test_apply_pins_to_raw_multiple_pins():
"""AP6 — multiple pins all land in raw."""
pin = {"gpu.vendor": "X", "gpu.renderer": "Y"}
out = _apply_pins_to_raw(_raw_baseline(), pin)
assert out["webgl_vendor"] == "X"
assert out["webgl_renderer"] == "Y"
@pytest.mark.unit
def test_apply_pins_to_raw_returns_copy_not_mutation():
"""AP7 — input dict is not mutated."""
raw = _raw_baseline()
snapshot = dict(raw)
_apply_pins_to_raw(raw, {"screen.width": 9999})
assert raw == snapshot
@pytest.mark.unit
def test_apply_pins_to_raw_unknown_key_silent():
"""AP8 — key not in `_PIN_TO_RAW` is ignored.
Validation happens upstream in `generate_profile`; the inner helper
guards defensively but does not raise.
"""
raw = _raw_baseline()
out = _apply_pins_to_raw(raw, {"some.unknown": 123})
# No change to known fields
assert out["screen_w"] == raw["screen_w"]
# No new key added
assert "some.unknown" not in out
# ─────────────────────────────────────────────────────────────────────
# generate_profile
# ─────────────────────────────────────────────────────────────────────
@pytest.mark.unit
def test_generate_profile_happy_path():
"""GP1 — returns a fully populated Profile."""
p = generate_profile(seed=42)
assert isinstance(p, Profile)
assert p.seed == 42
assert p.gpu.vendor
assert p.gpu.renderer
assert p.gpu.class_tier in _PIN_GROUPS["gpu"].union({"low_end", "mid_range",
"high_end", "integrated_old", "integrated_modern", "workstation"})
assert p.screen.width > 0
assert p.screen.height > 0
assert p.hardware.concurrency > 0
assert p.audio.sample_rate > 0
@pytest.mark.unit
def test_generate_profile_deterministic():
"""GP2 — same seed → identical Profile (equality on frozen dataclass)."""
a = generate_profile(seed=42)
b = generate_profile(seed=42)
assert a == b
@pytest.mark.unit
def test_generate_profile_seed_float_coerced():
"""GP3 — float seed is coerced to int (truncated)."""
a = generate_profile(seed=42.7)
b = generate_profile(seed=42)
assert a == b
@pytest.mark.unit
def test_generate_profile_seed_string_coerced():
"""GP4 — numeric string seed works via int() coercion."""
a = generate_profile(seed="42")
b = generate_profile(seed=42)
assert a == b
@pytest.mark.unit
def test_generate_profile_no_pin_samples_freely():
"""GP5 — no pin: every field is sampler-derived (sanity: 2 seeds differ)."""
a = generate_profile(seed=1)
b = generate_profile(seed=2)
assert a != b
@pytest.mark.unit
def test_generate_profile_pin_overrides_screen_width():
"""GP6 — pinned width visible on the Profile dataclass."""
p = generate_profile(seed=42, pin={"screen.width": 9999})
assert p.screen.width == 9999
@pytest.mark.unit
def test_generate_profile_pin_visible_in_prefs_dict():
"""GP7 — pinned values flow through to to_prefs_dict()."""
p = generate_profile(seed=42, pin={"screen.width": 9999})
assert p.to_prefs_dict()["screen_w"] == 9999
@pytest.mark.unit
def test_generate_profile_invalid_pin_raises():
"""GP8 — bad pin key surfaces ValueError from validation."""
with pytest.raises(ValueError):
generate_profile(seed=42, pin={"bogus": 1})
@pytest.mark.unit
def test_generate_profile_empty_pin_equals_no_pin():
"""GP9 — empty pin dict is a no-op."""
a = generate_profile(seed=42, pin={})
b = generate_profile(seed=42)
assert a == b
@pytest.mark.unit
def test_generate_profile_is_frozen():
"""GP10 — Profile dataclass is immutable."""
p = generate_profile(seed=42)
with pytest.raises(FrozenInstanceError):
p.seed = 99 # type: ignore[misc]
@pytest.mark.unit
def test_generate_profile_to_prefs_dict_flat_and_matches_raw():
"""GP12 — to_prefs_dict() returns a flat dict containing core sampler keys."""
p = generate_profile(seed=42)
d = p.to_prefs_dict()
assert isinstance(d, dict)
for key in ("screen_w", "screen_h", "webgl_vendor", "webgl_renderer",
"hw_concurrency", "stealth_seed"):
assert key in d
@pytest.mark.unit
def test_generate_profile_seed_zero():
"""GP13 — seed=0 is a valid lowest-value boundary."""
p = generate_profile(seed=0)
assert p.seed == 0
@pytest.mark.unit
def test_generate_profile_seed_max_int31():
"""GP14 — seed at int31 upper bound works."""
seed = (1 << 31) - 1
p = generate_profile(seed=seed)
assert p.seed == seed
@pytest.mark.unit
def test_generate_profile_dark_theme_is_bool():
"""GP15 — dark_theme is coerced to bool on the dataclass."""
p = generate_profile(seed=42)
assert isinstance(p.dark_theme, bool)
# ─────────────────────────────────────────────────────────────────────
# Additional pin coverage (recheck pass)
# ─────────────────────────────────────────────────────────────────────
@pytest.mark.unit
def test_generate_profile_pin_dark_theme_true():
"""Pinning dark_theme=True flows through coercion to bool."""
p = generate_profile(seed=42, pin={"dark_theme": True})
assert p.dark_theme is True
@pytest.mark.unit
def test_generate_profile_pin_dark_theme_false():
p = generate_profile(seed=42, pin={"dark_theme": False})
assert p.dark_theme is False
@pytest.mark.unit
def test_generate_profile_pin_gpu_renderer_propagates():
p = generate_profile(seed=42, pin={"gpu.renderer": "FORCED_RENDERER"})
assert p.gpu.renderer == "FORCED_RENDERER"
assert p.to_prefs_dict()["webgl_renderer"] == "FORCED_RENDERER"
@pytest.mark.unit
def test_generate_profile_pin_to_raw_keymap_complete():
"""Every dotted pin key has a `_PIN_TO_RAW` mapping.
Guards against silently-ignored pins if someone adds a key to `_PIN_GROUPS`
but forgets the raw-key mapping.
"""
dotted = {f"{group}.{field}" for group, fields in _PIN_GROUPS.items()
for field in fields}
# 'dark_theme' is top-level and present in _PIN_TO_RAW.
missing = dotted - set(_PIN_TO_RAW.keys())
assert missing == set(), f"pin keys without raw mapping: {sorted(missing)}"