neues Testsystem. Dazu Doku erstellt wie es funktioniert in dewr README.md Aufruf aller Tests im Bricscad durch eigenes Menü.
This commit is contained in:
+294
@@ -0,0 +1,294 @@
|
||||
# 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/<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
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Schritt 2: LISP-Testrunner erweitern (falls neues Modul)
|
||||
|
||||
Fuer ein neues Modul eine neue `.lsp`-Datei erstellen nach dem Muster:
|
||||
|
||||
```lisp
|
||||
;; test_<modul>.lsp
|
||||
|
||||
(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 als JSON speichern
|
||||
(setq out-json (strcat (getenv "DXFMAKRO") "/tests/output/<modul>_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_<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():
|
||||
path = os.path.join(_reference_dir(), "<modul>_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 |
|
||||
Reference in New Issue
Block a user