e3c7fb0d25
doc/VarioFoerderer_Linienzug_Prinzipien.md ist bewusst nicht versioniert
(siehe ce00233). Die drei Stellen, die von dort die Begruendung geholt
haben, tragen sie jetzt selbst: warum quer und dZ null sein MUESSEN und
laengs abweichen DARF - naemlich weil beides zugleich geometrisch nicht
geht und der Hauptstrang gerade bleiben soll.
Ein Verweis auf eine Datei, die im Repo fehlt, kostet den naechsten Leser
mehr Zeit als die zwei Zeilen Text.
Nicht angefasst: die aelteren Verweise in vf_linienzug.lsp (Abschnitt 14,
14.1) und vf_standard.lsp - sie stammen nicht aus dieser Arbeit und
zeigen auf Inhalte, die hier niemand kennt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
644 lines
26 KiB
Markdown
644 lines
26 KiB
Markdown
# Tests - SSG_LIB Testarchitektur
|
|
|
|
## Uebersicht
|
|
|
|
Die Tests folgen einer **zweistufigen Architektur**:
|
|
|
|
1. **LISP-Testrunner** (`.lsp`) — laufen in BricsCAD, erzeugen Ergebnisdateien
|
|
2. **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` — Befehl `TEST_UNIT`, Mini-Framework mit
|
|
`tu-eq` (exakt), `tu-eqf` (Fliesskomma-Toleranz), `tu-true`, `tu-nil`.
|
|
- **Startskript**: `tests/test_unit.scr` — laedt SSG_LIB + die Tests, ruft `TEST_UNIT`
|
|
auf und beendet BricsCAD (pfadunabhaengig ueber `DXFMAKRO`).
|
|
- **Ergebnis**: Konsole (PASS/FAIL je Test) und `tests/output/unit_results.txt`
|
|
(letzte Zeile `RESULT: OK` / `RESULT: FAIL`).
|
|
|
|
Ausfuehrung ueber die Kommandozeile (startet BricsCAD headless):
|
|
|
|
```cmd
|
|
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):
|
|
|
|
```lisp
|
|
(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 _Extents` auf 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:EXPORTSIVAS` laufen **nicht** automatisch mit — CSV/Sivas-
|
|
Export danach bei Bedarf manuell ueber `TEST_EXPORT_ALL`
|
|
(`tests/test_export_all.lsp`, Menuepunkt "Export CSV/Sivas ALL"), das alle
|
|
vorhandenen DXF/DWG-Dateien aus `tests/output/` einliest und je Datei
|
|
`<basis>_export.csv`/`<basis>_sivas.csv` erzeugt.
|
|
- **Automatik-Modus (Ja)**: Zeichnung wird gespeichert, direkt per
|
|
`c:EXPORTCSV`/`c:EXPORTSIVAS` exportiert (`<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.
|
|
|
|
## Referenzfaelle Linienzug Modus 2 (manuell, mit Soll-Werten)
|
|
|
|
Zwei Laeufe auf **demselben gezeichneten Pfad**, die sich nur in der
|
|
Segment-Klassifizierung unterscheiden. Sie decken die beiden Eck-Bauteilarten
|
|
ab (waagerecht eingebaute Vario-Kurve und geneigt eingebauter Gefaellebogen)
|
|
und haben am 2026-10-26 sieben Budget-Fehler aufgedeckt, die ein einzelner
|
|
Lauf nicht gezeigt haette - erst der VERGLEICH zweier Varianten machte
|
|
sichtbar, dass eine Korrektur am falschen Segment landete.
|
|
|
|
**Gezeichneter Pfad** (5 Objekte, Boegen als R500-Verrundung):
|
|
|
|
| Segment | Art | Mass |
|
|
|---|---|---|
|
|
| 1 | Linie | L = 9700 mm, hz = 0 |
|
|
| 2 | Bogen | 90 Grad rechts, R500 |
|
|
| 3 | Linie | L = 4296 mm, hz = -90 |
|
|
| 4 | Bogen | 30 Grad links, R500 |
|
|
| 5 | Linie | L = 1466 mm, hz = -60 |
|
|
|
|
**Gemeinsame Eingaben:** AS-Element ja, 90 Grad, links. ES-Element ja,
|
|
30 Grad, rechts. Lauf beginnen mit "Umlenkstation mit GF1 + Separator",
|
|
GF-Verteilung "alles am Einlauf", keine zusaetzlichen Separatoren.
|
|
|
|
| | Variante A | Variante B |
|
|
|---|---|---|
|
|
| Segment 2 | **Vario-Kurve** (aussen) | **GF-Bogen** |
|
|
| Segment 3 | VF (automatisch nach Kurve) | GF |
|
|
| Segment 4 | GF-Bogen | GF-Bogen |
|
|
| Segment 5 | GF | GF |
|
|
| VF-Lauf | Segment 1..3 | Segment 1..1 |
|
|
|
|
**Soll-Ergebnis fuer BEIDE Varianten:**
|
|
|
|
- `Quer zur Achse` **~0** (gemessen 0.2 mm - das ist die Aufloesung der
|
|
Blockmessung, kein Restfehler)
|
|
- `dZ` **0**
|
|
- `Laengs KS_AUS-Achse` darf abweichen (~104 mm) - das ist die Ausladung des
|
|
ES-Elements und ausdruecklich zulaessig.
|
|
|
|
Die Regel dahinter: **quer zur KS_AUS-Achse MUSS 0 sein, laengs darf
|
|
abweichen, dZ MUSS 0 sein.** Beides zugleich geht nicht - entweder liegt die
|
|
letzte Gerade auf der gezeichneten Linie und der ES-`KS_AUS` landet seitlich
|
|
daneben, oder umgekehrt. Entschieden ist: der Hauptstrang bleibt gerade, der
|
|
seitliche Versatz des ES-Elements ist dessen Bauform und kein Fehler.
|
|
|
|
Im Protokoll muessen die Zuschlags-Zeilen erscheinen: `AS-Fussabdruck`,
|
|
`Eck-Korrektur` je Ecke, und bei Variante A zusaetzlich
|
|
`Abschluss-Fussabdruck`. **Eine FEHLENDE Zeile ist das Warnsignal** - sie
|
|
bedeutet, dass ein Eck-Bauteil nicht messbar war und die Ecke unkorrigiert
|
|
blieb (so wurde der 2.4-mm-Rest bei Variante B gefunden).
|
|
|
|
> Noch nicht automatisiert. Dafuer muesste der Testfall den Pfad selbst per
|
|
> `entmake` zeichnen und das Eingabe-Journal replayen; das Journal einer
|
|
> gebauten Kette laesst sich mit `lib/vf_journal_export.py` aus der Zeichnung
|
|
> ziehen. Die reinen Rechenkerne dieser Faelle sind dagegen bereits file-frei
|
|
> abgedeckt (`tu-tests-vfl-parallel` in `test_unit.lsp`: `vfl-eck-zerlegen`,
|
|
> `vfl-es-achs-reserve`, `vfl-projiziere-neigung` - jeweils mit den echten
|
|
> Messwerten aus diesen beiden Laeufen).
|
|
|
|
## 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`.
|
|
|
|
```json
|
|
[
|
|
{ "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 soll
|
|
- `null` (oder Feld weglassen) — wenn nur der JSON-Export benoetigt wird
|
|
|
|
## Ablauf fuer den Anwender
|
|
|
|
1. **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 nach `tests/output/`.
|
|
|
|
2. **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.
|
|
|
|
3. **Kommandozeile**: Ergebnisse validieren:
|
|
- `bin\run_tests.bat --check` — pytest prueft die Ergebnisse aus `output/` gegen Testdefinitionen und Referenzdaten (optional: einzelnes Modul, z.B. `--check test_kreisel.py`)
|
|
- `bin\run_tests.bat --diff_references` — vergleicht die CSVs in `output/` gegen `reference/` (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 nach `reference/` 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` |
|
|
|
|
```cmd
|
|
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 Tippfehler `Kresel_ref.dxf`) -> jetzt
|
|
`reference/kreisel_tests.dxf`
|
|
- `reference/omniflo_export_reference.csv` -> jetzt `reference/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:
|
|
|
|
```lisp
|
|
(load (strcat (getenv "DXFMAKRO") "/Lisp/ssg_load.lsp"))
|
|
```
|
|
|
|
Dann alle Tests auf einmal ausfuehren:
|
|
|
|
```lisp
|
|
;; 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:
|
|
|
|
1. Zeichnung leeren (ERASE ALL + PURGE x5) — saubere Basis fuer jeden Test
|
|
2. `test_<name>.lsp` laden und `TEST_<NAME>` ausfuehren
|
|
3. JSON-Export aufrufen: `<name>:export-results` — **immer**, unabhaengig von `save`
|
|
4. Zeichnung speichern — nur wenn `"save"` in `alltests.json` gesetzt ist (`"dxf"` oder `"dwg"`)
|
|
5. Bei Fehler: weiter mit naechstem Test (`vl-catch-all-apply`)
|
|
|
|
**Einzeltest** (z.B. nur Kreisel pruefen):
|
|
|
|
```lisp
|
|
;; 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
|
|
|
|
```cmd
|
|
bin\run_tests.bat --check
|
|
```
|
|
|
|
Oder fuer ein einzelnes Testmodul:
|
|
|
|
```cmd
|
|
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.
|
|
|
|
```cmd
|
|
copy tests\output\<datei> tests\reference\<datei>
|
|
```
|
|
|
|
Beispiel (Kreisel-DXF und Omniflo-CSV als Referenz festlegen):
|
|
|
|
```cmd
|
|
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):
|
|
|
|
```cmd
|
|
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`):
|
|
|
|
```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 Testfalls
|
|
- `function` — `"insert"` oder `"connect"`
|
|
- Eingabeparameter (`x`, `y`, `z`, `abstand`, `rotation`, `typ` fuer Insert; `start_x/y/z`, `end_x/y/z`, `typ` fuer Connect)
|
|
- `expect_*` — Erwartete Ergebniswerte (fuer Python-Validierung)
|
|
|
|
**Omniflo-Beispiel** (`omniflo_tests.json`):
|
|
|
|
```json
|
|
[
|
|
{
|
|
"sivasnr": "834372001",
|
|
"type": "bogen",
|
|
"description": "Bogen 90 Grad R200",
|
|
"hoehe": 2000,
|
|
"drehung": 0
|
|
}
|
|
]
|
|
```
|
|
|
|
**Foerderer-Beispiel** (`foerderer_tests.json`):
|
|
|
|
```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 Testfalls
|
|
- `richtung` — `"Auf"` oder `"Ab"`
|
|
- `deltaL`, `deltaH` — Horizontal-/Hoehendistanz in mm
|
|
- `seite` *(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 der `bogen_winkel`-Kandidatenliste sein, sonst wird der Testfall `NICHT_GEBAUT`
|
|
- `gf_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:
|
|
|
|
```lisp
|
|
;; 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:
|
|
|
|
```json
|
|
{ "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-json` erwartet ein **flaches JSON-Array** mit flachen Objekten (kein Nesting)
|
|
- `omni:val` liest 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:
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
@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 |
|