fix: 记录规格面板误超时诊断 (#115)

This commit is contained in:
chengma
2026-08-10 18:38:56 +08:00
parent 6530b11125
commit 24f4ad1ba9
2 changed files with 196 additions and 13 deletions
+101 -2
View File
@@ -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, [])