Files
dxfmakros/tests
..

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
   |                                   |
   |  SSG_RUN_ALL_TESTS                |
   |  (TEST_KREISEL,                   |
   |   TEST_FOERDERER,                 |
   |   TEST_KSEINAUS,                  |
   |   TEST_OMNIFLO)                   |
   |        |                          |
   v        v                          |
tests/output/                          |
   kreisel_results.json                |
   kreisel_tests.dxf                   |
   foerderer_results.json              |
   foerderer_tests.dxf                 |
   kseinaus_results.json               |
   kseinaus_tests.dxf                  |
   omniflo_results.json                |
   omniflo_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_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_kseinaus.lsp        # LISP-Testrunner: C:TEST_KSEINAUS
  test_omniflo.lsp         # LISP-Testrunner: C:TEST_OMNIFLO
  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   # JSON-Ergebnisse pro Testmodul
    kreisel_tests.dxf      # DXF-Zeichnung pro Testmodul
    foerderer_results.json
    foerderer_tests.dxf
    kseinaus_results.json
    kseinaus_tests.dxf
    omniflo_results.json
    omniflo_tests.dxf
    omniflo_export.csv     # CSV-Export (Omniflo-spezifisch)
  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:

(load (strcat (getenv "DXFMAKRO") "/Lisp/ssg_load.lsp"))

Dann alle Tests auf einmal ausfuehren:

;; Alle Tests ausfuehren (empfohlen)
(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp"))
SSG_RUN_ALL_TESTS

Oder einen einzelnen Test:

;; Einzeltest (z.B. Kreisel)
(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp"))
TEST_KREISEL
;; Ergebnisse manuell exportieren:
(kreisel:export-results (strcat (getenv "DXFMAKRO") "/tests/output"))

SSG_RUN_ALL_TESTS fuehrt fuer jedes Modul in *test-module* folgendes aus:

  • Zeichnung leeren (ERASE + PURGE)
  • Testdatei laden und Befehl ausfuehren
  • JSON-Export aufrufen (<name>:export-results)
  • DXF speichern (<name>_tests.dxf)
  • Bei Fehler: weiter mit naechstem Test (vl-catch-all-apply)

2. Python-Validierung ausfuehren

bin\run_tests.bat --runall

Oder fuer ein einzelnes Testmodul:

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:

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):

[
  {
    "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):

[
  {
    "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:

;; 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 *test-module* (in test_run_all.lsp) eintragen:

(setq *test-module* '("kreisel" "foerderer" "kseinaus" "omniflo" "<modul>"))

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:

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:

@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