Files
dxfmakros/tests/test_hundm05.lsp
T
s.ayadi 2dd5382060 [FEAT] Weitere Objekte der Anlage in hundm05.json bauen (Kreisel)
tests/testdata/hundm05.json enthaelt jetzt neben den 5 Spec-Ketten auch
andere Objekte der Anlage - erkennbar am Feld "function", im Format der
jeweiligen Einzel-Testdaten (ein Kreisel also wie in kreisel_tests.json).
Damit beschreibt EINE Datei die ganze Anlage.

- tests/test_hundm05.lsp: hundm05:bau-zusatzobjekte sammelt alle Objekte mit
  "function" und baut sie; hundm05:bau-kreisel ruft kreisel-insert-script
  (wie tests/test_kreisel.lsp) und liefert einen Ergebnis-Record in derselben
  Form wie die Ketten. Eigener ssg-start-Rahmen mit ATTREQ/ATTDIA 0, weil
  vsp-bau-datei seinen schon geschlossen hat und (command "_.INSERT" ...)
  sonst nach Attributwerten fragt. Je Objekt gefangen, damit ein Fehler die
  restlichen nicht mitnimmt.
- Lisp/vf_spec.lsp: "kind" ist jetzt ein Record-Feld (Default "linienzug")
  statt eines Literals in vsp-result-json - so schreibt vsp-results-schreiben
  beide Objektarten in EINE Ergebnisdatei.
- lib/vf_spec_export.py: Objekte der Ziel-Datei, die keine Kette beschreiben,
  werden beim Neuschreiben unveraendert ans Dateiende uebernommen. Ohne das
  waere jeder von Hand ergaenzte Eintrag beim naechsten Generatorlauf weg -
  die Datei ist erzeugt UND handgepflegt. spec_aus_flachen_objekten
  ueberspringt sie jetzt statt zu werfen, genau wie vsp-gruppieren in LISP.
- tests/test_hundm05.py: die Ketten-Tests filtern auf kind "linienzug"
  (Records ohne das Feld stammen aus aelteren Laeufen und waren nur Ketten);
  neue Klasse TestZusatzobjekte prueft Status, expect_block_prefix,
  expect_hoehe (Attribut HOEHE), expect_kreiselart (KREISELART) und den
  Einfuegepunkt - dieselben Erwartungsfelder wie tests/test_kreisel.py.
- tests/test_vf_spec.py: neuer Test, dass die Zusatzobjekte die
  Ketten-Zerlegung nicht beeinflussen - auch nicht, wenn sie mitten zwischen
  den Sektionen stehen (der eingetragene Kreisel stand zunaechst inmitten
  der Sub-Knoten von Kette 5; nach dem Regenerieren steht er am Dateiende).

Verifiziert: 118 pytest-Tests gruen, Spec-Regenerierung uebernimmt das
Zusatzobjekt (58 Objekte), beide .lsp lint-sauber. Die Kreisel-Tests warten
auf den naechsten TEST_HUNDM05-Lauf.

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

225 lines
9.9 KiB
Common Lisp

