143 lines
7.5 KiB
Python
143 lines
7.5 KiB
Python
"""B2.7 真实 Style 逐层候选 Selector:混合层不误归残余的离线证据测试。"""
|
||
from __future__ import annotations
|
||
|
||
import copy
|
||
import importlib.util
|
||
from pathlib import Path
|
||
import sys
|
||
import unittest
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
DIR = ROOT / "tools/provider-pipeline"
|
||
sys.path.insert(0, str(DIR))
|
||
spec = importlib.util.spec_from_file_location("collision_audit", DIR / "selector_collision_evidence_audit.py")
|
||
audit = importlib.util.module_from_spec(spec)
|
||
assert spec and spec.loader
|
||
spec.loader.exec_module(audit)
|
||
|
||
|
||
def fixtures(*, provider="a", source_layer="raw", field="icon"):
|
||
style = {"layers": [
|
||
{"id": "original-picture", "source": provider, "source-layer": source_layer, "type": "symbol",
|
||
"filter": ["has", field], "layout": {"icon-image": ["get", field]}},
|
||
{"id": "original-caption", "source": provider, "source-layer": source_layer, "type": "symbol",
|
||
"filter": ["has", "label"], "layout": {"text-field": ["get", "label"]}},
|
||
]}
|
||
mapping = {"assignments": [
|
||
{"visualObjectId": "V-BOY", "status": "VISUAL_OBJECT_MATCH", "standardLayer": "navigation_marks",
|
||
"renderEvidence": {"sourceLayer": source_layer, "components": [
|
||
{"styleIndex": 0, "styleLayerId": "original-picture", "role": "icon", "resourceKey": "B"},
|
||
{"styleIndex": 1, "styleLayerId": "original-caption", "role": "text", "resourceKey": None},
|
||
]}},
|
||
{"visualObjectId": "V-UNKNOWN", "status": "UNKNOWN", "standardLayer": None,
|
||
"renderEvidence": {"sourceLayer": source_layer, "components": [
|
||
{"styleIndex": 0, "styleLayerId": "original-picture", "role": "icon", "resourceKey": "B"},
|
||
]}},
|
||
]}
|
||
return style, mapping
|
||
|
||
|
||
def evaluate_simple(expr, props, zoom):
|
||
if expr[0] == "all":
|
||
return all(evaluate_simple(x, props, zoom) for x in expr[1:])
|
||
if expr[0] == "==":
|
||
return props.get(expr[1][1]) == expr[2]
|
||
raise AssertionError(expr)
|
||
|
||
|
||
def obs(vo, values, layer_ids=("original-picture", "original-caption")):
|
||
return {"visualObjectTypeId": vo, "properties": values, "zoom": 12,
|
||
"matchedStyleLayerIds": list(layer_ids), "tile": "1/0/0", "featureId": 7}
|
||
|
||
|
||
class SelectorCollisionTests(unittest.TestCase):
|
||
def test_two_different_provider_property_names_and_original_style_preserved(self):
|
||
for provider, field in (("alpha", "icon"), ("beta", "providerSpecificCode")):
|
||
with self.subTest(provider=provider):
|
||
style, mapping = fixtures(provider=provider, field=field)
|
||
before = copy.deepcopy(style)
|
||
candidates = audit.build_candidates(mapping, style)
|
||
self.assertEqual(style, before)
|
||
self.assertEqual(len(candidates), 3)
|
||
target = [x for x in candidates if x["visualObjectTypeId"] == "V-BOY"]
|
||
self.assertEqual(len(target), 2)
|
||
self.assertTrue(all(x["selector"] == ["==", ["get", field], "B"] for x in target))
|
||
self.assertEqual(target[1]["selectorOrigin"], "INHERIT_SAME_RAW_FEATURE_CANDIDATE")
|
||
self.assertTrue(all(not x["runtimeAuthorized"] for x in target))
|
||
|
||
def test_unknown_same_resource_blocks_even_with_replayed_components(self):
|
||
style, mapping = fixtures()
|
||
rows = audit.build_candidates(mapping, style)
|
||
assignments = {x["visualObjectId"]: x for x in mapping["assignments"]}
|
||
outcome = audit.evaluate_examples(rows, [
|
||
obs("V-BOY", {"icon": "B", "label": "Known"}),
|
||
obs("V-UNKNOWN", {"icon": "B", "label": "Different"}),
|
||
], assignments, eval_selector=evaluate_simple)
|
||
target = [x for x in rows if x["visualObjectTypeId"] == "V-BOY"]
|
||
self.assertTrue(all(x["sampleStatus"] == "CROSS_CLASS_OR_RESIDUAL_COLLISION" for x in target))
|
||
self.assertTrue(all(x["unresolvedOrUnknownHits"] == 1 for x in target))
|
||
self.assertEqual(outcome["candidateStatusCounts"]["CROSS_CLASS_OR_RESIDUAL_COLLISION"], 2)
|
||
self.assertIsNone(next(x for x in rows if x["visualObjectTypeId"] == "V-UNKNOWN")["selector"])
|
||
|
||
def test_cross_standard_classification_blocks(self):
|
||
style, mapping = fixtures()
|
||
mapping["assignments"][1]["status"] = "LAYER_ONLY"
|
||
mapping["assignments"][1]["standardLayer"] = "facility_point"
|
||
rows = audit.build_candidates(mapping, style)
|
||
assignments = {x["visualObjectId"]: x for x in mapping["assignments"]}
|
||
audit.evaluate_examples(rows, [
|
||
obs("V-BOY", {"icon": "B", "label": "Known"}),
|
||
obs("V-UNKNOWN", {"icon": "B", "label": "Other"}),
|
||
], assignments, eval_selector=evaluate_simple)
|
||
target = [x for x in rows if x["visualObjectTypeId"] == "V-BOY"]
|
||
self.assertTrue(all(x["crossStandardHits"] == 1 for x in target))
|
||
|
||
def test_two_resource_fields_compile_conjunction_but_not_primary(self):
|
||
style, mapping = fixtures()
|
||
style["layers"][1]["layout"] = {"icon-image": ["get", "light"]}
|
||
mapping["assignments"][0]["renderEvidence"]["components"][1] = {
|
||
"styleIndex": 1, "styleLayerId": "original-caption", "role": "icon", "resourceKey": "L",
|
||
}
|
||
rows = audit.build_candidates(mapping, style)
|
||
target = [x for x in rows if x["visualObjectTypeId"] == "V-BOY"]
|
||
expected = ["all", ["==", ["get", "icon"], "B"], ["==", ["get", "light"], "L"]]
|
||
self.assertTrue(all(x["selector"] == expected for x in target))
|
||
self.assertTrue(all(not x["runtimeAuthorized"] for x in target))
|
||
|
||
def test_conflicting_direct_resource_values_and_invalid_style_fail_closed(self):
|
||
style, mapping = fixtures()
|
||
style["layers"][1]["layout"] = {"icon-image": ["get", "icon"]}
|
||
mapping["assignments"][0]["renderEvidence"]["components"][1] = {
|
||
"styleIndex": 1, "styleLayerId": "original-caption", "role": "icon", "resourceKey": "DIFFERENT",
|
||
}
|
||
rows = audit.build_candidates(mapping, style)
|
||
self.assertTrue(all(x["selector"] is None for x in rows if x["visualObjectTypeId"] == "V-BOY"))
|
||
mapping["assignments"][0]["renderEvidence"]["components"][0]["styleIndex"] = 999
|
||
with self.assertRaisesRegex(ValueError, "Style Index"):
|
||
audit.build_candidates(mapping, style)
|
||
|
||
def test_catalog_mismatch_and_multisource_same_layer_fail_closed(self):
|
||
style, mapping = fixtures()
|
||
catalog = {"visual_objects": [{"visual_object_id": "V-BOY", "example": {"tile": "1/0/0"}}]}
|
||
with self.assertRaisesRegex(ValueError, "覆盖不一致"):
|
||
audit.run_audit(style, mapping, catalog, Path("/tmp"), {}, {})
|
||
style["layers"].append({
|
||
"id": "other-source", "source": "second", "source-layer": "raw",
|
||
"type": "symbol", "layout": {"icon-image": ["get", "icon"]},
|
||
})
|
||
catalog["visual_objects"].append({"visual_object_id": "V-UNKNOWN", "example": {"tile": "1/0/0"}})
|
||
with self.assertRaisesRegex(ValueError, "多个 Style Source"):
|
||
audit.run_audit(style, mapping, catalog, Path("/tmp"), {}, {})
|
||
|
||
def test_unseen_target_is_not_claimed_safe(self):
|
||
style, mapping = fixtures()
|
||
rows = audit.build_candidates(mapping, style)
|
||
assignments = {x["visualObjectId"]: x for x in mapping["assignments"]}
|
||
audit.evaluate_examples(rows, [], assignments, eval_selector=evaluate_simple)
|
||
target = [x for x in rows if x["visualObjectTypeId"] == "V-BOY"]
|
||
self.assertTrue(all(x["sampleStatus"] == "TARGET_NOT_OBSERVED" for x in target))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|