mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 18:47:04 +01:00
Several sessions can now share one phone: a lease covers one package (the real screen stays the default scope, so existing callers keep their meaning), and only phone-wide actions take `--whole-phone`, which is capped at 300 s and runs its `--cleanup` commands exactly once on release or, via a detached reaper, on expiry. The serial is always resolved, never the "default" placeholder that let android_ui and phone_deploy lease different keys for the same phone (2026-09-25). Adds status --json, holds and whoami for phone_vd.sh and the raw-adb hook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Ajzz98xksbfak8CaunZWC
118 lines
4.5 KiB
Python
118 lines
4.5 KiB
Python
"""The CLI wrapper: exit codes and the one-line outputs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
import runpy
|
|
import sys
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from python_pkg.phone_lease import __main__ as cli
|
|
from python_pkg.phone_lease import _cleanup
|
|
from python_pkg.phone_lease import lease as lease_mod
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def lease_dir(tmp_path: Path) -> Iterator[None]:
|
|
with patch.object(lease_mod, "LEASE_DIR", tmp_path / "leases"):
|
|
yield
|
|
|
|
|
|
def test_acquire_then_status_then_release(capsys: pytest.CaptureFixture[str]) -> None:
|
|
assert cli.main(["acquire", "PIXEL", "--note", "deploy"]) == 0
|
|
assert "held by" in capsys.readouterr().out
|
|
assert cli.main(["status", "PIXEL"]) == 0
|
|
assert "(deploy)" in capsys.readouterr().out
|
|
assert cli.main(["release", "PIXEL"]) == 0
|
|
assert capsys.readouterr().out == "released\n"
|
|
assert cli.main(["status", "PIXEL"]) == 0
|
|
assert capsys.readouterr().out == "free\n"
|
|
|
|
|
|
def test_release_without_holding(capsys: pytest.CaptureFixture[str]) -> None:
|
|
assert cli.main(["release", "PIXEL"]) == 1
|
|
assert "not held by" in capsys.readouterr().out
|
|
|
|
|
|
def test_foreign_holder_exits_3(capsys: pytest.CaptureFixture[str]) -> None:
|
|
lease_mod.acquire(
|
|
"PIXEL", "manga", owner="someone-else", terms=lease_mod.Terms(ttl=1000)
|
|
)
|
|
assert cli.main(["acquire", "PIXEL", "--wait", "0"]) == 3
|
|
assert "held by someone-else (manga)" in capsys.readouterr().err
|
|
|
|
|
|
def test_module_entry_point() -> None:
|
|
# runpy warns if __main__ is already imported (this file imports it), so
|
|
# drop it first; the test then runs the real module top to bottom.
|
|
sys.modules.pop("python_pkg.phone_lease.__main__", None)
|
|
with (
|
|
patch("sys.argv", ["phone-lease", "status", "PIXEL"]),
|
|
pytest.raises(SystemExit) as excinfo,
|
|
):
|
|
runpy.run_module("python_pkg.phone_lease", run_name="__main__")
|
|
assert excinfo.value.code == 0
|
|
|
|
|
|
def test_package_scopes_coexist_and_holds(capsys: pytest.CaptureFixture[str]) -> None:
|
|
with patch.dict("os.environ", {"PHONE_LEASE_OWNER": "me"}):
|
|
assert cli.main(["acquire", "PIXEL", "--package", "a.b"]) == 0
|
|
assert "[pkg:a.b]" in capsys.readouterr().out
|
|
assert cli.main(["holds", "PIXEL", "--package", "a.b"]) == 0
|
|
assert cli.main(["holds", "PIXEL", "--package", "c.d"]) == 1
|
|
assert cli.main(["holds", "PIXEL", "--package", "a.b", "--owner", "x"]) == 1
|
|
assert cli.main(["release", "PIXEL", "--package", "a.b"]) == 0
|
|
|
|
|
|
def test_whole_phone_with_cleanup(capsys: pytest.CaptureFixture[str]) -> None:
|
|
with (
|
|
patch.object(_cleanup, "spawn_reaper") as spawn,
|
|
patch.object(_cleanup, "run") as run,
|
|
):
|
|
argv = ["acquire", "PIXEL", "--whole-phone", "--cleanup", "adb x", "--ttl", "9"]
|
|
assert cli.main(argv) == 0
|
|
spawn.assert_called_once()
|
|
assert cli.main(["release", "PIXEL", "--whole-phone"]) == 0
|
|
run.assert_called_once_with(("adb x",))
|
|
assert "[whole-phone]" in capsys.readouterr().out
|
|
|
|
|
|
def test_status_json_and_text(capsys: pytest.CaptureFixture[str]) -> None:
|
|
assert cli.main(["status", "PIXEL", "--json"]) == 0
|
|
assert json.loads(capsys.readouterr().out) == []
|
|
lease_mod.acquire("PIXEL", "n", owner="o")
|
|
assert cli.main(["status", "PIXEL", "--json"]) == 0
|
|
(only,) = json.loads(capsys.readouterr().out)
|
|
assert (only["owner"], only["scope"]) == ("o", "pkg:__screen__")
|
|
|
|
|
|
def test_whoami(capsys: pytest.CaptureFixture[str]) -> None:
|
|
with patch.dict("os.environ", {"PHONE_LEASE_OWNER": "claude:42"}):
|
|
assert cli.main(["whoami"]) == 0
|
|
assert capsys.readouterr().out == "claude:42\n"
|
|
|
|
|
|
def test_reap_subcommand_delegates() -> None:
|
|
with patch.object(cli, "reap") as reap:
|
|
assert cli.main(["reap", "S", "--owner", "o", "--scope", "whole-phone"]) == 0
|
|
reap.assert_called_once_with("S", "o", "whole-phone")
|
|
|
|
|
|
def test_serial_resolved_or_exit_4(capsys: pytest.CaptureFixture[str]) -> None:
|
|
with patch.dict("os.environ", {"ANDROID_SERIAL": "ENVPHONE"}):
|
|
assert cli.main(["status"]) == 0
|
|
assert capsys.readouterr().out == "free\n"
|
|
with (
|
|
patch.dict("os.environ", {"ANDROID_SERIAL": "", "ADB_SERIAL": ""}),
|
|
patch("python_pkg.phone_lease.serial._adb_serial", return_value=""),
|
|
):
|
|
assert cli.main(["status"]) == 4
|
|
assert "no phone serial" in capsys.readouterr().err
|