Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,631 @@
|
||||
"""Tests for the dylib-complete macOS TCC anchor (issue #95596).
|
||||
|
||||
Re-land of the interpreter anchor reverted in #95563. The first landing
|
||||
bricked real Macs two ways: dynamically-linked builds died in dyld because
|
||||
``@executable_path/../lib/libpython`` resolved into ``venv/lib/`` (#95425),
|
||||
and alias symlinks to the copied interpreter lost the venv prefix (#95541).
|
||||
|
||||
Linux tests use fake checkout/uv-store layouts with ``platform.system``
|
||||
monkeypatched. The real-interpreter E2E is ``macos_only`` so it runs on
|
||||
the existing macOS CI job, not against one-byte fixtures.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import errno
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import hermes_cli.doctor as doctor
|
||||
import hermes_cli.macos_tcc_anchor as tcc
|
||||
from hermes_constants import venv_python_path
|
||||
|
||||
|
||||
def _darwin(monkeypatch):
|
||||
monkeypatch.setattr(tcc.platform, "system", lambda: "Darwin")
|
||||
|
||||
|
||||
def _linux(monkeypatch):
|
||||
monkeypatch.setattr(tcc.platform, "system", lambda: "Linux")
|
||||
|
||||
|
||||
def _build_store(tmp_path, version: str = "3.11.15", *, with_libpython: bool = False) -> Path:
|
||||
store = (
|
||||
tmp_path
|
||||
/ "uv-store"
|
||||
/ "uv"
|
||||
/ "python"
|
||||
/ f"cpython-{version}-macos-aarch64-none"
|
||||
)
|
||||
store_bin = store / "bin"
|
||||
store_bin.mkdir(parents=True)
|
||||
store_py = store_bin / "python3.11"
|
||||
store_py.write_bytes(f"#!fake interpreter {version}".encode())
|
||||
store_py.chmod(0o755)
|
||||
if with_libpython:
|
||||
lib = store / "lib"
|
||||
lib.mkdir(parents=True)
|
||||
(lib / "libpython3.11.dylib").write_bytes(b"fake dylib")
|
||||
return store_bin
|
||||
|
||||
|
||||
def _build_checkout(
|
||||
tmp_path,
|
||||
*,
|
||||
store_bin: Path | None = None,
|
||||
version: str = "3.11.15",
|
||||
anchored: bool = False,
|
||||
homebrew: bool = False,
|
||||
with_libpython: bool = False,
|
||||
) -> Path:
|
||||
root = tmp_path / "checkout"
|
||||
venv = root / ".venv"
|
||||
venv_bin = venv / "bin"
|
||||
venv_bin.mkdir(parents=True)
|
||||
if homebrew:
|
||||
brew = tmp_path / "opt" / "homebrew" / "bin"
|
||||
brew.mkdir(parents=True)
|
||||
brew_py = brew / "python3.14"
|
||||
brew_py.write_bytes(b"#!homebrew")
|
||||
brew_py.chmod(0o755)
|
||||
(venv / "pyvenv.cfg").write_text(f"home = {brew}\n")
|
||||
os.symlink(brew_py, venv_bin / "python")
|
||||
os.symlink(brew_py, venv_bin / "python3")
|
||||
return root
|
||||
if store_bin is None:
|
||||
store_bin = _build_store(tmp_path, version, with_libpython=with_libpython)
|
||||
(venv / "pyvenv.cfg").write_text(f"home = {store_bin}\n")
|
||||
store_py = store_bin / "python3.11"
|
||||
if anchored:
|
||||
venv_py = venv_bin / "python"
|
||||
venv_py.write_bytes(store_py.read_bytes())
|
||||
venv_py.chmod(0o755)
|
||||
(venv_bin / ".tcc-anchor-source").write_text(str(store_py), encoding="utf-8")
|
||||
os.symlink(venv_py, venv_bin / "python3")
|
||||
else:
|
||||
os.symlink(store_py, venv_bin / "python")
|
||||
os.symlink(store_py, venv_bin / "python3")
|
||||
return root
|
||||
|
||||
|
||||
class TestUvStoreDetection:
|
||||
def test_matches_uv_macos_store_path(self):
|
||||
path = (
|
||||
"/Users/u/.local/share/uv/python/"
|
||||
"cpython-3.11.15-macos-aarch64-none/bin/python3.11"
|
||||
)
|
||||
assert tcc._is_uv_macos_store(path)
|
||||
|
||||
def test_matches_hermes_runtime_repair_generation(self):
|
||||
path = (
|
||||
"/Users/u/hermes-agent/.hermes-runtime/python/"
|
||||
"generation-a1b2c3/cpython-3.11.15-macos-aarch64-none/bin/python3.11"
|
||||
)
|
||||
assert tcc._is_uv_macos_store(path)
|
||||
|
||||
def test_rejects_homebrew_interpreter(self):
|
||||
path = (
|
||||
"/opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/"
|
||||
"Python.framework/Versions/3.14/bin/python3.14"
|
||||
)
|
||||
assert not tcc._is_uv_macos_store(path)
|
||||
|
||||
def test_rejects_linux_interpreter(self):
|
||||
assert not tcc._is_uv_macos_store("/usr/bin/python3")
|
||||
|
||||
def test_rejects_uv_store_on_linux(self):
|
||||
path = (
|
||||
"/home/u/.local/share/uv/python/"
|
||||
"cpython-3.11.15-x86_64-unknown-linux-gnu/bin/python3.11"
|
||||
)
|
||||
assert not tcc._is_uv_macos_store(path)
|
||||
|
||||
|
||||
class TestEnsureTccAnchor:
|
||||
def test_noop_on_non_macos(self, tmp_path, monkeypatch):
|
||||
_linux(monkeypatch)
|
||||
root = _build_checkout(tmp_path, store_bin=_build_store(tmp_path))
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
|
||||
assert tcc.ensure_tcc_anchor(root) is None
|
||||
assert venv_py.is_symlink()
|
||||
|
||||
def test_install_signs_the_anchor_copy(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
signed = []
|
||||
import hermes_cli.managed_uv as managed_uv
|
||||
|
||||
monkeypatch.setattr(
|
||||
managed_uv, "_macos_sign_managed_python", lambda p: signed.append(Path(p)) or True
|
||||
)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert anchored is not None
|
||||
assert len(signed) == 1
|
||||
assert signed[0].parent == anchored.parent
|
||||
|
||||
def test_anchors_repair_generation_interpreter(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store = (
|
||||
tmp_path
|
||||
/ "checkout"
|
||||
/ ".hermes-runtime"
|
||||
/ "python"
|
||||
/ "generation-a1b2c3"
|
||||
/ "cpython-3.11.15-macos-aarch64-none"
|
||||
)
|
||||
store_bin = store / "bin"
|
||||
store_bin.mkdir(parents=True)
|
||||
store_py = store_bin / "python3.11"
|
||||
store_py.write_bytes(b"#!fake generation interpreter")
|
||||
store_py.chmod(0o755)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
assert venv_py.is_symlink()
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert anchored == venv_py
|
||||
assert not venv_py.is_symlink()
|
||||
assert venv_py.read_bytes() == store_py.read_bytes()
|
||||
|
||||
def test_anchors_uv_managed_interpreter(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
assert venv_py.is_symlink()
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert anchored == venv_py
|
||||
assert venv_py.is_file() and not venv_py.is_symlink()
|
||||
assert venv_py.read_bytes() == (store_bin / "python3.11").read_bytes()
|
||||
assert os.access(venv_py, os.X_OK)
|
||||
marker = venv_py.parent / ".tcc-anchor-source"
|
||||
assert marker.read_text(encoding="utf-8").strip() == str(
|
||||
store_bin / "python3.11"
|
||||
)
|
||||
alias = venv_py.parent / "python3"
|
||||
assert alias.is_file() and not alias.is_symlink()
|
||||
assert alias.read_bytes() == venv_py.read_bytes()
|
||||
|
||||
def test_idempotent(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin, anchored=True)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
marker = venv_py.parent / ".tcc-anchor-source"
|
||||
before = marker.read_text(encoding="utf-8")
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert anchored == venv_py
|
||||
assert venv_py.is_file() and not venv_py.is_symlink()
|
||||
assert marker.read_text(encoding="utf-8") == before
|
||||
|
||||
def test_repairs_alias_symlinks_left_by_predecessor(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin, anchored=True)
|
||||
venv_bin = root / ".venv" / "bin"
|
||||
venv_py = venv_bin / "python"
|
||||
assert (venv_bin / "python3").is_symlink()
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert anchored == venv_py
|
||||
alias = venv_bin / "python3"
|
||||
assert alias.is_file() and not alias.is_symlink()
|
||||
assert alias.read_bytes() == venv_py.read_bytes()
|
||||
|
||||
def test_reanchors_after_patch_bump(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
old_bin = _build_store(tmp_path, version="3.11.15")
|
||||
root = _build_checkout(tmp_path, store_bin=old_bin, anchored=True)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
|
||||
new_bin = _build_store(tmp_path, version="3.11.16")
|
||||
new_py = new_bin / "python3.11"
|
||||
venv_py.unlink()
|
||||
os.symlink(new_py, venv_py)
|
||||
(root / ".venv" / "pyvenv.cfg").write_text(f"home = {new_bin}\n")
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert anchored == venv_py
|
||||
assert not venv_py.is_symlink()
|
||||
assert venv_py.read_bytes() == new_py.read_bytes()
|
||||
marker = venv_py.parent / ".tcc-anchor-source"
|
||||
assert marker.read_text(encoding="utf-8").strip() == str(new_py)
|
||||
alias = venv_py.parent / "python3"
|
||||
assert alias.is_file() and not alias.is_symlink()
|
||||
assert alias.read_bytes() == new_py.read_bytes()
|
||||
|
||||
def test_skips_homebrew_interpreter(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
root = _build_checkout(tmp_path, homebrew=True)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
|
||||
assert tcc.ensure_tcc_anchor(root) is None
|
||||
assert venv_py.is_symlink()
|
||||
|
||||
def test_no_venv_returns_none(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
assert tcc.ensure_tcc_anchor(tmp_path / "missing") is None
|
||||
|
||||
def test_preserves_stdlib_source_home(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
cfg = root / ".venv" / "pyvenv.cfg"
|
||||
|
||||
tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert f"home = {store_bin}" in cfg.read_text(encoding="utf-8")
|
||||
|
||||
def test_provisions_libpython_as_hardlink_when_present(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path, with_libpython=True)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
src_dylib = store_bin.parent / "lib" / "libpython3.11.dylib"
|
||||
|
||||
tcc.ensure_tcc_anchor(root)
|
||||
|
||||
dst = root / ".venv" / "lib" / "libpython3.11.dylib"
|
||||
assert dst.is_file()
|
||||
assert dst.read_bytes() == src_dylib.read_bytes()
|
||||
assert dst.stat().st_ino == src_dylib.stat().st_ino
|
||||
|
||||
def test_boot_gate_refusal_leaves_venv_untouched(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
monkeypatch.setattr(tcc, "_passes_boot_gate", lambda *a, **k: False)
|
||||
|
||||
assert tcc.ensure_tcc_anchor(root) is None
|
||||
assert venv_py.is_symlink()
|
||||
assert not (venv_py.parent / ".tcc-anchor-source").exists()
|
||||
|
||||
def test_alias_failure_leaves_anchor_unmarked(self, tmp_path, monkeypatch, caplog):
|
||||
# If an alias copy fails (e.g. ENOSPC), the marker must NOT be
|
||||
# written: a symlink alias to the anchored copy is the #95541 crash
|
||||
# shape, and a marker would make doctor report "active" over it.
|
||||
# The next ensure retries the whole install.
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
venv_py = venv_python_path(root / ".venv")
|
||||
monkeypatch.setattr(tcc, "_copy_alias", lambda *a, **k: False)
|
||||
|
||||
import logging
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger=tcc.__name__):
|
||||
tcc.ensure_tcc_anchor(root)
|
||||
|
||||
assert not (venv_py.parent / ".tcc-anchor-source").exists()
|
||||
status, _ = tcc.tcc_anchor_state(root)
|
||||
assert status != "active"
|
||||
assert any("alias" in r.message for r in caplog.records)
|
||||
|
||||
# Recovery: with alias copies working again the retry completes.
|
||||
monkeypatch.undo()
|
||||
monkeypatch.setattr(tcc.platform, "system", lambda: "Darwin")
|
||||
assert tcc.ensure_tcc_anchor(root) is not None
|
||||
assert tcc.tcc_anchor_state(root)[0] == "active"
|
||||
|
||||
def test_copy_alias_failure_warns_and_cleans_staging(self, tmp_path, monkeypatch, caplog):
|
||||
# _copy_alias must log the failure (silent skips hid the #95541
|
||||
# shape) and never leave a staging file behind.
|
||||
anchor = tmp_path / "anchor"
|
||||
anchor.write_bytes(b"#!anchor")
|
||||
anchor.chmod(0o755)
|
||||
|
||||
def boom(src, dst, **kw):
|
||||
raise OSError(28, "No space left on device")
|
||||
|
||||
monkeypatch.setattr(tcc.shutil, "copy2", boom)
|
||||
|
||||
import logging
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger=tcc.__name__):
|
||||
assert tcc._copy_alias(tmp_path, "python3", anchor) is False
|
||||
|
||||
assert any("python3" in r.message for r in caplog.records)
|
||||
assert not list(tmp_path.glob(".python3.tcc-*"))
|
||||
|
||||
def test_marker_written_atomically(self, tmp_path):
|
||||
# Marker goes through write-then-rename; no torn intermediate name
|
||||
# survives and the content matches the resolved source path.
|
||||
source = tmp_path / "store" / "python3.11"
|
||||
source.parent.mkdir(parents=True)
|
||||
source.write_bytes(b"#!store")
|
||||
venv_bin = tmp_path / "bin"
|
||||
venv_bin.mkdir()
|
||||
|
||||
tcc._write_marker(venv_bin, source)
|
||||
|
||||
marker = venv_bin / ".tcc-anchor-source"
|
||||
assert marker.read_text(encoding="utf-8") == tcc._marker_value(source)
|
||||
assert not list(venv_bin.glob(".tcc-anchor-source.*"))
|
||||
|
||||
def test_store_root_marker_tracks_managed_uv_constant(self):
|
||||
# The repair-generation store marker must stay derived from
|
||||
# managed_uv's directory constant, not drift as a hardcoded string.
|
||||
from hermes_cli.managed_uv import _RUNTIME_DIR_NAME
|
||||
|
||||
assert f"/{_RUNTIME_DIR_NAME}/python/" in tcc._STORE_ROOT_MARKERS
|
||||
|
||||
|
||||
class TestBootGate:
|
||||
"""Direct branch coverage for _passes_boot_gate.
|
||||
|
||||
The gate is the never-brick guarantee, so each refusal direction gets its
|
||||
own test rather than only being exercised through the install rollback.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _proc(argv, returncode, stdout="", stderr=""):
|
||||
return subprocess.CompletedProcess(argv, returncode, stdout, stderr)
|
||||
|
||||
def test_refuses_nonzero_exit(self, tmp_path, monkeypatch):
|
||||
# dyld / encodings crash in the staged copy → refuse install.
|
||||
monkeypatch.setattr(
|
||||
tcc.subprocess, "run",
|
||||
lambda *a, **k: self._proc(a[0], 1, "", "ModuleNotFoundError: encodings"),
|
||||
)
|
||||
assert not tcc._passes_boot_gate(tmp_path / "staged", tmp_path / "venv")
|
||||
|
||||
def test_refuses_wrong_prefix(self, tmp_path, monkeypatch):
|
||||
# Boots but resolves the build-time prefix (the /install signature).
|
||||
monkeypatch.setattr(
|
||||
tcc.subprocess, "run",
|
||||
lambda *a, **k: self._proc(a[0], 0, "/install\n", ""),
|
||||
)
|
||||
assert not tcc._passes_boot_gate(tmp_path / "staged", tmp_path / "venv")
|
||||
|
||||
def test_refuses_timeout(self, tmp_path, monkeypatch):
|
||||
def hang(*a, **k):
|
||||
raise subprocess.TimeoutExpired(cmd=a[0], timeout=30)
|
||||
|
||||
monkeypatch.setattr(tcc.subprocess, "run", hang)
|
||||
assert not tcc._passes_boot_gate(tmp_path / "staged", tmp_path / "venv")
|
||||
|
||||
def test_skips_unexecutable_staging(self, tmp_path, monkeypatch):
|
||||
# ENOENT/ENOEXEC (fixture bad-shebang binaries, wrong-arch images)
|
||||
# mean the binary cannot run here at all — the symlinked venv was
|
||||
# equally dead, so installing cannot make things worse.
|
||||
def noexec(*a, **k):
|
||||
raise OSError(errno.ENOENT, "No such file or directory")
|
||||
|
||||
monkeypatch.setattr(tcc.subprocess, "run", noexec)
|
||||
assert tcc._passes_boot_gate(tmp_path / "staged", tmp_path / "venv")
|
||||
|
||||
def test_refuses_eacces_after_chmod(self, tmp_path, monkeypatch):
|
||||
# EACCES after copy2+chmod is a broken install about to go live —
|
||||
# refuse, never skip (review: kokhlo on #95605).
|
||||
def denied(*a, **k):
|
||||
raise PermissionError(errno.EACCES, "Permission denied")
|
||||
|
||||
monkeypatch.setattr(tcc.subprocess, "run", denied)
|
||||
assert not tcc._passes_boot_gate(tmp_path / "staged", tmp_path / "venv")
|
||||
|
||||
def test_scrubs_pythonhome_pythonpath(self, tmp_path, monkeypatch):
|
||||
# A staged copy that dies with `No module named 'encodings'` boots
|
||||
# fine under PYTHONHOME=<venv> — the inherited var papers over the
|
||||
# exact failure the gate exists to catch (review: kokhlo on #95605,
|
||||
# verified live). The probe must run with a scrubbed environment.
|
||||
captured = {}
|
||||
|
||||
def spy(*a, **k):
|
||||
captured.update(k)
|
||||
return self._proc(a[0], 0, f"{tmp_path / 'venv'}\n", "")
|
||||
|
||||
monkeypatch.setattr(tcc.subprocess, "run", spy)
|
||||
monkeypatch.setenv("PYTHONHOME", "/poison")
|
||||
monkeypatch.setenv("PYTHONPATH", "/poison")
|
||||
venv = tmp_path / "venv"
|
||||
venv.mkdir(exist_ok=True)
|
||||
|
||||
assert tcc._passes_boot_gate(tmp_path / "staged", venv)
|
||||
env = captured.get("env")
|
||||
assert env is not None, "gate must pass an explicit scrubbed env"
|
||||
assert "PYTHONHOME" not in env
|
||||
assert "PYTHONPATH" not in env
|
||||
|
||||
def test_accepts_matching_prefix(self, tmp_path, monkeypatch):
|
||||
venv = tmp_path / "venv"
|
||||
venv.mkdir()
|
||||
monkeypatch.setattr(
|
||||
tcc.subprocess, "run",
|
||||
lambda *a, **k: self._proc(a[0], 0, f"{venv}\n", ""),
|
||||
)
|
||||
assert tcc._passes_boot_gate(tmp_path / "staged", venv)
|
||||
|
||||
|
||||
class TestTccAnchorState:
|
||||
def test_state_active_through_unpatched_home_symlink(self, tmp_path, monkeypatch):
|
||||
# The managed-runtime layout symlinks cpython-3.11-macos-* →
|
||||
# cpython-3.11.15-macos-*, so pyvenv.cfg home and the marker record
|
||||
# different spellings of the same binary. State must resolve both
|
||||
# sides before comparing, or a fresh install reports stale
|
||||
# (review: kokhlo on #95605, hit on a live venv).
|
||||
_darwin(monkeypatch)
|
||||
patched = _build_store(tmp_path, version="3.11.15")
|
||||
versionless = patched.parent.parent / "cpython-3.11-macos-aarch64-none"
|
||||
os.symlink(patched.parent, versionless)
|
||||
home = versionless / "bin"
|
||||
|
||||
root = tmp_path / "checkout"
|
||||
venv_bin = root / ".venv" / "bin"
|
||||
venv_bin.mkdir(parents=True)
|
||||
(root / ".venv" / "pyvenv.cfg").write_text(f"home = {home}\n")
|
||||
os.symlink(home / "python3.11", venv_bin / "python")
|
||||
|
||||
tcc.ensure_tcc_anchor(root)
|
||||
|
||||
status, _ = tcc.tcc_anchor_state(root)
|
||||
assert status == "active"
|
||||
|
||||
def test_state_missing_then_active(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
|
||||
status, detail = tcc.tcc_anchor_state(root)
|
||||
assert status == "missing"
|
||||
assert str(venv_python_path(root / ".venv")) in detail
|
||||
|
||||
tcc.ensure_tcc_anchor(root)
|
||||
|
||||
status, detail = tcc.tcc_anchor_state(root)
|
||||
assert status == "active"
|
||||
|
||||
def test_state_skip_on_linux(self, tmp_path, monkeypatch):
|
||||
_linux(monkeypatch)
|
||||
store_bin = _build_store(tmp_path)
|
||||
root = _build_checkout(tmp_path, store_bin=store_bin)
|
||||
status, detail = tcc.tcc_anchor_state(root)
|
||||
assert status == "skip"
|
||||
assert detail == "not macOS"
|
||||
|
||||
def test_state_skip_for_homebrew(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
root = _build_checkout(tmp_path, homebrew=True)
|
||||
status, detail = tcc.tcc_anchor_state(root)
|
||||
assert status == "skip"
|
||||
assert "not uv-managed" in detail
|
||||
|
||||
def test_state_stale_after_patch_bump(self, tmp_path, monkeypatch):
|
||||
_darwin(monkeypatch)
|
||||
old_bin = _build_store(tmp_path, version="3.11.15")
|
||||
root = _build_checkout(tmp_path, store_bin=old_bin, anchored=True)
|
||||
new_bin = _build_store(tmp_path, version="3.11.16")
|
||||
(root / ".venv" / "pyvenv.cfg").write_text(f"home = {new_bin}\n")
|
||||
status, _ = tcc.tcc_anchor_state(root)
|
||||
assert status == "stale"
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
assert anchored == venv_python_path(root / ".venv")
|
||||
assert (root / ".venv" / "bin" / "python").read_bytes() == (
|
||||
new_bin / "python3.11"
|
||||
).read_bytes()
|
||||
status, _ = tcc.tcc_anchor_state(root)
|
||||
assert status == "active"
|
||||
|
||||
|
||||
class TestDoctorCheck:
|
||||
def test_missing_warns_without_fix(self, monkeypatch, capsys):
|
||||
monkeypatch.setattr(
|
||||
tcc, "tcc_anchor_state", lambda *a, **k: ("missing", "/x/.venv/bin/python")
|
||||
)
|
||||
doctor.check_macos_tcc_anchor(should_fix=False)
|
||||
out = capsys.readouterr().out
|
||||
assert "macOS TCC anchor missing" in out
|
||||
|
||||
def test_fix_installs_anchor(self, monkeypatch, capsys):
|
||||
monkeypatch.setattr(
|
||||
tcc, "tcc_anchor_state", lambda *a, **k: ("missing", "/x/.venv/bin/python")
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
tcc, "ensure_tcc_anchor", lambda *a, **k: Path("/x/.venv/bin/python")
|
||||
)
|
||||
doctor.check_macos_tcc_anchor(should_fix=True)
|
||||
out = capsys.readouterr().out
|
||||
assert "macOS TCC anchor installed" in out
|
||||
|
||||
def test_active_reports_ok(self, monkeypatch, capsys):
|
||||
monkeypatch.setattr(
|
||||
tcc, "tcc_anchor_state", lambda *a, **k: ("active", "/x/.venv/bin/python")
|
||||
)
|
||||
doctor.check_macos_tcc_anchor(should_fix=False)
|
||||
out = capsys.readouterr().out
|
||||
assert "macOS TCC anchor active" in out
|
||||
|
||||
def test_skip_is_silent_on_non_macos(self, monkeypatch, capsys):
|
||||
monkeypatch.setattr(
|
||||
tcc, "tcc_anchor_state", lambda *a, **k: ("skip", "not macOS")
|
||||
)
|
||||
doctor.check_macos_tcc_anchor(should_fix=False)
|
||||
assert capsys.readouterr().out == ""
|
||||
|
||||
def test_never_crashes_on_exception(self, monkeypatch, capsys):
|
||||
def boom(*a, **k):
|
||||
raise RuntimeError("tccd down")
|
||||
|
||||
monkeypatch.setattr(tcc, "tcc_anchor_state", boom)
|
||||
doctor.check_macos_tcc_anchor(should_fix=False)
|
||||
out = capsys.readouterr().out
|
||||
assert "macOS TCC anchor check failed" in out
|
||||
|
||||
|
||||
@pytest.mark.macos_only
|
||||
class TestAnchoredAliasesBootE2E:
|
||||
"""Real-interpreter proof that the re-land stays bootable (#95596).
|
||||
|
||||
Copies the running interpreter's real base binary into a fake uv-store
|
||||
layout (stdlib via a ``lib`` symlink) and actually executes every
|
||||
entry point after anchoring. ``macos_only`` so Linux CI cannot
|
||||
greenwash this with a fixture.
|
||||
"""
|
||||
|
||||
def test_python_entry_points_boot_after_anchor(self, tmp_path):
|
||||
minor = f"python3.{sys.version_info.minor}"
|
||||
base = Path(sys.base_prefix)
|
||||
real_py = base / "bin" / minor
|
||||
if not real_py.is_file() or real_py.is_symlink():
|
||||
resolved = real_py.resolve() if real_py.exists() else None
|
||||
if resolved is None or not resolved.is_file():
|
||||
pytest.skip(f"no real base interpreter binary at {real_py}")
|
||||
real_py = resolved
|
||||
if not (base / "lib" / minor / "os.py").is_file():
|
||||
pytest.skip("base stdlib not in the expected lib layout")
|
||||
|
||||
store = (
|
||||
tmp_path
|
||||
/ "uv"
|
||||
/ "python"
|
||||
/ f"cpython-{platform.python_version()}-macos-aarch64-none"
|
||||
)
|
||||
store_bin = store / "bin"
|
||||
store_bin.mkdir(parents=True)
|
||||
shutil.copy2(real_py, store_bin / minor)
|
||||
os.symlink(base / "lib", store / "lib")
|
||||
|
||||
root = tmp_path / "checkout"
|
||||
venv = root / ".venv"
|
||||
venv_bin = venv / "bin"
|
||||
venv_bin.mkdir(parents=True)
|
||||
(venv / "lib" / minor / "site-packages").mkdir(parents=True)
|
||||
(venv / "pyvenv.cfg").write_text(
|
||||
f"home = {store_bin}\nversion = {platform.python_version()}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
os.symlink(store_bin / minor, venv_bin / "python")
|
||||
os.symlink("python", venv_bin / "python3")
|
||||
os.symlink("python", venv_bin / minor)
|
||||
|
||||
anchored = tcc.ensure_tcc_anchor(root)
|
||||
assert anchored is not None
|
||||
|
||||
for name in ("python", "python3", minor):
|
||||
probe = subprocess.run(
|
||||
[str(venv_bin / name), "-c",
|
||||
"import encodings, sys; print(sys.prefix)"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
assert probe.returncode == 0, (
|
||||
f"{name} failed to boot after anchoring:\n{probe.stderr}"
|
||||
)
|
||||
assert str(venv) in probe.stdout
|
||||
Reference in New Issue
Block a user