From dda70940c0d2d50a1a8d0ae77c09dc2a987ecac3 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 15 Jul 2026 19:38:07 +0800 Subject: [PATCH] feat(tools): humanize swipe into curved W3C path when enabled --- tests/test_swipe_humanize.py | 41 ++++++++++++++++++++++++++++++++++++ tools/swipe.py | 32 ++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/test_swipe_humanize.py diff --git a/tests/test_swipe_humanize.py b/tests/test_swipe_humanize.py new file mode 100644 index 0000000..c52eeed --- /dev/null +++ b/tests/test_swipe_humanize.py @@ -0,0 +1,41 @@ +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_swipe_disabled_uses_two_point_swipe(monkeypatch): + monkeypatch.setenv("APEX_HUMANIZE_ENABLED", "false") + from tools.swipe import swipe + + driver = FakeDriver() + swipe(0, 0, 100, 0, duration_ms=500, manager=_connected(driver)) + assert driver.calls[-1] == ("swipe", (0, 0, 100, 0, 500)) + + +def test_swipe_enabled_uses_curved_swipe_path(monkeypatch): + import random + + monkeypatch.setenv("APEX_HUMANIZE_ENABLED", "true") + from tools.humanize import set_rng + from tools.swipe import swipe + + set_rng(random.Random(21)) + try: + driver = FakeDriver() + swipe(0, 0, 100, 0, duration_ms=500, manager=_connected(driver)) + finally: + set_rng(None) + name, args = driver.calls[-1] + assert name == "swipe_path" + waypoints, _dms = args + assert waypoints[0] == (0, 0) + assert waypoints[-1] == (100, 0) + assert len(waypoints) > 2 # curved: more than just endpoints \ No newline at end of file diff --git a/tools/swipe.py b/tools/swipe.py index 180fd8d..8658fec 100644 --- a/tools/swipe.py +++ b/tools/swipe.py @@ -2,6 +2,12 @@ from __future__ import annotations from device.manager import DeviceManager from tools._device import get_driver +from tools.humanize import ( + get_rng, + jitter_duration, + load_humanize_config, + swipe_waypoints, +) def swipe( @@ -14,12 +20,26 @@ def swipe( device_id: str | None = None, manager: DeviceManager | None = None, ) -> dict[str, object]: + cfg = load_humanize_config() + if cfg.enabled: + rng = get_rng() + dms = jitter_duration(duration_ms, spread=cfg.duration_spread, rng=rng) + waypoints = swipe_waypoints( + (start_x, start_y), + (end_x, end_y), + curvature=cfg.swipe_curvature, + n=cfg.swipe_waypoints, + rng=rng, + ) + get_driver(device_id, manager=manager).swipe_path(waypoints, dms) + return { + "ok": True, + "action": "swipe", + "waypoints": waypoints, + "duration_ms": dms, + } get_driver(device_id, manager=manager).swipe( - start_x, - start_y, - end_x, - end_y, - duration_ms, + start_x, start_y, end_x, end_y, duration_ms ) return { "ok": True, @@ -27,4 +47,4 @@ def swipe( "start": {"x": start_x, "y": start_y}, "end": {"x": end_x, "y": end_y}, "duration_ms": duration_ms, - } + } \ No newline at end of file