30da5d0a89
many tests patched sys.platform or a module's _IS_WINDOWS flag, then ran on linux ci. the patch selects the branch under test, but the host does not have the behavior the branch exists for. the test proves the patch, not the platform. some gated assertions never ran on any host. this commit adds three markers: linux_only, macos_only, windows_only. a conftest hook skips a marked test on the other hosts, with a clear reason. no test fakes a host now. two documented fakes remain (android/termux, freebsd) because no ci runner exists for them. each fake site got one of four treatments: - gate it: the real host supplies the platform; mocks cover real dependencies only, never host identity - patch the module's own probe when the subject is the probe's consumer - assert against the real host when the fake stood in for any non-x host - delete the patch when it set the value the host already has bare skipif(sys.platform != ...) guards became markers too. the lane model skips these on linux and never imports them on windows, so they ran on no host. platform parametrize tables are now one marked test per os. running on real hosts found real errors: a chrome-sandbox failure in test_gui_command that main hides, and two windows failures fixed here. the agents.md testing section now documents the policy.
95 lines
3.4 KiB
Python
95 lines
3.4 KiB
Python
"""Legacy pythonw launcher normalization + post-update launcher refresh.
|
|
|
|
Covers the two halves of the "legacy pythonw gateways survive updates
|
|
forever" gap:
|
|
|
|
1. ``gateway_windows._resolve_detached_python`` — normalizes a legacy
|
|
``pythonw.exe`` interpreter (pre-aa2ae36c3f launchers / argv snapshots)
|
|
to the sibling console ``python.exe`` so respawns and regenerated
|
|
launchers use the hidden-console design (#54220/#56747) and don't die
|
|
with ``RuntimeError: sys.stderr is None`` (#71671).
|
|
2. ``hermes_cli.main._refresh_windows_gateway_launchers`` — ``hermes
|
|
update`` regenerates the installed Scheduled Task / Startup launcher
|
|
scripts instead of leaving install-time artifacts stale forever.
|
|
|
|
``_resolve_detached_python`` is a pure path helper and runs on any host.
|
|
``windowless_gateway_restart_spec`` returns its argv unchanged off Windows,
|
|
so the test that exercises the rewrite is ``windows_only`` rather than run
|
|
against a faked ``sys.platform``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
import hermes_cli.gateway_windows as gateway_windows
|
|
import hermes_cli.main as cli_main
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _resolve_detached_python: legacy pythonw normalization
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _make_venv(tmp_path: Path, *, with_console_python: bool) -> tuple[Path, Path]:
|
|
scripts = tmp_path / "venv" / "Scripts"
|
|
scripts.mkdir(parents=True)
|
|
pythonw = scripts / "pythonw.exe"
|
|
pythonw.write_text("", encoding="utf-8")
|
|
python = scripts / "python.exe"
|
|
if with_console_python:
|
|
python.write_text("", encoding="utf-8")
|
|
return pythonw, python
|
|
|
|
|
|
def test_resolve_detached_python_swaps_legacy_pythonw_for_console_sibling(tmp_path):
|
|
pythonw, python = _make_venv(tmp_path, with_console_python=True)
|
|
|
|
exe, venv_dir, extra = gateway_windows._resolve_detached_python(str(pythonw))
|
|
|
|
assert exe == str(python)
|
|
assert venv_dir == tmp_path / "venv"
|
|
assert extra == []
|
|
|
|
|
|
|
|
|
|
@pytest.mark.windows_only
|
|
def test_restart_spec_normalizes_legacy_pythonw_argv(tmp_path):
|
|
"""A pre-rework Scheduled Task argv snapshot (leading pythonw.exe) must be
|
|
respawned through the console python + hidden-console launch, with every
|
|
argument after the interpreter preserved verbatim.
|
|
|
|
``windows_only``: ``windowless_gateway_restart_spec`` returns the argv
|
|
untouched off Windows, so the fake was the only thing making the rewrite
|
|
(and its ``Scripts/``-layout venv derivation) run at all.
|
|
"""
|
|
pythonw, python = _make_venv(tmp_path, with_console_python=True)
|
|
|
|
argv = [str(pythonw), "-m", "hermes_cli.main", "gateway", "run"]
|
|
with mock.patch.object(
|
|
gateway_windows, "_stable_gateway_working_dir", return_value=str(tmp_path)
|
|
), mock.patch("hermes_cli.config.get_hermes_home", return_value=str(tmp_path)):
|
|
new_argv, cwd, env = gateway_windows.windowless_gateway_restart_spec(list(argv))
|
|
|
|
assert new_argv[0] == str(python)
|
|
assert new_argv[1:] == argv[1:]
|
|
assert cwd == str(tmp_path)
|
|
assert env["VIRTUAL_ENV"] == str(tmp_path / "venv")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _refresh_windows_gateway_launchers: hermes update regenerates launchers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|