#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ test_hundm05.py - Validiert die aus der SPEC gebaute Anlage HundM. Eingabe: tests/testdata/hundm05.json (die 5 Ketten im Spec-Format). Gebaut von TEST_HUNDM05 bzw. VF_SPEC_BAU in BricsCAD, Ergebnis in tests/output/hundm05_results.json. Diese Tests brauchen also einen CAD-Lauf und werden sonst uebersprungen. Den UEBERSETZER (Spec -> Journal) prueft tests/test_vf_spec.py ohne CAD; das Aufzeichnungsformat prueft tests/test_hm_recformat.py. Der Ergebnis-Record je Kette kommt aus Lisp/vf_spec.lsp (vsp-result-json). """ import json import os import pytest HIER = os.path.dirname(__file__) SPEC_JSON = os.path.join(HIER, "testdata", "hundm05.json") # TEST_HUNDM05 schreibt nach tests/output. Ein Lauf von VF_SPEC_BAU mit # gesetztem DXFM_VF_SPEC_OUT kann die Datei woanders ablegen - darum beide # ueblichen Orte pruefen, statt den Test stumm zu ueberspringen. ERGEBNIS_ORTE = [os.path.join(HIER, "output", "hundm05_results.json"), os.path.join(HIER, "..", "results", "hundm05_results.json")] @pytest.fixture(scope="module") def spec_objekte(): if not os.path.isfile(SPEC_JSON): pytest.skip("%s fehlt (lib/vf_spec_export.py erzeugt es)" % SPEC_JSON) with open(SPEC_JSON, encoding="utf-8") as fh: return json.load(fh) @pytest.fixture(scope="module") def spec_ergebnisse(): for pfad in ERGEBNIS_ORTE: if os.path.isfile(pfad): with open(pfad, encoding="utf-8") as fh: return json.load(fh) pytest.skip("hundm05_results.json fehlt - TEST_HUNDM05 in BricsCAD " "ausfuehren") class TestSpecBau: def test_je_spec_ein_ergebnis(self, spec_objekte, spec_ergebnisse): soll = [o["spec_id"] for o in spec_objekte if "spec_id" in o] ist = [r["spec_id"] for r in spec_ergebnisse] assert ist == soll def test_status_executed(self, spec_ergebnisse): for r in spec_ergebnisse: assert r["status"] == "executed", \ f'{r["spec_id"]}: Status={r["status"]}, {r.get("fehler_text")}' def test_keine_spec_fehler(self, spec_ergebnisse): for r in spec_ergebnisse: assert not r["spec_fehler"], \ f'{r["spec_id"]}: {r["spec_fehler"]}' def test_kein_prompt(self, spec_ergebnisse): """Der positive Beweis fuer "kein Dialog, keine Konsolenfrage": die zaehlenden Stubs wurden nie erreicht.""" for r in spec_ergebnisse: assert r["prompts"] == 0, \ f'{r["spec_id"]}: {r["prompts"]} Live-Eingabe(n) erreicht' def test_journal_vollstaendig_verbraucht(self, spec_ergebnisse): for r in spec_ergebnisse: assert r["eingaben_offen"] == 0, \ f'{r["spec_id"]}: {r["eingaben_offen"]} Werte offen' assert r["eingaben_gesamt"] > 0 def test_glieder_wie_geplant(self, spec_ergebnisse): """Der Desync-Detektor, der auch dann greift, wenn die Queue aufgeht: eine geometrisch abgewiesene Sektion verbraucht nur weniger Eintraege, die Glied-Folge weicht dann aber ab.""" for r in spec_ergebnisse: assert r["glieder_ist"] == r["glieder_soll"], \ f'{r["spec_id"]}: gebaut {r["glieder_ist"]}, ' \ f'geplant {r["glieder_soll"]}' def test_keine_meldungen(self, spec_ergebnisse): for r in spec_ergebnisse: assert not r["meldungen"], \ f'{r["spec_id"]}: Meldungen aus dem Bau: {r["meldungen"]}' def test_vf_block_erzeugt(self, spec_ergebnisse): for r in spec_ergebnisse: assert r["block_name"].startswith("VF_"), \ f'{r["spec_id"]}: Block {r["block_name"]!r}' assert r["block_handle"] def test_startpunkt_getroffen(self, spec_objekte, spec_ergebnisse): """Der Einfuegepunkt des VF_-Blocks ist der Kettenstart aus der Spec - er ist der einzige absolute Anker der ganzen Kette.""" soll = dict((o["spec_id"], o["start_punkt"]) for o in spec_objekte if "spec_id" in o) for r in spec_ergebnisse: erwartet = soll[r["spec_id"]] ist = r["insert_point"] for achse, (e, i) in enumerate(zip(erwartet, ist)): assert abs(e - i) < 1.0, \ f'{r["spec_id"]}: Achse {achse} ist {i}, erwartet {e}' def test_gleiche_ergebnisse_wie_journal_pfad(self, spec_ergebnisse): """Spec-Pfad == Journal-Pfad: dieselben Ketten, gebaut aus der Spec statt aus dem aufgezeichneten Journal, muessen dieselben Bloecke mit denselben Attributen ergeben. Vergleich gegen die Ergebnisse von TEST_HUNDM05, falls beide Laeufe vorliegen.""" pfad = os.path.join(HIER, "output", "hm_recformat_results.json") if not os.path.isfile(pfad): pytest.skip("tests/output/hm_recformat_results.json fehlt " "(TEST_HM_RECFORMAT nicht gelaufen)") with open(pfad, encoding="utf-8") as fh: journal_ergebnisse = json.load(fh) if len(journal_ergebnisse) != len(spec_ergebnisse): pytest.skip("unterschiedlich viele Ketten in den beiden Laeufen") for spec_r, journal_r in zip(spec_ergebnisse, journal_ergebnisse): if journal_r["status"] != "executed": continue for tag, wert in journal_r["actual_attributes"].items(): assert spec_r["actual_attributes"].get(tag) == wert, \ f'{spec_r["spec_id"]}: Attribut {tag} ist ' \ f'{spec_r["actual_attributes"].get(tag)!r}, ' \ f'Journal-Pfad hatte {wert!r}'