fix: 容忍规格面板临时空控件树 (#162)
This commit is contained in:
@@ -133,6 +133,45 @@ class PanelDoesNotOpenDevice(FakeCollectDevice):
|
||||
self.clicks.append((x, y))
|
||||
|
||||
|
||||
TRANSIENT_EMPTY_SPEC_XML = """<hierarchy>
|
||||
<node class="android.widget.FrameLayout" package="com.xunmeng.pinduoduo"
|
||||
bounds="[0,0][1080,2340]">
|
||||
<node class="android.widget.LinearLayout" package="com.xunmeng.pinduoduo"
|
||||
bounds="[0,0][1080,2214]" />
|
||||
</node>
|
||||
</hierarchy>"""
|
||||
|
||||
|
||||
class TransientSpecTreeDevice(FakeCollectDevice):
|
||||
"""规格面板阶段交替返回临时空树和有效树。"""
|
||||
|
||||
def __init__(self, home_xml, spec_xml):
|
||||
super().__init__(home_xml, spec_xml)
|
||||
self.panel_reads = 0
|
||||
|
||||
def dump_hierarchy(self):
|
||||
if self.panel_open:
|
||||
self.panel_reads += 1
|
||||
if self.panel_reads % 2 == 0:
|
||||
return TRANSIENT_EMPTY_SPEC_XML
|
||||
return super().dump_hierarchy()
|
||||
|
||||
|
||||
class SpecTreeStaysBlankDevice(FakeCollectDevice):
|
||||
"""面板首次有效,随后持续返回空树。"""
|
||||
|
||||
def __init__(self, home_xml, spec_xml):
|
||||
super().__init__(home_xml, spec_xml)
|
||||
self.panel_reads = 0
|
||||
|
||||
def dump_hierarchy(self):
|
||||
if self.panel_open:
|
||||
self.panel_reads += 1
|
||||
if self.panel_reads > 1:
|
||||
return TRANSIENT_EMPTY_SPEC_XML
|
||||
return super().dump_hierarchy()
|
||||
|
||||
|
||||
class SnakeColorDevice(FakeCollectDevice):
|
||||
"""模拟两行横向颜色网格和滚动后才出现的尺码区域。"""
|
||||
|
||||
@@ -1441,6 +1480,84 @@ class PddCollectParserTest(unittest.TestCase):
|
||||
artifacts = raised.exception.diagnostics["artifacts"]
|
||||
self.assertTrue(Path(artifacts[0]["path"]).is_file())
|
||||
|
||||
def test_transient_empty_spec_trees_are_skipped_during_full_collection(self):
|
||||
device = TransientSpecTreeDevice(
|
||||
self.home_xml, keep_only_one_sku(self.spec_xml)
|
||||
)
|
||||
service = PddCollectService(
|
||||
PddDeviceService(lambda _serial: device),
|
||||
"USB-001",
|
||||
"client-001",
|
||||
sleeper=lambda _seconds: None,
|
||||
max_spec_swipes=0,
|
||||
)
|
||||
|
||||
result = service.collect(
|
||||
FakeTask("https://mobile.yangkeduo.com/goods.html?goods_id=123")
|
||||
)
|
||||
|
||||
self.assertEqual("测试纯棉短袖商品", result.title)
|
||||
self.assertTrue(result.skus)
|
||||
self.assertGreater(device.panel_reads, 4)
|
||||
self.assertEqual(TRANSIENT_EMPTY_SPEC_XML, service._last_invalid_spec_xml)
|
||||
self.assertIn("颜色分类", service._last_valid_spec_xml)
|
||||
|
||||
def test_persistent_empty_spec_tree_has_lost_code_and_dual_evidence(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
device = SpecTreeStaysBlankDevice(
|
||||
self.home_xml, keep_only_one_sku(self.spec_xml)
|
||||
)
|
||||
clock = FakeClock()
|
||||
service = PddCollectService(
|
||||
PddDeviceService(lambda _serial: device),
|
||||
"USB-001",
|
||||
"client-001",
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
spec_panel_timeout=1.0,
|
||||
max_spec_swipes=0,
|
||||
artifact_directory=Path(directory),
|
||||
)
|
||||
|
||||
with self.assertRaises(PddCollectError) as raised:
|
||||
service.collect(
|
||||
FakeTask(
|
||||
"https://mobile.yangkeduo.com/goods.html?goods_id=123"
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual("PDD_PAGE_SPEC_PANEL_LOST", raised.exception.code)
|
||||
paths = [
|
||||
Path(item["path"]).name
|
||||
for item in raised.exception.diagnostics["artifacts"]
|
||||
]
|
||||
self.assertTrue(any(name.startswith("last-valid-spec-") for name in paths))
|
||||
self.assertTrue(any(name.startswith("last-invalid-spec-") for name in paths))
|
||||
self.assertLessEqual(clock.now, 1.2)
|
||||
|
||||
def test_special_page_is_not_retried_as_transient_empty_tree(self):
|
||||
device = FakeCollectDevice(self.home_xml, self.spec_xml)
|
||||
device.panel_open = True
|
||||
device.opened_url = "https://mobile.yangkeduo.com/goods.html?goods_id=123"
|
||||
device.spec_xml = (
|
||||
'<hierarchy><node text="请完成验证" '
|
||||
'package="com.xunmeng.pinduoduo" /></hierarchy>'
|
||||
)
|
||||
clock = FakeClock()
|
||||
service = PddCollectService(
|
||||
PddDeviceService(lambda _serial: device),
|
||||
"USB-001",
|
||||
"client-001",
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
)
|
||||
|
||||
with self.assertRaises(PddCollectError) as raised:
|
||||
service._read_valid_spec_panel(device)
|
||||
|
||||
self.assertEqual("PDD_PAGE_CAPTCHA", raised.exception.code)
|
||||
self.assertEqual([], clock.sleeps)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user