diff --git a/pythonlib/camoufox/coherence.py b/pythonlib/camoufox/coherence.py index bc0f055..37b1b0e 100644 --- a/pythonlib/camoufox/coherence.py +++ b/pythonlib/camoufox/coherence.py @@ -43,10 +43,15 @@ APPLE_SILICON_CORES = frozenset({8, 10, 11, 12, 14, 16, 20, 24, 28, 32}) # Linux reports 1, or 2 under HiDPI, with GNOME fractional scaling giving the # 1.25/1.5/1.75 steps. Values outside these (1.818, 1.09, 1.36) are scraped # artefacts -- a browser zoom level folded into the ratio, not a display mode. +# +# Ascending tuples, not sets: the repair keeps the first of two equally near +# steps, and a frozenset literal iterates in a different order when compiled +# than when loaded from a .pyc -- so a set made the first launch repair an +# identity differently from every later one. PLAUSIBLE_DPR = { - 'win': frozenset({1, 1.25, 1.5, 1.75, 2, 2.5, 3}), - 'mac': frozenset({1, 2}), - 'lin': frozenset({1, 1.25, 1.5, 1.75, 2}), + 'win': (1, 1.25, 1.5, 1.75, 2, 2.5, 3), + 'mac': (1, 2), + 'lin': (1, 1.25, 1.5, 1.75, 2), } # colorDepth: Firefox reports 24, or 30 on a deep-colour display. macOS defaults diff --git a/pythonlib/tests/test_coherence.py b/pythonlib/tests/test_coherence.py index fe0c774..c1d2956 100644 --- a/pythonlib/tests/test_coherence.py +++ b/pythonlib/tests/test_coherence.py @@ -149,3 +149,37 @@ class TestEveryIdentityIsCoherent: for i, preset in enumerate(fp.load_presets("150")["presets"][os_name]): config = launch(os=os_name, fingerprint_preset=preset) assert coherence.validate(config, get_target_os(config)) == [], (os_name, i) + + +_MIDPOINT_REPAIRS = """ +from camoufox import coherence +out = [] +for os_key, steps in sorted(coherence.PLAUSIBLE_DPR.items()): + steps = sorted(steps) + for low, high in zip(steps, steps[1:]): + config = {"window.devicePixelRatio": (low + high) / 2} + coherence.apply(config, os_key) + out.append(config["window.devicePixelRatio"]) +print(out) +""" + + +def test_a_midpoint_repairs_to_the_lower_step_whether_or_not_bytecode_is_cached(tmp_path): + """The steps were frozensets, and min() keeps the first of equal distances. + A frozenset literal iterates in one order when compiled from source and in + another when loaded back from a .pyc, so the same identity repaired + differently on its first launch than on later ones.""" + import subprocess + import sys + from pathlib import Path + + env = {"PYTHONPYCACHEPREFIX": str(tmp_path), "PYTHONPATH": str(Path(coherence.__file__).parents[1])} + runs = [ + subprocess.run([sys.executable, "-c", _MIDPOINT_REPAIRS], env=env, capture_output=True, + text=True, check=True).stdout + for _ in range(2) # the first compiles and writes the .pyc, the second loads it + ] + assert runs[0] == runs[1] + lower = [low for _, steps in sorted(coherence.PLAUSIBLE_DPR.items()) + for low in sorted(steps)[:-1]] + assert runs[0].strip() == str(lower)