Files
dxfmakros/tests/test_vf_spec.lsp
T
s.ayadi 5902343352 [CHANGE] Nur noch EIN Test baut die HundM-Geometrie: Spec wird TEST_HUNDM05
Nach dem gruenen VF_SPEC_BAU-Lauf bauten zwei Tests dieselben 5 Ketten. Der
Spec-Pfad ist jetzt der routinemaessige Bau-Test, der Aufzeichnungs-Pfad der
abgeschaltete Regressionstest fuer das Journal-Format.

Umbenennungen:
- tests/testdata/vf_spec_hundm05.json -> tests/testdata/hundm05.json
  Die Spec ist das Eingabeformat und die Datei, in der weitere Ketten der
  Anlage nachgetragen werden. spec_ids jetzt VF_hundm05_LZ_* (wie im
  Aufzeichnungs-Protokoll), damit sich die Ergebnisse beider Laeufe paaren
  lassen.
- tests/testdata/hundm05.json -> tests/testdata/hm_recformat.json
  tests/test_hundm05.lsp -> tests/test_hm_recformat.lsp (TEST_HM_RECFORMAT,
  Praefix hmrec:, Export hm_recformat:export-results)
  tests/test_hundm05.py -> tests/test_hm_recformat.py
  In alltests.json auf "disabled": true.

Neu: tests/test_hundm05.lsp (TEST_HUNDM05) baut die Spec - OHNE eigene
Bau-Logik, es ruft vsp-bau-datei aus Lisp/vf_spec.lsp. Damit gibt es genau
einen Code-Pfad, der aus einer Spec Geometrie erzeugt; c:VF_SPEC_BAU nutzt
denselben. tests/test_hundm05.py prueft das Ergebnis (die 9 Tests, die
vorher in test_vf_spec.py standen); test_vf_spec.py ist jetzt reiner
Uebersetzer-Test ohne CAD.

Warum das Aufzeichnungsformat bleibt (Details in
doc/TODO-plan-vf-interactive.md Abschnitt 4.7):
- hm_recformat.json ist die EINZIGE eingecheckte Kopie der Originaldaten
  (data/polylines.dxf ist mit 124 MB per .gitignore ausgeschlossen). Die Spec
  ist daraus abgeleitet; ohne die Aufzeichnung faellt die rechte Seite des
  Rundlauf-Beweises weg.
- Es prueft eine ANDERE Invariante: das Journal kommt roh aus der XDATA einer
  Kundenzeichnung. TEST_HM_RECFORMAT ist damit der einzige Test dafuer, dass
  ein BESTEHENDER VF_n-Block weiter abspielbar ist - also dass
  Doppelklick-Edit und 2D/3D-Konvertierung an Altbestand funktionieren. Ein
  spec-gebautes Journal kann das nicht zeigen, es kommt aus dem Uebersetzer.
- Es dokumentiert die Frage-Reihenfolge (kommentar je Eintrag); die Spec
  verbirgt den Dialog, das ist ihr Zweck.

Weitere Anpassungen:
- Ergebnisdatei heisst nach der Spec-Datei (<basisname>_results.json), damit
  Befehl und Testrunner in dieselbe Datei schreiben. Verzeichnis: tests/output
  (Override DXFM_VF_SPEC_OUT); NICHT DXFM_RESULTS - das sind die Sivas-/
  CSV-Exporte, dort landete die Datei ausserhalb des Testbaums.
- Neu *vsp-dim-override* fuer TEST_HUNDM05_2D/_3D: wird je Kette angewandt,
  weil der Abbruch-Handler in vf-linienzug-modus *ssg-ils-dim* zurueck setzt.
- Das Anlagenkuerzel der test_id in lib/vf_journal_export.py kam aus dem
  Namen der Ziel-JSON. Nach der Umbenennung haette eine Regenerierung die ids
  stillschweigend auf VF_hm_recformat_LZ_* geaendert und die Paarung der
  beiden Laeufe zerlegt - jetzt feste Konstante TEST_ID_ANLAGE.
- conftest: hundm05_* Fixtures -> hmrec_* (die Spec-Fixtures stehen in
  tests/test_hundm05.py).

