Files
dxfmakros/tests/test_mubea.py
T
2026-07-27 16:54:04 +02:00

152 lines
6.5 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 _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 _expected_counts(testdata):
return {
"kreisel": sum(1 for t in testdata if _is_kreisel(t)),
"vario": sum(1 for t in testdata if _is_vario(t)),
"gefaellestrecke": _gefaelle_count(testdata),
"separator": sum(1 for t in testdata if _is_separator(t)),
}
# ============================================================
# 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-Split = 500/4230 (Meter)."""
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 -> 500mm/4230mm = "0.500,4.230"
if "L_GF1" in t and "L_GF2" 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, h = float(teile[0]) * 1000.0, float(teile[1]) * 1000.0
assert abs(v - t["L_GF1"]) < 1.0, \
f'{t["test_id"]}: L_GF1 vorn ist={v}, erwartet={t["L_GF1"]}'
assert abs(h - t["L_GF2"]) < 1.0, \
f'{t["test_id"]}: L_GF2 hinten ist={h}, erwartet={t["L_GF2"]}'
# ============================================================
# 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'