test: mirror the source tree for 27 misfiled root tests; one PM home fixture

Root-level tests/ is for root-level modules; 19 files testing scripts/, 3 testing
pm/ and 5 testing hermes_cli/ move to the mirrored directory (workflow file lists
and cross-imports updated; path arithmetic bumped one level).

tests/pm gains a conftest with the isolated_machine_home fixture that seven
modules had copy-pasted verbatim.
This commit is contained in:
ethernet
2026-09-18 20:13:26 -04:00
parent f6a30447de
commit cb7c688171
42 changed files with 57 additions and 65 deletions
+1 -1
View File
@@ -123,7 +123,7 @@ jobs:
shell: bash
env:
HERMES_TEST_FILE_RETRIES: '0'
run: bash scripts/run_tests.sh tests/test_fresh_source_install.py -q -j 1
run: bash scripts/run_tests.sh tests/scripts/test_fresh_source_install.py -q -j 1
windows:
name: install.ps1 protocol surface
+1 -1
View File
@@ -56,4 +56,4 @@ jobs:
shell: bash
env:
HERMES_TEST_FILE_RETRIES: '0'
run: bash scripts/run_tests.sh tests/test_source_launcher_stages.py -k powershell_stage -q -j 1
run: bash scripts/run_tests.sh tests/pm/test_source_launcher_stages.py -k powershell_stage -q -j 1
+6 -6
View File
@@ -50,13 +50,13 @@ jobs:
- name: Run the native linker and wheel contracts
run: |
bash scripts/run_tests.sh -j 2 \
tests/test_termux_python_linkage.py tests/test_termux_retag_wheel.py \
tests/test_termux_build_wheels.py \
tests/test_termux_wheelhouse_cache.py tests/test_termux_runtime_libs.py \
tests/scripts/test_termux_python_linkage.py tests/scripts/test_termux_retag_wheel.py \
tests/scripts/test_termux_build_wheels.py \
tests/scripts/test_termux_wheelhouse_cache.py tests/scripts/test_termux_runtime_libs.py \
tests/scripts/test_archive_inputs.py \
tests/test_termux_launchers.py tests/test_pm_bionic.py \
tests/pm/test_stage_only.py tests/pm/test_deb_safety.py tests/test_stage_apt_repo.py \
tests/test_run_tests_shell.py
tests/scripts/test_termux_launchers.py tests/pm/test_pm_bionic.py \
tests/pm/test_stage_only.py tests/pm/test_deb_safety.py tests/scripts/test_stage_apt_repo.py \
tests/scripts/test_run_tests_shell.py
tui:
name: Prebuilt TUI checks
+1 -1
View File
@@ -8,7 +8,7 @@ rely on that early (no jq guarantee; python is what uv installs on Windows).
Instead, this script derives a plain-data fragment from pm/lock.json — the
SAME authority the pm package manager uses for every managed tool — and
splices it between markers in each installer. The bytes are stored, the
truth is derived, and the drift test (tests/test_bootstrap_pins_fragment.py)
truth is derived, and the drift test (tests/scripts/test_bootstrap_pins_fragment.py)
fails when they disagree.
Run after bumping a bootstrapped tool in pm/lock.json:
+2 -2
View File
@@ -2,7 +2,7 @@
"""Derive a Debian package version (or channel) from a hermes-agent release tag.
Pure function; imported by scripts/termux/build_deb.sh and unit-tested by
tests/test_termux_deb_version.py (Task 4 of .hermes/plans/2026-08-31_termux-deb.md).
tests/scripts/test_termux_deb_version.py (Task 4 of .hermes/plans/2026-08-31_termux-deb.md).
Mapping:
v1.2.3 -> 1.2.3-1
@@ -26,7 +26,7 @@ import sys
# The canary timestamp shape MUST match the canonical _CANARY_TAG_RE in
# hermes_cli/update_channel.py (exactly 8 or 14 digits, 20-prefixed) and
# channel_for_tag in scripts/releases/r2.py. Cross-referenced by
# tests/test_termux_deb_version.py::test_canary_tag_shape_matches_canonical.
# tests/scripts/test_termux_deb_version.py::test_canary_tag_shape_matches_canonical.
_TAG_RE = re.compile(
r"^v(?P<major>0|[1-9]\d*)\.(?P<minor>\d+)\.(?P<patch>\d+)"
r"(?:-canary\.(?P<ts>20\d{6}(?:\d{6})?))?$"
@@ -39,7 +39,7 @@ print(json.dumps(result))
"""
result = subprocess.run(
[sys.executable, "-c", script], env=env,
cwd=Path(__file__).resolve().parents[1],
cwd=Path(__file__).resolve().parents[2],
text=True, capture_output=True, check=True,
)
base = tmp_path / "AppData" / "Local" / "hermes" if sys.platform == "win32" else tmp_path / ".hermes"
@@ -168,7 +168,7 @@ def test_retired_probes_and_refreshes_do_no_work(no_external_work, tmp_path):
@pytest.fixture
def old_updater():
path = Path(__file__).parent / "compat" / "old_updater_dependencies.py"
path = Path(__file__).parents[1] / "compat" / "old_updater_dependencies.py"
spec = importlib.util.spec_from_file_location("old_updater_dependencies", path)
assert spec is not None and spec.loader is not None
old = importlib.util.module_from_spec(spec)
@@ -107,7 +107,7 @@ plugins_transaction.recover_plugin_publication(
)
"""
return subprocess.run(
[sys.executable, "-I", "-S", "-c", script, str(Path(__file__).resolve().parents[1]), str(project), str(journal)],
[sys.executable, "-I", "-S", "-c", script, str(Path(__file__).resolve().parents[2]), str(project), str(journal)],
input=json.dumps(row), text=True, capture_output=True, check=False,
env={**os.environ, "HERMES_HOME": str(Path(row["metadata"]).parent.parent)},
)
+15
View File
@@ -0,0 +1,15 @@
"""Shared fixtures for the PM suite."""
from pathlib import Path
import pytest
@pytest.fixture
def isolated_machine_home(tmp_path, monkeypatch):
"""Machine-scoped PM state (store, caches, profiles root) lands under tmp_path.
Opt in per module with ``pytestmark = pytest.mark.usefixtures("isolated_machine_home")``.
Does not cross a subprocess boundary — tests that spawn set HOME/USERPROFILE themselves.
"""
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
+1 -1
View File
@@ -23,7 +23,7 @@ def test_cold_consumers_recover_after_upstream_removal(tmp_path, dl_server, monk
archive.writestr("tool.txt", b"pinned and preserved")
body = data.getvalue()
if mode == "library":
from tests.test_termux_runtime_libs import _build_deb
from tests.scripts.test_termux_runtime_libs import _build_deb
deb = tmp_path / "test.deb"
_build_deb(deb, "libmirror.so", b"pinned and preserved")
body = deb.read_bytes()
+1 -5
View File
@@ -8,7 +8,6 @@ import json
from functools import partial
import threading
import zipfile
from pathlib import Path
import pytest
@@ -34,10 +33,7 @@ def archive(files: dict[str, bytes]) -> bytes:
return output.getvalue()
@pytest.fixture(autouse=True)
def isolate_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
@pytest.fixture(params=["install", "stage"])
@@ -13,7 +13,7 @@ from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[1]
REPO_ROOT = Path(__file__).resolve().parents[2]
@pytest.fixture(scope="module")
+1 -4
View File
@@ -15,10 +15,7 @@ from pm.lock import Facts
from pm.runtime import runtime_environment
from tests.pm._fixtures import _wheel
@pytest.fixture(autouse=True)
def isolated_machine_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
@@ -11,7 +11,7 @@ from pm.environments import install_state_dir, site_packages
from pm.lock import Lockfile
from tests.hermes_cli.test_source_launcher_publication import fixture_tree
ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
@pytest.mark.platforms("windows")
+1 -5
View File
@@ -2,16 +2,12 @@
from __future__ import annotations
from pathlib import Path
import pytest
import pm.packages as pkgs
@pytest.fixture(autouse=True)
def isolated_machine_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
+1 -4
View File
@@ -16,10 +16,7 @@ from pm.package import InstallError
from pm.packages import Python, Uv
from pm.store import current_target
@pytest.fixture(autouse=True)
def isolated_machine_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
+1 -4
View File
@@ -10,10 +10,7 @@ import sys
import pytest
@pytest.fixture(autouse=True)
def isolated_machine_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
def test_ambient_uv_config_does_not_affect_pm_venv_sync(tmp_path, monkeypatch):
+1 -4
View File
@@ -22,10 +22,7 @@ from pm.environment import managed_environment
from tests.pm.test_environment_build import locked_project # noqa: F401
@pytest.fixture(autouse=True)
def isolated_machine_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
@pytest.fixture
+1 -4
View File
@@ -12,10 +12,7 @@ from tests.pm import _fixtures
from pm.environment import managed_environment
@pytest.fixture(autouse=True)
def isolated_machine_home(tmp_path, monkeypatch):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
pytestmark = pytest.mark.usefixtures("isolated_machine_home")
def _buildable_source(plugin):
+1 -1
View File
@@ -13,7 +13,7 @@ from pm.downloader import HashError
from scripts.ci import archive_inputs as inputs
from scripts.releases import r2
from tests.scripts.test_release_r2 import r2_server # noqa: F401
from tests.test_termux_runtime_libs import _Server, _build_deb
from tests.scripts.test_termux_runtime_libs import _Server, _build_deb
def write_pins(repo, packages, libs=None):
@@ -12,7 +12,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import pytest
ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
@pytest.fixture
@@ -5,7 +5,7 @@ from pathlib import Path
def test_fragments_match_the_pin_table():
root = Path(__file__).resolve().parents[1]
root = Path(__file__).resolve().parents[2]
result = subprocess.run(
[sys.executable, str(root / "scripts/gen-bootstrap-pins.py"), "--check"],
cwd=root, capture_output=True, text=True, timeout=60,
@@ -14,7 +14,7 @@ import pytest
from tests.installation_launcher_fixture import publish_fixture_launcher
SCRIPTS = Path(__file__).resolve().parents[1] / "scripts" / "desktop-update"
SCRIPTS = Path(__file__).resolve().parents[2] / "scripts" / "desktop-update"
FAKE_CLI = """
import json
import os
@@ -23,7 +23,7 @@ import sys
def main():
if '--version' in sys.argv:
print('Install directory: ' + os.environ.get('HANDOFF_FOREIGN', str(Path(__file__).resolve().parents[1]))); return 0
print('Install directory: ' + os.environ.get('HANDOFF_FOREIGN', str(Path(__file__).resolve().parents[2]))); return 0
if '--help' in sys.argv:
print('update options')
sys.exit(0)
@@ -20,7 +20,7 @@ import pytest
from pm.store import current_target
from tests.pm._fixtures import _wheel, served as served
ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
@pytest.mark.platforms("linux")
@@ -6,7 +6,7 @@ from pathlib import Path
def test_icon_group_is_separate_from_runtime_extras():
root = Path(__file__).resolve().parents[1]
root = Path(__file__).resolve().parents[2]
uv = shutil.which("uv")
assert uv, "uv is required for the dependency-selection contract"
@@ -35,7 +35,7 @@ from tests.installation_launcher_fixture import publish_fixture_launcher
pytestmark = pytest.mark.platforms("windows")
REPO_ROOT = Path(__file__).resolve().parents[1]
REPO_ROOT = Path(__file__).resolve().parents[2]
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
_FAKE_PY = r'''
@@ -7,7 +7,7 @@ import subprocess
import pytest
from tests.installation_launcher_fixture import publish_fixture_launcher
ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
@pytest.mark.platforms('posix')
@pytest.mark.parametrize('stage, expected', [('setup', ['setup']), ('gateway', ['gateway', 'install']), ('desktop', ['desktop', '--build-only'])])
@@ -14,7 +14,7 @@ import sys
import pytest
ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
pytestmark = pytest.mark.platforms("posix")
@@ -7,7 +7,7 @@ import subprocess
import pytest
ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
SCRIPT = ROOT / "scripts" / "install.sh"
@@ -21,7 +21,7 @@ from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parent.parent
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
# Every PowerShell script a user or CI machine actually runs. Discovered,
# not hardcoded: a new script in scripts/ gets gated automatically.
@@ -18,7 +18,7 @@ def test_shell_runner_executes_tests_and_propagates_failure(tmp_path):
" assert False, 'runner failure propagation canary'\n",
encoding="utf-8",
)
root = Path(__file__).resolve().parents[1]
root = Path(__file__).resolve().parents[2]
result = subprocess.run(
["bash", str(root / "scripts/run_tests.sh"), "-j", "1", str(case)],
cwd=tmp_path, capture_output=True, text=True, timeout=180,
@@ -16,7 +16,7 @@ from pathlib import Path
import pytest
from tests.termux_fixtures import build_deb
REPO_ROOT = Path(__file__).resolve().parents[1]
REPO_ROOT = Path(__file__).resolve().parents[2]
SCRIPTS = REPO_ROOT / "scripts" / "termux"
sys.path.insert(0, str(SCRIPTS))
@@ -7,7 +7,7 @@ from pathlib import Path
import pytest
from scripts.termux.deb_version import channel_for_tag, deb_version_for_tag
SCRIPT = Path(__file__).resolve().parents[1] / "scripts/termux/deb_version.py"
SCRIPT = Path(__file__).resolve().parents[2] / "scripts/termux/deb_version.py"
@pytest.mark.parametrize("bom", ["", "\ufeff"])
@@ -17,7 +17,7 @@ from pathlib import Path
import pytest
from tests.termux_fixtures import write_wheel, verify_record
SCRIPTS_DIR = Path(__file__).resolve().parent.parent / "scripts" / "termux"
SCRIPTS_DIR = Path(__file__).resolve().parent.parent.parent / "scripts" / "termux"
sys.path.insert(0, str(SCRIPTS_DIR))
import retag_wheel # noqa: E402
@@ -17,7 +17,7 @@ from tests.termux_fixtures import build_deb
import sys
REPO_ROOT = Path(__file__).resolve().parent.parent
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(REPO_ROOT))
sys.path.insert(0, str(REPO_ROOT / "scripts" / "termux"))
@@ -65,7 +65,7 @@ def test_cache_rejects_incomplete_or_inconsistent_manifests(tmp_path, damage):
def test_cache_cli_keeps_commit_and_tag_provenance_distinct(tmp_path):
payload, _ = cache_tree(tmp_path)
repo = Path(__file__).resolve().parents[1]
repo = Path(__file__).resolve().parents[2]
args = [sys.executable, str(repo / 'scripts/termux/wheelhouse_cache.py'), 'write',
'--payload', str(payload), '--repo', str(repo), '--builder', 'fixture-image',
'--platform-tag', 'android_24_arm64_v8a', '--python-abi', 'cp314']