Files
dxfmakros/doc/LispTests.md
T

319 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Testkonzept SSG_LIB - Hybrid AutoLISP + Python
## Uebersicht
Die SSG_LIB AutoLISP-Makros werden ueber einen zweistufigen Hybrid-Ansatz getestet:
1. **AutoLISP** (in BricsCAD): Liest JSON-Testfaelle, fuehrt die Funktionen aus,
schreibt Ergebnisse als JSON und DXF/DWG. Jedes Testmodul laeuft dabei in einer
eigenen, neuen Zeichnung (kein Wiederverwenden/Leeren der aktuellen).
`SSG_RUN_ALL_TESTS` fragt beim Start interaktiv, ob die Zeichnung nach dem
Speichern (Zoom Extents) als Tab offen bleibt (Kontroll-Modus, Default) oder
ob sie zusaetzlich per `c:EXPORTCSV`/`c:EXPORTSIVAS` exportiert und danach
sofort geschlossen wird (Automatik-Modus).
2. **Python** (pytest + ezdxf): Validiert die Ergebnisse automatisch gegen
erwartete Werte und optionale Referenzdaten.
```text
Python (pytest) AutoLISP (BricsCAD) Python (pytest)
=============== ==================== ===============
Testfall-Definitionen --> Ausfuehrung der --> Validierung:
als JSON bereitstellen LISP-Funktionen - JSON-Ergebnisse
+ DXF/DWG + results.json - DXF-Geometrie
- Referenz-Vergleich
```
## Verzeichnisstruktur
```text
tests/
├── alltests.json # Zentrale Testregistry (welche Module + Speicherformat)
├── test_run_all.lsp # C:SSG_RUN_ALL_TESTS - fuehrt alle Tests aus
├── test_kreisel.lsp # C:TEST_KREISEL - Kreisel-Testrunner
├── test_foerderer.lsp # C:TEST_FOERDERER - Variofoerderer-Testrunner
├── test_omniflo.lsp # C:TEST_OMNIFLO - Omniflo-Testrunner
├── test_gefaellestrecke.lsp # C:TEST_GEFAELLESTRECKE - Gefaellestrecke-Testrunner
├── test_export_all.lsp # C:TEST_EXPORT_ALL - manuell: oeffnet alle DXF/DWG aus output/ und exportiert sie (nicht in alltests.json)
├── conftest.py # pytest Fixtures
├── test_kreisel.py # pytest Kreisel-Validierung
├── test_omniflo.py # pytest Omniflo-Validierung
├── create_testbase.py # Hilfsskript (optional, i.d.R. nicht benoetigt)
├── requirements.txt # Python-Abhaengigkeiten (ezdxf, pytest)
├── testdata/ # JSON-Testfall-Definitionen (versioniert)
│ ├── kreisel_tests.json
│ ├── foerderer_tests.json
│ └── omniflo_tests.json
├── reference/ # Abgenommene Referenzdaten (versioniert)
│ └── omniflo_export_reference.csv
└── output/ # Generierte Ergebnisse (nicht in Git)
├── kreisel_results.json
├── kreisel_tests.dxf
├── foerderer_results.json
├── foerderer_tests.dwg
├── omniflo_results.json
├── omniflo_tests.dxf
├── gefaellestrecke_results.json
├── gefaellestrecke_tests.dwg
├── export_all_results.json # nur nach manuellem TEST_EXPORT_ALL-Lauf
├── kreisel_tests_export.csv # EXPORTCSV/EXPORTSIVAS je Testmodul - manuell via
├── kreisel_tests_sivas.csv # TEST_EXPORT_ALL, oder automatisch im Automatik-Modus
└── ... # von SSG_RUN_ALL_TESTS
```
## Testregistry (alltests.json)
Die Datei `tests/alltests.json` ist die zentrale Registry aller Testmodule.
`SSG_RUN_ALL_TESTS` liest sie beim Start — die Testliste ist nicht hartcodiert.
```json
[
{ "name": "kreisel", "save": "dxf", "module": "KreiselInsert" },
{ "name": "foerderer", "save": "dwg", "module": "VarioFoerderer" },
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }
]
```
`TEST_EXPORT_ALL` (`tests/test_export_all.lsp`) ist **nicht** in `alltests.json`
registriert und laeuft daher nicht automatisch mit `SSG_RUN_ALL_TESTS`. Es wird
manuell aufgerufen (Menuepunkt "Export CSV/Sivas ALL"), oeffnet dann jede zu diesem
Zeitpunkt in `tests/output/` vorhandene DXF/DWG-Datei und exportiert sie per
`c:EXPORTCSV`/`c:EXPORTSIVAS` nach `<basis>_export.csv`/`<basis>_sivas.csv`.
Alternativ: `SSG_RUN_ALL_TESTS` im Automatik-Modus (interaktive Abfrage beim
Start) erledigt Speichern, Export und Schliessen direkt je Testmodul, ohne dass
`TEST_EXPORT_ALL` danach noch manuell aufgerufen werden muss.
| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `name` | String | Basisname des Testmoduls (Pflicht) |
| `save` | `"dxf"` / `"dwg"` / `null` | Speicherformat der Zeichnung |
| `module` | String | LISP-Modul (wird von ssg-ensure geladen) |
Aus dem `name` werden alle Dateinamen abgeleitet:
| Element | Schema | Beispiel (`kreisel`) |
| --- | --- | --- |
| LISP-Datei | `tests/test_<name>.lsp` | `tests/test_kreisel.lsp` |
| BricsCAD-Befehl | `TEST_<NAME>` | `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` |
## Schritt 1: Testfaelle definieren (JSON)
Jedes Modul hat eine eigene JSON-Datei in `tests/testdata/`. Format: **flaches JSON-Array**
(kompatibel mit `omni:load-json`). Kein Nesting, keine Wrapper-Objekte.
### Kreisel-Format (kreisel_tests.json)
```json
[
{
"id": "KR_Kreuz_Q1_0Grad",
"function": "insert",
"x": 500, "y": 500, "z": 2500,
"abstand": 2000,
"rotation": 0.0,
"typ": "STANDARD",
"expect_block_prefix": "KREISEL_",
"expect_hoehe": "2500",
"expect_kreiselart": "STANDARD"
}
]
```
Felder:
- `id` — Eindeutiger Testfall-Name
- `function``"insert"` oder `"connect"`
- Eingabeparameter: `x`, `y`, `z`, `abstand`, `rotation`, `typ`
- `expect_*` — Erwartete Ergebniswerte (fuer Python-Validierung)
### Foerderer-Format (foerderer_tests.json)
```json
[
{ "y_abstand": 400 },
{
"test_id": "VF_Auf_0",
"richtung": "Auf",
"deltaL": 7000,
"deltaH": 0
}
]
```
- Erster Eintrag (ohne `test_id`): Konfiguration (`y_abstand` zwischen Foerderern)
- Felder: `test_id`, `richtung` (`"Auf"` / `"Ab"`), `deltaL`, `deltaH`
- Ergebnisstatus: `GEBAUT`, `NICHT_GEBAUT`, `GEOMETRISCH_UNMOEGLICH`
### Omniflo-Format (omniflo_tests.json)
```json
[
{
"id": "821104025",
"type": "bogen",
"sivasnr": "821104025",
"x": 219.4, "y": 223.06,
"hoehe": 2000,
"drehung": 0.0,
"description": "APB 110 R 550/22,5 84/522",
"row": "Boegen 22.5"
}
]
```
## Schritt 2: LISP-Testrunner ausfuehren (BricsCAD)
### Alle Tests auf einmal
```lisp
(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp"))
SSG_RUN_ALL_TESTS
```
`SSG_RUN_ALL_TESTS` liest `alltests.json` und fuehrt fuer jedes Modul aus:
1. Zeichnung leeren (ERASE ALL + PURGE x5)
2. `test_<name>.lsp` laden und `TEST_<NAME>` ausfuehren
3. JSON-Export: `<name>:export-results` aufrufen (immer)
4. Zeichnung speichern gemaess `save`-Feld (`"dxf"`, `"dwg"` oder kein Speichern)
5. Bei Fehler: weiter mit naechstem Test (Fehlerisolation)
### Einzeltest (z.B. nur Kreisel)
```lisp
(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp"))
TEST_KREISEL
(kreisel:export-results (strcat (getenv "DXFMAKRO") "/tests/output"))
```
### Ausgabe: results.json
```json
[
{
"test_id": "KR_Kreuz_Q1_0Grad",
"status": "GEBAUT",
"block_name": "KREISEL_STANDARD",
"insert_point": [500.0, 500.0, 2500.0],
"rotation": 0.0,
"actual_kreiselart": "STANDARD",
"actual_hoehe": "2500"
}
]
```
## Schritt 3: Python-Validierung (pytest)
```cmd
bin\run_tests.bat --runall
```
Oder einzelnes Testmodul:
```cmd
bin\run_tests.bat --runall test_kreisel.py
```
### 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: Merkmale, Datentypen, Vollstaendigkeit (ohne BricsCAD) |
| `TestOmnifloExportCSV` | CSV aus BricsCAD: Header, Spalten, JSON-Merkmale |
Tests, deren Eingabedaten fehlen (kein BricsCAD-Lauf), werden automatisch
uebersprungen (`pytest.skip`).
## Schritt 4: Referenz-Management
```cmd
# Aktuelle Ergebnisse als neue Referenz speichern
bin\run_tests.bat --set-as-reference
```
Kopiert alle Dateien aus `output/` nach `reference/`. Nur bei bestandenen Tests verwenden.
## Modulspezifische Testfaelle
### Kreisel (kreisel_tests.json)
| Test-ID | Beschreibung | Params |
| --- | --- | --- |
| KR_Kreuz_Q1_0Grad | Standard Kreisel, 0 Grad | rot=0, z=2500, abstand=2000 |
| KR_Kreuz_Q2_90Grad | Standard Kreisel, 90 Grad | rot=90, z=2500, abstand=3000 |
| KR_Kreuz_Q3_180Grad | Standard Kreisel, 180 Grad | rot=180, z=2500, abstand=4000 |
| KR_Kreuz_Q4_270Grad | Standard Kreisel, 270 Grad | rot=270, z=2500, abstand=5000 |
| KR_Schraeg_Q1_30Grad | Standard Kreisel, schraeg | rot=30, z=3000, abstand=3000 |
| KR_Pin_Q1_0Grad | Pin-Kreisel, 0 Grad | rot=0, typ=PIN, z=1500 |
| KR_Pin_Q1_90Grad | Pin-Kreisel, 90 Grad | rot=90, typ=PIN, z=1500 |
| KR_Pin_Q1_30Grad_Schraeg | Pin-Kreisel, schraeg | rot=30, typ=PIN, z=1500 |
### Foerderer (foerderer_tests.json)
| Test-ID | Richtung | deltaL | deltaH |
| --- | --- | --- | --- |
| VF_Auf_0 | Auf | 7000 | 0 |
| VF_Auf_1000 | Auf | 7000 | 1000 |
| VF_Auf_2000 | Auf | 7000 | 2000 |
| VF_Auf_3000 | Auf | 7000 | 3000 |
| VF_Auf_4000 | Auf | 7000 | 4000 |
| VF_Auf_5000 | Auf | 7000 | 5000 |
| VF_Ab_0 | Ab | 7000 | 0 (GEOMETRISCH_UNMOEGLICH) |
| VF_Ab_1000 | Ab | 7000 | 1000 |
| VF_Ab_2000 | Ab | 7000 | 2000 |
| VF_Ab_3000 | Ab | 7000 | 3000 |
| VF_Ab_4000 | Ab | 7000 | 4000 |
| VF_Ab_5000 | Ab | 7000 | 5000 |
### Omniflo (omniflo_tests.json)
Testfaelle basieren auf echten Sivas-Nummern (Boegen und Zusatzmodule).
Beispiele: `821104025`, `821104025+0_B10090`, `821104025+0_B10091`, ...
## Umgebungsvariablen
Werden via `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 |
## Abhaengigkeiten
### Python (tests/requirements.txt)
```text
ezdxf>=1.0
pytest>=7.0
```
### AutoLISP
- SSG_LIB muss geladen sein (Menue per `_MENULOAD` eingerichtet, siehe CLAUDE.md)
- `test_run_all.lsp` laedt die einzelnen Testrunner automatisch
## Neuen Test erstellen
1. **Testdaten**: `testdata/<modul>_tests.json` erstellen (flaches JSON-Array)
2. **LISP-Testrunner**: `test_<modul>.lsp` erstellen mit `c:TEST_<MODUL>` und `<modul>:export-results`
3. **Testregistry**: Eintrag in `alltests.json` hinzufuegen
4. **Python-Validierung** (optional): `test_<modul>.py` und Fixtures in `conftest.py`
Detailliertes Vorgehen und Codevorlagen: siehe `tests/README.md`.