Files
cdsl-cad/cadfs_to_cdsl/tests/test_describe.py
T
2026-09-04 11:17:36 +08:00

148 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import json
import tempfile
import unittest
from pathlib import Path
from cadfs_to_cdsl.describe import describe_samples, select_description_samples
SAMPLE_FS = r'''
FeatureScript 1511;
import(path : "onshape/std/geometry.fs", version : "1511.0");
const mm = millimeter;
const FACE = EntityType.FACE;
function v(x, y){return vector(x, y);}
function sQuery(a, b, c) {return sketchEntityQuery(a, b, c);}
export const myFeature = defineFeature(function(context is Context, id is Id, definition is map)
precondition{}
{
{
var Q0;
Q0=qCreatedBy(makeId("Top.planeOp"),FACE);
var sketch = newSketch(context, id + "F0", { "sketchPlane" : qUnion([Q0])});
skLineSegment(sketch, "E0", {"start": v(-269.41, -156.6) * mm, "end": v(-0.92, 311.62) * mm});
skLineSegment(sketch, "E1", {"start": v(-0.92, 311.62) * mm, "end": v(270.33, -155.02) * mm});
skLineSegment(sketch, "E2", {"start": v(270.33, -155.02) * mm, "end": v(-269.41, -156.6) * mm});
skSolve(sketch);
}
{
var Q0;
Q0 = qSketchRegion(id + "F0", true);
extrude(context, id + "F1", {"entities" : qUnion([Q0]), "depth" : 1828.8 * mm});
}
});
'''
SAMPLE_ANNOTATION = """Step 1 - Sketch
Draw a closed triangle.
Step 2 - Extrude NEW
Extrude the triangular area upward a distance of 1828.8 mm.
"""
class FailingVision:
def describe(self, *, sample_id: str, image_path: Path, local_facts: dict[str, object]) -> dict[str, object]:
raise RuntimeError("vision offline")
class StaticVision:
def describe(self, *, sample_id: str, image_path: Path, local_facts: dict[str, object]) -> dict[str, object]:
return {
"category": "视觉增强三角楔块(候选)",
"category_confidence": 0.7,
"candidate_names": ["视觉楔块"],
"summary_zh": "视觉模型认为该模型是三角楔块候选件。",
"possible_functions": ["可能用于定位。"],
"applications": ["夹具。"],
"structural_features": ["斜面"],
"geometric_features": ["三角截面"],
"keywords_zh": ["视觉识别"],
"keywords_en": ["vision wedge"],
"uncertainties": ["视觉语义仍需人工确认。"],
}
class DescribeTests(unittest.TestCase):
def test_selects_only_requested_shard(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
self._write_sample(root, "0005", "00050089")
self._write_sample(root, "0006", "00060001")
selected = select_description_samples(root, shard="0005")
self.assertEqual([sample.sample_id for sample in selected], ["00050089"])
def test_hybrid_falls_back_and_writes_txt_manifest(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
self._write_sample(root, "0005", "00050089")
records, summary = describe_samples(root, shard="0005", mode="hybrid", vision_client=FailingVision())
self.assertEqual(summary["sample_count"], 1)
self.assertEqual(summary["statuses"], {"local_fallback": 1})
txt_path = root / "description_txt/0005/00050089.txt"
self.assertTrue(txt_path.is_file())
text = txt_path.read_text(encoding="utf-8")
self.assertIn("样本ID00050089", text)
self.assertIn("三角棱柱", text)
self.assertIn("证据与不确定性", text)
manifest = root / "description_txt/description_manifest.jsonl"
rows = [json.loads(line) for line in manifest.read_text(encoding="utf-8").splitlines()]
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["sample_id"], "00050089")
self.assertEqual(rows[0]["status"], "local_fallback")
self.assertIn("vision_failed", [item["code"] for item in records[0]["diagnostics"]])
def test_hybrid_uses_vision_when_available(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
self._write_sample(root, "0005", "00050089")
records, _summary = describe_samples(root, shard="0005", mode="hybrid", vision_client=StaticVision())
self.assertEqual(records[0]["status"], "described_hybrid")
self.assertEqual(records[0]["category"], "视觉增强三角楔块(候选)")
self.assertIn("视觉楔块", records[0]["candidate_names"])
def test_existing_txt_is_skipped_without_force(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
self._write_sample(root, "0005", "00050089")
first, _ = describe_samples(root, shard="0005", mode="local")
txt_path = root / "description_txt/0005/00050089.txt"
before = txt_path.read_text(encoding="utf-8")
second, _ = describe_samples(root, shard="0005", mode="local")
self.assertEqual(second[0]["status"], first[0]["status"])
self.assertEqual(txt_path.read_text(encoding="utf-8"), before)
@staticmethod
def _write_sample(root: Path, shard: str, sample_id: str) -> None:
for directory, suffix, content in (
("featurescript_rp", ".txt", SAMPLE_FS),
("text_annotations", ".txt", SAMPLE_ANNOTATION),
("step_abc", ".step", "ISO-10303-21;\nEND-ISO-10303-21;\n"),
("stl_abc", ".stl", "solid sample\nendsolid sample\n"),
):
path = root / directory / shard / f"{sample_id}{suffix}"
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
image = root / "multiview_images_abc" / shard / f"{sample_id}.png"
image.parent.mkdir(parents=True, exist_ok=True)
image.write_bytes(
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR"
b"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00"
b"\x90wS\xde\x00\x00\x00\x00IEND\xaeB`\x82"
)
if __name__ == "__main__":
unittest.main()