from __future__ import annotations import json import sys import tempfile import unittest from copy import deepcopy from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "backend" / "engine")) from cdsl_engine.batch_rebuild import _failure_category, _verification_classification, batch_analyze # noqa: E402 from cdsl_engine.capabilities import CapabilityAnalyzer # noqa: E402 from cdsl_engine.runtime_types import HoleSpec, PlaneSpec, TopologyRecord, TopologyRegistry # noqa: E402 from cdsl_engine.sketch_solver import SHAPE_GENERATORS, resolve_all_sketches # noqa: E402 def _workplane() -> dict: return {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]} def _rectangle(minimum: list[float], maximum: list[float]) -> dict: return {"type": "polygon", "vertices": [ [minimum[0], minimum[1]], [maximum[0], minimum[1]], [maximum[0], maximum[1]], [minimum[0], maximum[1]], ]} class EngineRuntimeFoundationTests(unittest.TestCase): def _base_block(self) -> dict: return { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "runtime-block", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "base", "workplane": _workplane(), "profile": _rectangle([-5, -5], [5, 5]), }]}, "features": [{ "id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": [], "params": {"distance_mm": 10}, "sketch_id": "base", }], } def test_runtime_module_has_no_build123d_import(self) -> None: runtime_source = (ROOT / "backend" / "engine" / "cdsl_engine" / "runtime.py").read_text(encoding="utf-8") self.assertNotIn("from build123d", runtime_source) self.assertNotIn("import build123d", runtime_source) def test_execution_session_declares_a_kernel_neutral_adapter_protocol(self) -> None: from cdsl_engine.runtime import ExecutionSession, GeometryAdapter self.assertIn("adapter", ExecutionSession.__dataclass_fields__) self.assertTrue(getattr(GeometryAdapter, "_is_protocol", False)) self.assertIn("export", GeometryAdapter.__dict__) def test_hole_spec_normalizes_wizard_subtypes_without_occ_dependencies(self) -> None: spec = HoleSpec.from_feature("hole_wizard", { "diameter_mm": 2, "depth_mm": 6, "end_condition": {"type": "blind"}, "positions": [{"mm": [1, 2, 3]}], "countersink": {"diameter_mm": 4, "angle_rad": 1.5707963267948966}, }, wizard=True) self.assertEqual(spec.positions_mm, ((1.0, 2.0, 3.0),)) self.assertEqual(spec.countersink, (4.0, 1.5707963267948966)) with self.assertRaisesRegex(ValueError, "larger than the main hole"): HoleSpec.from_feature("hole_wizard", { "diameter_mm": 2, "depth_mm": 6, "positions": [{"mm": [0, 0, 0]}], "counterbore": {"diameter_mm": 2, "depth_mm": 1}, }, wizard=True) def test_analytic_contours_create_a_region_with_hole(self) -> None: cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "sketch", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [ {"role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [10, 0]}, {"type": "line", "start": [10, 0], "end": [10, 10]}, {"type": "line", "start": [10, 10], "end": [0, 10]}, {"type": "line", "start": [0, 10], "end": [0, 0]}, ]}, {"role": "inner", "closed": True, "segments": [ {"type": "circle", "center": [5, 5], "radius_mm": 2}, ]}, ]}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] region = sketch["contour_regions_mm"][0] self.assertEqual(len(region["outer"]), 4) self.assertEqual(len(region["holes"]), 1) self.assertEqual(len(region["holes"][0]), 4) def test_analytic_contours_normalize_disjoint_inner_roles(self) -> None: cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "sketch", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [ {"role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [2, 0]}, {"type": "line", "start": [2, 0], "end": [2, 2]}, {"type": "line", "start": [2, 2], "end": [0, 2]}, {"type": "line", "start": [0, 2], "end": [0, 0]}, ]}, {"role": "inner", "closed": True, "segments": [ {"type": "line", "start": [4, 0], "end": [6, 0]}, {"type": "line", "start": [6, 0], "end": [6, 2]}, {"type": "line", "start": [6, 2], "end": [4, 2]}, {"type": "line", "start": [4, 2], "end": [4, 0]}, ]}, ]}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] self.assertEqual(len(sketch["contour_regions_mm"]), 2) self.assertTrue(all(not region["holes"] for region in sketch["contour_regions_mm"])) def test_deferred_reference_is_currently_executable_without_a_sketch(self) -> None: cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{"id": "s", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}}]}, "features": [{ "id": "plane", "atomic_id": "reference_plane", "depends_on": [], "execution_status": "deferred", "params": {"plane": _workplane()}, }], } analysis = CapabilityAnalyzer(atomic_ids={"reference_plane"}, profile_types=SHAPE_GENERATORS).analyze(cdsl) self.assertFalse(analysis.runtime_eligible) self.assertEqual(analysis.feature_results[0].resolved_status, "executable") self.assertEqual(analysis.document_blockers[0].code, "no_solid_feature") def test_unused_invalid_profile_does_not_block_runtime_preflight(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "abandoned", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [{"type": "line", "start": [0, 0], "end": [1, 0]}], }]}, }) analysis = analyze_cdsl(cdsl) self.assertTrue(analysis.runtime_eligible) def test_used_invalid_profile_is_a_feature_level_blocker(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "broken", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [{"type": "line", "start": [0, 0], "end": [1, 0]}], }]}, }) cdsl["features"].append({ "id": "broken_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 1}, "sketch_id": "broken", }) analysis = analyze_cdsl(cdsl) broken = next(item for item in analysis.feature_results if item.feature_id == "broken_cut") self.assertEqual(broken.resolved_status, "blocked") self.assertIn("profile_resolution_failed", [item.code for item in broken.blockers]) def test_used_empty_analytic_profile_is_a_feature_level_blocker(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "construction_only", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": []}, }) cdsl["features"].append({ "id": "empty_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 1}, "sketch_id": "construction_only", }) analysis = analyze_cdsl(cdsl) empty = next(item for item in analysis.feature_results if item.feature_id == "empty_cut") self.assertIn("profile_no_closed_region", [item.code for item in empty.blockers]) def test_selector_resolver_refuses_equal_candidates(self) -> None: registry = TopologyRegistry() plane = PlaneSpec.from_mapping(_workplane()) registry.register(TopologyRecord("first", "plane", "f1", geometry=plane.as_dict(), value=plane)) registry.register(TopologyRecord("second", "plane", "f2", geometry=plane.as_dict(), value=plane)) resolution = registry.resolve({"kind": "plane", "geometry": plane.as_dict()}) self.assertEqual(resolution.status, "ambiguous") self.assertEqual(resolution.diagnostic.code, "selector_ambiguous") def test_selector_resolver_normalizes_legacy_solidworks_plane_evidence(self) -> None: registry = TopologyRegistry() registry.register(TopologyRecord( "face", "face", "f1", "body:f1", geometry={ "surface_type": "plane", "plane_normal": [1, 0, 0], "plane_offset_mm": 12.0, "bbox_mm": [12, -2, -3, 12, 2, 3], "area_mm2": 24, }, )) resolution = registry.resolve({ "kind": "face", "geometry": { "surface": {"type": "plane", "parameters": [-1, 0, 0, -0.012, 0, 0]}, "box": [0.012, -0.002, -0.003, 0.012, 0.002, 0.003], "area": 0.000024, }, }) self.assertEqual(resolution.status, "resolved") self.assertEqual(resolution.record.record_id, "face") def test_owner_selector_survives_a_later_body_snapshot_when_geometry_is_unchanged(self) -> None: registry = TopologyRegistry() geometry = { "bbox_mm": [0, 0, 0, 0, 0, 10], "center_mm": [0, 0, 5], "length_mm": 10.0, "curve_type": "line", "start_mm": [0, 0, 0], "end_mm": [0, 0, 10], } registry.replace_body_topology("base_add", "body:base_add", [ TopologyRecord("body:base_add:edge:0", "edge", "base_add", "body:base_add", geometry, "old-edge"), ]) registry.replace_body_topology("later_cut", "body:later_cut", [ TopologyRecord("body:later_cut:edge:3", "edge", "later_cut", "body:later_cut", geometry, "current-edge"), ]) resolution = registry.resolve( {"kind": "edge", "owner_feature_id": "base_add", "geometry": geometry}, active_body_id="body:later_cut", ) self.assertEqual(resolution.status, "resolved") self.assertEqual(resolution.record.value, "current-edge") self.assertEqual(resolution.record.feature_id, "later_cut") self.assertEqual(resolution.record.owner_feature_ids, ("base_add",)) def test_ambiguous_predecessors_do_not_invent_topology_ownership(self) -> None: registry = TopologyRegistry() geometry = {"center_mm": [1, 2, 3]} registry.replace_body_topology("base_add", "body:base_add", [ TopologyRecord("body:base_add:vertex:0", "vertex", "base_add", "body:base_add", geometry), TopologyRecord("body:base_add:vertex:1", "vertex", "base_add", "body:base_add", geometry), ]) registry.replace_body_topology("later_cut", "body:later_cut", [ TopologyRecord("body:later_cut:vertex:0", "vertex", "later_cut", "body:later_cut", geometry), ]) resolution = registry.resolve( {"kind": "vertex", "owner_feature_id": "base_add", "geometry": geometry}, active_body_id="body:later_cut", ) self.assertEqual(resolution.status, "not_found") def test_changed_adjacency_prevents_owner_provenance_transfer(self) -> None: registry = TopologyRegistry() unchanged_geometry = { "bbox_mm": [0, 0, 0, 1, 1, 0], "center_mm": [0.5, 0.5, 0], "normal": [0, 0, 1], "area_mm2": 1, "surface_type": "plane", } registry.replace_body_topology("base_add", "body:base_add", [ TopologyRecord( "body:base_add:face:0", "face", "base_add", "body:base_add", {**unchanged_geometry, "adjacency_signature": ["line:1.000000:2"]}, ), ]) registry.replace_body_topology("later_cut", "body:later_cut", [ TopologyRecord( "body:later_cut:face:0", "face", "later_cut", "body:later_cut", {**unchanged_geometry, "adjacency_signature": ["line:1.000000:2", "circle:1.000000:1"]}, ), ]) resolution = registry.resolve( {"kind": "face", "owner_feature_id": "base_add", "geometry": unchanged_geometry}, active_body_id="body:later_cut", ) self.assertEqual(resolution.status, "not_found") def test_batch_analysis_writes_one_report_per_input(self) -> None: document = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "batch-part", "meta": {"unit": "mm"}, "geometry": {"sketches": [{"id": "s", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}}]}, "features": [{ "id": "f", "atomic_id": "reference_plane", "depends_on": [], "execution_status": "deferred", "params": {"plane": _workplane()}, }], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) input_path = root / "input" input_path.mkdir() (input_path / "batch-part.cdsl.json").write_text(json.dumps(document), encoding="utf-8") manifest = batch_analyze(input_path, root / "out", atomic_ids=frozenset({"reference_plane"})) report = json.loads((root / "out" / "parts" / "batch-part.report.json").read_text(encoding="utf-8")) self.assertEqual(manifest["part_count"], 1) self.assertTrue(report["semantic_valid"]) self.assertFalse(report["runtime_eligible"]) self.assertEqual(report["first_blocker"]["code"], "no_solid_feature") def test_batch_analysis_part_filter_is_exact_and_rejects_unknown_ids(self) -> None: document = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) input_path = root / "input" input_path.mkdir() for part_id in ("first", "second"): (input_path / f"{part_id}.cdsl.json").write_text( json.dumps({**document, "part_id": part_id}), encoding="utf-8", ) manifest = batch_analyze( input_path, root / "out", atomic_ids=frozenset({"reference_plane"}), part_ids=["second"], ) self.assertEqual(manifest["part_count"], 1) self.assertEqual(manifest["results"], [{"part_id": "second", "report": "parts/second.report.json"}]) with self.assertRaisesRegex(ValueError, "do not exist"): batch_analyze(input_path, root / "bad", part_ids=["missing"]) def test_batch_resume_migrates_derived_failure_category_without_rebuilding(self) -> None: document = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "cached", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) source = root / "input" source.mkdir() (source / "cached.cdsl.json").write_text(json.dumps(document), encoding="utf-8") report_path = root / "out" / "parts" / "cached.report.json" report_path.parent.mkdir(parents=True) report_path.write_text(json.dumps({ "part_id": "cached", "semantic_valid": True, "runtime_eligible": False, "compiled": False, "build_attempted": False, "built": False, "geometry_verified": False, "feature_results": [], "first_blocker": {"code": "missing_host_face"}, }), encoding="utf-8") manifest = batch_analyze(source, root / "out") report = json.loads(report_path.read_text(encoding="utf-8")) self.assertEqual(manifest["failure_category_counts"], {"input_incomplete": 1}) self.assertEqual(report["failure_category"], "input_incomplete") def test_full_export_batch_has_a_machine_readable_report_for_every_input(self) -> None: """Keep the shipped 998-part corpus on the capability-report path. STEP construction is intentionally left to the resumable build job; this CI-sized pass proves that every current export gets a semantic result and a first blocker rather than being silently skipped. """ source = ROOT / "json_to_cdsl" / "output" input_count = len(list(source.glob("*.cdsl.json"))) self.assertGreater(input_count, 0) with tempfile.TemporaryDirectory() as directory: out = Path(directory) / "baseline" manifest = batch_analyze(source, out) reports = [json.loads(path.read_text(encoding="utf-8")) for path in (out / "parts").glob("*.report.json")] self.assertEqual(manifest["part_count"], input_count) self.assertEqual(manifest["completed_count"], input_count) self.assertTrue(manifest["complete"]) self.assertEqual(manifest["semantic_valid_count"], input_count) self.assertEqual(len(reports), input_count) self.assertTrue(all(report["runtime_eligible"] or report.get("first_blocker") for report in reports)) def test_p3_static_pool_is_explicit_and_reports_missing_capture_separately(self) -> None: from cdsl_engine.phase_pools import select_p3_static_pool from cdsl_engine.runtime import analyze_cdsl source = ROOT / "json_to_cdsl" / "output" part_ids = select_p3_static_pool(source) self.assertEqual(len(part_ids), 266) ineligible: dict[str, set[str]] = {} for part_id in part_ids: cdsl = json.loads((source / f"{part_id}.cdsl.json").read_text(encoding="utf-8")) result = analyze_cdsl(cdsl) if not result.runtime_eligible: ineligible[part_id] = { blocker.code for feature in result.feature_results for blocker in feature.blockers } self.assertEqual(len(part_ids) - len(ineligible), 262) self.assertEqual( ineligible, { "027784": {"missing_revolve_axis"}, "104237": {"missing_extent_reference"}, "239358": {"missing_extent_reference", "dependency_unavailable"}, "241720": {"missing_extent_reference"}, }, ) def test_p3_phase_pool_can_be_passed_to_batch_rebuild(self) -> None: from cdsl_engine.phase_pools import select_p3_static_pool source = ROOT / "json_to_cdsl" / "output" pool = select_p3_static_pool(source) with tempfile.TemporaryDirectory() as directory: manifest = batch_analyze(source, Path(directory) / "p3", part_ids=pool, max_parts=1) self.assertEqual(manifest["part_count"], len(pool)) self.assertEqual(manifest["completed_count"], 1) self.assertFalse(manifest["complete"]) def test_phase_pool_membership_is_regressed_for_p4_and_p6(self) -> None: from cdsl_engine.phase_pools import select_static_phase_pool source = ROOT / "json_to_cdsl" / "output" self.assertEqual(len(select_static_phase_pool(source, "p4")), 309) self.assertEqual(len(select_static_phase_pool(source, "p6")), 341) with self.assertRaisesRegex(ValueError, "Unknown CDSL runtime phase"): select_static_phase_pool(source, "p5") def test_nested_pattern_source_is_replayable_after_its_first_execution(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].extend([ { "id": "first_pattern", "atomic_id": "pattern_linear", "depends_on": ["base_add"], "params": {"source_feature_ids": ["base_add"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2}, }, { "id": "nested_pattern", "atomic_id": "pattern_linear", "depends_on": ["first_pattern"], "params": {"source_feature_ids": ["first_pattern"], "direction_1": [0, 1, 0], "spacing_1_mm": 20, "pattern_count_1": 2}, }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "nested.step") self.assertEqual(result["engine"], "cdsl_session_runtime") def test_pattern_source_without_body_definition_is_blocked_in_preflight(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].insert(0, { "id": "context", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": _workplane()}, }) cdsl["features"].append({ "id": "pattern", "atomic_id": "pattern_linear", "depends_on": ["base_add", "context"], "params": {"source_feature_ids": ["context"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2}, }) analysis = analyze_cdsl(cdsl) pattern = next(result for result in analysis.feature_results if result.feature_id == "pattern") self.assertEqual(pattern.resolved_status, "unsupported") self.assertIn("unsupported_pattern_source", [blocker.code for blocker in pattern.blockers]) def test_truth_comparison_keeps_frame_mismatch_distinct_from_verified_geometry(self) -> None: truth = {"bounding_box_mm": [-10, -200, -10, 10, 200, 10], "solid_count": 1} common = { "truth": truth, "volume_relative_error": 1e-8, "area_relative_error": 1e-8, "solid_count_matches": True, } self.assertEqual( _verification_classification( actual_box=[-10, 990, 0, 10, 1010, 400], box_delta=1190, **common, ), "coordinate_frame_mismatch_candidate", ) self.assertEqual( _verification_classification( actual_box=[-10, -200, -10, 10, 200, 10], box_delta=0, **common, ), "verified", ) self.assertEqual( _verification_classification( actual_box=[-10, 990, 0, 10, 1010, 400], box_delta=1190, truth=truth, volume_relative_error=0.1, area_relative_error=1e-8, solid_count_matches=True, ), "geometry_mismatch", ) def test_batch_failure_categories_preserve_input_selector_capability_and_occ_boundaries(self) -> None: self.assertEqual(_failure_category({"geometry_verified": True}), "geometry_verified") self.assertEqual(_failure_category({"runtime_eligible": True, "build_attempted": False}), "runtime_eligible_not_built") self.assertEqual(_failure_category({"built": True, "numeric_comparison": {"classification": "coordinate_frame_mismatch_candidate"}}), "coordinate_frame_mismatch_candidate") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "missing_host_face"}}), "input_incomplete") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "extent_target_not_reached"}}), "input_incomplete") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "selector_ambiguous"}}), "selector_resolution") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "unsupported_hole_subtype"}}), "unsupported_capability") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "build_timeout"}}), "occ_execution_failure") def test_mirror_pattern_replays_a_selector_free_cut(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "mirror-test", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "base", "workplane": _workplane(), "profile": _rectangle([-5, -5], [5, 5])}, {"id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [2, 0], "radius_mm": 1}}, ]}, "features": [ {"id": "ref", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}}}, {"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": ["ref"], "params": {"distance_mm": 2}, "sketch_id": "base"}, {"id": "cut_1", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 2}, "sketch_id": "cut"}, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["cut_1", "ref"], "params": {"source_feature_ids": ["cut_1"], "mirror_plane": {"kind": "plane", "stable_id": "source", "source": "solidworks", "confidence": 1, "owner_feature_id": "ref"}}, "selectors": [{"kind": "plane", "stable_id": "source", "source": "solidworks", "confidence": 1, "owner_feature_id": "ref"}], }, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirror.step") self.assertAlmostEqual(result["volume_mm3"], 200 - 4 * 3.141592653589793, places=5) self.assertIn("mirror", [item["feature_id"] for item in result["feature_results"]]) def test_mirrored_local_circle_preserves_its_reflected_world_position(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter from cdsl_engine.runtime import _mirrored_sketch from cdsl_engine.sketch_solver import resolve_all_sketches cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "circles", "workplane": _workplane(), "profile": {"type": "circle", "center": [2, 3], "radius_mm": 1}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] mirror_plane = PlaneSpec.from_mapping({ "origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0], }) mirrored = _mirrored_sketch(sketch, mirror_plane) face = Build123dGeometryAdapter().faces_for_sketch(mirrored)[0] center = face.center() self.assertAlmostEqual(center.X, -2.0) self.assertAlmostEqual(center.Y, 3.0) self.assertAlmostEqual(center.Z, 0.0) def test_fillet_and_hole_wizard_use_resolved_face_edge_selectors(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") edge = next(item for item in baseline["topology_records"] if item["kind"] == "edge") top_face = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["geometry"]["surface_type"] == "plane" and item["geometry"]["normal"][2] > 0.9 ) with_fillet = deepcopy(base) with_fillet["features"].append({ "id": "fillet", "atomic_id": "fillet", "depends_on": ["base_add"], "params": {"radius_mm": 1}, "selectors": [{"kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"]}], }) filleted = rebuild_cdsl(with_fillet, root / "fillet.step") with_hole = deepcopy(base) with_hole["features"].append({ "id": "hole", "atomic_id": "hole_wizard", "depends_on": ["base_add"], "params": { "hole_type": "plain", "diameter_mm": 2, "depth_mm": 5, "end_condition": {"type": "blind", "solidworks_code": 0}, "positions": [{"mm": [0, 0, 10]}], "host_face": {"kind": "face", "stable_id": "top", "source": "inferred_from_step", "confidence": 1, "geometry": top_face["geometry"]}, }, "selectors": [{"kind": "face", "stable_id": "top", "source": "inferred_from_step", "confidence": 1, "geometry": top_face["geometry"]}], }) holed = rebuild_cdsl(with_hole, root / "hole.step") self.assertLess(filleted["volume_mm3"], baseline["volume_mm3"]) self.assertAlmostEqual(holed["volume_mm3"], 1000 - 5 * 3.141592653589793, places=5) def test_hole_frame_uses_local_coordinates(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["features"].append({ "id": "hole", "atomic_id": "hole_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": {"origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1]}}, }, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(base, Path(directory) / "local-hole.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 5 * 3.141592653589793, places=5) def test_pattern_with_selector_source_is_blocked_before_execution(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].extend([ {"id": "fillet", "atomic_id": "fillet", "depends_on": ["base_add"], "params": {"radius_mm": 1}, "selectors": [{"kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1}]}, {"id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["fillet"], "params": {"source_feature_ids": ["fillet"], "direction_1": [1, 0, 0], "spacing_1_mm": 10, "pattern_count_1": 2}}, ]) analysis = analyze_cdsl(cdsl) pattern = next(item for item in analysis.feature_results if item.feature_id == "repeat") self.assertFalse(pattern.executable) self.assertIn("unsupported_pattern_selector_transform", [item.code for item in pattern.blockers]) def test_chamfer_and_linear_pattern_execute_without_selector_guessing(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [-2, 0], "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") edge = next(item for item in baseline["topology_records"] if item["kind"] == "edge") chamfered = deepcopy(base) chamfered["features"].append({ "id": "chamfer", "atomic_id": "chamfer", "depends_on": ["base_add"], "params": {"distance_mm": 1}, "selectors": [{"kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"]}], }) chamfer_result = rebuild_cdsl(chamfered, root / "chamfer.step") patterned = deepcopy(base) patterned["features"].extend([ {"id": "cut_1", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 10}, "sketch_id": "cut"}, {"id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["cut_1"], "params": {"source_feature_ids": ["cut_1"], "direction_1": [1, 0, 0], "spacing_1_mm": 2, "pattern_count_1": 3}}, ]) pattern_result = rebuild_cdsl(patterned, root / "pattern.step") self.assertLess(chamfer_result["volume_mm3"], baseline["volume_mm3"]) self.assertAlmostEqual(pattern_result["volume_mm3"], 1000 - 3 * 10 * 3.141592653589793, places=5) def test_linear_pattern_replays_hole_with_explicit_host_frame(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].extend([ { "id": "hole_1", "atomic_id": "hole_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": { "origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1], }}, }, }, { "id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["hole_1"], "params": { "source_feature_ids": ["hole_1"], "direction_1": [1, 0, 0], "spacing_1_mm": 4, "pattern_count_1": 2, }, }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "patterned-holes.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 2 * 5 * 3.141592653589793, places=5) def test_mirror_pattern_replays_local_hole_coordinates_in_the_correct_quadrant(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].insert(0, { "id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}}, }) cdsl["features"].extend([ { "id": "hole_1", "atomic_id": "hole_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [2, 3, 0]}], "host_face": {"frame": { "origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1], }}, }, }, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["hole_1", "mirror_plane"], "params": { "source_feature_ids": ["hole_1"], "mirror_plane": {"kind": "plane", "owner_feature_id": "mirror_plane"}, }, "selectors": [{"kind": "plane", "owner_feature_id": "mirror_plane"}], }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirrored-holes.step") mirrored_cylinder = next( item for item in result["topology_records"] if item["record_id"].startswith("body:mirror.m.hole_1:face") and item["geometry"].get("surface_type") == "cylinder" and item["geometry"]["bbox_mm"][0] < -2.9 ) self.assertEqual(mirrored_cylinder["geometry"]["bbox_mm"][:2], [-3.0, 2.0]) self.assertEqual(mirrored_cylinder["geometry"]["bbox_mm"][3:5], [-1.0, 4.0]) def test_pattern_replays_selected_sources_in_history_order(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) cdsl["features"].extend([ { "id": "cut_1", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 10}, "sketch_id": "cut", }, { "id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["cut_1"], # SolidWorks selection exports are not guaranteed to match # history order; the executor must replay the boss before its cut. "params": { "source_feature_ids": ["cut_1", "base_add"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2, }, }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "ordered-pattern.step") self.assertAlmostEqual(result["volume_mm3"], 2 * (1000 - 10 * 3.141592653589793), places=5) def test_through_all_extent_uses_current_body(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) cdsl["features"].append({ "id": "cut_all", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 0, "end_condition": {"type": "through_all", "solidworks_code": 1}}, "sketch_id": "cut", }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "through.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 10 * 3.141592653589793, places=5) def test_two_sided_extrude_uses_independent_forward_and_reverse_distances(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() feature = cdsl["features"][0] feature["atomic_id"] = "extrude_add_two_sided" feature["params"] = { "distance_mm": 2, "reverse_distance_mm": 3, "end_condition": {"type": "blind"}, "reverse_end_condition": {"type": "blind"}, } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "two-sided.step") self.assertAlmostEqual(result["volume_mm3"], 500.0) self.assertEqual(result["bbox_mm"]["min"][2], -3.0) self.assertEqual(result["bbox_mm"]["max"][2], 2.0) def test_two_sided_extrude_requires_reverse_distance_contract(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() feature = cdsl["features"][0] feature["atomic_id"] = "extrude_add_two_sided" analysis = analyze_cdsl(cdsl) self.assertIn("missing_parameter", [item.code for item in analysis.feature_results[0].blockers]) def test_revolve_can_resolve_an_owner_qualified_reference_axis(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "axis-revolve", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "profile", "workplane": _workplane(), "profile": _rectangle([2, 1], [4, 2]), }]}, "features": [ { "id": "axis", "atomic_id": "reference_axis", "depends_on": [], "params": {"axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]}}, }, { "id": "revolve", "atomic_id": "revolve_add", "depends_on": ["axis"], "sketch_id": "profile", "params": { "angle_deg": 360, "axis": {"selector": {"kind": "axis", "owner_feature_id": "axis"}}, }, "selectors": [{"kind": "axis", "owner_feature_id": "axis"}], }, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "axis-revolve.step") self.assertAlmostEqual(result["volume_mm3"], 6 * 3.141592653589793, places=5) def test_unowned_revolve_feature_selector_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = json.loads((ROOT / "json_to_cdsl" / "output" / "027784.cdsl.json").read_text(encoding="utf-8")) analysis = analyze_cdsl(cdsl) revolve = next(item for item in analysis.feature_results if item.feature_id == "f_007") self.assertIn("missing_revolve_axis", [item.code for item in revolve.blockers]) def test_tangent_propagation_does_not_expand_to_unrelated_box_edges(self) -> None: from build123d import Box from cdsl_engine.build123d_adapter import Build123dGeometryAdapter body = Box(10, 10, 10) selected = body.edges()[0] expanded = Build123dGeometryAdapter().tangent_edges(body, [selected]) self.assertEqual(len(expanded), 1) self.assertTrue(expanded[0].is_same(selected)) def test_inconsistent_quarter_arc_flags_are_normalized_from_loop_orientation(self) -> None: from cdsl_engine.batch_rebuild import analyze_document fixture = ROOT / "json_to_cdsl" / "output" / "053393.cdsl.json" with tempfile.TemporaryDirectory() as directory: report = analyze_document(fixture, out_step=Path(directory) / "053393.step") self.assertTrue(report["runtime_eligible"]) self.assertTrue(report["built"]) self.assertTrue(report["geometry_verified"]) def test_exported_two_sided_fixture_is_geometry_verified(self) -> None: from cdsl_engine.batch_rebuild import analyze_document fixture = ROOT / "json_to_cdsl" / "output" / "046112.cdsl.json" with tempfile.TemporaryDirectory() as directory: report = analyze_document(fixture, out_step=Path(directory) / "046112.step") self.assertTrue(report["runtime_eligible"]) self.assertTrue(report["built"]) self.assertTrue(report["geometry_verified"]) def test_selector_driven_extrude_extents_require_unique_rebuilt_topology(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") top_face = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["geometry"]["surface_type"] == "plane" and item["geometry"]["normal"][2] > 0.9 ) top_vertex = next( item for item in baseline["topology_records"] if item["kind"] == "vertex" and item["geometry"]["center_mm"][2] > 9.9 ) for name, end_condition, expected_depth in ( ("surface", {"type": "up_to_surface", "reference": {"kind": "face", "owner_feature_id": "base_add", "geometry": top_face["geometry"]}}, 10.0), ("vertex", {"type": "up_to_vertex", "reference": {"kind": "vertex", "owner_feature_id": "base_add", "geometry": top_vertex["geometry"]}}, 10.0), ("offset", {"type": "offset_from_surface", "reference": {"kind": "face", "owner_feature_id": "base_add", "geometry": top_face["geometry"]}}, 8.0), ("next", {"type": "through_next"}, 10.0), ): cdsl = deepcopy(base) cdsl["features"].append({ "id": f"cut_{name}", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 2 if name == "offset" else 0, "end_condition": end_condition}, "sketch_id": "cut", }) result = rebuild_cdsl(cdsl, root / f"{name}.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - expected_depth * 3.141592653589793, places=5) def test_up_to_body_extent_uses_a_uniquely_resolved_body_record(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") body = next(item for item in baseline["topology_records"] if item["kind"] == "body") cdsl = deepcopy(base) cdsl["features"].append({ "id": "cut_to_body", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 0, "end_condition": { "type": "up_to_body", "reference": { "kind": "body", "owner_feature_id": "base_add", "geometry": body["geometry"], }, }}, "sketch_id": "cut", }) result = rebuild_cdsl(cdsl, root / "up-to-body.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 10 * 3.141592653589793, places=5) def test_selector_dependent_extent_without_reference_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"][0]["params"]["end_condition"] = {"type": "up_to_surface"} analysis = analyze_cdsl(cdsl) result = analysis.feature_results[0] self.assertIn("missing_extent_reference", [item.code for item in result.blockers]) def test_hole_wizard_unsupported_extent_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].append({ "id": "hole", "atomic_id": "hole_wizard", "depends_on": ["base_add"], "params": { "hole_type": "plain", "diameter_mm": 2, "depth_mm": 2, "positions": [{"mm": [0, 0, 0]}], "host_face": {"kind": "face"}, "end_condition": {"type": "up_to_surface"}, }, }) analysis = analyze_cdsl(cdsl) result = next(item for item in analysis.feature_results if item.feature_id == "hole") self.assertIn("unsupported_hole_extent", [item.code for item in result.blockers]) def test_legacy_hole_atomics_have_the_same_host_and_shape_preflight_contract(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].append({ "id": "hole", "atomic_id": "hole_counterbore", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 3, "positions": [{"mm": [0, 0, 0]}], "counterbore_diameter_mm": 2, "counterbore_depth_mm": 1, }, }) analysis = analyze_cdsl(cdsl) result = next(item for item in analysis.feature_results if item.feature_id == "hole") codes = {item.code for item in result.blockers} self.assertIn("missing_host_face", codes) self.assertIn("invalid_hole_spec", codes) def test_body_mutation_without_a_preceding_solid_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "no-body", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [{ "id": "hole", "atomic_id": "hole_wizard", "depends_on": [], "params": { "hole_type": "plain", "diameter_mm": 2, "depth_mm": 3, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": _workplane()}, }, }], } result = analyze_cdsl(cdsl).feature_results[0] self.assertIn("missing_active_body", [item.code for item in result.blockers]) if __name__ == "__main__": unittest.main()