fix(client): bind T-103 XML coordinates

This commit is contained in:
QiuSW
2026-08-04 14:04:49 +08:00
parent 946b064470
commit acf7e11114
2 changed files with 27 additions and 27 deletions
@@ -21,7 +21,7 @@ from ..pdd.product_url import ProductUrl, ProductUrlError, parse_product_url
from ..pdd.sku_panel_state import HUMAN_DECLARED_STATES from ..pdd.sku_panel_state import HUMAN_DECLARED_STATES
SANITIZER_VERSION = "t103-privacy-v2" SANITIZER_VERSION = "t103-privacy-v3"
EXPECTED_GOODS_ID = "937122477375" EXPECTED_GOODS_ID = "937122477375"
EXPECTED_PDD_VERSION = "8.17.0" EXPECTED_PDD_VERSION = "8.17.0"
EXPECTED_DEVICE_MODEL = "PKG110" EXPECTED_DEVICE_MODEL = "PKG110"
@@ -29,7 +29,7 @@ EXPECTED_ANDROID_VERSION = "16"
EXPECTED_SCREENSHOT_WIDTH = 1080 EXPECTED_SCREENSHOT_WIDTH = 1080
EXPECTED_SCREENSHOT_HEIGHT = 2376 EXPECTED_SCREENSHOT_HEIGHT = 2376
EXPECTED_XML_WIDTH = 1080 EXPECTED_XML_WIDTH = 1080
EXPECTED_XML_HEIGHT = 2400 EXPECTED_XML_HEIGHT = 2376
_ARTIFACT_FILES = ("screenshot.png", "hierarchy.xml") _ARTIFACT_FILES = ("screenshot.png", "hierarchy.xml")
_SHA256_RE = re.compile(r"[0-9a-f]{64}\Z") _SHA256_RE = re.compile(r"[0-9a-f]{64}\Z")
_BOUNDS_RE = re.compile(r"\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]\Z") _BOUNDS_RE = re.compile(r"\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]\Z")
@@ -48,7 +48,7 @@ def _default_xml() -> str:
return ( return (
"<hierarchy rotation='0'>" "<hierarchy rotation='0'>"
f"<node bounds='[0,0][1080,540]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE} {FULL_PHONE}' />" f"<node bounds='[0,0][1080,540]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE} {FULL_PHONE}' />"
f"<node bounds='[0,540][1080,2400]' text='{SAFE_TEXT}' />" f"<node bounds='[0,540][1080,2376]' text='{SAFE_TEXT}' />"
"</hierarchy>" "</hierarchy>"
) )
@@ -117,17 +117,17 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
self.assertNotIn(MASKED_PHONE, derived_xml) self.assertNotIn(MASKED_PHONE, derived_xml)
self.assertIn(f'"human_declared_state": "{state}"', manifest) self.assertIn(f'"human_declared_state": "{state}"', manifest)
self.assertIn('"privacy_tier": "SANITIZED"', manifest) self.assertIn('"privacy_tier": "SANITIZED"', manifest)
self.assertIn('"sanitizer_version": "t103-privacy-v2"', manifest) self.assertIn('"sanitizer_version": "t103-privacy-v3"', manifest)
self.assertIn('"screenshot_space": {', manifest) self.assertIn('"screenshot_space": {', manifest)
self.assertIn('"xml_coordinate_space": {', manifest) self.assertIn('"xml_coordinate_space": {', manifest)
self.assertIn('"height": 2376', manifest) self.assertIn('"height": 2376', manifest)
self.assertIn('"height": 2400', manifest) self.assertIn('"height": 2376', manifest)
self.assertIn('"privacy_mask_rectangle": [', manifest) self.assertIn('"privacy_mask_rectangle": [', manifest)
self.assertIn('"removed_nodes": 1', manifest) self.assertIn('"removed_nodes": 1', manifest)
self.assertIn('"cleared_crossing_nodes": 0', manifest) self.assertIn('"cleared_crossing_nodes": 0', manifest)
self.assertIn('"retained_below_nodes": 1', manifest) self.assertIn('"retained_below_nodes": 1', manifest)
self.assertIn('"max_right": 1080', manifest) self.assertIn('"max_right": 1080', manifest)
self.assertIn('"max_bottom": 2400', manifest) self.assertIn('"max_bottom": 2376', manifest)
self.assertNotIn("canonical_url", manifest) self.assertNotIn("canonical_url", manifest)
self.assertNotIn(TEST_SERIAL, manifest) self.assertNotIn(TEST_SERIAL, manifest)
self.assertNotIn("serial", manifest) self.assertNotIn("serial", manifest)
@@ -138,9 +138,9 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
def test_crossing_container_keeps_lower_children_but_clears_its_text(self) -> None: def test_crossing_container_keeps_lower_children_but_clears_its_text(self) -> None:
xml = ( xml = (
"<hierarchy>" "<hierarchy>"
f"<node bounds='[0,0][1080,2400]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE}'>" f"<node bounds='[0,0][1080,2376]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE}'>"
f"<node bounds='[0,0][1080,540]' text='{FULL_PHONE}' />" f"<node bounds='[0,0][1080,540]' text='{FULL_PHONE}' />"
f"<node bounds='[0,540][1080,2400]' text='{SAFE_TEXT}' />" f"<node bounds='[0,540][1080,2376]' text='{SAFE_TEXT}' />"
"</node></hierarchy>" "</node></hierarchy>"
) )
with TemporaryDirectory() as temporary: with TemporaryDirectory() as temporary:
@@ -151,7 +151,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
self.assertIsNotNone(crossing) self.assertIsNotNone(crossing)
assert crossing is not None assert crossing is not None
self.assertEqual(crossing.attrib, {"bounds": "[0,0][1080,2400]"}) self.assertEqual(crossing.attrib, {"bounds": "[0,0][1080,2376]"})
self.assertEqual(len(list(crossing)), 1) self.assertEqual(len(list(crossing)), 1)
self.assertEqual(list(crossing)[0].get("text"), SAFE_TEXT) self.assertEqual(list(crossing)[0].get("text"), SAFE_TEXT)
manifest = json.loads(result.manifest_path.read_text(encoding="utf-8")) manifest = json.loads(result.manifest_path.read_text(encoding="utf-8"))
@@ -162,7 +162,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
"cleared_crossing_nodes": 1, "cleared_crossing_nodes": 1,
"retained_below_nodes": 1, "retained_below_nodes": 1,
"max_right": 1080, "max_right": 1080,
"max_bottom": 2400, "max_bottom": 2376,
}, },
) )
@@ -195,9 +195,9 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
manifest["xml_coordinate_space"], manifest["xml_coordinate_space"],
{ {
"width": 1080, "width": 1080,
"height": 2400, "height": 2376,
"privacy_mask_rectangle": [0, 0, 1080, 540], "privacy_mask_rectangle": [0, 0, 1080, 540],
"observed_max": {"right": 1080, "bottom": 2400}, "observed_max": {"right": 1080, "bottom": 2376},
}, },
) )
@@ -243,10 +243,10 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
("bounds", "<hierarchy><node text='missing bounds' /></hierarchy>"), ("bounds", "<hierarchy><node text='missing bounds' /></hierarchy>"),
( (
"private-parent-with-lower-child", "private-parent-with-lower-child",
"<hierarchy><node bounds='[0,0][1080,540]'><node bounds='[0,540][1080,2400]' text='x' /></node></hierarchy>", "<hierarchy><node bounds='[0,0][1080,540]'><node bounds='[0,540][1080,2376]' text='x' /></node></hierarchy>",
), ),
("full-phone-below", f"<hierarchy><node bounds='[0,540][1080,2400]' text='{FULL_PHONE}' /></hierarchy>"), ("full-phone-below", f"<hierarchy><node bounds='[0,540][1080,2376]' text='{FULL_PHONE}' /></hierarchy>"),
("masked-phone-below", f"<hierarchy><node bounds='[0,540][1080,2400]' text='{MASKED_PHONE}' /></hierarchy>"), ("masked-phone-below", f"<hierarchy><node bounds='[0,540][1080,2376]' text='{MASKED_PHONE}' /></hierarchy>"),
) )
for kind, xml in malformed: for kind, xml in malformed:
with self.subTest(kind=kind), TemporaryDirectory() as temporary: with self.subTest(kind=kind), TemporaryDirectory() as temporary:
@@ -281,7 +281,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
xml = ( xml = (
"<hierarchy>" "<hierarchy>"
"<node bounds='[0,0][1080,540]' text='private' />" "<node bounds='[0,0][1080,540]' text='private' />"
f"<node bounds='[0,540][1080,2400]' text='{value}' />" f"<node bounds='[0,540][1080,2376]' text='{value}' />"
"</hierarchy>" "</hierarchy>"
) )
with self.subTest(value=value), TemporaryDirectory() as temporary: with self.subTest(value=value), TemporaryDirectory() as temporary:
@@ -294,7 +294,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
"<hierarchy>" "<hierarchy>"
"<node bounds='[0,0][1080,540]' text='private' />" "<node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,1000]' text='138' content-desc='0013' />" "<node bounds='[0,540][1080,1000]' text='138' content-desc='0013' />"
"<node bounds='[0,1000][1080,2400]' text='8000' />" "<node bounds='[0,1000][1080,2376]' text='8000' />"
"</hierarchy>" "</hierarchy>"
) )
with TemporaryDirectory() as temporary: with TemporaryDirectory() as temporary:
@@ -305,7 +305,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
def test_privacy_geometry_requires_removed_and_retained_nodes(self) -> None: def test_privacy_geometry_requires_removed_and_retained_nodes(self) -> None:
scenarios = ( scenarios = (
("no-private", f"<hierarchy><node bounds='[0,540][1080,2400]' text='{SAFE_TEXT}' /></hierarchy>"), ("no-private", f"<hierarchy><node bounds='[0,540][1080,2376]' text='{SAFE_TEXT}' /></hierarchy>"),
("no-below", "<hierarchy><node bounds='[0,0][1080,540]' text='private' /></hierarchy>"), ("no-below", "<hierarchy><node bounds='[0,0][1080,540]' text='private' /></hierarchy>"),
) )
for name, xml in scenarios: for name, xml in scenarios:
@@ -321,26 +321,26 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
( (
"short-width", "short-width",
"<hierarchy><node bounds='[0,0][1079,540]' text='private' />" "<hierarchy><node bounds='[0,0][1079,540]' text='private' />"
"<node bounds='[0,540][1079,2400]' text='safe' /></hierarchy>", "<node bounds='[0,540][1079,2376]' text='safe' /></hierarchy>",
"1079x2400", "1079x2376",
), ),
( (
"short-height", "short-height",
"<hierarchy><node bounds='[0,0][1080,540]' text='private' />" "<hierarchy><node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,2376]' text='safe' /></hierarchy>", "<node bounds='[0,540][1080,2375]' text='safe' /></hierarchy>",
"1080x2376", "1080x2375",
), ),
( (
"wide-width", "wide-width",
"<hierarchy><node bounds='[0,0][1081,540]' text='private' />" "<hierarchy><node bounds='[0,0][1081,540]' text='private' />"
"<node bounds='[0,540][1081,2400]' text='safe' /></hierarchy>", "<node bounds='[0,540][1081,2376]' text='safe' /></hierarchy>",
"1081x2400", "1081x2376",
), ),
( (
"tall-height", "old-v2-xml-height",
"<hierarchy><node bounds='[0,0][1080,540]' text='private' />" "<hierarchy><node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,2401]' text='safe' /></hierarchy>", "<node bounds='[0,540][1080,2400]' text='safe' /></hierarchy>",
"1080x2401", "1080x2400",
), ),
) )
for name, xml, observed in scenarios: for name, xml, observed in scenarios: