Files
cadSet/designir-pipeline/tests/test_designir_pipeline.py

209 lines
8.8 KiB
Python

from __future__ import annotations
import copy
import importlib.util
import json
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "designir_pipeline.py"
SPEC = importlib.util.spec_from_file_location("designir_pipeline", SCRIPT)
MODULE = importlib.util.module_from_spec(SPEC)
assert SPEC.loader
SPEC.loader.exec_module(MODULE)
class DesignIRPipelineTests(unittest.TestCase):
def payload(self) -> dict:
return json.loads(
(ROOT / "examples" / "raised_hub_flange.designir.json").read_text()
)
def test_example_is_valid_and_compiles(self) -> None:
payload = MODULE.validate_designir(self.payload())
with tempfile.TemporaryDirectory() as temporary:
output = Path(temporary) / "flange.step"
result = MODULE.compile_designir(payload, output)
self.assertTrue(output.is_file())
self.assertEqual(1, result["facts"]["solid_count"])
self.assertGreater(result["facts"]["volume_mm3"], 30_000)
def test_legacy_model_migrates_to_semantic_designir_3(self) -> None:
migrated = MODULE.migrate_designir_2_to_3(self.payload())
self.assertEqual("3.0", migrated["schema_version"])
self.assertEqual(
"fully_semantic_parametric", migrated["reconstruction_mode"]
)
self.assertIn(
"outer_radius", migrated["edit_interface"]["semantic_parameters"]
)
with tempfile.TemporaryDirectory() as temporary:
output = Path(temporary) / "flange.step"
result = MODULE.compile_designir(migrated, output)
self.assertEqual(1, result["facts"]["solid_count"])
def test_generated_step_materializes_hybrid_surface_snapshot(self) -> None:
migrated = MODULE.migrate_designir_2_to_3(self.payload())
with tempfile.TemporaryDirectory() as temporary:
output = Path(temporary) / "flange.step"
MODULE.compile_designir(migrated, output)
hybrid = MODULE.materialize_hybrid_designir(migrated, output)
self.assertEqual(
"hybrid_semantic_surface_parametric",
hybrid["reconstruction_mode"],
)
self.assertTrue(hybrid["surface_layer"]["solids"])
self.assertTrue(
hybrid["compiled_surface_provenance"][
"semantic_layer_authoritative"
]
)
self.assertTrue(MODULE.anti_cheat(hybrid)["valid"])
rebuilt = Path(temporary) / "hybrid-rebuilt.step"
MODULE.compile_designir(hybrid, rebuilt)
self.assertTrue(rebuilt.is_file())
def test_teacher_dependency_is_rejected(self) -> None:
payload = self.payload()
payload["source_step"] = "teacher.step"
with self.assertRaisesRegex(MODULE.DesignIRError, "Teacher geometry"):
MODULE.validate_designir(payload)
def test_example_compiles_with_simplecadapi_and_exports_graph(self) -> None:
payload = self.payload()
with tempfile.TemporaryDirectory() as temporary:
output = Path(temporary) / "flange.step"
result = MODULE.compile_designir(
payload, output, backend="simplecadapi"
)
self.assertEqual("simplecadapi", result["backend"])
self.assertGreater(result["facts"]["volume_mm3"], 30_000)
model_json_path = Path(result["model_json"])
self.assertTrue(model_json_path.is_file())
model_json = json.loads(model_json_path.read_text(encoding="utf-8"))
serialized = json.dumps(model_json)
self.assertIn("feature.base.flange", serialized)
self.assertIn("feature.central.bore", serialized)
self.assertTrue(model_json.get("leaf_ids"))
def test_arbitrary_axis_geometric_center_preserves_pose(self) -> None:
payload = self.payload()
payload["family"] = "axis_aligned_cylinder"
payload["parameters"] = {
"outer_radius": {"value": 10, "unit": "mm", "editable": True},
"overall_length": {"value": 400, "unit": "mm", "editable": True},
}
payload["expressions"] = {}
payload["features"] = [
{
"id": "main_body",
"operation": "extrude_circle",
"radius": {"parameter": "outer_radius"},
"height": {"parameter": "overall_length"},
"axis": [0, -1, 0],
"center": [7, 11, 13],
"anchor": "geometric_center",
}
]
payload["edit_interface"] = {
"editable_parameters": ["outer_radius", "overall_length"],
"preserved_interfaces": ["part_center", "primary_axis"],
}
payload["validation_contract"]["perturbations"] = [
{
"parameter": "outer_radius",
"scale": 1.1,
"expected_change": "radius",
"preserve": ["overall_length", "part_center"],
},
{
"parameter": "overall_length",
"scale": 1.1,
"expected_change": "length",
"preserve": ["outer_radius", "part_center"],
},
]
shape = MODULE.build_shape(payload)
bounds = shape.bounding_box()
self.assertAlmostEqual(-189, bounds.min.Y, places=6)
self.assertAlmostEqual(211, bounds.max.Y, places=6)
self.assertAlmostEqual(7, shape.center(MODULE.CenterOf.MASS).X, places=6)
self.assertAlmostEqual(11, shape.center(MODULE.CenterOf.MASS).Y, places=6)
self.assertAlmostEqual(13, shape.center(MODULE.CenterOf.MASS).Z, places=6)
def test_embedded_mesh_is_rejected(self) -> None:
payload = self.payload()
payload["features"][0]["mesh"] = {"vertices": [[0, 0, 0]]}
with self.assertRaisesRegex(MODULE.DesignIRError, "Teacher geometry"):
MODULE.validate_designir(payload)
def test_isolated_rebuild_has_no_teacher(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
designir = root / "flange.designir.json"
designir.write_text(json.dumps(self.payload()), encoding="utf-8")
output = root / "rebuilt.step"
result = MODULE.isolated_rebuild(designir, output)
self.assertTrue(result["valid"])
self.assertFalse(result["teacher_step_available"])
self.assertTrue(output.is_file())
def test_acceptance_passes_identical_designir_rebuild(self) -> None:
payload = self.payload()
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
teacher = root / "teacher.step"
rebuilt = root / "rebuilt.step"
MODULE.compile_designir(payload, teacher)
MODULE.compile_designir(payload, rebuilt)
report = MODULE.acceptance_report(teacher, rebuilt, payload)
self.assertEqual("pass", report["verdict"])
self.assertEqual("replay_validated", report["next_state"])
self.assertEqual(
1.0, report["editability"]["parameter_perturbation_success_rate"]
)
def test_unsupported_vocabulary_is_honest_failure(self) -> None:
payload = self.payload()
payload["reconstruction_status"] = "unsupported_feature_vocabulary"
payload["missing_capabilities"] = ["multi_rail_sweep"]
with self.assertRaisesRegex(MODULE.DesignIRError, "multi_rail_sweep"):
MODULE.build_shape(payload)
def test_promotion_requires_independent_replay_validated_cases(self) -> None:
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
reports = []
for index in range(3):
report = root / f"case-{index}.json"
report.write_text(
json.dumps(
{
"model_id": f"case-{index}",
"teacher_sha256": f"teacher-{index}",
"applied_experience_ids": [
"reconstruction.flange.primary"
],
"next_state": "replay_validated",
}
),
encoding="utf-8",
)
reports.append(report)
result = MODULE.promotion_check(
reports, ROOT / "config" / "promotion-policy.json"
)
self.assertTrue(result["eligible"])
self.assertEqual(
["reconstruction.flange.primary"],
result["validated_experience_ids"],
)
self.assertEqual("promoted", result["next_state"])
if __name__ == "__main__":
unittest.main()