428f1ced70
Stufe 1 des Fahrplans in doc/TODO-plan-vf-interactive.md, fertig fuer
Modus 1. Eine SPEC beschreibt eine Kette in Domaenenwerten ("links",
"winkel", "aussen", ja/nein) statt in Menue-Codes; vsp-journal uebersetzt sie
in ein Eingabe-Journal, vsp-bau-aus-spec spielt es per
vfl-journal-replay-start durch den UNVERAENDERTEN vf-linienzug-modus. Der
fertige Block traegt damit dasselbe SSG_VF_EDIT-Journal wie eine handgebaute
Kette und bleibt per Doppelklick editier- und 2D/3D-konvertierbar - kein
Journal-Konsument muss etwas von Specs wissen.
CAD-Lauf VF_SPEC_BAU: 5 OK, 0 Fehler. Je Kette status "executed",
prompts 0 (keine Live-Eingabe erreicht), eingaben_offen 0, keine
Spec-Fehler, keine Bau-Meldungen, glieder_ist == glieder_soll,
Einfuegepunkt gleich dem Kettenstart der Spec.
Neu:
- Lisp/vf_spec.lsp: Code-Tabellen, Emitter vsp-journal, Validierung
vsp-pruefen, flacher JSON-Loader, Headless-Rahmen vsp-headless-an/-aus,
Ergebnis-Record, vsp-bau-aus-spec/-liste, c:VF_SPEC_BAU. Von vf_core.lsp
NACH vf_linienzug.lsp geladen - damit sind beide Ladewege abgedeckt (Menue
und der .scr-Loader), das MNL bleibt unveraendert.
- lib/vf_spec_export.py: derselbe Emitter in Python PLUS die Umkehrung
(spec_aus_journal), erzeugt die Spec-Testdaten.
- tests/testdata/vf_spec_hundm05.json: die 5 echten Ketten als Spec,
erzeugt statt handgeschrieben.
- tests/test_vf_spec.lsp (C:TEST_VF_SPEC, in alltests.json): 7 Faelle je
Glied-Typ, 7 Validierungsfaelle, Gruppieren, und die 5 echten Ketten gegen
ihr aufgezeichnetes Journal.
- tests/test_vf_spec.py: 36 Tests (Rundlauf, Spec-Daten, Validierung,
Erwartungswert-Abgleich, Ergebnisse von VF_SPEC_BAU).
Drei Regeln, die der Uebersetzer besitzen muss - jede wuerde sonst die ganze
Replay-Queue verschieben:
- Der Menue-Code haengt am Frame: erste Sektion 4 Optionen (kein GF-Bogen,
es gibt noch keine Richtung), jede spaetere 5.
- hz steht genau EINMAL im Journal, beim ersten Segment (vfl-in-abstand
journalisiert die Richtung nur bei freier Richtungswahl).
- Nur ein HORIZONTALER Erstkoerper einer VF-Einheit stellt die Separator-/
Endpunktfragen vorab (winkel1=0 laeuft durch vfl-baue-horizontal-koerper);
ein gewinkelter wird ohne jede Frage gebaut.
Wie die Richtigkeit belegt ist - drei Implementierungen derselben Grammatik,
die einander pruefen:
- Rundlauf gegen echte Daten: journal_aus_spec(spec_aus_journal(tok)) == tok
fuer alle 5 HundM-Ketten, tokenweise numerisch verglichen. Damit ist
bewiesen, dass die Spec das Journal verlustfrei abbildet.
- Der Weg, den LISP nimmt (flaches JSON -> Spec -> Journal), ergibt separat
geprueft dieselben Journale wie das aufgezeichnete Protokoll.
- Der unabhaengige Dekoder aus lib/vf_journal_export.py verdaut alle 7
synthetischen Emitter-Ausgaben restlos, auch die zwei Zweige, die in den
echten Daten fehlen (Linie-VF, gewinkelter Erstkoerper).
- Die Erwartungswerte im LISP-Test kommen aus dem geprueften Python-Emitter;
TestLispErwartungen liest sie zurueck und vergleicht erneut. Ohne das
koennte ein Uebertragungsfehler einen falschen Erwartungswert
festschreiben und der CAD-Lauf waere gruen, obwohl der Emitter falsch
liegt.
Abweichungen von der Planung (Begruendungen in doc/TODO-plan-vf-interactive.md
Abschnitt 4.6): vsp-pruefen ruft den Emitter statt eine zweite Regelmenge zu
pflegen; das vollstaendige Journal steht nicht im Record (statt dessen
glieder_soll/glieder_ist als Desync-Detektor); Zusatzfeld segment_typ am
Glied "Linie", weil dort vfl-segment-entscheidung selbst zwischen GF und VF
waehlt; vf_ende-Werte "automatisch"/"zielpunkt-ohne-es" fuer die Faelle ohne
Frage; Praefix vsp- statt vfs- (das gehoert vf_standard.lsp).
Zusaetzlich:
- tests/test_vf_headless_statisch.py prueft jetzt auch, dass
Lisp/vf_spec.lsp UEBERHAUPT keine Eingabe-/Dialogfunktion aufruft - beim
Daten-Pfad ist die Null die Vorgabe.
- tests/testdata/mubea.json: die drei einzeln eingefuegten S-LP-Separatoren
entfernt (Fortsetzung von 699744e - die Separatoren stecken jetzt in den
Staustreckenbloecken).
Noch offen: die Uebersetzer fuer Modus 2 und 3. Modus 2 braucht laut
Fahrplan erst 2-3 echte "linienzug2"-Journale als Referenz -
tests/testdata/hundm05.json enthaelt ausschliesslich Modus-1-Ketten.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
492 lines
22 KiB
Common Lisp
492 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/vf_spec_hundm05.json muss dieselben Journale ergeben
|
|
;; wie das aufgezeichnete Protokoll tests/testdata/hundm05.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
|
|
;;
|
|
;; Speichert tests/output/vf_spec_results.json ist NICHT dieser Test -
|
|
;; das schreibt VF_SPEC_BAU. Hier: tests/output/vf_spec_unit_results.json.
|
|
;; ============================================================
|
|
|
|
(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 (hundm05.json) in Journal-Tokens
|
|
;; zurueckuebersetzen - dieselbe Umrechnung wie in test_hundm05.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/hundm05.json"))
|
|
(setq s-datei (strcat (getenv "DXFMAKRO")
|
|
"/tests/testdata/vf_spec_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)
|