Files
agentic-mobile-control/tests/test_describe_screen.py
2026-07-16 08:13:04 +08:00

120 lines
3.1 KiB
Python

from __future__ import annotations
from pathlib import Path
from core.errors import DriverError
from core.models import ActiveApp, Bounds, Scene
from device.manager import DeviceManager
from driver.base import Driver
from perception.ocr import OCRBox
from tools.describe_screen import describe_screen
from tests.fakes import FakeDriver
PNG_10X20 = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x0a\x00\x00\x00\x14"
class FakeOCREngine:
def __init__(self, boxes: list[OCRBox]) -> None:
self._boxes = boxes
def extract(self, image: bytes | str | Path) -> list[OCRBox]:
return self._boxes
class TreeFailingDriver(Driver):
def connect(self) -> None:
return None
def disconnect(self) -> None:
return None
def screenshot(self) -> bytes:
return PNG_10X20
def tap(self, x: float, y: float) -> None:
return None
def long_press(self, x: float, y: float, duration_ms: int = 1200) -> None:
return None
def swipe(
self,
start_x: float,
start_y: float,
end_x: float,
end_y: float,
duration_ms: int = 500,
) -> None:
return None
def swipe_path(
self, waypoints: list[tuple[float, float]], duration_ms: int
) -> None:
return None
def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None:
return None
def input(self, text: str) -> None:
return None
def launch(self, app_id: str) -> None:
return None
def terminate(self, app_id: str) -> None:
return None
def tree(self):
raise DriverError("ui tree retrieval failed")
def home(self) -> None:
return None
def lock(self) -> None:
return None
def unlock(self) -> None:
return None
def test_describe_screen_degrades_to_ocr_only_when_ui_tree_unavailable() -> None:
manager = DeviceManager()
manager.register_device("phone-1", lambda: TreeFailingDriver())
manager.connect("phone-1")
fake_box = OCRBox(text="Search", bounds=Bounds(1, 2, 4, 4), confidence=0.9)
fake_engine = FakeOCREngine([fake_box])
scene = describe_screen(
"phone-1",
manager=manager,
ocr_engine=fake_engine, # type: ignore[arg-type]
)
assert [element.text for element in scene.elements] == ["Search"]
assert all(element.source == "ocr" for element in scene.elements)
def test_describe_screen_includes_live_active_app_metadata() -> None:
active_app = ActiveApp(
platform="android",
package="com.example.mobile",
activity=".MainActivity",
)
manager = DeviceManager()
manager.register_device("phone-1", lambda: FakeDriver(active_app=active_app))
manager.connect("phone-1")
scene = describe_screen(
"phone-1",
manager=manager,
ocr_engine=FakeOCREngine([]), # type: ignore[arg-type]
)
assert scene.active_app == active_app
assert scene.to_dict()["app"] == {
"platform": "android",
"package": "com.example.mobile",
"activity": ".MainActivity",
}
assert Scene.from_dict(scene.to_dict()).active_app == active_app