fix: 记录规格面板误超时诊断 (#115)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""uiautomator2 采购演练 Adapter 测试;不连接真实手机。"""
|
||||
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from src.pdd_device_service import PddDeviceService
|
||||
@@ -27,9 +28,11 @@ class FakeClock:
|
||||
def home_xml() -> str:
|
||||
return """<hierarchy>
|
||||
<node package="com.xunmeng.pinduoduo" bounds="[0,0][1080,2376]">
|
||||
<node clickable="true" enabled="true" visible-to-user="true"
|
||||
<node package="com.xunmeng.pinduoduo" clickable="true"
|
||||
enabled="true" visible-to-user="true"
|
||||
bounds="[500,2100][1080,2328]">
|
||||
<node text="立即购买 ¥5.03" enabled="true" visible-to-user="true"
|
||||
<node package="com.xunmeng.pinduoduo" text="立即购买 ¥5.03"
|
||||
enabled="true" visible-to-user="true"
|
||||
bounds="[600,2150][1000,2280]"/>
|
||||
</node>
|
||||
</node>
|
||||
@@ -73,9 +76,11 @@ class FakeDevice:
|
||||
self.clicks = []
|
||||
self.opened_urls = []
|
||||
self.app_wait_calls = 0
|
||||
self.app_current_calls = 0
|
||||
self.has_opened = False
|
||||
|
||||
def app_current(self):
|
||||
self.app_current_calls += 1
|
||||
return {"package": "com.xunmeng.pinduoduo"}
|
||||
|
||||
def app_start(self, _package):
|
||||
@@ -132,6 +137,24 @@ class StaleGoodsDevice(FakeDevice):
|
||||
return home_xml()
|
||||
|
||||
|
||||
class SlowSettingsFocusDevice(FakeDevice):
|
||||
"""模拟真机:前台查询慢,而且错误报告为设置页。"""
|
||||
|
||||
def __init__(self, clock: FakeClock) -> None:
|
||||
super().__init__()
|
||||
self.clock = clock
|
||||
|
||||
def app_current(self):
|
||||
self.app_current_calls += 1
|
||||
self.clock.sleep(11.0)
|
||||
return {"package": "com.android.settings"}
|
||||
|
||||
|
||||
class PanelDoesNotOpenDevice(FakeDevice):
|
||||
def click(self, x, y):
|
||||
self.clicks.append((x, y))
|
||||
|
||||
|
||||
class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
def _adapter(self, device, calls):
|
||||
def select_color_fn(_device, _xml, target, **_kwargs):
|
||||
@@ -253,6 +276,29 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
self.assertEqual(2, len(device.opened_urls))
|
||||
self.assertEqual([], device.clicks)
|
||||
|
||||
def test_slow_wrong_focus_still_completes_home_reopen_state_machine(self):
|
||||
clock = FakeClock()
|
||||
device = SlowSettingsFocusDevice(clock)
|
||||
device.mode = "special"
|
||||
device.special_xml = (
|
||||
FIXTURES / "pdd_home_page.xml"
|
||||
).read_text(encoding="utf-8")
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
page_timeout=10.0,
|
||||
)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.open_goods(GOODS_URL)
|
||||
|
||||
self.assertEqual("PDD_GOODS_UNAVAILABLE", raised.exception.code)
|
||||
self.assertEqual(1, device.app_current_calls)
|
||||
self.assertEqual(2, len(device.opened_urls))
|
||||
adapter.close()
|
||||
|
||||
def test_stale_goods_page_is_not_accepted_as_target(self):
|
||||
device = StaleGoodsDevice()
|
||||
clock = FakeClock()
|
||||
@@ -271,6 +317,59 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
self.assertEqual([], device.clicks)
|
||||
adapter.close()
|
||||
|
||||
def test_slow_wrong_focus_does_not_consume_panel_timeout(self):
|
||||
clock = FakeClock()
|
||||
device = SlowSettingsFocusDevice(clock)
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
panel_timeout=1.0,
|
||||
select_color_fn=lambda *_args, **_kwargs: True,
|
||||
select_size_fn=lambda *_args, **_kwargs: True,
|
||||
)
|
||||
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options(
|
||||
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||
)
|
||||
|
||||
self.assertEqual(1, device.app_current_calls)
|
||||
self.assertEqual(11.0, clock.now)
|
||||
self.assertEqual(1, len(device.clicks))
|
||||
adapter.close()
|
||||
|
||||
def test_panel_timeout_saves_sanitized_last_hierarchy(self):
|
||||
clock = FakeClock()
|
||||
device = PanelDoesNotOpenDevice()
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(
|
||||
connector=lambda _serial: device
|
||||
),
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
panel_timeout=0.5,
|
||||
artifact_directory=Path(directory),
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.select_options({"color": "黑色"})
|
||||
|
||||
diagnostics = raised.exception.diagnostics
|
||||
self.assertEqual("goods", diagnostics["page_kind"])
|
||||
self.assertGreaterEqual(diagnostics["hierarchy_reads"], 2)
|
||||
self.assertGreaterEqual(diagnostics["panel_hierarchy_reads"], 1)
|
||||
artifact = diagnostics["artifacts"][0]
|
||||
content = Path(artifact["path"]).read_text(encoding="utf-8")
|
||||
self.assertNotIn("5.03", content)
|
||||
self.assertNotIn("立即购买", content)
|
||||
self.assertIn("[已脱敏]", content)
|
||||
adapter.close()
|
||||
|
||||
def test_invalid_non_pdd_url_is_rejected_before_connect(self):
|
||||
device = FakeDevice()
|
||||
adapter = self._adapter(device, [])
|
||||
|
||||
Reference in New Issue
Block a user