# 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 ``` BricsCAD Kommandozeile | | | TEST_KREISEL | | TEST_OMNIFLO_EXPORT | | | | v v | tests/output/ | kreisel_results.json | kreisel_tests.dxf | omniflo_export.csv | | bin\run_tests.bat --runall | pytest -v | test_kreisel.py test_omniflo.py ``` ## 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). Die Zeichnung bleibt offen und kann visuell inspiziert werden. 3. **Kommandozeile**: Ergebnisse validieren oder als neue Referenz speichern: - `bin\run_tests.bat --runall` — pytest prueft die Ergebnisse aus `output/` gegen Testdefinitionen und Referenzdaten - `bin\run_tests.bat --set-as-reference` — nach erfolgreicher Pruefung die aktuellen Ergebnisse als neue Referenz uebernehmen (kopiert `output/` nach `reference/`) ## 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_kreisel.lsp # LISP-Testrunner: C:TEST_KREISEL test_kreisel.py # pytest-Validierung der Kreisel-Ergebnisse test_omniflo.lsp # LISP-Testrunner: C:TEST_OMNIFLO_EXPORT test_omniflo.py # pytest-Validierung der Omniflo-Ergebnisse testdata/ kreisel_tests.json # Testfall-Definitionen fuer Kreisel omniflo_tests.json # Testfall-Definitionen fuer Omniflo output/ # Ergebnisse aus BricsCAD (nicht in Git) kreisel_results.json kreisel_tests.dxf omniflo_export.csv reference/ # Abgenommene Referenzdaten (in Git) kreisel_ref.dxf ... ``` ## 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 den gewuenschten Testrunner laden und ausfuehren: ```lisp ;; Kreisel-Test (load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) TEST_KREISEL ;; Omniflo-Test (load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) TEST_OMNIFLO_EXPORT ``` Die Testrunner: - Lesen Testfaelle aus `testdata/*.json` - Fuegen Bloecke per Script-Funktionen ein (ohne User-Interaktion) - Speichern Ergebnisse nach `output/` ### 2. Python-Validierung ausfuehren ```cmd bin\run_tests.bat --runall ``` Oder fuer ein einzelnes Testmodul: ```cmd bin\run_tests.bat --runall 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** (Omniflo: Spalten, Merkmale, Datentypen) Tests, deren Eingabedaten fehlen (z.B. kein BricsCAD-Lauf), werden automatisch uebersprungen (`pytest.skip`). ### 3. Referenz erstellen/aktualisieren Nach einem erfolgreichen Testlauf die Ergebnisse als Referenz speichern: ```cmd bin\run_tests.bat --set-as-reference ``` Kopiert alle Dateien aus `output/` nach `reference/`. Nur bei bestandenen Tests verwenden. ## Neuen Test erstellen ### Schritt 1: Testdaten definieren (JSON) Neue Testfaelle in `testdata/_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 } ] ``` ### Schritt 2: LISP-Testrunner erweitern (falls neues Modul) Fuer ein neues Modul eine neue `.lsp`-Datei erstellen nach dem Muster: ```lisp ;; test_.lsp (defun c:TEST_ (/ ...) (ssg-start "TEST_" '(("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/_tests.json"))) ;; 2. Testfaelle ausfuehren, Ergebnisse sammeln (foreach eintrag testfaelle ;; ... Script-Funktion aufrufen, Ergebnis pruefen ... ) ;; 3. Ergebnisse als JSON speichern (setq out-json (strcat (getenv "DXFMAKRO") "/tests/output/_results.json")) ;; ... JSON schreiben ... ;; 4. DXF speichern (command "_.SAVEAS" "DXF" out-dxf) (ssg-end) ) ``` 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_.py` erstellen: ```python import pytest class TestModulAttributes: """Prueft Attribute gegen Testdefinitionen.""" def test_all_executed(self, _results): """Alle Testfaelle muessen ausgefuehrt worden sein.""" # ... def test_attribute_values(self, _testdata, _results): """Attributwerte muessen mit Erwartungen uebereinstimmen.""" # ... class TestModulGeometry: """Prueft DXF-Geometrie mit ezdxf.""" def test_blocks_exist(self, _dxf): """Bloecke muessen im Modelspace vorhanden sein.""" # ... class TestModulReference: """Vergleicht gegen Referenz.""" def test_matches_reference(self, _dxf, _ref_dxf): # ... ``` ### Schritt 4: Fixtures in conftest.py ergaenzen Neue Fixtures fuer Testdaten, Ergebnisse und DXF in `conftest.py` hinzufuegen: ```python @pytest.fixture def _testdata(): path = os.path.join(_testdata_dir(), "_tests.json") return _load_json(path) @pytest.fixture def _results(): path = os.path.join(_output_dir(), "_results.json") if not os.path.exists(path): pytest.skip("_results.json nicht vorhanden") return _load_json(path) @pytest.fixture def _dxf(): path = os.path.join(_output_dir(), "_tests.dxf") if not os.path.exists(path): pytest.skip("_tests.dxf nicht vorhanden") return ezdxf.readfile(path) @pytest.fixture def _ref_dxf(): path = os.path.join(_reference_dir(), "_ref.dxf") if not os.path.exists(path): pytest.skip("Referenz nicht vorhanden") 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: Merkmale, Datentypen, Vollstaendigkeit (ohne BricsCAD) | | `TestOmnifloExportCSV` | CSV aus BricsCAD: Header, Spalten, JSON-Merkmale | ## 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 |