# 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. 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_kseinaus.lsp # C:TEST_KSEINAUS - KreiselEinAus-Testrunner ├── test_omniflo.lsp # C:TEST_OMNIFLO - Omniflo-Testrunner ├── 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 ├── kseinaus_results.json ├── omniflo_results.json ├── omniflo_tests.dxf └── omniflo_export.csv ``` ## 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": "kseinaus", "save": null, "module": "VarioFoerderer" }, { "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" } ] ``` | 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_.lsp` | `tests/test_kreisel.lsp` | | BricsCAD-Befehl | `TEST_` | `TEST_KREISEL` | | Export-Funktion | `:export-results` | `kreisel:export-results` | | JSON-Ergebnis | `output/_results.json` | `output/kreisel_results.json` | | DXF/DWG-Datei | `output/_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_.lsp` laden und `TEST_` ausfuehren 3. JSON-Export: `: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/_tests.json` erstellen (flaches JSON-Array) 2. **LISP-Testrunner**: `test_.lsp` erstellen mit `c:TEST_` und `:export-results` 3. **Testregistry**: Eintrag in `alltests.json` hinzufuegen 4. **Python-Validierung** (optional): `test_.py` und Fixtures in `conftest.py` Detailliertes Vorgehen und Codevorlagen: siehe `tests/README.md`.