;; ============================================================
;; test_hundm05.lsp - Integrationstest: baut die 5 VarioFoerderer-Linienzuege
;; der Anlage HundM (Kreisel mit fuenf daran haengenden Ketten) aus ihrer
;; SPEC nach.
;;
;; Datenquelle: tests/testdata/hundm05.json - die Ketten im Spec-Format
;; (Domaenenwerte statt Menue-Codes, siehe Lisp/vf_spec.lsp und
;; doc/variofoerderer.md Abschnitt 8b). Erzeugt aus den echten
;; Aufzeichnungen mit
;; python lib/vf_spec_export.py tests/testdata/hm_recformat.json \
;; tests/testdata/hundm05.json
;; und damit nachweislich dasselbe Journal wie die Vorlage
;; (Rundlauf-Beweis in tests/test_vf_spec.py). Die Datei ist die Stelle, an
;; der weitere Ketten der Anlage nachgetragen werden.
;;
;; Dieser Test hat KEINE eigene Bau-Logik: er ruft vsp-bau-datei aus
;; Lisp/vf_spec.lsp - denselben Weg, den auch der Befehl VF_SPEC_BAU nimmt.
;; Es gibt also genau einen Code-Pfad, der aus einer Spec Geometrie erzeugt.
;;
;; ABGRENZUNG zu TEST_HM_RECFORMAT (tests/test_hm_recformat.lsp): dort werden
;; dieselben Ketten aus dem ROHEN Journal der Kundenzeichnung gebaut. Das ist
;; der Regressionstest fuer das Aufzeichnungsformat (bleibt ein bestehender
;; VF_n-Block abspielbar - Doppelklick-Edit, 2D/3D-Konvertierung?) und
;; deshalb dort auf "disabled": true, um nicht zweimal dieselbe Geometrie zu
;; bauen.
;;
;; Voraussetzung:
;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug + vf_spec,
;; Gefaellestrecke, ssg_core, ssg_dbg)
;; - Umgebungsvariable DXFMAKRO gesetzt
;;
;; Speichert (via SSG_RUN_ALL_TESTS bei "save":"dxf"):
;; tests/output/hundm05_tests.dxf
;; tests/output/hundm05_results.json (via hundm05:export-results)
;;
;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp"))
;; TEST_HUNDM05 ; in der aktuellen Dimension (ssg-ils-dim-aktuell)
;; TEST_HUNDM05_2D ; erzwungen 2D
;; TEST_HUNDM05_3D ; erzwungen 3D
;; ============================================================
(if (not (boundp '*hundm05-records*)) (setq *hundm05-records* nil))
(defun hundm05:spec-datei ()
(strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json"))
;; ============================================================
;; Weitere Objekte der Anlage (nicht-Ketten)
;; ============================================================
;; Neben den Spec-Ketten (Kopf "spec_id" + "glied"/"sub") duerfen in
;; hundm05.json auch andere Objekte der Anlage stehen - erkennbar an
;; "function". Format wie in tests/testdata/kreisel_tests.json, damit die
;; Felder nicht zweimal beschrieben werden muessen (Schema: siehe
;; tests/testdata/object_data.md Abschnitt 1).
;; vsp-gruppieren ignoriert diese Objekte, sie stoeren die Ketten also nicht.
;; lib/vf_spec_export.py uebernimmt sie beim Neuerzeugen der Spec-Daten
;; unveraendert ans Dateiende (sonst waeren sie beim naechsten Lauf weg).
;;
;; Bisher unterstuetzt: Kreisel (function "insert", wie test_kreisel.lsp).
;; Weitere Arten kommen als eigener cond-Zweig dazu.
(defun hundm05:zusatz-objekt-p (obj)
(and (ssg-val obj "function")
(null (ssg-val obj "spec_id"))
(null (ssg-val obj "glied"))
(null (ssg-val obj "sub"))))
;; Kreisel bauen (kreisel-insert-script wie in tests/test_kreisel.lsp).
;; Rueckgabe: Ergebnis-Record in derselben Form wie die Ketten, damit
;; vsp-results-schreiben beide in EINE Datei schreibt (unterschieden ueber
;; das Feld "kind").
(defun hundm05:bau-kreisel (obj / id x y z abstand rotation typ pt ent ed rec)
(setq id (ssg-val obj "id"))
(setq x (ssg-val obj "x")
y (ssg-val obj "y")
z (ssg-val obj "z")
abstand (ssg-val obj "abstand")
rotation (ssg-val obj "rotation")
typ (ssg-val obj "typ"))
(if (numberp x) (setq x (float x)))
(if (numberp y) (setq y (float y)))
(if (numberp z) (setq z (float z)))
(if (numberp abstand) (setq abstand (float abstand)))
(if (numberp rotation) (setq rotation (float rotation)))
(if (null typ) (setq typ "STANDARD"))
(setq rec (vsp-rec-neu (if id id "(ohne id)")))
(setq rec (vsp-set rec "kind" "kreisel"))
(setq rec (vsp-set rec "dimension" (vsp-dim-text)))
(cond
((not (and (numberp x) (numberp y) (numberp abstand)))
(princ (strcat "\n [KREISEL] " (if id id "?")
" - FEHLER: x, y und abstand muessen Zahlen sein"))
(vsp-set (vsp-set rec "status" "spec-fehler") "fehler_text"
"x, y oder abstand fehlt bzw. ist keine Zahl"))
(T
(setq pt (list x y z))
(princ (strcat "\n [KREISEL] " id " -> (" (rtos x 2 0) ", "
(rtos y 2 0) ", " (rtos (if z z 0.0) 2 0) ") A="
(rtos abstand 2 0) " R=" (rtos (if rotation rotation 0.0) 2 1)
" " typ))
(setq ent (kreisel-insert-script pt abstand
(if rotation rotation 0.0) typ z))
(if ent
(progn
(setq ed (entget ent))
(princ (strcat " -> Block " (cdr (assoc 2 ed))))
(setq rec (vsp-set rec "block_name" (cdr (assoc 2 ed))))
(setq rec (vsp-set rec "block_handle" (cdr (assoc 5 ed))))
(setq rec (vsp-set rec "insert_point" (cdr (assoc 10 ed))))
(setq rec (vsp-set rec "attribs" (ssg-attrib-read ent)))
(vsp-set rec "status" "executed"))
(progn
(princ " FEHLER: kein Kreisel-Block entstanden")
(vsp-set (vsp-set rec "status" "abbruch") "fehler_text"
"kreisel-insert-script lieferte nil"))))))
;; Alle Zusatzobjekte der Datei bauen. Eigener ssg-start-Rahmen, weil
;; vsp-bau-datei seinen schon geschlossen hat; ATTREQ/ATTDIA 0 ist Pflicht -
;; sonst fragt (command "_.INSERT" ...) nach Attributwerten.
(defun hundm05:bau-zusatzobjekte (datei / daten objekte out rec)
(setq daten (ssg-load-json datei))
(setq objekte '())
(foreach obj daten
(if (hundm05:zusatz-objekt-p obj) (setq objekte (cons obj objekte))))
(setq objekte (reverse objekte))
(cond
((null objekte) nil)
((null (car (atoms-family 1 '("KREISEL-INSERT-SCRIPT"))))
(princ (strcat "\n[TEST_HUNDM05] WARNUNG: " (itoa (length objekte))
" Zusatzobjekt(e) uebersprungen - KreiselInsert nicht"
" geladen."))
nil)
(T
(princ (strcat "\n " (itoa (length objekte))
" weitere(s) Objekt(e) der Anlage"))
(ssg-start "TEST_HUNDM05_ZUSATZ"
'(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
(setq out '())
(foreach obj objekte
(setq rec (vl-catch-all-apply 'hundm05:bau-kreisel (list obj)))
(if (vl-catch-all-error-p rec)
(progn
(princ (strcat "\n [KREISEL] Ausnahme: "
(vl-catch-all-error-message rec)))
(setq rec (vsp-set (vsp-set (vsp-set
(vsp-rec-neu (ssg-val obj "id"))
"kind" "kreisel") "status" "abbruch")
"fehler_text"
(vl-catch-all-error-message rec)))))
(setq out (cons rec out)))
(ssg-end)
(reverse out))))
;; ============================================================
;; Testlauf
;; ============================================================
(defun c:TEST_HUNDM05 ( / datei)
(setq datei (hundm05:spec-datei))
(princ "\n\n================================================================")
(princ "\n TEST_HUNDM05 - Anlage HundM aus der Spec (5 Linienzuege)")
(princ (strcat "\n " datei))
(princ "\n================================================================")
(cond
((null (car (atoms-family 1 '("VSP-BAU-DATEI"))))
(princ "\n[TEST_HUNDM05] FEHLER: vf_spec.lsp nicht geladen"
" (VarioFoerderer laden).")
(setq *hundm05-records* nil))
((null (findfile datei))
(princ (strcat "\n[TEST_HUNDM05] FEHLER: " datei " nicht gefunden!"))
(setq *hundm05-records* nil))
(T
(vsp-modul-sichern "KreiselInsert")
(setq *hundm05-records* (vsp-bau-datei datei))
(setq *hundm05-records*
(append *hundm05-records* (hundm05:bau-zusatzobjekte datei)))))
(princ))
;; ============================================================
;; Dieselbe Anlage in einer bestimmten Dimension bauen
;; ============================================================
;; Die Spec ist dimensionsfrei - sie beschreibt die Eingaben, mit denen die
;; Ketten gebaut wurden. Ob daraus flache 2D-Symbole oder 3D-Modelle werden,
;; entscheidet allein die Blockvariante (_2D/_3D), die ssg-ils-dim-aktuell
;; aufloest. Unsere Bibliothek hat beide, also laesst sich dieselbe Anlage
;; ohne Datenaenderung in 2D ODER 3D aufbauen.
;; *vsp-dim-override* wird von vsp-bau-aus-spec JE KETTE angewandt (der
;; Abbruch-Handler in vf-linienzug-modus setzt *ssg-ils-dim* zurueck).
(defun hundm05:mit-dimension (dim / alt)
(setq alt (if (boundp '*vsp-dim-override*) *vsp-dim-override*))
(setq *vsp-dim-override* dim)
(vl-catch-all-apply 'c:TEST_HUNDM05 '())
(setq *vsp-dim-override* alt)
(princ))
(defun c:TEST_HUNDM05_2D () (hundm05:mit-dimension "2D"))
(defun c:TEST_HUNDM05_3D () (hundm05:mit-dimension "3D"))
;; ============================================================
;; Ergebnis-Export (Konvention <name>:export-results fuer test_run_all.lsp)
;; ============================================================
;; Geschrieben wird hier und nicht in c:TEST_HUNDM05, damit der Testrunner
;; das Zielverzeichnis bestimmt (er uebergibt tests/output).
(defun hundm05:export-results (out-dir / pfad)
(if (null *hundm05-records*)
(princ "\n Keine HundM05-Ergebnisse vorhanden.")
(progn
(vl-mkdir out-dir)
(setq pfad (strcat out-dir "/hundm05_results.json"))
(vsp-results-schreiben *hundm05-records* pfad)))
(princ))
(princ "\nTEST_HUNDM05 geladen. Aufruf: TEST_HUNDM05 (bzw. _2D / _3D)")
(princ)