From 85f0d6e188c9b95e9d31d8afa15db8987a1e6f98 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 15 Jul 2026 19:29:57 +0800 Subject: [PATCH] feat(tools): humanize tap coordinates; default off in tests --- tests/conftest.py | 11 +++++++++++ tests/test_tap_humanize.py | 38 ++++++++++++++++++++++++++++++++++++++ tools/humanize.py | 5 ++++- tools/tap.py | 9 +++++++-- 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 tests/conftest.py create mode 100644 tests/test_tap_humanize.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..2b91784 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,11 @@ +import pytest + + +@pytest.fixture(autouse=True) +def _humanize_disabled_by_default_in_tests(monkeypatch): + """Humanize defaults ON in production; tests default it OFF so existing + exact-coordinate assertions stay deterministic. Tests that want to + exercise humanize call ``monkeypatch.setenv("APEX_HUMANIZE_ENABLED", "true")`` + in their own body, which overrides this fixture (test body runs after + fixture setup).""" + monkeypatch.setenv("APEX_HUMANIZE_ENABLED", "false") diff --git a/tests/test_tap_humanize.py b/tests/test_tap_humanize.py new file mode 100644 index 0000000..9f4bfef --- /dev/null +++ b/tests/test_tap_humanize.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from device.manager import DeviceManager +from tests.fakes import FakeDriver + + +def _connected(driver: FakeDriver) -> DeviceManager: + manager = DeviceManager() + manager.register_device("phone", lambda: driver) + manager.connect("phone", max_retries=1) + return manager + + +def test_tap_default_off_in_tests_passes_exact(monkeypatch): + # Autouse conftest fixture sets APEX_HUMANIZE_ENABLED=false; reaffirm here. + monkeypatch.setenv("APEX_HUMANIZE_ENABLED", "false") + from tools.tap import tap + + driver = FakeDriver() + tap(10, 20, manager=_connected(driver)) + assert driver.calls[-1] == ("tap", (10, 20)) + + +def test_tap_enabled_jitters_within_radius(monkeypatch): + import random + + monkeypatch.setenv("APEX_HUMANIZE_ENABLED", "true") + from tools.humanize import set_rng + from tools.tap import tap + + set_rng(random.Random(11)) + try: + driver = FakeDriver() + tap(10, 20, manager=_connected(driver)) + finally: + set_rng(None) + px, py = driver.calls[-1][1] + assert abs(px - 10) <= 5.0 and abs(py - 20) <= 5.0 diff --git a/tools/humanize.py b/tools/humanize.py index 298db03..ff032dc 100644 --- a/tools/humanize.py +++ b/tools/humanize.py @@ -28,7 +28,10 @@ class HumanizeConfig: def load_humanize_config(env: Mapping[str, str] | None = None) -> HumanizeConfig: - values = env or os.environ + if env is None: + values: Mapping[str, str] = os.environ + else: + values = env return HumanizeConfig( enabled=_parse_bool(values.get(ENABLED_ENV), default=True), tap_radius_px=_parse_float(values.get(TAP_RADIUS_ENV), DEFAULT_TAP_RADIUS_PX), diff --git a/tools/tap.py b/tools/tap.py index c8e0a98..dcea22a 100644 --- a/tools/tap.py +++ b/tools/tap.py @@ -2,6 +2,7 @@ from __future__ import annotations from device.manager import DeviceManager from tools._device import get_driver +from tools.humanize import get_rng, jitter_point, load_humanize_config def tap( @@ -11,5 +12,9 @@ def tap( device_id: str | None = None, manager: DeviceManager | None = None, ) -> dict[str, object]: - get_driver(device_id, manager=manager).tap(x, y) - return {"ok": True, "action": "tap", "x": x, "y": y} + cfg = load_humanize_config() + px, py = x, y + if cfg.enabled: + px, py = jitter_point(x, y, radius=cfg.tap_radius_px, rng=get_rng()) + get_driver(device_id, manager=manager).tap(px, py) + return {"ok": True, "action": "tap", "x": px, "y": py}