from __future__ import annotations import tempfile import unittest from pathlib import Path from cadfs_to_cdsl.dataset import Sample from cadfs_to_cdsl.reports import read_json from cadfs_to_cdsl.selector_candidate_demo import ( _query_candidate_groups, _search_candidate_records, _strip_selector_search_tokens, run_geometry_probe, strip_provenance_intents, ) def _contains_selector_intent(value: object) -> bool: if isinstance(value, dict): return "selector_intent" in value or any(_contains_selector_intent(child) for child in value.values()) if isinstance(value, list): return any(_contains_selector_intent(child) for child in value) return False class SelectorCandidateDemoTests(unittest.TestCase): @staticmethod def _query_group_candidate(query_family: str, source_entity: str | None = None, source_entities: list[str] | None = None) -> dict: intent = { "version": "1.0", "kind": "edge" if query_family == "SWEPT_EDGE" else "face", "query_family": query_family, "source_query": {"ast": {"call": "makeQuery", "args": ["owner", query_family]}}, "derivation_policy": {"allowed": ["boundary"], "multiplicity": "all_fragments" if query_family == "SWEPT_FACE" else "one"}, } if source_entity is not None: intent["source_entity"] = {"sketch_id": "S", "entity_id": source_entity} if source_entities is not None: intent["source_entities"] = [{"sketch_id": "S", "entity_id": entity_id} for entity_id in source_entities] return {"kind": intent["kind"], "owner_feature_id": "f_extrude", "selector_intent": intent} def _query_group_provenance(self, selector: dict) -> dict: return { "geometry": { "sketches": [{ "id": "sketch_S", "source_sketch_id": "S", "workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]}, "profile": {"type": "planar_imprint", "source_entities": [ {"id": "E1", "curve": {"type": "line", "start": [0, 0], "end": [3, 0]}}, {"id": "E2", "curve": {"type": "line", "start": [3, 0], "end": [3, 2]}}, ]}, }], }, "features": [ {"id": "f_extrude", "atomic_id": "extrude_add_blind", "sketch_id": "sketch_S", "params": {"distance_mm": 5}}, {"id": "f_consumer", "selectors": [selector]}, ], } def test_search_candidates_excludes_records_below_normal_resolution_threshold(self) -> None: resolution = type("Resolution", (), { "candidates": ( {"record_id": "low", "score": 0.5}, {"record_id": "high-b", "score": 1.0}, {"record_id": "high-a", "score": 1.0}, {"record_id": "zero", "score": 0.0}, ), })() candidates, count = _search_candidate_records(resolution, maximum=8) self.assertEqual(count, 2) self.assertEqual([item["record_id"] for item in candidates], ["high-a", "high-b"]) def test_strip_provenance_intents_removes_nested_selector_metadata(self) -> None: value = { "selector_intent": {"version": "1.0"}, "output_role": "extrude.end", "output_role_source": {"owner_feature_id": "f0", "output_role": "extrude.end"}, "params": {"reference": {"selector_intent_version": "1.0"}}, } self.assertEqual(strip_provenance_intents(value), 2) self.assertEqual(value, {"params": {"reference": {}}}) def test_query_group_keeps_all_ranked_swept_face_fragments_together(self) -> None: selector = self._query_group_candidate("SWEPT_FACE", source_entity="E1") provenance = self._query_group_provenance(selector) resolution = type("Resolution", (), {"candidates": ( {"record_id": "fragment-a", "score": 1.0, "geometry": {"surface_type": "plane", "plane_normal": [0, -1, 0], "plane_offset_mm": 0}}, {"record_id": "fragment-b", "score": 1.0, "geometry": {"surface_type": "plane", "plane_normal": [0, 1, 0], "plane_offset_mm": 0}}, {"record_id": "wrong-wall", "score": 1.0, "geometry": {"surface_type": "plane", "plane_normal": [1, 0, 0], "plane_offset_mm": 3}}, )})() descriptor, groups, count = _query_candidate_groups( provenance, "f_consumer:selectors[0]", selector, resolution, maximum=8, ) self.assertEqual(count, 1) self.assertEqual(descriptor["ranking_status"], "source_geometry_ranked") self.assertEqual(descriptor["multiplicity"], "all_fragments") self.assertEqual([record["record_id"] for record in groups[0]["records"]], ["fragment-a", "fragment-b"]) self.assertEqual(groups[0]["ranking"]["method"], "all_fragments_source_geometry") def test_query_group_ranks_the_source_pair_vertical_edge(self) -> None: selector = self._query_group_candidate("SWEPT_EDGE", source_entities=["E1", "E2"]) provenance = self._query_group_provenance(selector) resolution = type("Resolution", (), {"candidates": ( {"record_id": "source-vertex", "score": 1.0, "geometry": {"curve_type": "line", "start_mm": [3, 0, 0], "end_mm": [3, 0, 5]}}, {"record_id": "other-vertex", "score": 1.0, "geometry": {"curve_type": "line", "start_mm": [0, 0, 0], "end_mm": [0, 0, 5]}}, )})() descriptor, groups, count = _query_candidate_groups( provenance, "f_consumer:selectors[0]", selector, resolution, maximum=8, ) self.assertEqual(count, 1) self.assertEqual(descriptor["query_family"], "SWEPT_EDGE") self.assertEqual(groups[0]["records"][0]["record_id"], "source-vertex") self.assertEqual(groups[0]["ranking"]["method"], "source_pair_extrusion_vertex") def test_query_group_ranks_the_line_arc_intersection_edge(self) -> None: selector = self._query_group_candidate("SWEPT_EDGE", source_entities=["E1", "E2"]) provenance = self._query_group_provenance(selector) provenance["geometry"]["sketches"][0]["profile"]["source_entities"][0]["curve"] = { "type": "arc", "start": [-1, 0], "end": [1, 0], "center": [0, 0], "radius_mm": 1, "clockwise": True, } provenance["geometry"]["sketches"][0]["profile"]["source_entities"][1]["curve"] = { "type": "line", "start": [0, -2], "end": [0, 2], } resolution = type("Resolution", (), {"candidates": ( {"record_id": "arc-line-intersection", "score": 1.0, "geometry": {"curve_type": "line", "start_mm": [0, 1, 0], "end_mm": [0, 1, 5]}}, {"record_id": "unrelated-vertical", "score": 1.0, "geometry": {"curve_type": "line", "start_mm": [0, -1, 0], "end_mm": [0, -1, 5]}}, )})() _descriptor, groups, count = _query_candidate_groups( provenance, "f_consumer:selectors[0]", selector, resolution, maximum=8, ) self.assertEqual(count, 1) self.assertEqual(groups[0]["records"][0]["record_id"], "arc-line-intersection") def test_query_group_ignores_a_candidate_missing_cylinder_axis_evidence(self) -> None: selector = self._query_group_candidate("SWEPT_FACE", source_entity="E1") provenance = self._query_group_provenance(selector) provenance["geometry"]["sketches"][0]["profile"]["source_entities"][0]["curve"] = { "type": "arc", "start": [-1, 0], "end": [1, 0], "center": [0, 0], "radius_mm": 1, "clockwise": True, } resolution = type("Resolution", (), {"candidates": ( {"record_id": "missing-axis", "score": 1.0, "geometry": {"surface_type": "cylinder", "radius_mm": 1}}, )})() descriptor, groups, count = _query_candidate_groups( provenance, "f_consumer:selectors[0]", selector, resolution, maximum=8, ) self.assertEqual(count, 1) self.assertEqual(descriptor["ranking_status"], "source_geometry_unavailable") self.assertEqual(groups[0]["records"][0]["record_id"], "missing-axis") def test_experiment_search_tokens_are_not_persisted(self) -> None: value = {"selectors": [{"_selector_search_token": "f:selectors[0]"}]} self.assertEqual(_strip_selector_search_tokens(value), 1) self.assertEqual(value, {"selectors": [{}]}) def test_geometry_probe_keeps_heuristic_success_separate_from_provenance(self) -> None: root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test" feature = root / "featurescript_rp/0000/00002243.txt" step = root / "step_abc/0000/00002243.step" if not feature.exists() or not step.exists(): self.skipTest("CADFS sample is not installed") sample = Sample("00002243", {"featurescript": str(feature), "step": str(step)}) with tempfile.TemporaryDirectory() as temporary: output = Path(temporary) report = run_geometry_probe(sample, output) record = read_json(output / "samples" / sample.sample_id / "selector-record.json") compiled = read_json(output / "samples" / sample.sample_id / "selector_record.compiled.cdsl.json") self.assertEqual(report["provenance"]["rebuild_status"], "rebuild_failed") self.assertEqual(report["geometry_probe"]["rebuild_status"], "rebuilt") self.assertEqual(report["geometry_probe"]["comparison"], { "decision": "strict_pass", "strict_passed": True, "rp_passed": True, }) self.assertEqual(report["strategy"]["classification"], "heuristic_only") self.assertEqual(report["selector_record"]["status"], "strict_replayed") self.assertEqual(report["selector_record"]["record_count"], 2) self.assertEqual(record["verification"], { "geometry_probe_strict": True, "compiled_cdsl_strict": True, "compiled_cdsl": report["selector_record"]["compiled_cdsl"], }) self.assertEqual([entry["mapping"] for entry in record["records"]], ["one_to_one", "one_to_one"]) self.assertTrue(all(entry["source_selector"]["selector_intent"]["query_family"] == "CAP_EDGE" for entry in record["records"])) self.assertFalse(_contains_selector_intent(compiled))