Skip to content

Commit 74a783f

Browse files
committed
fix: dedup README roadmap + handle WebGL absence on headless Linux CI
README roadmap: leftover rows for M3-M6 from the M1-era table were sitting below the current M0-M12 table. Cleaned up — only the canonical table remains. test_fingerprint: two integration tests assumed the WEBGL_debug_renderer_info extension was always present. On headless Linux CI (no GPU), that extension isn't exposed, so reading ext.UNMASKED_RENDERER_WEBGL threw "Uncaught" via Runtime.evaluate. Both tests now use a shared WEBGL_RENDERER_JS probe that: - returns null if WebGL itself isn't available - falls back to gl.getParameter(gl.RENDERER) when the debug-info extension is missing (our webgl.js stealth hook already returns the spoofed renderer for the RENDERER constant 7937 alongside the UNMASKED_RENDERER_WEBGL constant 37446, so the override assertion still validates the right behaviour) test_preset_overrides_webgl_renderer now pytest.skip()s if WebGL is entirely unavailable. test_no_fingerprint_keeps_default_behavior tolerates a null renderer in that case.
1 parent 8ec7698 commit 74a783f

2 files changed

Lines changed: 22 additions & 20 deletions

File tree

‎README.md‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,6 @@ See `CHANGELOG.md` for what's landed.
135135
| M10 | TLS JA3/JA4 fingerprint | post-v0.1 | mitm with curl_cffi/utls, or BoringSSL patch in M11 |
136136
| M11 | Browser fork | post-v0.1 | Camoufox or ungoogled-chromium with C++ stealth patches |
137137
| M12 | Tauri UI for manual mode | post-v0.1 | desktop app with tabs/address bar/settings |
138-
| M3 | Solver bridge + Turnstile | pending |
139-
| M4 | reCAPTCHA, hCaptcha, FunCaptcha, GeeTest | pending |
140-
| M5 | Production hardening | pending |
141-
| M6 | v0.1 release | pending |
142138

143139
## Development
144140

‎tests/test_fingerprint.py‎

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@
88
from funbrowser import Fingerprint, presets
99
from funbrowser._launcher import find_chrome
1010

11+
# Robust WebGL renderer probe — falls back to gl.RENDERER when the
12+
# WEBGL_debug_renderer_info extension is unavailable (headless Linux CI),
13+
# returns null when WebGL itself is missing.
14+
WEBGL_RENDERER_JS = """
15+
(() => {
16+
const gl = document.createElement('canvas').getContext('webgl');
17+
if (!gl) return null;
18+
const ext = gl.getExtension('WEBGL_debug_renderer_info');
19+
if (ext) return gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
20+
return gl.getParameter(gl.RENDERER);
21+
})()
22+
"""
23+
1124

1225
def test_all_presets_have_required_fields() -> None:
1326
for fp in presets.ALL:
@@ -93,12 +106,9 @@ async def test_preset_overrides_webgl_renderer() -> None:
93106
fp = presets.windows_11_amd_radeon_6700_xt()
94107
async with await funbrowser.start(headless=True, fingerprint=fp) as browser:
95108
tab = await browser.get("https://example.com")
96-
renderer = await tab.evaluate(
97-
"(() => { const c = document.createElement('canvas');"
98-
" const gl = c.getContext('webgl');"
99-
" const ext = gl.getExtension('WEBGL_debug_renderer_info');"
100-
" return gl.getParameter(ext.UNMASKED_RENDERER_WEBGL); })()"
101-
)
109+
renderer = await tab.evaluate(WEBGL_RENDERER_JS)
110+
if renderer is None:
111+
pytest.skip("WebGL unavailable in this environment")
102112
assert "AMD" in renderer
103113
assert "RX 6700" in renderer
104114

@@ -137,13 +147,9 @@ async def test_no_fingerprint_keeps_default_behavior() -> None:
137147
ua = await tab.evaluate("navigator.userAgent")
138148
# default mode still strips HeadlessChrome
139149
assert "HeadlessChrome" not in ua
140-
# webgl renderer comes from real hardware (no override)
141-
renderer = await tab.evaluate(
142-
"(() => { const c = document.createElement('canvas');"
143-
" const gl = c.getContext('webgl');"
144-
" const ext = gl.getExtension('WEBGL_debug_renderer_info');"
145-
" return gl.getParameter(ext.UNMASKED_RENDERER_WEBGL); })()"
146-
)
147-
# something real should come back, exact value depends on host
148-
assert isinstance(renderer, str)
149-
assert len(renderer) > 0
150+
renderer = await tab.evaluate(WEBGL_RENDERER_JS)
151+
# On environments with WebGL we expect a real renderer string;
152+
# CI without GPU may have WebGL disabled entirely, which is fine.
153+
if renderer is not None:
154+
assert isinstance(renderer, str)
155+
assert len(renderer) > 0

0 commit comments

Comments
 (0)