Skip to content

Commit 29d2fe5

Browse files
committed
tests/playwright: resolve shim target through shell-script shims on macOS
Two playwright tests assert ``loaded_abspath.resolve()`` lands inside ``playwright_root/cache``. On Linux the shim is a plain symlink so ``.resolve()`` follows naturally. On macOS chromium ships inside a ``.app`` bundle and the shim is a shell script (``exec '<path>'``) instead -- ``.resolve()`` on a regular file just returns the file itself, failing the ``in updated_target.parents`` assertion. Add ``_resolve_shim_target()`` that falls back to parsing the ``exec`` line from the shell script when ``.resolve()`` doesn't follow. Also update ``copy_seeded_playwright_root()`` to rewrite the shell script's hardcoded absolute path from the seeded root to the per-test copy (previously it only repointed symlinks), so the pre-``load()`` shim state is consistent across both shim flavors.
1 parent a464e53 commit 29d2fe5

1 file changed

Lines changed: 56 additions & 10 deletions

File tree

tests/test_playwrightprovider.py

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import re
23
import shutil
34
import tempfile
45
from pathlib import Path
@@ -8,6 +9,29 @@
89
from abxpkg import Binary, PlaywrightProvider
910

1011

12+
def _resolve_shim_target(shim: Path) -> Path:
13+
"""Resolve a managed bin_dir shim to its real browser target.
14+
15+
On Linux the shim is a symlink, so ``.resolve()`` naturally follows
16+
it. On macOS the shim is a shell script that ``exec``s the binary
17+
inside a ``.app`` bundle (a symlink would leave the browser launching
18+
without bundle context), so ``.resolve()`` just returns the script
19+
path. Parse the ``exec <path>`` line to recover the target in that
20+
case.
21+
"""
22+
resolved = shim.resolve()
23+
if resolved != shim:
24+
return resolved
25+
try:
26+
script = shim.read_text(encoding="utf-8")
27+
except OSError:
28+
return resolved
29+
match = re.search(r"exec '([^']+)'", script)
30+
if not match:
31+
return resolved
32+
return Path(match.group(1)).resolve()
33+
34+
1135
@pytest.fixture(scope="module")
1236
def seeded_playwright_root():
1337
with tempfile.TemporaryDirectory() as temp_dir:
@@ -35,15 +59,37 @@ def copy_seeded_playwright_root(
3559
copied_bin_dir = install_root / "bin"
3660
if not copied_bin_dir.is_dir():
3761
return
62+
seeded_resolved = seeded_playwright_root.resolve()
3863
for link_path in copied_bin_dir.iterdir():
39-
if not link_path.is_symlink():
64+
if link_path.is_symlink():
65+
link_target = link_path.resolve(strict=False)
66+
if seeded_resolved not in link_target.parents:
67+
continue
68+
relative_target = link_target.relative_to(seeded_resolved)
69+
link_path.unlink()
70+
link_path.symlink_to(install_root / relative_target)
71+
continue
72+
# macOS chrome/chromium shims are shell scripts that hardcode
73+
# the seeded install_root path; rewrite them so they exec the
74+
# copy under this test's install_root instead.
75+
if not link_path.is_file():
4076
continue
41-
link_target = link_path.resolve(strict=False)
42-
if seeded_playwright_root.resolve() not in link_target.parents:
77+
try:
78+
script = link_path.read_text(encoding="utf-8")
79+
except OSError:
4380
continue
44-
relative_target = link_target.relative_to(seeded_playwright_root.resolve())
45-
link_path.unlink()
46-
link_path.symlink_to(install_root / relative_target)
81+
match = re.search(r"exec '([^']+)'", script)
82+
if not match:
83+
continue
84+
target_path = Path(match.group(1))
85+
if seeded_resolved not in target_path.resolve().parents:
86+
continue
87+
relative_target = target_path.resolve().relative_to(seeded_resolved)
88+
new_target = install_root / relative_target
89+
link_path.write_text(
90+
script.replace(str(target_path), str(new_target)),
91+
encoding="utf-8",
92+
)
4793

4894
def test_chromium_install_puts_real_browser_into_managed_bin_dir(
4995
self,
@@ -70,9 +116,9 @@ def test_chromium_install_puts_real_browser_into_managed_bin_dir(
70116
assert provider.bin_dir is not None
71117
assert installed.loaded_abspath.parent == provider.bin_dir
72118
assert installed.loaded_abspath == provider.bin_dir / "chromium"
73-
# The symlink resolves into ``playwright_root/cache`` (the
119+
# The shim resolves into ``playwright_root/cache`` (the
74120
# managed ``PLAYWRIGHT_BROWSERS_PATH`` for this provider).
75-
real_target = installed.loaded_abspath.resolve()
121+
real_target = _resolve_shim_target(installed.loaded_abspath)
76122
assert (playwright_root / "cache").resolve() in real_target.parents
77123
# Playwright lays out chromium builds as chromium-<build>/.
78124
assert any(
@@ -374,12 +420,12 @@ def test_update_refreshes_chromium_in_place(
374420
)
375421
assert updated is not None
376422
assert updated.loaded_abspath is not None
377-
# The symlink resolves to a chromium build that actually
423+
# The shim resolves to a chromium build that actually
378424
# exists on disk after update (whether the build-id moved
379425
# depends on the current playwright release, but the
380426
# resolved target must always exist and still live inside
381427
# ``playwright_root``).
382-
updated_target = updated.loaded_abspath.resolve()
428+
updated_target = _resolve_shim_target(updated.loaded_abspath)
383429
assert updated_target.exists()
384430
assert (playwright_root / "cache").resolve() in updated_target.parents
385431
assert any(

0 commit comments

Comments
 (0)