120 lines
3.1 KiB
Python
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
|