from __future__ import annotations
|
|
from pathlib import Path
|
import unittest
|
|
from hibor_fast_collection.adb import AdbClient, UiNode
|
from hibor_fast_collection.models import ContractError, ErrorCode
|
from hibor_fast_collection.process import ProcessResult
|
from hibor_fast_collection.ui import HiborUiDriver, UiConfig
|
|
|
def result(exit_code: int = 0, *, stdout: bytes = b"", stderr: bytes = b"") -> ProcessResult:
|
return ProcessResult(
|
(), True, 1, exit_code, False, False, True, False,
|
stdout, stderr, False, False, None, None, 1, None,
|
)
|
|
|
class ScriptedSupervisor:
|
def __init__(self, responses: list[ProcessResult]):
|
self.responses = list(responses)
|
self.calls: list[tuple[str, ...]] = []
|
|
def run(self, argv, *, timeout_ms, **kwargs):
|
self.calls.append(tuple(argv))
|
if not self.responses:
|
raise AssertionError(f"unexpected command: {argv!r}")
|
return self.responses.pop(0)
|
|
|
def client(responses: list[ProcessResult]) -> tuple[AdbClient, ScriptedSupervisor]:
|
supervisor = ScriptedSupervisor(responses)
|
return AdbClient(Path("adb.exe"), supervisor, serial="emulator-test"), supervisor
|
|
|
class AdbInputCompatibilityTests(unittest.TestCase):
|
def test_safe_ascii_uses_generic_input_without_clipboard_or_paste(self):
|
adb, supervisor = client([result(), result(), result()])
|
|
adb.input_text("603179")
|
|
commands = [call[3:] for call in supervisor.calls]
|
self.assertEqual(commands[-1], ("shell", "input", "text", "603179"))
|
self.assertFalse(any("memu_clip_board_base64" in call for call in supervisor.calls))
|
self.assertFalse(any(call[-2:] == ("keyevent", "279") for call in supervisor.calls))
|
|
def test_memu_service_failure_never_pastes(self):
|
adb, supervisor = client([
|
result(stdout=b"com.microvirt.memuime/.MemuIME\n"),
|
result(), result(), result(exit_code=1, stderr=b"service unavailable"),
|
])
|
|
with self.assertRaisesRegex(ContractError, "Memu clipboard failed"):
|
adb.input_text("\u56fd\u74f7\u6750\u6599")
|
|
self.assertTrue(any("memu_clip_board_base64" in call for call in supervisor.calls))
|
self.assertFalse(any(call[-2:] == ("keyevent", "279") for call in supervisor.calls))
|
|
def test_monkey_missing_falls_back_to_exported_welcome_activity(self):
|
adb, supervisor = client([
|
result(exit_code=127, stderr=b"/system/bin/sh: monkey: not found"),
|
result(stdout=b"Starting: Intent { cmp=cn.com.hibor/.WelcomeActivity }\nStatus: ok\n"),
|
])
|
|
adb.start_package()
|
|
self.assertEqual(
|
supervisor.calls[-1][3:],
|
("shell", "am", "start", "-W", "-n", "cn.com.hibor/.WelcomeActivity"),
|
)
|
|
def test_unsupported_non_ascii_or_sensitive_input_fails_before_edit_or_paste(self):
|
for value in ("\u4e09\u73af\u96c6\u56e2", "603179;paste"):
|
with self.subTest(value=value):
|
adb, supervisor = client([result(stdout=b"com.android.inputmethod.pinyin/.InputService\n")])
|
with self.assertRaisesRegex(ContractError, "MemuIME unsupported") as raised:
|
adb.input_text(value)
|
self.assertEqual(raised.exception.code, ErrorCode.UI_ANCHOR_DRIFT)
|
self.assertEqual(len(supervisor.calls), 1)
|
self.assertEqual(supervisor.calls[0][3:], ("shell", "ime", "list", "-s"))
|
|
def test_ui_requires_exact_character_for_character_readback(self):
|
field = UiNode("", "search-input", "android.widget.EditText", (0, 0, 10, 10), True)
|
wrong = UiNode("603178", "search-input", "android.widget.EditText", (0, 0, 10, 10), True)
|
|
class UiAdb:
|
def __init__(self):
|
self.snapshots = [(field,), (wrong,)]
|
|
def ui_nodes(self):
|
return self.snapshots.pop(0)
|
|
def tap(self, *args):
|
pass
|
|
def input_text(self, value):
|
self.value = value
|
|
config = UiConfig((), "search-input", ("\u641c\u7d22",), "", (), "", "", "", "", (0, 0, 0, 0, 1))
|
driver = HiborUiDriver(UiAdb(), config)
|
|
with self.assertRaisesRegex(ContractError, "text mismatch"):
|
driver.search("603179")
|
|
|
if __name__ == "__main__":
|
unittest.main()
|