5aed90dd56
doc/todo/: TODO-plan-vf-interactive.md -> plan-vf-interactive.md, TODO-modus2-achsenweise-bilanz.md -> modus2-achsenweise-bilanz.md (offene Fahrplaene/TODOs). doc/plan/: KS-Frame-Architektur.md, vf-parameter-struct-analyse.md, translate.md, Entwicklungsplan.md (Architektur-Analysen und Planungsdokumente, kein Anwender-Material). Alle internen Verweise (CLAUDE.md, Lisp/README.md, README.md, doc/variofoerderer.md, Lisp/vf_spec.lsp, lib/dxf_vf_abbild.py, lib/vf_spec_export.py, tests/test_hm_recformat.lsp, tests/test_vf_geometrie.py, tests/test_vf_headless_statisch.py) auf die neuen Pfade angepasst. Reine Pfad-Updates in Kommentaren/Docstrings, keine funktionale Aenderung - Python-Syntax nach dem Umbau geprueft. Anwender-/Entwickler-Referenzdoku (faq.md, kommandos.md, tools.md, dbg2lsp.md, csv-export.md, json-input-lisp.md, lisp_methods.md, LispTests.md, variofoerderer.md, vf_linienzug_modus1_anleitung.md) bleibt direkt in doc/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
152 lines
6.1 KiB
Python
152 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
test_vf_geometrie.py - Regressionsnetz fuer die GEBAUTE Geometrie der
|
|
HundM-Ketten.
|
|
|
|
Warum zusaetzlich zu tests/test_hundm05.py: der Ergebnis-Record sagt
|
|
"executed, kein Prompt, Journal aufgegangen" - er sagt NICHT, ob dieselbe
|
|
Geometrie herauskam wie vorher. Genau das ist bei der Funktionstrennung in
|
|
Stufe 2 (doc/todo/plan-vf-interactive.md) das Risiko: die Reihenfolge der
|
|
dL-Subtraktionen und der vfl-acc-*-Aufrufe IST Geometrie, und eine falsche
|
|
Reihenfolge faellt in der Zeichnung nicht auf - nur im Sivas-Export.
|
|
|
|
Verglichen wird gegen tests/reference/hundm05_attribute.json:
|
|
|
|
- alle Attribute des VF_n-Blocks, insbesondere die reihenfolgeabhaengigen
|
|
Komma-Listen L_VF_m / L_GF_m / GF_WINKEL / ANTRIEBFAHRTRICHTUNG
|
|
- die Bausteinzusammensetzung des Blocks (faengt eine falsche
|
|
Blockvariante, die kein Attribut verraet)
|
|
- der Einfuegepunkt
|
|
|
|
Die laufende ID und der Blockname sind bewusst NICHT Teil des Vergleichs:
|
|
die VF-Nummer beginnt in jeder neuen Zeichnung wieder bei 1.
|
|
|
|
Braucht eine gebaute Zeichnung (TEST_HUNDM05 mit "save": "dxf") und wird
|
|
sonst uebersprungen. Referenz auffrischen - nur nach geprueftem Lauf:
|
|
|
|
python lib/dxf_vf_abbild.py <dxf> --spec tests/testdata/hundm05.json \
|
|
--referenz tests/reference/hundm05_attribute.json \
|
|
--schreibe-referenz
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
|
|
|
import dxf_vf_abbild as dva # noqa: E402
|
|
|
|
HIER = os.path.dirname(__file__)
|
|
SPEC = os.path.join(HIER, "testdata", "hundm05.json")
|
|
REFERENZ = os.path.join(HIER, "reference", "hundm05_attribute.json")
|
|
|
|
# SSG_RUN_ALL_TESTS legt die Zeichnung in tests/output ab; ein Lauf mit
|
|
# gesetztem DXFM_RESULTS speichert sie nach results/.
|
|
DXF_ORTE = [os.path.join(HIER, "output", "hundm05_tests.dxf"),
|
|
os.path.join(HIER, "..", "results", "test_hundm05.dxf")]
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def gebautes_abbild():
|
|
for pfad in DXF_ORTE:
|
|
if os.path.isfile(pfad):
|
|
return dva.abbild(pfad, SPEC, ("VF_",))
|
|
pytest.skip("keine gebaute HundM05-Zeichnung gefunden (%s) - "
|
|
"TEST_HUNDM05 in BricsCAD ausfuehren"
|
|
% " bzw. ".join(os.path.basename(p) for p in DXF_ORTE))
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def referenz():
|
|
if not os.path.isfile(REFERENZ):
|
|
pytest.skip("%s fehlt - mit lib/dxf_vf_abbild.py "
|
|
"--schreibe-referenz anlegen" % REFERENZ)
|
|
with open(REFERENZ, encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def test_referenz_deckt_alle_specs(referenz):
|
|
"""Jede Kette der Spec muss in der Referenz stehen - sonst laeuft der
|
|
Vergleich unbemerkt ins Leere."""
|
|
if not os.path.isfile(SPEC):
|
|
pytest.skip("Spec fehlt")
|
|
with open(SPEC, encoding="utf-8") as fh:
|
|
specs = [o["spec_id"] for o in json.load(fh) if "spec_id" in o]
|
|
fehlt = sorted(set(specs) - set(referenz))
|
|
assert not fehlt, (
|
|
"Ketten ohne Referenz: %s. Nach einem geprueften Lauf die Referenz "
|
|
"mit lib/dxf_vf_abbild.py --schreibe-referenz auffrischen." % fehlt)
|
|
|
|
|
|
def test_alle_ketten_gebaut(gebautes_abbild, referenz):
|
|
fehlt = sorted(set(referenz) - set(gebautes_abbild))
|
|
assert not fehlt, "in der Zeichnung nicht gebaut: %s" % fehlt
|
|
|
|
|
|
@pytest.mark.parametrize("feld", ["bausteine", "bausteine_gesamt"])
|
|
def test_bausteine_wie_referenz(gebautes_abbild, referenz, feld):
|
|
"""Die Bausteinliste faengt, was kein Attribut verraet: eine falsche
|
|
Blockvariante (z.B. auf_3 statt ab_3, links statt rechts)."""
|
|
for kette, soll in sorted(referenz.items()):
|
|
if kette not in gebautes_abbild:
|
|
continue
|
|
ist = gebautes_abbild[kette]
|
|
assert ist[feld] == soll[feld], \
|
|
f"{kette}: {feld} ist {ist[feld]}, Referenz {soll[feld]}"
|
|
|
|
|
|
def test_attribute_wie_referenz(gebautes_abbild, referenz):
|
|
for kette, soll in sorted(referenz.items()):
|
|
if kette not in gebautes_abbild:
|
|
continue
|
|
ist = gebautes_abbild[kette]["attribs"]
|
|
for tag in sorted(set(ist) | set(soll["attribs"])):
|
|
assert ist.get(tag) == soll["attribs"].get(tag), \
|
|
f'{kette}: Attribut {tag} ist {ist.get(tag)!r}, ' \
|
|
f'Referenz {soll["attribs"].get(tag)!r}'
|
|
|
|
|
|
def test_reihenfolge_listen_wie_referenz(gebautes_abbild, referenz):
|
|
"""Eigener Test fuer die Komma-Listen: sie sind das einzige, was eine
|
|
vertauschte vfl-acc-*-Reihenfolge sichtbar macht - in der Zeichnung
|
|
unsichtbar, im Sivas-Export falsch."""
|
|
listen = ("L_VF_m", "L_GF_m", "GF_WINKEL", "ANTRIEBFAHRTRICHTUNG")
|
|
for kette, soll in sorted(referenz.items()):
|
|
if kette not in gebautes_abbild:
|
|
continue
|
|
ist = gebautes_abbild[kette]["attribs"]
|
|
for tag in listen:
|
|
if tag not in soll["attribs"]:
|
|
continue
|
|
assert ist.get(tag) == soll["attribs"][tag], \
|
|
f'{kette}: {tag} ist {ist.get(tag)!r}, ' \
|
|
f'Referenz {soll["attribs"][tag]!r} - Reihenfolge der ' \
|
|
f'vfl-acc-*-Aufrufe geaendert?'
|
|
|
|
|
|
def test_einfuegepunkt_wie_referenz(gebautes_abbild, referenz):
|
|
for kette, soll in sorted(referenz.items()):
|
|
if kette not in gebautes_abbild:
|
|
continue
|
|
for achse, (a, b) in enumerate(
|
|
zip(gebautes_abbild[kette]["insert"], soll["insert"])):
|
|
assert abs(a - b) < 1.0, \
|
|
f"{kette}: Einfuegepunkt Achse {achse} ist {a}, Referenz {b}"
|
|
|
|
|
|
def test_vergleichsfunktion_meldet_abweichung(gebautes_abbild, referenz):
|
|
"""Gegenprobe: der Vergleich muss eine verfaelschte Referenz erkennen -
|
|
sonst bewiesen die Tests oben nur, dass beide Seiten gleich gelesen
|
|
werden."""
|
|
kette = sorted(referenz)[0]
|
|
gift = json.loads(json.dumps(referenz))
|
|
gift[kette]["attribs"]["DELTA_L_mm"] = "999999"
|
|
assert dva.vergleiche(gebautes_abbild, gift), \
|
|
"verfaelschte Referenz wurde NICHT erkannt"
|
|
assert not dva.vergleiche(gebautes_abbild, referenz), \
|
|
"echte Referenz wird als abweichend gemeldet"
|