Drei zusammenhaengende Korrekturen am GF-Zweig mit ES-Element (Modus 1). 1) ES-Fussabdruck wurde massiv unterschaetzt (333.65 mm zu lang) Reserviert wurde (abs ein-dx) - die Block-X-Komponente aus vf-element-masse. Ein Element wird aber so gedreht eingebaut, dass sein KS_EIN auf der Fahrtrichtung liegt; beim drehenden ES_Element_30 (ein-ang=120 Grad) landet dadurch ein Teil der Block-Y-Komponente auf der Fahrtachse. Real verbraucht es laengs 576.2 mm statt der reservierten 242.6 mm - die Kette ragte um die Differenz ueber den gepickten Endpunkt hinaus (Nutzer-Befund 2026-10-09). Warum das lange unentdeckt blieb: beim ES_Element_90 ist ein-ang=0, dort ist (abs ein-dx) ZUFAELLIG richtig. Der frueher bestaetigende 90-Grad-Test konnte den Fehler strukturell nicht finden (Nutzer-Hinweis). Neu in vf_core.lsp: vf-element-laengs / vf-element-quer (hz-unabhaengige Projektion auf bzw. quer zur Fahrtrichtung), vf-set-es-masse setzt zusaetzlich ein-laengs / ein-quer. In vf_linienzug.lsp liest vfl-es-laengs den Wert mit Rueckfallkette; ein bewusst genulltes ein-dx gewinnt dabei, damit die rund zwei Dutzend Null-/Restore-Stellen keinen Doppelabzug bekommen. In BricsCAD bestaetigt: Separator+ES verbrauchen 875.83 mm gegen 875.79 mm Soll (0.04 mm), Hoehe 151.23 gegen 151.20 mm. 2) Endpunkt per Ziellinie statt per Klick Steht die Fahrtrichtung durch den Vorgaenger fest, ist die Laenge nicht mehr frei: die (um den ES-Querversatz seitlich versetzte) Bahn des ES-KS_AUS trifft eine Ziellinie in genau einem Punkt. Ein Klick kann den nur annaehern. Neue Frage mit zwei gleichwertigen Optionen (Radio-Buttons im Wizard-Modus): Ziellinie durch ZWEI PUNKTE zeichnen - Schnittpunkt wird gerechnet - oder wie bisher einen Punkt waehlen. REIN PLANAR: die Hoehe kommt weiterhin aus dem Gefaellewinkel, die Ziellinie bestimmt nur X/Y (Nutzer-Praezisierung). Beide Wege schreiben denselben einzelnen "DL"-Journaleintrag - Replay, Doppelklick-Edit und 2D/3D-Konvertierung muessen von Ziellinien nichts wissen. Die Bindung an die Linie besteht nur beim Bauen: wird die Linie spaeter verschoben, folgt die Kette NICHT. Abwaertskompatibilitaet: die neue Frage erzeugt einen zusaetzlichen Journaleintrag, den aeltere Ketten nicht haben. vfl-replay-peek-art sieht beim Replay nach, was tatsaechlich ansteht; steht dort ein "DL", ist es ein Altbestand und die Frage entfaellt - sonst haette sie den Laengenwert als Antwort verschluckt (Queue-Desync). 3) Bedienung: Achsenzwang und ruhige Ansicht vfl-getpoint-achse zwingt den Cursor beim Picken auf die Anschlussachse (SNAPANG + ORTHOMODE, kein BKS-Wechsel - getpoint liefert daher unveraenderte Koordinaten). Nur die horizontale Richtung wird erzwungen, die Hoehe bleibt frei. vfl-view-refresh zoomte vor JEDER Laengeneingabe hart auf _Extents - die Ansicht sprang bei grossen Anlagen nach jedem Klick weg. Jetzt wird nur noch eingegriffen, wenn der Baupunkt aus dem sichtbaren Ausschnitt laeuft, und dann per _ZOOM _Center unter Beibehaltung der Zoomstufe. 13 neue Unit-Tests (etage-laengs/-quer, vfl-es-laengs, vfl-kreuz2, vfl-laenge-bis-ziellinie inkl. Realfall aus dem BricsCAD-Log), Suite 533/533 gruen. lang-Paritaet geprueft. Offen und bewusst nicht angefasst: vfl-winkel-machbar-p nutzt aus-dx/ein-dx ebenfalls als Laengsmass und hat damit denselben Fehler (AS_30: 276 mm, ES_30: 334 mm). Der saubere Fix fuellt ein-dx/aus-dx an der Quelle mit dem Laengswert - das beruehrt rund 87 Fundstellen samt ihrer Null-/Restore- Konvention und gehoert separat getestet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tests - SSG_LIB Testarchitektur
Uebersicht
Die Tests folgen einer zweistufigen Architektur:
- LISP-Testrunner (
.lsp) — laufen in BricsCAD, erzeugen Ergebnisdateien - Python-Validierung (
.py) — pruefen die Ergebnisse mit pytest + ezdxf
tests/alltests.json
|
v
BricsCAD Kommandozeile
| |
| SSG_RUN_ALL_TESTS |
| (fuehrt je Eintrag aus |
| alltests.json den passenden |
| TEST_<NAME> aus) |
| | |
v v |
tests/output/ |
<name>_results.json (immer) |
<name>_tests.dxf/.dwg (gemaess save)|
|
bin\run_tests.bat --check
|
pytest -v
|
test_<name>.py je Modul
LISP-Unit-Tests fuer reine Standardfunktionen (--lisp)
Neben den zeichnungsbasierten Integrationstests gibt es Unit-Tests fuer einfache LISP-Standardfunktionen — reine Funktionen ohne Zeichnungsdatenbank, Auswahlsaetze, Dialoge oder Blockdateien (String-/Zahl-/Vektor-/Alist-Helfer). Sie funktionieren analog zu den Python-Unittests: jeder Testfall vergleicht einen Funktionsaufruf gegen ein erwartetes Ergebnis.
- Testrunner:
tests/test_unit.lsp— BefehlTEST_UNIT, Mini-Framework mittu-eq(exakt),tu-eqf(Fliesskomma-Toleranz),tu-true,tu-nil. - Startskript:
tests/test_unit.scr— laedt SSG_LIB + die Tests, ruftTEST_UNITauf und beendet BricsCAD (pfadunabhaengig ueberDXFMAKRO). - Ergebnis: Konsole (PASS/FAIL je Test) und
tests/output/unit_results.txt(letzte ZeileRESULT: OK/RESULT: FAIL).
Ausfuehrung ueber die Kommandozeile (startet BricsCAD headless):
bin\run_tests.bat --lisp
run_tests.bat --lisp loescht das alte Ergebnis, startet BricsCAD mit test_unit.scr,
gibt danach unit_results.txt aus und liefert Exit-Code 1, wenn ein Test fehlschlaegt
oder kein Report erzeugt wurde.
Direkt in BricsCAD (SSG_LIB geladen):
(load (strcat (getenv "DXFMAKRO") "/tests/test_unit.lsp"))
TEST_UNIT
Getestete Module: ssg_core, ssg_lang, ssg_id, vf_core, export,
Gefaellestrecke, OmniModulInsert, KreiselInsert.
Zeichnungsbasierte Integrationstests
Jedes Testmodul laeuft in einer eigenen, neuen Zeichnung (statt die aktuelle zu
leeren/wiederzuverwenden). SSG_RUN_ALL_TESTS fragt beim Start interaktiv nach
dem gewuenschten Modus:
- Kontroll-Modus (Default, Enter/Nein): Zeichnung wird gespeichert, per
_.ZOOM _Extentsauf den Inhalt gezoomt und bleibt als eigener Tab offen — der naechste Test startet in einer weiteren neuen Zeichnung. Am Ende des Laufs sind alle Testzeichnungen gleichzeitig offen und direkt sichtbar. Sinnvoll, wenn neue/geaenderte Testfaelle visuell geprueft werden sollen.c:EXPORTCSV/c:EXPORTSIVASlaufen nicht automatisch mit — CSV/Sivas- Export danach bei Bedarf manuell ueberTEST_EXPORT_ALL(tests/test_export_all.lsp, Menuepunkt "Export CSV/Sivas ALL"), das alle vorhandenen DXF/DWG-Dateien austests/output/einliest und je Datei<basis>_export.csv/<basis>_sivas.csverzeugt. - Automatik-Modus (Ja): Zeichnung wird gespeichert, direkt per
c:EXPORTCSV/c:EXPORTSIVASexportiert (<name>_tests_export.csv/<name>_tests_sivas.csv) und danach sofort wieder geschlossen. Kompletter Lauf ohne manuellen Zusatzschritt, aber ohne gleichzeitige visuelle Kontrolle aller Zeichnungen am Ende. Sinnvoll fuer reine CI-artige Validierung.
alltests.json — Testregistry
Die Datei tests/alltests.json ist die zentrale Registry aller Test-Module.
SSG_RUN_ALL_TESTS liest sie beim Start — die Testliste ist nicht hartcodiert in test_run_all.lsp.
[
{ "name": "kreisel", "save": "dxf", "module": "KreiselInsert" },
{ "name": "foerderer", "save": "dwg", "module": "VarioFoerderer" },
{ "name": "linienzug", "save": "dwg", "module": "VarioFoerderer" },
{ "name": "vfl_schema", "module": "VarioFoerderer" },
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
{ "name": "mubea", "save": "dxf", "module": "KreiselInsert" },
{ "name": "hundm05", "save": "dxf", "module": "VarioFoerderer" }
]
Felder pro Eintrag:
| Feld | Typ | Bedeutung |
|---|---|---|
name |
String | Basisname des Testmoduls (Pflicht) |
save |
"dxf" / "dwg" / null |
Ob und als welches Format die Zeichnung gespeichert wird |
module |
String | LISP-Feature-Modul, das vor dem Test per ssg-ensure geladen wird |
disabled |
true |
(optional) Schaltet den Eintrag ab: SSG_RUN_ALL_TESTS ueberspringt ihn komplett (kein Bauen/Speichern/Export) und gibt nur einen Hinweis auf der Konsole aus. Zum Reaktivieren das Feld entfernen. |
JSON-Export laeuft immer — unabhaengig vom save-Feld. Die Datei <name>_results.json
wird in tests/output/ geschrieben, sobald der Test abgeschlossen ist.
GUI-Aus waehrend des Laufs: SSG_RUN_ALL_TESTS (und der Einzeltest TEST_LINIENZUG)
rufen (ssg-gui-aus) (ssg_core.lsp) auf, damit keine DCL-/Wizard-Dialoge den automatischen
Lauf blockieren; danach wird (ssg-gui-an) wieder gesetzt. Bei abgeschalteter GUI fallen die
Eingaben auf den Konsolen-/Mock-/Replay-Pfad zurueck. Interaktiver Betrieb (FOERDERANLAGE
etc.) ist davon unberuehrt (GUI standardmaessig an).
Wann welches save verwenden:
"dxf"— wenn ein Python-Test (ezdxf) die Geometrie der Zeichnung pruefen soll"dwg"— wenn die Zeichnung zur manuellen Kontrolle oder als DWG-Referenz erhalten bleiben sollnull(oder Feld weglassen) — wenn nur der JSON-Export benoetigt wird
Ablauf fuer den Anwender
-
BricsCAD starten (ueber
bin\start_briscad.bat) und die LISP-Testrunner ausfuehren (siehe Abschnitt "LISP-Tests in BricsCAD ausfuehren"). Die Testrunner erzeugen Bloecke in der Zeichnung und speichern Ergebnisse nachtests/output/. -
Optische Kontrolle in BricsCAD — pruefen ob die erzeugten Bloecke korrekt aussehen (Positionen, Drehungen, Beschriftungen). Jede Testzeichnung bleibt als eigener Tab offen (auf den Inhalt gezoomt) und kann direkt visuell inspiziert werden.
-
Kommandozeile: Ergebnisse validieren:
bin\run_tests.bat --check— pytest prueft die Ergebnisse ausoutput/gegen Testdefinitionen und Referenzdaten (optional: einzelnes Modul, z.B.--check test_kreisel.py)bin\run_tests.bat --diff_references— vergleicht die CSVs inoutput/gegenreference/(gleicher Dateiname, siehe Abschnitt "Referenz-Namenskonvention") und oeffnet bei Unterschieden das Diff-Tool (meld); optional eine einzelne Datei, z.B.--diff_references kreisel_tests_export.csv- Referenz aktualisieren (nach geprueftem Lauf): die betreffende(n) Datei(en) aus
output/unter DEMSELBEN Namen nachreference/kopieren (es gibt keinen eigenen Schalter dafuer)
Verzeichnisstruktur
tests/
conftest.py # Gemeinsame pytest-Fixtures (Pfade, JSON/DXF-Laden)
create_testbase.py # Erzeugt Basis-DXF (optional, wird nicht mehr benoetigt)
requirements.txt # Python-Abhaengigkeiten (ezdxf, pytest)
test_run_all.lsp # Fuehrt alle Tests aus: C:SSG_RUN_ALL_TESTS
test_kreisel.lsp # LISP-Testrunner: C:TEST_KREISEL
test_kreisel.py # pytest-Validierung der Kreisel-Ergebnisse
test_foerderer.lsp # LISP-Testrunner: C:TEST_FOERDERER
test_omniflo.lsp # LISP-Testrunner: C:TEST_OMNIFLO
test_omniflo.py # pytest-Validierung der Omniflo-Ergebnisse
test_omniflo_strecke.lsp # LISP-Testrunner: C:TEST_OMNIFLO_STRECKE (Streckenzug)
test_omniflo_strecke.py # pytest-Validierung des Omniflo-Streckenzugs
test_export_all.lsp # LISP-Testrunner: C:TEST_EXPORT_ALL (manuell, nicht in alltests.json)
test_foerderer.py # pytest-Validierung Foerderer (Status/Mathematik/CSV/Sivas)
test_gefaellestrecke.py # pytest-Validierung Gefaellestrecke
test_mubea.py # pytest-Validierung Mubea-Gesamtmodell
test_hundm05.lsp # LISP: baut die 5 HundM-Ketten aus ihrer
# Spec (testdata/hundm05.json) - ruft
# vsp-bau-datei, keine eigene Bau-Logik
test_hundm05.py # pytest: die so gebaute Anlage pruefen
test_hm_recformat.lsp # LISP: dieselben Ketten aus dem ROHEN
# Journal der Kundenzeichnung - der
# Regressionstest fuers Aufzeichnungs-
# format (in alltests.json abgeschaltet,
# baut sonst dieselbe Geometrie zweimal)
test_hm_recformat.py # pytest-Validierung dazu
test_vf_geometrie.py # pytest: die GEBAUTE Geometrie gegen
# reference/hundm05_attribute.json - Attribute,
# Bausteine, Einfuegepunkt (faengt eine
# vertauschte vfl-acc-*-Reihenfolge)
test_vf_spec.lsp # LISP-Unittest: Spec-Uebersetzer (vsp-journal),
# je Glied-Typ + die 5 echten HundM-Ketten
# gegen ihr aufgezeichnetes Journal
test_vf_spec.py # pytest: Rundlauf Journal->Spec->Journal, die
# Spec-Testdaten, und die Ergebnisse von
# VF_SPEC_BAU (letztere brauchen BricsCAD)
test_vf_journal_grammatik.py # pytest ohne CAD: Journal-Dekoder aus
# lib/vf_journal_export.py gegen handgebaute
# Journale (Linie-VF, gewinkelter Erstkoerper)
test_vf_headless_statisch.py # pytest ohne CAD: statischer Waechter, dass
# im VF-Bau-Pfad kein alert/get*/ssget neu
# auftaucht und der Headless-Riegel an allen
# vfl-in-*-Wrappern sitzt
test_unit.lsp / .scr # LISP-Unit-Tests: C:TEST_UNIT (ueber --lisp)
testdata/
kreisel_tests.json # Testfall-Definitionen fuer Kreisel
omniflo_tests.json # Testfall-Definitionen fuer Omniflo
omniflo_strecke_tests.json # Streckenzug: 4 Geraden + 2 Boegen + 1 Weiche (7 Elemente)
hm_recformat.json # Aufzeichnung: rohes Journal der 5 Ketten
hundm05.json # daraus abgeleitete Spec (wird erweitert)
foerderer_tests.json / mubea.json / ...
output/ # Ergebnisse aus BricsCAD (nicht in Git)
<name>_results.json # JSON-Ergebnisse pro Testmodul (immer)
<name>_tests.dxf/.dwg # Zeichnung pro Testmodul (gemaess save in alltests.json)
export_all_results.json # nur nach manuellem TEST_EXPORT_ALL-Lauf
<basis>_export.csv # EXPORTCSV je Quelldatei, nur nach TEST_EXPORT_ALL
<basis>_sivas.csv # EXPORTSIVAS je Quelldatei, nur nach TEST_EXPORT_ALL
reference/ # Abgenommene Referenzdaten (in Git): *_results.json,
... # *_tests_export.csv / *_tests_sivas.csv, *_tests.dxf,
... # omniflo_export.csv, export_raw.json
Referenz-Namenskonvention (vereinheitlicht)
Eine Referenz traegt IMMER denselben Dateinamen wie das zugehoerige Ist-Ergebnis
in output/. Der einzige Unterschied ist das Verzeichnis:
output/<datei> # Ist-Ergebnis (bei jedem Lauf neu, nicht in Git)
reference/<datei> # abgenommener Soll-Stand (identischer Name, in Git)
Damit gilt durchgehend: Referenz festlegen = Datei aus output/ nach reference/
kopieren - kein Umbenennen, kein Suffix. Beispiele:
Ist-Ergebnis in output/ |
Referenz in reference/ |
|---|---|
kreisel_tests.dxf |
kreisel_tests.dxf |
kreisel_tests_export.csv |
kreisel_tests_export.csv |
foerderer_results.json |
foerderer_results.json |
omniflo_export.csv |
omniflo_export.csv |
copy tests\output\kreisel_tests.dxf tests\reference\kreisel_tests.dxf
Frueher gab es zwei Ausreisser, die bewusst auf dieses Schema gezogen wurden (nicht mehr verwenden):
reference/kreisel_ref.dxf(bzw. der TippfehlerKresel_ref.dxf) -> jetztreference/kreisel_tests.dxfreference/omniflo_export_reference.csv-> jetztreference/omniflo_export.csv
Die pytest-Fixtures und -Checks suchen ausschliesslich nach dem vereinheitlichten Namen; fehlt die Referenz, wird der Vergleich mit einem Hinweis uebersprungen (kein Fehler).
Workflow
1. LISP-Tests in BricsCAD ausfuehren
BricsCAD starten (ueber bin\start_briscad.bat) und Module laden:
(load (strcat (getenv "DXFMAKRO") "/Lisp/ssg_load.lsp"))
Dann alle Tests auf einmal ausfuehren:
;; Alle Tests ausfuehren (empfohlen)
(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp"))
SSG_RUN_ALL_TESTS
SSG_RUN_ALL_TESTS liest die Testliste aus tests/alltests.json und fuehrt fuer jedes Modul aus:
- Zeichnung leeren (ERASE ALL + PURGE x5) — saubere Basis fuer jeden Test
test_<name>.lspladen undTEST_<NAME>ausfuehren- JSON-Export aufrufen:
<name>:export-results— immer, unabhaengig vonsave - Zeichnung speichern — nur wenn
"save"inalltests.jsongesetzt ist ("dxf"oder"dwg") - Bei Fehler: weiter mit naechstem Test (
vl-catch-all-apply)
Einzeltest (z.B. nur Kreisel pruefen):
;; Test-Datei laden und Befehl ausfuehren
(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp"))
TEST_KREISEL
;; JSON-Export manuell aufrufen:
(kreisel:export-results (strcat (getenv "DXFMAKRO") "/tests/output"))
Unterschied SSG_RUN_ALL_TESTS vs. Einzeltest:
| SSG_RUN_ALL_TESTS | Einzeltest | |
|---|---|---|
| Testliste | aus alltests.json |
manuell gewaehlter Test |
| Zeichnung leeren | automatisch vor jedem Test | nein (Zeichnung bleibt) |
| JSON-Export | automatisch nach jedem Test | manuell aufrufen |
| DXF/DWG speichern | gemaess save in alltests.json |
manuell (SAVEAS / DXFOUT) |
| Fehlerisolation | ja (Fehler unterbricht nicht weitere Tests) | nein |
2. Python-Validierung ausfuehren
bin\run_tests.bat --check
Oder fuer ein einzelnes Testmodul:
bin\run_tests.bat --check test_kreisel.py
Die Python-Tests pruefen:
- Attribut-Werte gegen die Testdefinitionen (JSON)
- DXF-Geometrie mit ezdxf (Bloecke, Positionen, Radien)
- Referenz-Vergleich gegen abgenommene Referenz-DXF
- CSV-Export ueber
lib/export_csv.py(Omniflo: Spalten, Merkmale, Sum-Zeile, Datentypen)
Tests, deren Eingabedaten fehlen (z.B. kein BricsCAD-Lauf), werden automatisch uebersprungen (pytest.skip).
3. Referenz erstellen/aktualisieren
Nach einem erfolgreichen, geprueften Testlauf die betreffende(n) Ergebnisdatei(en)
manuell aus output/ unter DEMSELBEN Dateinamen nach reference/ kopieren (nur bei
bestandenen Tests, siehe Abschnitt "Referenz-Namenskonvention"). Es gibt keinen
eigenen Batch-Schalter dafuer.
copy tests\output\<datei> tests\reference\<datei>
Beispiel (Kreisel-DXF und Omniflo-CSV als Referenz festlegen):
copy tests\output\kreisel_tests.dxf tests\reference\kreisel_tests.dxf
copy tests\output\omniflo_export.csv tests\reference\omniflo_export.csv
Zum Vergleich der aktuellen CSVs gegen die Referenz (mit Diff-Tool meld):
bin\run_tests.bat --diff_references
Neuen Test erstellen
Schritt 1: Testdaten definieren (JSON)
Neue Testfaelle in testdata/<modul>_tests.json eintragen. Das Format ist ein flaches JSON-Array (kompatibel mit omni:load-json).
Kreisel-Beispiel (kreisel_tests.json):
[
{
"id": "KR_Insert_Neu",
"function": "insert",
"x": 5000, "y": 5000, "z": 2000,
"abstand": 5000,
"rotation": 0.0,
"typ": "STANDARD",
"expect_block_prefix": "KREISEL_",
"expect_hoehe": "2000",
"expect_kreiselart": "STANDARD"
}
]
Felder:
id— Eindeutiger Name des Testfallsfunction—"insert"oder"connect"- Eingabeparameter (
x,y,z,abstand,rotation,typfuer Insert;start_x/y/z,end_x/y/z,typfuer Connect) expect_*— Erwartete Ergebniswerte (fuer Python-Validierung)
Omniflo-Beispiel (omniflo_tests.json):
[
{
"sivasnr": "834372001",
"type": "bogen",
"description": "Bogen 90 Grad R200",
"hoehe": 2000,
"drehung": 0
}
]
Foerderer-Beispiel (foerderer_tests.json):
[
{ "y_abstand": 800 },
{
"test_id": "VF_Auf_2000_Rechts",
"richtung": "Auf",
"deltaL": 7000,
"deltaH": 2000,
"seite": "rechts",
"winkel": 27,
"gf_verteilung": "vorne"
}
]
Felder:
test_id— Eindeutiger Name des Testfallsrichtung—"Auf"oder"Ab"deltaL,deltaH— Horizontal-/Hoehendistanz in mmseite(optional, Default"links") —"links"oder"rechts", steuert AS-/ES-Seite (SEITE_AS/SEITE_ES-Attribut)winkel(optional) — erzwingt einen bestimmten Kandidatenwinkel statt des automatisch gewaehlten kleinsten gueltigen Winkels; muss ein gueltiger Wert aus derbogen_winkel-Kandidatenliste sein, sonst wird der TestfallNICHT_GEBAUTgf_verteilung(optional, Default"gleichmaessig") —"gleichmaessig"(50/50 auf Einlauf/Auslauf),"vorne"(alles am Einlauf) oder"hinten"(alles am Auslauf)
Schritt 2: LISP-Testrunner erweitern (falls neues Modul)
Fuer ein neues Modul eine neue .lsp-Datei erstellen nach dem Muster:
;; test_<modul>.lsp
;; Konvention: Basisname <modul> bestimmt Befehl, DXF und Export-Funktion
;; Export-Funktion (wird von test_run_all.lsp aufgerufen)
(defun <modul>:export-results (tests-out-dir / out-json f first)
(if (null *<modul>-test-results*)
(princ "\n Keine <modul>-Ergebnisse vorhanden.")
(progn
(vl-mkdir tests-out-dir)
(setq out-json (strcat tests-out-dir "/<modul>_results.json"))
;; ... JSON-Array schreiben ...
)
)
)
(defun c:TEST_<MODUL> (/ ...)
(ssg-start "TEST_<MODUL>" '(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
;; 1. Testdaten laden
(setq testfaelle (omni:load-json
(strcat (getenv "DXFMAKRO") "/tests/testdata/<modul>_tests.json")))
;; 2. Testfaelle ausfuehren, Ergebnisse sammeln
(foreach eintrag testfaelle
;; ... Script-Funktion aufrufen, Ergebnis pruefen ...
)
;; 3. Ergebnisse in globaler Variable speichern
(setq *<modul>-test-results* (reverse results-list))
(ssg-end)
)
Dann den Basisnamen in tests/alltests.json eintragen:
{ "name": "<modul>", "save": "dxf" }
"save" waehlen je nach Bedarf ("dxf", "dwg" oder null).
test_run_all.lsp leitet alle Namen aus dem "name"-Feld ab — nichts in test_run_all.lsp aendern.
Namenskonventionen (werden von test_run_all.lsp automatisch abgeleitet)
Aus dem Basisnamen <name> in alltests.json entstehen durch Konvention:
| Element | Schema | Beispiel fuer "name": "kreisel" |
|---|---|---|
| LISP-Datei | tests/test_<name>.lsp |
tests/test_kreisel.lsp |
| BricsCAD-Befehl | TEST_<NAME> (Grossbuchstaben) |
TEST_KREISEL |
| Export-Funktion | <name>:export-results |
kreisel:export-results |
| JSON-Ergebnis | output/<name>_results.json |
output/kreisel_results.json |
| DXF/DWG-Datei | output/<name>_tests.dxf/.dwg |
output/kreisel_tests.dxf |
| Referenz (Soll) | reference/<gleicher Name wie output> |
reference/kreisel_tests.dxf |
Die Export-Funktion muss exakt so heissen: <name>:export-results (Namespace-Notation mit Doppelpunkt).
Sie nimmt genau einen Parameter entgegen: den Pfad zum Output-Verzeichnis.
Wichtig:
omni:load-jsonerwartet ein flaches JSON-Array mit flachen Objekten (kein Nesting)omni:valliest Werte aus den geladenen Eintraegen- Script-Funktionen (z.B.
kreisel-insert-script) muessen ohne User-Interaktion funktionieren
Schritt 3: Python-Validierung erstellen
Neue test_<modul>.py erstellen:
import pytest
class TestModulAttributes:
"""Prueft Attribute gegen Testdefinitionen."""
def test_all_executed(self, <modul>_results):
"""Alle Testfaelle muessen ausgefuehrt worden sein."""
# ...
def test_attribute_values(self, <modul>_testdata, <modul>_results):
"""Attributwerte muessen mit Erwartungen uebereinstimmen."""
# ...
class TestModulGeometry:
"""Prueft DXF-Geometrie mit ezdxf."""
def test_blocks_exist(self, <modul>_dxf):
"""Bloecke muessen im Modelspace vorhanden sein."""
# ...
class TestModulReference:
"""Vergleicht gegen Referenz."""
def test_matches_reference(self, <modul>_dxf, <modul>_ref_dxf):
# ...
Schritt 4: Fixtures in conftest.py ergaenzen
Neue Fixtures fuer Testdaten, Ergebnisse und DXF in conftest.py hinzufuegen:
@pytest.fixture
def <modul>_testdata():
path = os.path.join(_testdata_dir(), "<modul>_tests.json")
return _load_json(path)
@pytest.fixture
def <modul>_results():
path = os.path.join(_output_dir(), "<modul>_results.json")
if not os.path.exists(path):
pytest.skip("<modul>_results.json nicht vorhanden")
return _load_json(path)
@pytest.fixture
def <modul>_dxf():
path = os.path.join(_output_dir(), "<modul>_tests.dxf")
if not os.path.exists(path):
pytest.skip("<modul>_tests.dxf nicht vorhanden")
return ezdxf.readfile(path)
@pytest.fixture
def <modul>_ref_dxf():
# Konvention: Referenz = GLEICHER Dateiname wie das Ist-Ergebnis in output/.
path = os.path.join(_reference_dir(), "<modul>_tests.dxf")
if not os.path.exists(path):
pytest.skip("reference/<modul>_tests.dxf nicht vorhanden - Referenz noch "
"nicht festgelegt (output/<modul>_tests.dxf nach reference/ kopieren)")
return ezdxf.readfile(path)
Testklassen
test_kreisel.py
| Klasse | Prueft |
|---|---|
TestKreiselAttributes |
Attribute (KREISELART, HOEHE), Block-Prefixe, Status |
TestKreiselGeometry |
DXF-Bloecke, Positionen, Kreis-Radien (400mm) |
TestKreiselReference |
Block-Anzahl und -Typen gegen Referenz-DXF |
test_omniflo.py
| Klasse | Prueft |
|---|---|
TestOmnifloExportUnit |
Mock-Export ueber lib/export_csv.py: Merkmale, Datentypen, Sum-Zeile, Vollstaendigkeit (ohne BricsCAD) |
TestOmnifloResults |
omniflo_results.json aus BricsCAD: Testfaelle vollstaendig, Status OK, Hoehe |
TestOmnifloReferenceCSV |
CSV-Export aus lib/export_csv.py gegen abgenommene Referenz-CSV |
test_omniflo_strecke.py
Streckenzug 4 Geraden -> 180-Grad-Kurve (4x 45-Grad-Bogen) -> 4 Geraden.
Die Geometrie wird ohne BricsCAD direkt aus omniflo_strecke_tests.json geprueft.
| Klasse | Prueft |
|---|---|
TestStreckeStruktur |
4+4+4 = 12 Elemente, Reihenfolge der Gruppen, Typen |
TestStreckeKatalog |
Bogen-SivasNummern existieren in omniflo_boegen.json (Radius/Winkel) |
TestStreckeGeometrie |
Lueckenlose Verkettung, Kurvensumme 180 Grad, gemeinsamer Bogen-Mittelpunkt |
TestStreckeResults |
omniflo_strecke_results.json aus BricsCAD: Elemente vollstaendig, Status OK |
Umgebungsvariablen
Werden von bin\setenv.bat gesetzt. Fuer Tests relevant:
| Variable | Beschreibung |
|---|---|
DXFMAKRO |
Projektwurzel |
DXFM_TESTS |
tests/-Verzeichnis |
DXFM_TESTDATA |
testdata/-Verzeichnis |
DXFM_TESTOUT |
output/-Verzeichnis |
DXFM_TESTREF |
reference/-Verzeichnis |
DXFM_OMNIFLO |
Pfad zu Omniflo-DXF-Dateien |