TRO-Typen an Kreisel-Beruehrpunkten (lib/kreisel_junctions.py, derive_tros Schritt 3c): jeder Kreisel als Stadion-Form mit Laufrichtung; jeder gleichlaufende Beruehrpunkt ist eine Weiche (exportierte ILS Weiche oder abgeleitet W<a>_<b>), Beruehrpunkte derselben Umlenkung bilden einen Knoten. Ein Separator steuert ihn, wenn er in Laufrichtung unmittelbar davor liegt (keine andere Weiche dazwischen). Typ = Separatoren x Weichen. export.csv: 1Sep1Swi (9/1, 9/8, 8/7), 2Sep1Swi (7/2), 2Sep2Swi (2/3/4), 3Sep2Swi (4/5/6). Neuer Katalogtyp 3Sep2Swi (nicht aus SCL belegt). Gegenlaeufige Beruehrpunkte (Mubea, Dematic) bleiben bei der alten Regel; Mubea/HundM05/Dematic unveraendert, Showroom 0018+0019 jetzt 2Sep1Swi. material_flow --track: Mouseover je Knoten mit Zugehoerigkeit zu Kreisel/VF/GF (Zuordnung, TrackIds-Kette, Lage laut Geometrie), Weiche mit beiden Kreiseln, Be-/Entladung mit ihrem Kreisel; Widersprueche mit "!". Zahlenwerte (Toleranzen, Schwellen, Massstaebe, Groessen) aus allen lib-Modulen nach cfg/parameter.ini (lib/parameter.py, Abschnitt je Modul, Ueberschreiben je Anlage ueber %SKEL_CFG%/parameter.ini). Ausgaben aller Anlagen bytegleich. tro_flow.py: Docstrings mit Parametern/Rueckgabe, fehlende Typangaben. Geprueft: 90 Tests gruen; tro_flow/material_flow/scl_skeleton auf 5 CSVs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests fuer lib/parameter.py und cfg/parameter.ini."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import configparser
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import parameter
|
|
from parameter import ParameterError, get_float, get_int
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
BASE = ROOT / "cfg" / parameter.PARAMETER_FILE
|
|
|
|
|
|
@pytest.fixture
|
|
def frisch(monkeypatch: pytest.MonkeyPatch):
|
|
"""Parameter-Cache leeren, damit SKEL_CFG im Test wirkt - und danach wieder."""
|
|
parameter._parser.cache_clear()
|
|
yield monkeypatch
|
|
parameter._parser.cache_clear()
|
|
|
|
|
|
def _base() -> configparser.ConfigParser:
|
|
parser = configparser.ConfigParser(inline_comment_prefixes=(";", "#"))
|
|
parser.read(BASE, encoding="utf-8")
|
|
return parser
|
|
|
|
|
|
def test_base_values_are_read(frisch: pytest.MonkeyPatch) -> None:
|
|
frisch.delenv("SKEL_CFG", raising=False)
|
|
assert get_float("kreisel_junctions", "contact_tolerance") == 50.0
|
|
assert get_int("tro_flow", "min_store_lanes") == 2
|
|
|
|
|
|
def test_skel_cfg_overrides_single_value(frisch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
(tmp_path / parameter.PARAMETER_FILE).write_text(
|
|
"[kreisel_junctions]\ncontact_tolerance = 75 ; Anlage X\n", encoding="utf-8")
|
|
frisch.setenv("SKEL_CFG", str(tmp_path))
|
|
assert get_float("kreisel_junctions", "contact_tolerance") == 75.0
|
|
# nicht ueberschriebene Werte kommen weiter aus der Grunddatei
|
|
assert get_float("kreisel_junctions", "separator_tolerance") == 100.0
|
|
|
|
|
|
def test_missing_key_is_an_error(frisch: pytest.MonkeyPatch) -> None:
|
|
frisch.delenv("SKEL_CFG", raising=False)
|
|
with pytest.raises(ParameterError, match=r"\[tro_flow\] gibt_es_nicht fehlt"):
|
|
get_float("tro_flow", "gibt_es_nicht")
|
|
|
|
|
|
def test_non_number_is_an_error(frisch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
(tmp_path / parameter.PARAMETER_FILE).write_text(
|
|
"[tro_flow]\nmin_store_lanes = zwei\n", encoding="utf-8")
|
|
frisch.setenv("SKEL_CFG", str(tmp_path))
|
|
with pytest.raises(ParameterError, match="keine ganze Zahl"):
|
|
get_int("tro_flow", "min_store_lanes")
|
|
|
|
|
|
def test_every_key_is_used_by_its_module() -> None:
|
|
"""Jeder Schluessel der Grunddatei wird in lib/<Abschnitt>.py gelesen -
|
|
keine verwaisten Werte, die nichts mehr steuern."""
|
|
parser = _base()
|
|
for section in parser.sections():
|
|
source = (ROOT / "lib" / f"{section}.py").read_text(encoding="utf-8")
|
|
for key in parser.options(section):
|
|
assert re.search(rf'"{key}"', source), f"[{section}] {key} wird nicht gelesen"
|