Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
"""LIVE Windows E2E for the Desktop-lifecycle cold-start skip (#76129/#76745).
|
||||
|
||||
Runs ONLY on a real Windows host (the on-demand ``windows-venv-e2e.yml``
|
||||
lane). Exercises the REAL ownership predicate against REAL processes:
|
||||
|
||||
1. A real child process self-registers in the REAL spawn ledger as a
|
||||
``serve`` purpose with THIS process as its live spawner — ownership
|
||||
must hold, and ``_pause_windows_gateways_for_update`` must return None
|
||||
(no cold-start plan) even with an autostart artifact present.
|
||||
2. Kill the child (dead serve) — ownership drops, the pause plan carries
|
||||
``cold_start_if_installed`` again.
|
||||
3. Venv-holder fallback rung: a real venv-python process with true
|
||||
``serve`` argv is detected by the scan and, having a live parent,
|
||||
confers ownership; the token classifier rejects a ``kanban
|
||||
--preserve-cache`` lookalike (the #90778 class the salvage fixed).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
sys.platform != "win32", reason="live Windows lifecycle E2E"
|
||||
)
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def sleeper():
|
||||
procs: list[subprocess.Popen] = []
|
||||
|
||||
def _spawn(*tail: str) -> subprocess.Popen:
|
||||
p = subprocess.Popen(
|
||||
[sys.executable, "-c", "import time; time.sleep(120)", *tail],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
procs.append(p)
|
||||
time.sleep(0.5)
|
||||
assert p.poll() is None
|
||||
return p
|
||||
|
||||
yield _spawn
|
||||
for p in procs:
|
||||
if p.poll() is None:
|
||||
p.kill()
|
||||
p.wait()
|
||||
|
||||
|
||||
def _write_ledger(entries: list[dict]) -> None:
|
||||
from hermes_cli import process_identity as pid_mod
|
||||
|
||||
path = pid_mod._ledger_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(entries), encoding="utf-8")
|
||||
|
||||
|
||||
def _entry(proc: subprocess.Popen, purpose: str = "serve") -> dict:
|
||||
import psutil
|
||||
|
||||
from hermes_cli import process_identity as pid_mod
|
||||
|
||||
return {
|
||||
"install": pid_mod.install_id(None),
|
||||
"pid": proc.pid,
|
||||
"create_time": psutil.Process(proc.pid).create_time(),
|
||||
"purpose": purpose,
|
||||
"spawner_pid": os.getpid(),
|
||||
"spawner_create": psutil.Process(os.getpid()).create_time(),
|
||||
}
|
||||
|
||||
|
||||
def test_live_supervised_serve_suppresses_cold_start(sleeper, monkeypatch, tmp_path):
|
||||
from hermes_cli import gateway as hermes_gateway
|
||||
from hermes_cli import gateway_windows
|
||||
from hermes_cli import update_cmd
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
|
||||
(tmp_path / ".hermes").mkdir()
|
||||
|
||||
serve = sleeper()
|
||||
_write_ledger([_entry(serve)])
|
||||
|
||||
assert update_cmd._desktop_owns_gateway_lifecycle() is True
|
||||
|
||||
# Autostart artifact present + no gateway running: WITHOUT ownership the
|
||||
# pause phase would plan a cold start; WITH it, no plan.
|
||||
monkeypatch.setattr(hermes_gateway, "find_gateway_pids", lambda **_k: [])
|
||||
monkeypatch.setattr(gateway_windows, "is_installed", lambda: True)
|
||||
assert update_cmd._pause_windows_gateways_for_update() is None
|
||||
|
||||
# Dead serve → ownership drops → plan returns.
|
||||
serve.kill()
|
||||
serve.wait()
|
||||
time.sleep(0.5)
|
||||
assert update_cmd._desktop_owns_gateway_lifecycle() is False
|
||||
token = update_cmd._pause_windows_gateways_for_update()
|
||||
assert token is not None and token.get("cold_start_if_installed") is True
|
||||
|
||||
|
||||
def test_holder_scan_fallback_respects_token_classifier(sleeper, monkeypatch, tmp_path):
|
||||
from hermes_cli import update_cmd
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
|
||||
(tmp_path / ".hermes").mkdir()
|
||||
_write_ledger([]) # force the fallback rung
|
||||
|
||||
# Real process whose argv carries genuine serve shape, visible to psutil.
|
||||
serve_like = sleeper("-m", "hermes_cli.main", "serve")
|
||||
# Lookalike from the #90778 class — must NOT confer ownership.
|
||||
kanban_like = sleeper("-m", "hermes_cli.main", "kanban", "--preserve-cache")
|
||||
|
||||
def fake_holders():
|
||||
import psutil
|
||||
|
||||
out = []
|
||||
for p in (serve_like, kanban_like):
|
||||
proc = psutil.Process(p.pid)
|
||||
out.append((p.pid, proc.name(), " ".join(proc.cmdline())))
|
||||
return out
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.main._detect_venv_python_processes", fake_holders
|
||||
)
|
||||
|
||||
# serve-shaped holder with a live parent (us) → owns
|
||||
assert update_cmd._desktop_owns_gateway_lifecycle() is True
|
||||
|
||||
# Only the kanban lookalike left → classifier rejects → does not own
|
||||
serve_like.kill()
|
||||
serve_like.wait()
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.main._detect_venv_python_processes",
|
||||
lambda: [fake_holders()[1]],
|
||||
)
|
||||
assert update_cmd._desktop_owns_gateway_lifecycle() is False
|
||||
Reference in New Issue
Block a user