Files
Umutcan Edizsalan 712cf00c5c v1.2.0
Adds production-grade engineering features and tools: 2D GD&T (feature control frames + datum features) via engineering/gdt.py, ISO‑129 dimension tolerance support (engineering/tolerances.py) and an ezdxf/COM plumbing for toleranced dims, a scalar drawing score + invalidity ratio (engineering/scoring.py) wired into drawing_finalize, in-place entity editors (entity_edit_text/entity_edit_geometry), and a friendly drawing_settings facade. Includes server tool wiring, COM/ezdxf backend implementations, datum-consistency critique, many unit tests, README/CLAUDE/CHANGELOG updates, and a version bump to 1.2.0. Also corrects COM backend capability label (entities_2d).
2026-07-07 11:03:08 +03:00

144 lines
4.7 KiB
Python

"""Tests for 2D GD&T (ISO 1101) — pure layout math, backend composition, and
the datum-consistency critique focus."""
from __future__ import annotations
import pytest
from engineering.gdt import (
DIAMETER_PREFIX,
GEOMETRIC_SYMBOLS,
MATERIAL_MODIFIERS,
datum_triangle,
fcf_compartments,
fcf_layout,
)
pytestmark = pytest.mark.asyncio
# ── pure: compartments ──────────────────────────────────────────────────────
async def test_form_tolerance_needs_no_datum():
comps = fcf_compartments("flatness", 0.1)
assert comps[0] == GEOMETRIC_SYMBOLS["flatness"]
assert comps[1] == "0.1"
assert len(comps) == 2
async def test_position_requires_datum():
with pytest.raises(ValueError):
fcf_compartments("position", 0.2) # location control, no datum
async def test_position_with_datums_and_diameter_and_modifier():
comps = fcf_compartments(
"position",
0.2,
["A", "B", "C"],
diameter=True,
modifier="M",
)
assert comps[0] == GEOMETRIC_SYMBOLS["position"]
assert comps[1] == f"{DIAMETER_PREFIX}0.2{MATERIAL_MODIFIERS['M']}"
assert comps[2:] == ["A", "B", "C"]
async def test_unknown_symbol_and_modifier_raise():
with pytest.raises(ValueError):
fcf_compartments("bogus", 0.1)
with pytest.raises(ValueError):
fcf_compartments("flatness", 0.1, modifier="Z")
# ── pure: layout geometry ───────────────────────────────────────────────────
async def test_fcf_layout_box_closed_and_dividers_count():
comps = fcf_compartments("position", 0.2, ["A", "B"], diameter=True)
lay = fcf_layout(comps, 0.0, 0.0, 5.0)
# Closed rectangle: first == last point.
assert lay["box"][0] == lay["box"][-1]
# n compartments -> n-1 internal dividers, n labels.
assert len(lay["dividers"]) == len(comps) - 1
assert len(lay["labels"]) == len(comps)
assert lay["width"] > 0 and lay["height"] == 5.0
async def test_datum_triangle_is_closed_triangle():
tri = datum_triangle(10.0, 20.0, 4.0, down=True)
assert tri[0] == (10.0, 20.0) # apex
assert tri[0] == tri[-1] # closed
assert len(tri) == 4
# ── backend composition ─────────────────────────────────────────────────────
async def test_draw_feature_control_frame_creates_entities(backend):
await backend.drawing_apply_iso_layers("mech")
res = await backend.draw_feature_control_frame(
"perpendicularity",
0.05,
0.0,
0.0,
datums=["A"],
height=5.0,
)
assert res["ok"] is True
assert res["layer"] == "DIM"
# box + 2 dividers (3 compartments) + 3 texts = several handles
assert len(res["handles"]) >= 5
for h in res["handles"]:
assert await backend.entity_get(h) is not None
async def test_draw_datum_feature_records_letter(backend):
await backend.drawing_apply_iso_layers("mech")
res = await backend.draw_datum_feature("A", 0.0, 0.0, size=5.0)
assert res["datum"] == "A"
assert "A" in backend._gdt_datums_defined
# ── gdt critique focus ──────────────────────────────────────────────────────
async def test_gdt_critique_flags_missing_datum(backend):
await backend.drawing_apply_iso_layers("mech")
await backend.draw_feature_control_frame(
"position",
0.2,
0.0,
0.0,
datums=["A", "B"],
diameter=True,
)
issues = await backend.drawing_critique(focus=["gdt"])
assert len(issues) == 1
assert issues[0].focus == "gdt"
assert issues[0].severity == "error"
assert set(issues[0].detail["missing_datums"]) == {"A", "B"}
async def test_gdt_critique_passes_when_datums_defined(backend):
await backend.drawing_apply_iso_layers("mech")
await backend.draw_feature_control_frame(
"position",
0.2,
0.0,
0.0,
datums=["A"],
diameter=True,
)
await backend.draw_datum_feature("A", 50.0, 0.0)
issues = await backend.drawing_critique(focus=["gdt"])
assert issues == []
async def test_clean_drawing_gdt_focus_is_noop(backend):
await backend.drawing_apply_iso_layers("mech")
# No FCFs at all — gdt must not fire, and full critique stays clean.
assert await backend.drawing_critique(focus=["gdt"]) == []
assert await backend.drawing_critique() == []