Verifiziert: 98 pytest-Tests gruen (die 9 Ergebnis-Tests warten auf einen
neuen TEST_HUNDM05-Lauf), alle .lsp lint-sauber, Spec-Regenerierung
idempotent, TEST_VF_SPEC in BricsCAD 28 PASS / 0 FAIL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 11:11:12 +02:00

493 lines
22 KiB
Common Lisp

;; ============================================================
;; test_vf_spec.lsp - Unittest fuer den Spec-Uebersetzer (vf_spec.lsp)
;;
;; Prueft vsp-journal OHNE Geometrie und ohne Zeichnung: aus einer Spec muss
;; genau die Tokenfolge entstehen, die der interaktive Ablauf aufzeichnen
;; wuerde. Weicht sie ab, baut der Spec-Pfad eine ANDERE Kette als die
;; Vorlage - der Fehler waere in der Zeichnung schwer zu sehen und im
;; Sivas-Export falsch.
;;
;; Zwei Arten von Testfaellen:
;; 1. Sieben handgebaute Specs, je ein Glied-Typ bzw. Sonderfall. Die
;; erwarteten Tokenfolgen sind NICHT von Hand abgeleitet, sondern mit
;; dem geprueften Python-Emitter erzeugt (lib/vf_spec_export.py) und
;; zusaetzlich durch den unabhaengigen Python-DEKODER gelaufen
;; (lib/vf_journal_export.py) - drei Implementierungen derselben
;; Grammatik, die einander pruefen.
;; 2. Die 5 echten HundM-Ketten: die Spec-Datei
;; tests/testdata/hundm05.json muss dieselben Journale ergeben
;; wie das aufgezeichnete Protokoll tests/testdata/hm_recformat.json.
;; Das ist der Beweis "Spec-Pfad == Journal-Pfad" auf Datenebene; der
;; Beweis auf Geometrie-Ebene ist VF_SPEC_BAU (siehe tests/test_vf_spec.py).
;;
;; Voraussetzung: VarioFoerderer.lsp geladen (vf_core laedt vf_spec mit).
;;
;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_vf_spec.lsp"))
;; TEST_VF_SPEC
;;
;; Dieser Test baut KEINE Geometrie - er prueft nur den Uebersetzer.
;; Ergebnis: tests/output/vf_spec_unit_results.json (Pass/Fail-Zaehler).
;; Die gebaute Anlage prueft TEST_HUNDM05 (tests/test_hundm05.lsp).
;; ============================================================
(setq *tsp-pass* 0 *tsp-fail* 0 *tsp-fails* '())
(defun tsp-eq (name erwartet ist)
(if (equal erwartet ist)
(progn (setq *tsp-pass* (1+ *tsp-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tsp-fail* (1+ *tsp-fail*))
(setq *tsp-fails* (cons name *tsp-fails*))
(princ (strcat "\n [FAIL] " name
"\n erwartet: " (vl-princ-to-string erwartet)
"\n ist: " (vl-princ-to-string ist))))))
(defun tsp-truthy (name ist)
(if ist
(progn (setq *tsp-pass* (1+ *tsp-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tsp-fail* (1+ *tsp-fail*))
(setq *tsp-fails* (cons name *tsp-fails*))
(princ (strcat "\n [FAIL] " name " (erwartet nicht-nil, war nil)")))))
(defun tsp-nil (name ist)
(if (null ist)
(progn (setq *tsp-pass* (1+ *tsp-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tsp-fail* (1+ *tsp-fail*))
(setq *tsp-fails* (cons name *tsp-fails*))
(princ (strcat "\n [FAIL] " name
" (erwartet nil, war " (vl-princ-to-string ist) ")")))))
;; Journal-Vergleich mit Fundstelle: bei 15-70 Tokens ist "erwartet (...)
;; ist (...)" unlesbar, der Index der ersten Abweichung dagegen sofort
;; brauchbar.
(defun tsp-journal-eq (name erwartet ist / i a b diff)
(setq i 0 diff nil)
(while (and (null diff) (or erwartet ist))
(setq i (1+ i) a (car erwartet) b (car ist))
(if (not (equal a b))
(setq diff (strcat "Token " (itoa i) ": erwartet "
(vl-princ-to-string a) ", ist "
(vl-princ-to-string b))))
(setq erwartet (cdr erwartet) ist (cdr ist)))
(if diff
(progn (setq *tsp-fail* (1+ *tsp-fail*))
(setq *tsp-fails* (cons name *tsp-fails*))
(princ (strcat "\n [FAIL] " name "\n " diff)))
(progn (setq *tsp-pass* (1+ *tsp-pass*))
(princ (strcat "\n [PASS] " name)))))
;; ============================================================
;; Spec-Bausteine
;; ============================================================
;; Eine Spec ist (kopf sektionen), eine Sektion (felder subs) - genau die
;; Struktur, die vsp-gruppieren aus dem flachen JSON baut.
(defun tsp-kopf (as-p)
(if as-p
(list (cons "spec_id" "TEST") (cons "modus" 1)
(cons "start_punkt" (list 1000.0 2000.0 3000.0))
(cons "start_hoehe" 3000.0) (cons "as" 1)
(cons "as_winkel" "90") (cons "as_seite" "links"))
(list (cons "spec_id" "TEST") (cons "modus" 1)
(cons "start_punkt" (list 1000.0 2000.0 3000.0))
(cons "start_hoehe" 3000.0) (cons "as" 0))))
(defun tsp-sek (felder subs) (list felder subs))
(defun tsp-es (winkel seite)
(tsp-sek (list (cons "glied" "ES") (cons "winkel" winkel)
(cons "seite" seite)) nil))
(defun tsp-spec (as-p sektionen) (list (tsp-kopf as-p) sektionen))
;; Praeambel-Tokens (kommen in fast jedem Fall vor)
(defun tsp-pre-mit-as ()
(list (cons "PT" (list 1000.0 2000.0 3000.0))
(cons "REAL" 3000.0)
(cons "STR" "1")
(cons "STR" "90")
(cons "STR" "1")))
(defun tsp-pre-ohne-as ()
(list (cons "PT" (list 1000.0 2000.0 3000.0))
(cons "REAL" 3000.0)
(cons "STR" "2")))
;; ============================================================
;; 1. Handgebaute Faelle je Glied-Typ
;; ============================================================
(defun tsp-tests-glieder ( / spec)
;; --- Linie-GF, Kettenende mit ES ---
;; Erste Sektion: Menue-Code "1" (4 Optionen, ohne Frame).
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 5500.0)
(cons "hz" 0.0) (cons "gefaelle" "winkel")
(cons "winkel" 3.0) (cons "ende" "ja-mit-es")) nil)
(tsp-es "90" "rechts"))))
(tsp-journal-eq "Linie-GF + ES"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "DL" 5500.0) (cons "REAL" 0.0)
(cons "STR" "2") (cons "REAL" 3.0) (cons "STR" "1")
(cons "STEP" "ES") (cons "STR" "90") (cons "STR" "2")))
(vsp-journal spec))
;; --- Horizontal-VF: horizontaler Erstkoerper ---
;; Nur hier stehen Separator vor/nach VOR der Endpunkt-Antwort
;; (vfl-baue-horizontal-koerper im Ziel-Modus).
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Horizontal-VF") (cons "dl" 8000.0)
(cons "hz" 90.0) (cons "gf_verteilung" "einlauf")
(cons "vf_sep_vor" 0) (cons "vf_sep_nach" 0)
(cons "vf_ende" "ja-mit-es"))
(list (list (cons "sub" "motorstation"))))
(tsp-es "90" "rechts"))))
(tsp-journal-eq "Horizontal-VF + Motorstation + ES"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Horizontal-VF") (cons "STR" "3")
(cons "DL" 8000.0) (cons "REAL" 90.0)
(cons "STR" "2") ; GF-Verteilung: Einlauf
(cons "STR" "2") (cons "STR" "2") ; Separator vor / nach
(cons "STR" "1") ; Endpunkt: Motorstation
(cons "STR" "1") ; Kettenende: ja mit ES
(cons "STEP" "ES") (cons "STR" "90") (cons "STR" "2")))
(vsp-journal spec))
;; --- Linie-VF: GEWINKELTER Erstkoerper ---
;; Kein Separator-Fragenpaar (der Koerper wird ohne Frage gebaut), dafuer
;; Zielhoehe + optionale Winkelwahl. Danach eine Vario-Kurve und ein
;; Abschluss "nein" mit Separator.
(setq spec (tsp-spec nil (list
(tsp-sek (list (cons "glied" "Linie-VF") (cons "dl" 12000.0)
(cons "hz" 0.0) (cons "hoehe" 2000.0)
(cons "winkel_idx" 2) (cons "gf_verteilung" "haelfte")
(cons "vf_ende" "nein") (cons "vf_separator" 1))
(list (list (cons "sub" "vario-kurve") (cons "winkel" 90)
(cons "seite" "links") (cons "variante" "aussen"))
(list (cons "sub" "motorstation")))))))
(tsp-journal-eq "Linie-VF + Vario-Kurve (kein Separator-Paar)"
(append (tsp-pre-ohne-as)
(list (cons "STEP" "Linie-VF") (cons "STR" "2")
(cons "DL" 12000.0) (cons "REAL" 0.0)
(cons "REAL" 2000.0) (cons "INT" 2)
(cons "STR" "1") ; GF-Verteilung: Haelfte
(cons "STR" "2") (cons "STR" "2") ; weiter -> Vario-Kurve
(cons "STEP" "Vario-Kurve") (cons "INT" 90)
(cons "STR" "1") (cons "STR" "1") ; links, aussen
(cons "STR" "1") ; Endpunkt: Motorstation
(cons "STR" "2") (cons "STR" "1"))) ; kein Kettenende, Separator
(vsp-journal spec))
;; --- GF-Bogen als ZWEITE Sektion ---
;; Ab der zweiten Sektion gilt die 5-Optionen-Tabelle: Linie-GF ist dort
;; "2" (nicht mehr "1"), GF-Bogen ist "1".
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 3000.0)
(cons "hz" 180.0) (cons "gefaelle" "hoehe")
(cons "hoehe" 2500.0) (cons "ende" "nein")) nil)
(tsp-sek (list (cons "glied" "GF-Bogen") (cons "winkel" 60)
(cons "seite" "rechts")) nil))))
(tsp-journal-eq "GF-Bogen als zweite Sektion (Menue-Codes am Frame)"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "DL" 3000.0) (cons "REAL" 180.0)
(cons "STR" "1") (cons "REAL" 2500.0) (cons "STR" "3")
(cons "STEP" "GF-Bogen") (cons "STR" "1")
(cons "INT" 60) (cons "STR" "2")))
(vsp-journal spec))
;; --- Linie bis Kettenende, als reine Gefaellestrecke aufgeloest ---
;; Kein VF-Block: nach Laenge und Zielhoehe folgt direkt das ES-Glied.
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie") (cons "dl" 4000.0)
(cons "hz" 270.0) (cons "hoehe" 2000.0)
(cons "segment_typ" "GF")) nil)
(tsp-es "30" "links"))))
(tsp-journal-eq "Linie -> GF + ES"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie") (cons "STR" "4")
(cons "DL" 4000.0) (cons "REAL" 270.0) (cons "REAL" 2000.0)
(cons "STEP" "ES") (cons "STR" "30") (cons "STR" "1")))
(vsp-journal spec))
;; --- Linie bis Kettenende, als VF-Einheit aufgeloest ---
;; auto_ende: das ES-Glied folgt OHNE Kettenende-Frage.
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie") (cons "dl" 9000.0)
(cons "hz" 0.0) (cons "hoehe" 1500.0)
(cons "segment_typ" "VF")
(cons "gf_verteilung" "einlauf")
(cons "vf_ende" "automatisch"))
(list (list (cons "sub" "auf-ab") (cons "dl" 3000.0)
(cons "hoehe" 1200.0))
(list (cons "sub" "motorstation"))))
(tsp-es "90" "rechts"))))
(tsp-journal-eq "Linie -> VF (auto-Ende, ES ohne Frage)"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie") (cons "STR" "4")
(cons "DL" 9000.0) (cons "REAL" 0.0) (cons "REAL" 1500.0)
(cons "STR" "2") ; GF-Verteilung: Einlauf
(cons "STR" "2") (cons "STR" "3") ; weiter -> Auf/Ab
(cons "DL" 3000.0) (cons "REAL" 1200.0)
(cons "STR" "1") ; Endpunkt: Motorstation
(cons "STEP" "ES") (cons "STR" "90") (cons "STR" "2")))
(vsp-journal spec))
;; --- Kettenende am Zielpunkt OHNE ES ---
;; Die Kette endet direkt am Zielpunkt: kein Separator, kein ES, keine
;; weitere Frage.
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Horizontal-VF") (cons "dl" 7000.0)
(cons "hz" 0.0) (cons "gf_verteilung" "einlauf")
(cons "vf_sep_vor" 1) (cons "vf_sep_nach" 1)
(cons "vf_ende" "zielpunkt-ohne-es"))
(list (list (cons "sub" "kettenende") (cons "es" 0)
(cons "dl" 2500.0) (cons "hoehe" 1000.0)))))))
(tsp-journal-eq "Kettenende am Zielpunkt ohne ES"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Horizontal-VF") (cons "STR" "3")
(cons "DL" 7000.0) (cons "REAL" 0.0)
(cons "STR" "2") ; GF-Verteilung: Einlauf
(cons "STR" "1") (cons "STR" "1") ; Separator vor / nach: ja
(cons "STR" "3") ; Endpunkt: Kettenende
(cons "STR" "2") ; ES setzen? nein
(cons "DL" 2500.0) (cons "REAL" 1000.0)))
(vsp-journal spec))
(princ))
;; ============================================================
;; 2. Validierung: die Spec darf nichts raten
;; ============================================================
(defun tsp-tests-validierung ( / spec)
;; --- hz nur beim ERSTEN Segment ---
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 3000.0)
(cons "hz" 0.0) (cons "gefaelle" "winkel")
(cons "winkel" 3.0) (cons "ende" "nein")) nil)
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 2000.0)
(cons "hz" 90.0) ; <- verboten
(cons "gefaelle" "winkel") (cons "winkel" 3.0)
(cons "ende" "nein")) nil))))
(tsp-nil "hz in spaeterer Sektion: kein Journal" (vsp-journal spec))
(tsp-truthy "hz in spaeterer Sektion: Fehler gemeldet" *vsp-fehler*)
;; --- hz fehlt beim ersten Segment ---
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 3000.0)
(cons "gefaelle" "winkel") (cons "winkel" 3.0)
(cons "ende" "nein")) nil))))
(tsp-nil "hz fehlt: kein Journal" (vsp-journal spec))
;; --- GF-Bogen in der ERSTEN Sektion (ohne Frame nicht waehlbar) ---
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "GF-Bogen") (cons "winkel" 90)
(cons "seite" "links")) nil))))
(tsp-nil "GF-Bogen als erste Sektion: kein Journal" (vsp-journal spec))
;; --- unbekannter Domaenenwert ---
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "GF-Bogen") (cons "winkel" 90)
(cons "seite" "mitte")) nil))))
(tsp-nil "Seite \"mitte\": kein Journal" (vsp-journal spec))
;; --- VF-Einheit ohne Abschluss-Knoten ---
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Horizontal-VF") (cons "dl" 8000.0)
(cons "hz" 0.0) (cons "gf_verteilung" "einlauf")
(cons "vf_sep_vor" 0) (cons "vf_sep_nach" 0)
(cons "vf_ende" "ja-mit-es"))
(list (list (cons "sub" "vario-kurve") (cons "winkel" 90)
(cons "seite" "links") (cons "variante" "innen")))))))
(tsp-nil "VF-Einheit endet mit Vario-Kurve: kein Journal" (vsp-journal spec))
;; --- Pflichtfeld fehlt ---
(setq spec (list (list (cons "spec_id" "X")
(cons "start_punkt" (list 0.0 0.0 0.0)))
(list (tsp-sek (list (cons "glied" "Linie-GF")
(cons "dl" 1000.0) (cons "hz" 0.0)
(cons "gefaelle" "winkel")
(cons "winkel" 3.0)
(cons "ende" "nein")) nil))))
(tsp-nil "start_hoehe fehlt: kein Journal" (vsp-journal spec))
;; --- gueltige Spec: vsp-pruefen meldet nichts ---
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie") (cons "dl" 4000.0)
(cons "hz" 0.0) (cons "hoehe" 2000.0)
(cons "segment_typ" "GF")) nil)
(tsp-es "90" "links"))))
(tsp-nil "gueltige Spec: keine Fehler" (vsp-pruefen spec))
(princ))
;; ============================================================
;; 3. Gruppieren des flachen JSON
;; ============================================================
(defun tsp-tests-gruppieren ( / objekte specs sek)
(setq objekte (list
(list (cons "spec_id" "A") (cons "start_punkt" (list 0.0 0.0 0.0)))
(list (cons "glied" "Horizontal-VF") (cons "nr" 1))
(list (cons "sub" "vario-kurve"))
(list (cons "sub" "motorstation"))
(list (cons "glied" "ES") (cons "nr" 2))
(list (cons "spec_id" "B") (cons "start_punkt" (list 1.0 1.0 1.0)))
(list (cons "glied" "Linie") (cons "nr" 1))))
(setq specs (vsp-gruppieren objekte))
(tsp-eq "gruppieren: 2 Specs" 2 (length specs))
(tsp-eq "gruppieren: Spec A hat 2 Sektionen" 2
(length (vsp-sektionen (car specs))))
(setq sek (car (vsp-sektionen (car specs))))
(tsp-eq "gruppieren: erste Sektion ist Horizontal-VF" "Horizontal-VF"
(ssg-val (vsp-sek-felder sek) "glied"))
(tsp-eq "gruppieren: sie hat 2 subs in Reihenfolge"
(list "vario-kurve" "motorstation")
(mapcar '(lambda (s) (ssg-val s "sub")) (vsp-sek-subs sek)))
(tsp-eq "gruppieren: ES-Sektion hat keine subs" nil
(vsp-sek-subs (cadr (vsp-sektionen (car specs)))))
(tsp-eq "gruppieren: Spec B hat 1 Sektion" 1
(length (vsp-sektionen (cadr specs))))
(princ))
;; ============================================================
;; 4. Die 5 echten HundM-Ketten: Spec == aufgezeichnetes Journal
;; ============================================================
;; Das aufgezeichnete Protokoll (hm_recformat.json) in Journal-Tokens
;; zurueckuebersetzen - dieselbe Umrechnung wie in test_hm_recformat.lsp, hier
;; bewusst dupliziert, damit der Test ohne den anderen Treiber laeuft.
(defun tsp-als-string (v)
(cond ((null v) "")
((= (type v) 'STR) v)
((= (type v) 'INT) (itoa v))
((= (type v) 'REAL) (rtos v 2 6))
(T (vl-princ-to-string v))))
(defun tsp-eintrag->journal (e / typ w hz)
(setq typ (ssg-val e "typ"))
(cond
((= typ "point_abs")
(setq w (ssg-val e "wert"))
(if (and w (listp w) (>= (length w) 3))
(list (cons "PT" (mapcar 'float w))) nil))
((= typ "point_rel")
(setq w (ssg-val e "dL") hz (ssg-val e "hz"))
(cond ((not (numberp w)) nil)
((numberp hz) (list (cons "DL" (float w)) (cons "REAL" (float hz))))
(T (list (cons "DL" (float w))))))
((= typ "real")
(setq w (ssg-val e "wert"))
(if (numberp w) (list (cons "REAL" (float w))) nil))
((= typ "int")
(setq w (ssg-val e "wert"))
(if (numberp w) (list (cons "INT" (fix w))) nil))
((= typ "string") (list (cons "STR" (tsp-als-string (ssg-val e "wert")))))
((= typ "step") (list (cons "STEP" (tsp-als-string (ssg-val e "wert")))))
(T nil)))
(defun tsp-protokoll-journale (daten / ketten aktuell journal)
(setq ketten '() journal '())
(foreach obj daten
(cond
((ssg-val obj "test_id")
(if aktuell (setq ketten (cons (list aktuell (reverse journal)) ketten)))
(setq aktuell (ssg-val obj "test_id") journal '()))
((ssg-val obj "typ")
(foreach j (tsp-eintrag->journal obj) (setq journal (cons j journal))))
(T nil)))
(if aktuell (setq ketten (cons (list aktuell (reverse journal)) ketten)))
(reverse ketten))
(defun tsp-tests-echte-ketten ( / p-datei s-datei p-daten specs journale i n
spec soll ist sid)
(setq p-datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hm_recformat.json"))
(setq s-datei (strcat (getenv "DXFMAKRO")
"/tests/testdata/hundm05.json"))
(cond
((null (findfile p-datei))
(princ (strcat "\n [SKIP] " p-datei " fehlt")))
((null (findfile s-datei))
(princ (strcat "\n [SKIP] " s-datei " fehlt")))
(T
(setq p-daten (ssg-load-json p-datei))
(setq journale (tsp-protokoll-journale p-daten))
(setq specs (vsp-json-laden s-datei))
(tsp-eq "echte Ketten: 5 Protokolle" 5 (length journale))
(tsp-eq "echte Ketten: 5 Specs" 5 (length specs))
(setq n (min (length journale) (length specs)) i 0)
(while (< i n)
(setq spec (nth i specs))
(setq soll (cadr (nth i journale)))
(setq sid (vsp-spec-id (vsp-kopf spec)))
(setq ist (vsp-journal spec))
(if (null ist)
(progn
(tsp-truthy (strcat sid ": Spec fehlerfrei") nil)
(foreach f *vsp-fehler* (princ (strcat "\n - " f))))
(tsp-journal-eq (strcat sid " == aufgezeichnetes Journal ("
(car (nth i journale)) ")")
soll ist))
(setq i (1+ i)))))
(princ))
;; ============================================================
;; Ergebnis-Export (Konvention <name>:export-results fuer test_run_all.lsp)
;; ============================================================
(defun vf_spec:export-results (out-dir / f pfad)
(setq pfad (strcat out-dir "/vf_spec_unit_results.json"))
(setq f (open pfad "w"))
(if f
(progn
(write-line "{" f)
(write-line (strcat " \"pass\": " (itoa *tsp-pass*) ",") f)
(write-line (strcat " \"fail\": " (itoa *tsp-fail*) ",") f)
(write-line (strcat " \"status\": \""
(if (= *tsp-fail* 0) "OK" "FEHLER") "\"") f)
(write-line "}" f)
(close f)
(princ (strcat "\n Ergebnis: " pfad))))
(princ))
(defun c:TEST_VF_SPEC ( / )
(setq *tsp-pass* 0 *tsp-fail* 0 *tsp-fails* '())
(princ "\n================================================================")
(princ "\n TEST_VF_SPEC - Spec-Uebersetzer (vf_spec.lsp)")
(princ "\n================================================================")
(if (null (car (atoms-family 1 '("VSP-JOURNAL"))))
(princ "\n FEHLER: vf_spec.lsp nicht geladen (VarioFoerderer laden).")
(progn
(princ "\n\n-- Glied-Typen --")
(tsp-tests-glieder)
(princ "\n\n-- Validierung --")
(tsp-tests-validierung)
(princ "\n\n-- Gruppieren --")
(tsp-tests-gruppieren)
(princ "\n\n-- Echte HundM-Ketten --")
(tsp-tests-echte-ketten)))
(princ "\n\n----------------------------------------------------------------")
(princ (strcat "\n Ergebnis: " (itoa *tsp-pass*) " PASS, "
(itoa *tsp-fail*) " FAIL"))
(if (> *tsp-fail* 0)
(progn
(princ "\n Fehlgeschlagen:")
(foreach nm (reverse *tsp-fails*) (princ (strcat "\n - " nm)))))
(princ "\n================================================================")
(princ))
(princ "\nTEST_VF_SPEC geladen. Aufruf: TEST_VF_SPEC")
(princ)