feat(cadfs): expand selector provenance coverage
This commit is contained in:
@@ -59,7 +59,8 @@ class IntegrationTests(unittest.TestCase):
|
||||
])
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
|
||||
rebuilt = Path(tmp) / "rebuild.step"
|
||||
outcome = rebuild_candidate(candidate, rebuilt)
|
||||
self.assertEqual(outcome["status"], "rebuilt")
|
||||
self.assertEqual(
|
||||
[item["feature_id"] for item in outcome["result"]["feature_results"]],
|
||||
@@ -123,7 +124,10 @@ class IntegrationTests(unittest.TestCase):
|
||||
[4.48, 2.36],
|
||||
)
|
||||
|
||||
self.assertNotIn("result_mode", next(item for item in result.cdsl["features"] if item["id"] == "f_F7")["params"])
|
||||
self.assertEqual(
|
||||
next(item for item in result.cdsl["features"] if item["id"] == "f_F7")["params"]["result_mode"],
|
||||
"new_body",
|
||||
)
|
||||
self.assertEqual(next(item for item in result.cdsl["features"] if item["id"] == "f_F9")["params"]["result_mode"], "new_body")
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
@@ -140,7 +144,8 @@ class IntegrationTests(unittest.TestCase):
|
||||
|
||||
candidate = lower_model(parse_featurescript(feature.read_text(), "00925274"), {}).cdsl
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
|
||||
rebuilt = Path(tmp) / "rebuild.step"
|
||||
outcome = rebuild_candidate(candidate, rebuilt)
|
||||
|
||||
self.assertEqual(outcome["status"], "rebuilt")
|
||||
result = outcome["result"]
|
||||
@@ -166,9 +171,13 @@ class IntegrationTests(unittest.TestCase):
|
||||
result = lower_model(parse_featurescript(feature.read_text(), "00007264"), {})
|
||||
fillet = next(item for item in result.cdsl["features"] if item["id"] == "f_F2")
|
||||
self.assertEqual(fillet["atomic_id"], "fillet")
|
||||
self.assertEqual(len(fillet["selectors"]), 1)
|
||||
query_set = fillet["selectors"][0]
|
||||
self.assertEqual(query_set["selector_intent"]["query_family"], "QUERY_SET")
|
||||
self.assertEqual(query_set["selector_intent"]["query_set_contract"], "proven_operand_union")
|
||||
self.assertTrue(all(
|
||||
selector["selector_intent"]["query_family"] == "SWEPT_EDGE"
|
||||
for selector in fillet["selectors"]
|
||||
operand["selector_intent"]["query_family"] == "SWEPT_EDGE"
|
||||
for operand in query_set["query_operands"]
|
||||
))
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
@@ -176,6 +185,8 @@ class IntegrationTests(unittest.TestCase):
|
||||
self.assertEqual(outcome["status"], "rebuilt")
|
||||
binding = next(item for item in outcome["selector_binding"] if item["feature_id"] == "f_F2")
|
||||
self.assertEqual(len(binding["resolved"]), 4)
|
||||
resolution = next(item for item in outcome["result"]["selector_resolution"] if item["feature_id"] == "f_F2")
|
||||
self.assertEqual(resolution["resolution_mode"], "query_set_union")
|
||||
vertical_edges = [
|
||||
relation
|
||||
for delta in outcome["result"]["topology_deltas"]
|
||||
@@ -287,11 +298,13 @@ class IntegrationTests(unittest.TestCase):
|
||||
self.assertNotIn("stable_id", selector)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
|
||||
rebuilt = Path(tmp) / "rebuild.step"
|
||||
outcome = rebuild_candidate(candidate, rebuilt)
|
||||
self.assertTrue(rebuilt.exists())
|
||||
|
||||
self.assertEqual(outcome["status"], "rebuild_failed")
|
||||
self.assertEqual(outcome["error"]["message"], "f_F4: selector_query_unsupported during incremental replay")
|
||||
self.assertEqual(outcome["status"], "runtime_ineligible")
|
||||
prefix = outcome["last_executable_prefix"]["result"]
|
||||
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F3")
|
||||
self.assertEqual([item["feature_id"] for item in prefix["feature_results"]], ["f_F1", "f_F2", "f_F3"])
|
||||
resolution = next(item for item in prefix["selector_resolution"] if item["feature_id"] == "f_F2")
|
||||
self.assertEqual(resolution["status"], "resolved")
|
||||
@@ -359,7 +372,7 @@ class IntegrationTests(unittest.TestCase):
|
||||
result = lower_model(parse_featurescript(feature.read_text(), "00542223"), {})
|
||||
sweep = next(item for item in result.cdsl["features"] if item["id"] == "f_F5")
|
||||
segment = sweep["params"]["path"]["segment"]
|
||||
self.assertEqual(result.status, "converted_complete")
|
||||
self.assertEqual(result.status, "converted_partial")
|
||||
self.assertEqual(sweep["atomic_id"], "sweep_add")
|
||||
self.assertEqual(sweep["sketch_id"], "sketch_F4")
|
||||
self.assertEqual(segment["points"], [[-40.0, -50.0], [-20.0, -14.96], [0.0, 5.0]])
|
||||
@@ -367,6 +380,7 @@ class IntegrationTests(unittest.TestCase):
|
||||
self.assertEqual(segment["end_tangent"], [52.88, 49.52])
|
||||
self.assertNotIn("F2", [item.get("feature_id") for item in result.diagnostics])
|
||||
self.assertNotIn("F5", [item.get("feature_id") for item in result.diagnostics])
|
||||
self.assertIn("F7", [item.get("feature_id") for item in result.diagnostics])
|
||||
|
||||
def test_direct_sketch_wire_qbodytype_path_lowers_and_rebuilds_00896761(self):
|
||||
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
|
||||
@@ -390,17 +404,18 @@ class IntegrationTests(unittest.TestCase):
|
||||
self.assertEqual(outcome["status"], "rebuilt")
|
||||
self.assertEqual([item["feature_id"] for item in outcome["result"]["feature_results"]], ["f_F2"])
|
||||
|
||||
def test_multi_entity_sketch_wire_qbodytype_path_remains_deferred_00786708(self):
|
||||
def test_multi_entity_sketch_wire_qbodytype_path_lowers_as_a_source_ordered_wire_00786708(self):
|
||||
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
|
||||
feature = root / "featurescript_rp/0078/00786708.txt"
|
||||
if not feature.exists(): self.skipTest("CADFS sample is not installed")
|
||||
|
||||
result = lower_model(parse_featurescript(feature.read_text(), "00786708"), {})
|
||||
self.assertIn({
|
||||
"code": "unsupported_engine_capability", "feature_id": "F2", "operation": "sweep", "capability": "sweep_path_query",
|
||||
"message": "current CDSL sweep requires one direct sketch line or B-spline path",
|
||||
}, result.diagnostics)
|
||||
self.assertNotIn("f_F2", {item["id"] for item in result.cdsl["features"]})
|
||||
self.assertEqual(result.status, "converted_complete")
|
||||
first_sweep = next(item for item in result.cdsl["features"] if item["id"] == "f_F2")
|
||||
self.assertEqual(
|
||||
[(item["source_entity_id"], item["type"]) for item in first_sweep["params"]["path"]["segments"]],
|
||||
[("E1", "arc"), ("E0", "line"), ("E2.MirrorCS", "arc")],
|
||||
)
|
||||
later_sweep = next(item for item in result.cdsl["features"] if item["id"] == "f_F5")
|
||||
self.assertEqual(later_sweep["params"]["path"]["segment"]["source_entity_id"], "E5")
|
||||
|
||||
@@ -417,14 +432,13 @@ class IntegrationTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
rebuilt = rebuild_cdsl(candidate, Path(tmp) / "pattern.step")
|
||||
|
||||
self.assertEqual(rebuilt["solid_count"], 1)
|
||||
bbox = rebuilt["bbox_mm"]
|
||||
self.assertAlmostEqual(bbox["min"][0], -52.14101625137758)
|
||||
self.assertAlmostEqual(bbox["min"][1], -57.500000100000065)
|
||||
self.assertAlmostEqual(bbox["max"][0], 52.14101625137762)
|
||||
self.assertAlmostEqual(bbox["max"][1], 37.5000001000001)
|
||||
self.assertEqual(rebuilt["solid_count"], 2)
|
||||
self.assertEqual(
|
||||
[item["feature_id"] for item in rebuilt["feature_results"]],
|
||||
["f_F1", "f_F3", "f_F5", "f_F6"],
|
||||
)
|
||||
|
||||
def test_fused_body_circular_copy_faces_preserve_shell_prefix_00542223(self):
|
||||
def test_deferred_pattern_shell_keeps_later_executable_feature_00542223(self):
|
||||
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
|
||||
feature = root / "featurescript_rp/0054/00542223.txt"
|
||||
if not feature.exists(): self.skipTest("CADFS sample is not installed")
|
||||
@@ -433,9 +447,11 @@ class IntegrationTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
|
||||
|
||||
self.assertEqual(outcome["status"], "rebuild_failed")
|
||||
self.assertEqual(outcome["error"]["message"], "f_F7: selector_query_unsupported during incremental replay")
|
||||
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F6")
|
||||
self.assertEqual(outcome["status"], "rebuilt")
|
||||
self.assertEqual(
|
||||
[item["feature_id"] for item in outcome["result"]["feature_results"]],
|
||||
["f_F1", "f_F3", "f_F5", "f_F6", "f_F10"],
|
||||
)
|
||||
|
||||
def test_face_chamfer_source_query_is_not_geometry_bound_00111611(self):
|
||||
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@ import unittest
|
||||
from cadfs_to_cdsl.featurescript_lexer import lex
|
||||
from cadfs_to_cdsl.featurescript_parser import parse_featurescript
|
||||
from cadfs_to_cdsl.ir import Call
|
||||
from cadfs_to_cdsl.lowering import _queries, _source_refs
|
||||
from cadfs_to_cdsl.query_parser import parse_query
|
||||
from cadfs_to_cdsl.lowering import _number, _queries, _source_refs
|
||||
from cadfs_to_cdsl.query_parser import parse_query, query_expr
|
||||
from cadfs_to_cdsl.units import length_mm
|
||||
|
||||
|
||||
@@ -28,6 +28,14 @@ TRANSFORM_SOURCE = SOURCE.replace(
|
||||
''',
|
||||
)
|
||||
|
||||
UNKNOWN_FEATURE_SOURCE = SOURCE.replace(
|
||||
'\n});\n',
|
||||
'''
|
||||
assignVariable(context, id + "F2", {"name" : "diameter", "value" : 10 * mm});
|
||||
});
|
||||
''',
|
||||
)
|
||||
|
||||
|
||||
class ParserTests(unittest.TestCase):
|
||||
def test_lexer_ignores_comments_and_preserves_lines(self):
|
||||
@@ -47,6 +55,10 @@ class ParserTests(unittest.TestCase):
|
||||
self.assertEqual(model.features[-1].operation, "transform")
|
||||
self.assertEqual(model.features[-1].params["transformType"], "TransformType.TRANSLATION_3D")
|
||||
|
||||
def test_unknown_direct_context_feature_is_preserved_for_capability_diagnostics(self):
|
||||
model = parse_featurescript(UNKNOWN_FEATURE_SOURCE, "unknown-feature")
|
||||
self.assertEqual([(feature.feature_id, feature.operation) for feature in model.features], [("F1", "extrude"), ("F2", "assignVariable")])
|
||||
|
||||
def test_query_parser(self):
|
||||
query = Call("makeQuery", [Call("__binary__", ["id", "+", "F1.opExtrude"]), "CAP_EDGE", "EDGE", {"isStart": False, "x": Call("sQuery", [Call("__binary__", ["id", "+", "F0.wireOp"]), "EDGE", "E0"])}])
|
||||
value = parse_query(query)
|
||||
@@ -55,6 +67,43 @@ class ParserTests(unittest.TestCase):
|
||||
self.assertEqual(value.ast["call"], "makeQuery")
|
||||
self.assertEqual(value.ast["args"][0]["call"], "__binary__")
|
||||
|
||||
def test_signed_parenthesized_scalar_preserves_unit_expression_semantics(self):
|
||||
model = parse_featurescript(SOURCE.replace(
|
||||
'"depth":120 * mm', '"depth":-(10 + 2) / 2 * mm', 1,
|
||||
), "signed-scalar")
|
||||
depth = model.features[-1].params["depth"]
|
||||
self.assertEqual((depth.name, depth.args[1]), ("__binary__", "*"))
|
||||
self.assertEqual(_number(depth), -6.0)
|
||||
|
||||
def test_query_expression_preserves_nested_set_and_filter_boundaries(self):
|
||||
source = Call("qUnion", [[
|
||||
Call("qConstructionFilter", [
|
||||
Call("qBodyType", [
|
||||
Call("qCreatedBy", [Call("__binary__", ["id", "+", "F1"]), "EDGE"]),
|
||||
"BodyType.WIRE",
|
||||
]),
|
||||
"ConstructionObject.NO",
|
||||
]),
|
||||
Call("qSubtraction", [
|
||||
Call("sQuery", [Call("__binary__", ["id", "+", "F0.wireOp"]), "EDGE", "E0"]),
|
||||
Call("sQuery", [Call("__binary__", ["id", "+", "F0.wireOp"]), "EDGE", "E1"]),
|
||||
]),
|
||||
]])
|
||||
expression = query_expr(source)
|
||||
self.assertEqual(expression["version"], "1.0")
|
||||
self.assertEqual(expression["root"]["node"], "set")
|
||||
self.assertEqual(expression["root"]["operator"], "union")
|
||||
filtered, subtraction = expression["root"]["operands"]
|
||||
self.assertEqual((filtered["node"], filtered["filter"]), ("filter", "construction"))
|
||||
self.assertEqual((filtered["input"]["node"], filtered["input"]["filter"]), ("filter", "body_type"))
|
||||
self.assertEqual((subtraction["node"], subtraction["operator"]), ("set", "subtraction"))
|
||||
parsed = parse_query(source)
|
||||
self.assertEqual(parsed.query_combinators, ["qUnion", "qSubtraction"])
|
||||
self.assertEqual(
|
||||
parsed.filters,
|
||||
["qConstructionFilter", "qBodyType"],
|
||||
)
|
||||
|
||||
def test_source_version_and_standard_library_are_retained(self):
|
||||
source = '''FeatureScript 1511;
|
||||
import(path : "onshape/std/geometry.fs", version : "1511.0");
|
||||
|
||||
@@ -3,7 +3,9 @@ from __future__ import annotations
|
||||
import json, tempfile, unittest
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
from cadfs_to_cdsl.reports import write_json
|
||||
from cadfs_to_cdsl.dataset import Sample
|
||||
from cadfs_to_cdsl.operation_registry import build_operation_registry
|
||||
from cadfs_to_cdsl.reports import generate_markdown_report, write_json
|
||||
|
||||
|
||||
class ReportTests(unittest.TestCase):
|
||||
@@ -17,5 +19,36 @@ class ReportTests(unittest.TestCase):
|
||||
self.assertIn(payload["index"], range(64))
|
||||
self.assertEqual(list(path.parent.glob(".summary.json.*.tmp")), [])
|
||||
|
||||
def test_operation_registry_distinguishes_absent_roadmap_work_items(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
source = root / "00000001.txt"
|
||||
source.write_text(
|
||||
'FeatureScript 1511; export const f = defineFeature(function(context, id, definition) {'
|
||||
' extrude(context, id + "F1", {"depth": 1 * mm}); });',
|
||||
encoding="utf-8",
|
||||
)
|
||||
registry = build_operation_registry([Sample("00000001", files={"featurescript": str(source)})])
|
||||
|
||||
self.assertEqual(registry["schema"], "cadfs_to_cdsl.operation_registry.v1")
|
||||
self.assertEqual(registry["operations"]["extrude"]["feature_count"], 1)
|
||||
self.assertEqual(registry["operations"]["transform"]["state"], "not_observed_in_current_source")
|
||||
self.assertEqual(registry["operations"]["thicken"]["feature_count"], 0)
|
||||
|
||||
def test_markdown_report_uses_source_registry_instead_of_static_operation_claims(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
output = Path(temporary)
|
||||
write_json(output / "operation_registry.json", {
|
||||
"operations": {
|
||||
"transform": {"roadmap_work_item": True, "state": "observed"},
|
||||
"thicken": {"roadmap_work_item": True, "state": "not_observed_in_current_source"},
|
||||
},
|
||||
})
|
||||
report = generate_markdown_report(output, [])
|
||||
text = report.read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn("thicken", text)
|
||||
self.assertNotIn("draft, thicken, split", text)
|
||||
|
||||
|
||||
if __name__ == "__main__": unittest.main()
|
||||
|
||||
@@ -6,7 +6,13 @@ 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 _search_candidate_records, run_geometry_probe, strip_provenance_intents
|
||||
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:
|
||||
@@ -18,6 +24,39 @@ def _contains_selector_intent(value: object) -> bool:
|
||||
|
||||
|
||||
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": (
|
||||
@@ -34,11 +73,85 @@ class SelectorCandidateDemoTests(unittest.TestCase):
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user