Files
dxfmakros/tests/test_mubea.py
T
m.stangl 1d5d7c141e [FIX] Scanner-Insert in TEST_MUBEA: eff wurde durch AND statt IF zu T
mubea:build-separator-one nutzte (and blockname (ssg-ils-block-laden
blockname)) fuer eff - AutoLISPs AND liefert bei mehreren wahren
Ausdruecken T zurueck statt des letzten Werts (anders als in Common
Lisp/Scheme). Sobald ein Blockname (z.B. "Scanner") direkt gefunden
wurde, statt ueber den Separator_SP-Fallback zu laufen, wurde eff=T
an "_.INSERT" uebergeben. Fix: (if blockname (ssg-ils-block-laden
blockname)).

Ausserdem: Scanner-Testeintrag in mubea.json ergaenzt und
_vario_count/_separator_count in test_mubea.py, damit erwartete
Ergebniszahlen auch bei "anzahl">1 (Template->mehrere Kopien) stimmen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:49:21 +02:00

164 lines
7.0 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
test_mubea.py - Validiert das Mubea-Gesamtmodell.
Prueft die von TEST_MUBEA (test_mubea.lsp) erzeugten Ergebnisse:
1. Alle Eintraege aus mubea.json wurden gebaut (results.json)
2. Statuswerte (executed / placeholder fuer Separatoren)
3. Kreisel-Attribute (HOEHE, KREISELART, Block-Praefix)
4. Vario: VF_-Block mit VF_WINKEL=21 und L_GF-Split 500/4230
5. DXF-Geometrie: Block-Anzahlen je Typ (ezdxf)
"""
import pytest
# ============================================================
# Klassifikation der mubea.json-Eintraege (analog test_mubea.lsp)
# ============================================================
def _is_separator(item):
return "block" in item
def _is_kreisel(item):
return "id" in item and "block" not in item
def _is_vario(item):
tid = item.get("test_id", "")
return tid.startswith("VF_")
def _is_gefaelle(item):
tid = item.get("test_id", "")
return tid.startswith("GF_")
def _vario_count(testdata):
"""Wie _gefaelle_count: ein VF-Eintrag ist ein Template, das ueber 'anzahl'
expandiert wird (Default 1, falls Feld fehlt)."""
return sum(int(t.get("anzahl", 1)) for t in testdata if _is_vario(t))
def _gefaelle_count(testdata):
"""Anzahl erzeugter Gefaellestrecken: ein GF-Eintrag ist ein Template, das
ueber 'anzahl' expandiert wird (Default 1, falls Feld fehlt)."""
return sum(int(t.get("anzahl", 1)) for t in testdata if _is_gefaelle(t))
def _separator_count(testdata):
"""Wie _gefaelle_count: ein Separator-Eintrag kann per 'anzahl' mehrere
identische, versetzte Kopien erzeugen (Default 1, falls Feld fehlt)."""
return sum(int(t.get("anzahl", 1)) for t in testdata if _is_separator(t))
def _expected_counts(testdata):
return {
"kreisel": sum(1 for t in testdata if _is_kreisel(t)),
"vario": _vario_count(testdata),
"gefaellestrecke": _gefaelle_count(testdata),
"separator": _separator_count(testdata),
}
# ============================================================
# Ergebnis-Pruefung (results.json vs. testdata)
# ============================================================
class TestMubeaResults:
def test_alle_gebaut(self, mubea_testdata, mubea_results):
"""Je Kind muss die erwartete Anzahl an Ergebnissen vorliegen."""
expected = _expected_counts(mubea_testdata)
got = {}
for r in mubea_results:
got[r["kind"]] = got.get(r["kind"], 0) + 1
for kind, n in expected.items():
assert got.get(kind, 0) == n, \
f'{kind}: erwartet {n}, gebaut {got.get(kind, 0)}'
def test_status_ok(self, mubea_results):
"""Alle Ergebnisse muessen 'executed' sein (Separatoren duerfen 'placeholder')."""
for r in mubea_results:
if r["kind"] == "separator":
assert r["status"] in ("executed", "placeholder"), \
f'{r["test_id"]}: Status={r["status"]}'
else:
assert r["status"] == "executed", \
f'{r["test_id"]}: Status={r["status"]}'
def test_kreisel_attribute(self, mubea_testdata, mubea_results):
"""Kreisel: HOEHE / KREISELART / Block-Praefix wie in mubea.json erwartet."""
by_id = {r["test_id"]: r for r in mubea_results}
for t in mubea_testdata:
if not _is_kreisel(t):
continue
r = by_id.get(t["id"])
assert r, f'Kreisel {t["id"]} fehlt im Ergebnis'
if "expect_hoehe" in t:
assert r["actual_attributes"].get("HOEHE") == t["expect_hoehe"], \
f'{t["id"]}: HOEHE ist={r["actual_attributes"].get("HOEHE")}, erwartet={t["expect_hoehe"]}'
if "expect_kreiselart" in t:
assert r["actual_attributes"].get("KREISELART") == t["expect_kreiselart"], \
f'{t["id"]}: KREISELART ist={r["actual_attributes"].get("KREISELART")}'
prefix = t.get("expect_block_prefix", "KREISEL_")
assert r["block_name"].startswith(prefix), \
f'{t["id"]}: Block "{r["block_name"]}" ohne Praefix "{prefix}"'
def test_vario_winkel_und_split(self, mubea_testdata, mubea_results):
"""Vario: VF_WINKEL entspricht Vorgabe, L_GF-Vorlauf entspricht L_GF1-Ziel,
Vorlauf+Nachlauf (L_GF_m) ergeben zusammen die aus deltaL geloeste Gesamtlaenge."""
by_id = {r["test_id"]: r for r in mubea_results}
for t in mubea_testdata:
if not _is_vario(t):
continue
r = by_id.get(t["test_id"])
assert r, f'Vario {t["test_id"]} fehlt im Ergebnis'
attrs = r["actual_attributes"]
assert r["block_name"].startswith("VF_"), \
f'{t["test_id"]}: Block "{r["block_name"]}" ist kein VF_-Block'
if "winkel" in t:
assert attrs.get("VF_WINKEL") == str(t["winkel"]), \
f'{t["test_id"]}: VF_WINKEL ist={attrs.get("VF_WINKEL")}, erwartet={t["winkel"]}'
# L_GF_m = "vorne,hinten" in Metern; L_GF1 (Vorlauf-Ziel) muss exakt
# getroffen werden, L_GF2 (Nachlauf) ist der aus deltaL geloeste Rest.
if "L_GF1" in t and "L_GF_m" in attrs:
teile = attrs["L_GF_m"].split(",")
assert len(teile) == 2, f'{t["test_id"]}: L_GF_m Format "{attrs["L_GF_m"]}"'
v = float(teile[0]) * 1000.0
assert abs(v - t["L_GF1"]) < 1.0, \
f'{t["test_id"]}: L_GF1 vorn ist={v}, erwartet={t["L_GF1"]}'
# ============================================================
# DXF Geometrie-Pruefung (ezdxf)
# ============================================================
class TestMubeaGeometry:
def _counts(self, dxf):
c = {}
for e in dxf.modelspace():
if e.dxftype() == "INSERT":
c[e.dxf.name] = c.get(e.dxf.name, 0) + 1
return c
def test_block_anzahlen(self, mubea_dxf, mubea_testdata):
"""Anzahl der KREISEL_/VF_/GF_-Bloecke >= Vorgabe aus mubea.json."""
c = self._counts(mubea_dxf)
n_kr = sum(v for n, v in c.items() if n.startswith("KREISEL_"))
n_vf = sum(v for n, v in c.items() if n.startswith("VF_"))
n_gf = sum(v for n, v in c.items() if n.startswith("GF_"))
exp = _expected_counts(mubea_testdata)
assert n_kr >= exp["kreisel"], f"KREISEL_: {n_kr} < {exp['kreisel']}"
assert n_vf >= exp["vario"], f"VF_: {n_vf} < {exp['vario']}"
assert n_gf >= exp["gefaellestrecke"], f"GF_: {n_gf} < {exp['gefaellestrecke']}"
def test_handles_im_modelspace(self, mubea_dxf, mubea_results):
"""Jedes gebaute Block-Handle (executed) liegt im Modelspace."""
handles = {e.dxf.handle for e in mubea_dxf.modelspace()
if e.dxftype() == "INSERT"}
for r in mubea_results:
if r["status"] == "executed" and r["block_handle"]:
assert r["block_handle"] in handles, \
f'{r["test_id"]}: Handle {r["block_handle"]} nicht im Modelspace'