Files
pythonocc-step-editor/scripts/verify_scdm_result_validator.py
T

170 lines
7.7 KiB
Python
Raw Normal View History

from __future__ import annotations
import sys
import tempfile
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from step_editor.scdm_result_validator import ( # noqa: E402
build_scdm_id_mapping,
check_scdm_summary_delta,
check_scdm_unedited_objects,
check_scdm_target,
match_scdm_object_by_signature,
rewrite_scdm_relation_formula_ids,
validate_scdm_edit_result,
)
def _assert(condition: bool, message: str) -> None:
if not condition:
raise AssertionError(message)
def _hole(object_id: str, face_id: int, *, diameter: float, center: tuple[float, float, float]) -> dict[str, object]:
return {
"objectId": object_id,
"objectType": "hole",
"geometrySignature": {
"objectType": "hole",
"faceIds": [face_id],
"surfaceType": "cylinder",
"center": list(center),
"axis": [0.0, 0.0, 1.0],
"diameter": diameter,
},
"capabilities": [
{"key": "hole.diameter", "currentValue": diameter},
{"key": "hole.position", "currentValue": list(center)},
],
}
def _cache(*objects: dict[str, object]) -> dict[str, object]:
return {
"schemaVersion": 1,
"source": "SCDM",
"objects": list(objects),
"diagnostics": {},
}
def _cache_with_summary(summary: dict[str, int], *objects: dict[str, object]) -> dict[str, object]:
cache = _cache(*objects)
cache["diagnostics"] = {"raw_summary": dict(summary)}
return cache
def main() -> int:
with tempfile.TemporaryDirectory(prefix="step_editor_scdm_validate_") as temp:
root = Path(temp)
output_step = root / "result.step"
output_step.write_text("ISO-10303-21;\nEND-ISO-10303-21;\n", encoding="utf-8")
before = _cache(
_hole("hole:85", 85, diameter=0.5, center=(0.5, 1.0, 9.5)),
_hole("hole:87", 87, diameter=0.5, center=(2.0, 1.0, 9.5)),
)
after = _cache(
_hole("hole:90", 90, diameter=0.75, center=(0.5, 1.0, 9.5)),
_hole("hole:91", 91, diameter=0.5, center=(2.0, 1.0, 9.5)),
)
before_signature = before["objects"][0]["geometrySignature"] # type: ignore[index]
match = match_scdm_object_by_signature(before_signature, after, capability_key="hole.diameter")
_assert(match.get("status") == "unique", f"changed diameter should still match by center/axis/type: {match}")
_assert(match.get("object", {}).get("objectId") == "hole:90", f"wrong match: {match}")
ok = validate_scdm_edit_result(
{"ok": True, "output_step": str(output_step)},
before_signature=before_signature,
before_cache=before,
after_cache=after,
capability_key="hole.diameter",
expected_target=0.75,
edited_object_id="hole:85",
brep_validator=lambda path: {"ok": path.is_file(), "reason": "ok"},
)
_assert(ok.get("ok") is True, f"validated edit should pass: {ok}")
_assert(ok.get("targetCheck", {}).get("ok") is True, f"target diameter should be checked: {ok}")
_assert(ok.get("topologyCheck", {}).get("ok") is True, f"unchanged objects should be checked: {ok}")
drift_after = _cache(_hole("hole:90", 90, diameter=0.75, center=(0.5, 1.0, 9.5)))
drift = validate_scdm_edit_result(
{"ok": True, "output_step": str(output_step)},
before_signature=before_signature,
before_cache=before,
after_cache=drift_after,
capability_key="hole.diameter",
expected_target=0.75,
edited_object_id="hole:85",
)
_assert(drift.get("ok") is False and drift.get("reason") == "unexpected-object-drift", f"missing unrelated hole should fail: {drift}")
direct_drift = check_scdm_unedited_objects(before, drift_after, edited_object_id="hole:85", edited_signature=before_signature)
_assert(direct_drift.get("ok") is False and direct_drift.get("checked") == 1, f"direct drift check should inspect one unedited object: {direct_drift}")
mismatch = validate_scdm_edit_result(
{"ok": True, "output_step": str(output_step)},
before_signature=before_signature,
after_cache=after,
capability_key="hole.diameter",
expected_target=0.9,
)
_assert(mismatch.get("ok") is False and mismatch.get("reason") == "target-mismatch", f"wrong target should fail: {mismatch}")
missing = validate_scdm_edit_result({"ok": True, "output_step": str(root / "missing.step")})
_assert(missing.get("ok") is False and missing.get("reason") == "missing-output-step", f"missing result STEP should fail: {missing}")
mapping = build_scdm_id_mapping(before, after, capability_key="hole.diameter")
_assert(mapping.get("faceIdMap") == {85: 90, 87: 91}, f"face IDs should remap through signatures: {mapping}")
rewritten = rewrite_scdm_relation_formula_ids("Face87.直径 = Face85.半径", mapping)
_assert(rewritten == "Face91.直径 = Face90.半径", f"formula IDs should follow SCDM remap: {rewritten}")
position_after = _cache(_hole("hole:91", 91, diameter=0.5, center=(2.0, 1.0, 6.0)))
position_check = check_scdm_target(position_after["objects"][0], capability_key="hole.position", expected_target=[2.0, 1.0, 6.0])
_assert(position_check.get("ok") is True, f"position target should pass: {position_check}")
summary_before = _cache_with_summary(
{"bodyCount": 13, "objectCount": 554, "faceCount": 158, "edgeCount": 396},
_hole("hole:85", 85, diameter=0.5, center=(0.5, 1.0, 9.5)),
)
summary_after_ok = _cache_with_summary(
{"bodyCount": 13, "objectCount": 550, "faceCount": 157, "edgeCount": 390},
_hole("hole:90", 90, diameter=0.75, center=(0.5, 1.0, 9.5)),
)
summary_ok = check_scdm_summary_delta(summary_before, summary_after_ok, capability_key="hole.diameter")
_assert(summary_ok.get("ok") is True, f"small summary changes should pass: {summary_ok}")
summary_after_bad = _cache_with_summary(
{"bodyCount": 13, "objectCount": 80, "faceCount": 20, "edgeCount": 45},
_hole("hole:90", 90, diameter=0.75, center=(0.5, 1.0, 9.5)),
)
summary_bad = validate_scdm_edit_result(
{"ok": True, "output_step": str(output_step)},
before_signature=summary_before["objects"][0]["geometrySignature"], # type: ignore[index]
before_cache=summary_before,
after_cache=summary_after_bad,
capability_key="hole.diameter",
expected_target=0.75,
)
_assert(summary_bad.get("ok") is False and summary_bad.get("reason") == "summary-drift", f"large summary drift should fail: {summary_bad}")
summary_fill = check_scdm_summary_delta(summary_before, summary_after_bad, capability_key="feature.fill")
_assert(summary_fill.get("ok") is None and summary_fill.get("reason") == "skipped-command-feature", f"fill should skip summary count guard: {summary_fill}")
ambiguous_after = _cache(
_hole("hole:100", 100, diameter=0.75, center=(0.5, 1.0, 9.5)),
_hole("hole:101", 101, diameter=0.75, center=(0.5, 1.0, 9.5)),
)
ambiguous = match_scdm_object_by_signature(before_signature, ambiguous_after, capability_key="hole.diameter")
_assert(ambiguous.get("status") == "multiple", f"ambiguous matches should be reported: {ambiguous}")
print("scdm result validator ok")
return 0
if __name__ == "__main__":
raise SystemExit(main())