diff --git a/docs/README.md b/docs/README.md index e50fd4d..bbf4bb7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,6 +19,7 @@ | `tests/testkatalog.html` | generiert | dieselben Daten als lokal zu öffnendes Dashboard „Prüfstand“ (Baumdiagramm je Testdatei, Filter, Suche) – vom selben Lauf erzeugt | | `tests/*.md` | lebend | Testkonzepte je Fachablauf, mit Mermaid; Vorlage in `vorlagen/` | | `konzept/07_qgis_tests_plan.md` | Vorschlag | Stufenplan für Tests gegen echtes QGIS | +| `konzept/10_basemap_legende.md` | zur Freigabe | Basemap-Legende im Druck und Basemap-Stilvorlagen (Basis #88) | | `konzept/09_alkis7_umstellung.md` | Prüfliste | ALKIS 6 ↔ 7 (GID 6.0/7.1), was in `sn_verfahrensgebiet` anzupassen ist | | `konzept/08_repo_einrichten.md` | Prüfliste | Repo auf Gitea anlegen, `stable` und Branch-Schutz | | `konzept/02_zusammenarbeit_ki_agenten.md` | stabil | Begründung und Aufbau der Zusammenarbeit | diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md index 0bab1bb..ac50a37 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -17,17 +17,18 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `ui/dockmanager.py` | 86 | `DockManager.show(dock)`: entfernt alle anderen `sn_dock_*`-Docks, zeigt das neue rechts an | | `ui/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| “, Tabs aus Klassenattribut `tabs`, ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück | | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) | -| `ui/tabs/print_tab.py` ⚠ | 709 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas; nutzt `print_logic` | +| `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` | +| `ui/widgets/basemap_bereich.py` | 271 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Auswertebereich, Legende aktualisieren, Stilvorlagen | ### functions/ – Wrapper-Schicht (Qt/QGIS/OS mit Mock-Fallback) | Datei | Zeilen | Zweck | |---|---:|---| | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) | -| `functions/qt_wrapper.py` ⚠ | 796 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | -| `functions/qgiscore_wrapper.py` ⚠ | 1 122 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | +| `functions/qt_wrapper.py` ⚠ | 832 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | +| `functions/qgiscore_wrapper.py` ⚠ | 1 382 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | | `functions/qgisui_wrapper.py` | 258 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner; Mock-`iface` | -| `functions/dialog_wrapper.py` | 393 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog` | +| `functions/dialog_wrapper.py` | 521 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) | | `functions/message_wrapper.py` | 85 | MessageBar: `info`, `warning`, `error`, `success`, `push_message` | | `functions/variable_wrapper.py` | 120 | `get_variable(key, scope)`, `set_variable(...)` mit Präfix `sn_`; Mock-Speicher | | `functions/settings_logic.py` | 50 | `SettingsLogic.VARIABLEN`: Liste der gespeicherten Projektvariablen, `load()/save()` | @@ -55,9 +56,14 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `modules/Datenabruf.py` ⚠ | 636 | `Datenabruf.datenabruf(...)`: WFS, ArcGIS REST, OGR laden; Raumfilter Verfahrensgebiet/Puffer/ohne | | `modules/LayerLoader.py` | 422 | `create_layer(provider, link, thema)`, `apply_style`, `filter_by_extent`, `filter_by_layer`, `add_to_project` | | `modules/Datenschreiber.py` | 448 | `schreibe_Daten(...)` in GeoPackage, `lade_Layer(...)` ins Projekt, `schreibe_log(...)` HTML-Protokoll | -| `modules/print_logic.py` ⚠ | 1 321 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf | -| `modules/print_layout.py` ⚠ | 1 078 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) | +| `modules/print_logic.py` ⚠ | 1 360 | `PrintLogic`, `AtlasConfig`, `RectangleCaptureTool`: Druckbereich, Maßstab, Atlas, Layoutaufruf | +| `modules/print_layout.py` ⚠ | 1 111 | `PrintLayout`: Layoutaufbau für § 38 / § 41 (Karte, Legende, Gitter, Schriftfeld) | | `modules/print_layout_baufreigabe.py` | 403 | Layout „Anlage zur Baufreigabe“ (feste DIN A3/A4 quer) | +| `modules/basemap_legende.py` | 471 | Basemap-Legende (#88): Legendenkatalog laden, Styles → Einträge, Auswahl merken, Basemap-Layer erkennen, Auswertebereich; `BasemapLegende.aktualisiere(layout)` als Einstieg | +| `modules/basemap_kacheln.py` | 192 | Kacheln des Basemap-Layers kachelweise über `selectByGeometry` auswerten, Renderer-Styles lesen, Zoomstufen wie der QGIS-Renderer | +| `modules/basemap_legende_layout.py` | 396 | Legendensymbole aus Projektlayer-Styles zusammensetzen, leere Hilfslayer, Legendenelement „Legende Basemap“ schreiben und platzieren | +| `modules/basemap_katalog.py` | 418 | Erzeugt/aktualisiert `data/basemap_legende.json` aus der basemap.de-Style-JSON (nur Standardbibliothek, eigenständig aufrufbar) | +| `modules/basemap_stilvorlagen.py` | 181 | Lokale Stilvorlagen des Basemap-Layers (QML + JSON im QGIS-Profil), Standard in `QSettings` | ### Sonstiges @@ -66,6 +72,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | `tests/` | 13 Testdateien, `unittest`, Mock-Modus; `run_tests.py` (farbiger Runner), `test_qgis.bat` (Coverage in OSGeo4W) | | `styles/` | QML-Stile (Verfahrensgebiet, LSG, Denkmalschutz, Flurstücksbeschriftung) | | `data/landkreise.json` | Landkreise mit Kennzahl | +| `data/basemap_legende.json` | Legendenkatalog basemap.de: Style-ID → Legendeneintrag (190 Einträge), erzeugt mit `basemap_katalog.py`, fachlich nachbearbeitet | | `assets/plan-qgis340ExitCrashFix.prompt.md` | KI-Planungsnotiz zum Exit-Crash (historisch) | | `.gitea/workflows/release.yaml`, `.gitea/templates/metadata.template` | Release-Auslöser, Vorlage für `metadata.txt` | @@ -129,12 +136,12 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl | Datei | Zeilen | |---|---:| | `sn_verfahrensgebiet/functions/grenzpunkt_alkis.py` | 1 674 | -| `sn_basis/modules/print_logic.py` | 1 321 | +| `sn_basis/modules/print_logic.py` | 1 360 | | `sn_plan41/ui/tab_a_logic.py` | 1 239 | -| `sn_basis/functions/qgiscore_wrapper.py` | 1 122 | -| `sn_basis/modules/print_layout.py` | 1 078 | +| `sn_basis/functions/qgiscore_wrapper.py` | 1 382 | +| `sn_basis/modules/print_layout.py` | 1 111 | | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 | | `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 | | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 | -| `sn_basis/functions/qt_wrapper.py` | 796 | -| `sn_basis/ui/tabs/print_tab.py` | 709 | +| `sn_basis/functions/qt_wrapper.py` | 832 | +| `sn_basis/ui/tabs/print_tab.py` | 730 | diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md index a3bffa4..dc8eafa 100644 --- a/docs/architektur/schnittstellen.md +++ b/docs/architektur/schnittstellen.md @@ -55,6 +55,9 @@ Der Wrapper setzt das Präfix `sn_` selbst; Schlüssel **ohne** Präfix übergeb | `linkliste`, `tab_a_linkliste` | Plan41 Tab A | Plan41 | Pfad der Linkliste (XLSX) | | `tab_a_layer_id`, `Raumfilter` | Plan41 Tab A | Plan41 | Raumfilter-Einstellung | | `druckrechteck_xmin/xmax/ymin/ymax` | PrintTab/PrintLogic | Druck | gespeicherter Druckbereich | +| `basemap_auswertebereich` | Basemap-Bereich im Druck-Tab | `basemap_legende` | `verfahrensgebiet` (Standard) oder `druckbereich` (Basis #88) | +| `basemap_legende_erzeugen` | Basemap-Bereich | `print_logic` | `1`/`0`, Standard `1`: Basemap-Legende beim Erstellen einer §-38/§-41-Vorlage | +| `basemap_legende_auswahl` | `basemap_legende`, Stilvorlage laden | Auswahldialog | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` mit Eintragsschlüsseln des Legendenkatalogs | | `sn_abdeckungslayer` ⚠ | PrintTab/PrintLogic | Druck (Atlas) | Atlas-Abdeckungslayer. **Schlüssel enthält bereits `sn_` → tatsächliche Variable `sn_sn_abdeckungslayer`**; funktioniert, ist aber inkonsistent | | `verfahrensgebietslayer` ⚠ | `print_logic.py` | `print_logic.py` | **Zweite Schreibweise neben `verfahrensgebiet_layer`** – vermutlich Altlast, beim Refactoring zusammenführen | @@ -126,3 +129,25 @@ flowchart LR ## 10. Linkliste-Repository `linkliste.csv` (Semikolon, Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) → Gitea Action → `linkliste.xml/json/yaml` auf `main`. Die Plan41-Laufzeit nutzt stattdessen die mitgelieferte `assets/Linkliste.xlsx` mit abweichenden Spaltennamen. + +## 11. Basemap-Legende im Druck (Basis #88) + +- **Welche Layouts:** `print_logic.LAYOUTS_MIT_BASEMAP_LEGENDE` (heute §38, §41). Ein Layout nutzt die Funktion, + wenn es die Layout-Eigenschaft `sn_basemap_legende = 1` trägt (`basemap_legende_layout.markiere_layout`); + `PrintLayout.create_single_page_layout(..., basemap_legende=True)` setzt sie und erzeugt die Legende vor dem + Öffnen des Designers. Neue Layouts: in die Konstante aufnehmen bzw. das Kennzeichen setzen. +- **Layout-Elemente nach ID:** `Hauptkarte` (Bezugskarte), `Legende` (Hauptlegende, Spalte und Oberkante), + `Quellenangabe` (Unterkante der Basemap-Legende), `Legende Basemap` (das Basemap-Element selbst). + Umbenennen dieser IDs bricht die Platzierung. +- **Weitere Layout-Eigenschaften:** `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` (JSON), + `sn_basemap_hilfslayer` (IDs der Hilfslayer, werden beim Aktualisieren ersetzt). +- **Hilfslayer:** leere Speicherlayer, nicht im Layerbaum, Eigenschaften `sn_basemap_legende = ` und + `skipMemoryLayersCheck = 1`. Code, der über `QgsProject.mapLayers()` iteriert, sieht sie. +- **Basemap-Layer-Erkennung:** Vektorkachel-Layer, Quelle enthält `gdz_basemapde_vektor`; Name „basemap.de“ nur + als Rückfall. +- **Legendenkatalog:** `sn_basis/data/basemap_legende.json` (Style-ID → Eintrag), erzeugt/aktualisiert mit + `python modules/basemap_katalog.py`; gehört zur Style-Version `bm_web_col` v2. +- **Stilvorlagen:** lokal unter `/sn_basis/basemap_stile/`, Standard in `QSettings` + `sn_basis/basemap_standardstil`. +- **Neue öffentliche Funktion:** `dialog_wrapper.ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)` – + Liste mit Häkchen, Gruppen, Suche; liefert gewählte Schlüssel oder `None` bei Abbruch. diff --git a/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md b/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md new file mode 100644 index 0000000..c1a1860 --- /dev/null +++ b/docs/entscheidungen/0010-basemap-legende-eigenes-legendenelement.md @@ -0,0 +1,42 @@ +# 0010 – Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog + +Status: vorgeschlagen · Datum: 2026-10-02 · Beteiligte: Daniel Helbig, Claude Code · Ticket Plugin_SN_Basis #88 + +## Kontext +QGIS erzeugt für Vektorkachel-Layer keine Legende (`QgsVectorTileLayer` setzt keine `QgsMapLayerLegend`, +geprüft im Quellcode 3.40, 3.44, master). Die §-38-/§-41-Drucklayouts brauchen aber eine Legende der +basemap.de-Objekte, die im Verfahrensgebiet tatsächlich dargestellt werden, mit der Signatur des Projektlayers. +Die Style-JSON von basemap.de hat 569 Style-Layer mit technischen IDs; ein Legendeneintrag wie „Kreisstraße“ +verteilt sich auf bis zu 20 Styles (Kontur, Decker, Brücke, getrennte Fahrbahn …). + +Ein Spike in QGIS 3.44.4 (offscreen, echte Kacheln, Gebiet bei Meißen, 619 ha) am 02.10.2026 ergab: +- `QgsVectorTileLayer.selectByGeometry()` holt und dekodiert die Kacheln zur Zoomstufe des Maßstabs selbst + (5,8 s für 10 250 Objekte, entdoppelt 3 990; Style-Zuordnung 0,5 s). +- Hilfslayer in der **Hauptlegende** verschwinden beim Rendern, weil diese nach Karteninhalt filtert + (`setLegendFilterByMapEnabled(True)`) und die Hilfslayer nicht in der Karte liegen. Außerdem müsste die + Hauptlegende dafür ihre automatische Aktualisierung verlieren. +- Ein **eigenes Legendenelement** ohne Kartenfilter zeigt alle Einträge, übersteht Speichern und Neuladen des + Projekts; leere Speicherlayer lösen beim Schließen keine Rückfrage aus. + +## Entscheidung +Die Basemap-Legende ist ein **eigenes Legendenelement „Legende Basemap“** ohne Kartenfilter und ohne +automatische Aktualisierung, dessen Einträge aus **leeren, kategorisierten Speicherlayern** stammen (je Gruppe +und Geometrietyp, nicht im Layerbaum, mit dem Projekt gespeichert). Welche Styles zu welchem Eintrag gehören +und welche das Symbol bilden, steht in einem **versionierten Legendenkatalog** `sn_basis/data/basemap_legende.json`, +der aus der Style-JSON erzeugt und fachlich nachbearbeitet wird. Signaturen werden zur Laufzeit aus dem +Projektlayer genommen, nicht aus dem Katalog. + +## Folgen +- Hauptlegende bleibt unverändert (Auto-Update, Kartenfilter); der Basemap-Layer selbst erscheint dort nicht, + weil er keine Legendenknoten hat und der Filter ihn ausblendet. +- Lokale Änderungen am Basemap-Stil (Farben, abgeschaltete Styles) wirken automatisch auch in der Legende. +- Die Basemap-Legende ist eine Momentaufnahme; nach Änderungen an Karte oder Stil „Basemap-Legende aktualisieren“. +- Bei einer neuen Basemap-Stilversion muss der Katalog mit `modules/basemap_katalog.py` erneuert werden; + unbekannte Styles erscheinen bis dahin unter „Sonstiges“ (abgewählt). +- Je Layout liegen einige leere Speicherlayer im Projekt (nicht im Layerbaum sichtbar). + +## Verworfene Alternativen +- **Hilfslayer in der Hauptlegende:** vom Kartenfilter ausgeblendet (Spike), und Verlust der Auto-Aktualisierung. +- **Eigene `QgsMapLayerLegend` (Python-Unterklasse) am Vektorkachel-Layer:** wird nicht im Projekt gespeichert, + nach Neuladen wäre die Legende weg. +- **Legende direkt aus dem Signaturenkatalog:** zeigt die Standarddarstellung, nicht die im Projekt angepasste. diff --git a/docs/entscheidungen/README.md b/docs/entscheidungen/README.md index 33a4c75..453a71f 100644 --- a/docs/entscheidungen/README.md +++ b/docs/entscheidungen/README.md @@ -34,3 +34,4 @@ Kurz, mit Grund. | 0007 | Branchmodell: drei Kanäle, `stable` als stabiler Kanal, Rückführungspflicht | vorgeschlagen | | 0008 | Werkzeugunterstützung auf Claude Code fokussiert | vorgeschlagen | | 0009 | Tests sind Teil der Definition of Done, und sie werden für Menschen dokumentiert | vorgeschlagen | +| 0010 | Basemap-Legende als eigenes Legendenelement mit Hilfslayern und Legendenkatalog | vorgeschlagen | diff --git a/docs/konzept/10_basemap_legende.md b/docs/konzept/10_basemap_legende.md new file mode 100644 index 0000000..29b1108 --- /dev/null +++ b/docs/konzept/10_basemap_legende.md @@ -0,0 +1,263 @@ +# Basemap-Legende und Basemap-Stilvorlagen im Druck + +Stand 02.10.2026. Konzept freigegeben und umgesetzt (Branch `feature/88-basemap-legende`, Abschnitt 10), Ticket **Plugin_SN_Basis #88** („Legende für basemap"). +Betroffen ist ausschließlich **`sn_basis`** (Druck); `sn_plan41` und `sn_verfahrensgebiet` nutzen den +Druck-Tab aus `sn_basis` und bekommen die Funktion ohne eigene Änderung. + +## 1. Ziel + +Der Vektorkachel-Dienst basemap.de +(`https://sgx.geodatenzentrum.de/gdz_basemapde_vektor/tiles/v2/bm_web_de_3857/{z}/{x}/{y}.pbf`) hat keine +Legende, die nur die tatsächlich dargestellten Objekte zeigt. Für die Drucklayouts (§ 38, § 41) wird eine solche +Legende gebraucht: + +1. Beim Anlegen einer Druckvorlage wird geprüft, ob ein Basemap-Layer im Projekt ist. +2. Die Objekte der Kacheln **im Auswertebereich** (Standard: Verfahrensgebiet) werden ausgewertet. +3. Ein Auswahldialog zeigt die gefundenen Kategorien; der Nutzer wählt ab, was nicht in die Legende soll. +4. Die gewählten Kategorien erscheinen mit der Signatur **aus dem Projektlayer** (Standard „Basemap.de - Farbe") + in der Legende – nicht mit der Signatur aus dem Signaturenkatalog, weil die Darstellung im Projekt abweichen kann. + +Dazu kommt ein einklappbarer Bereich **„Basemap"** im Druck-Tab (Auswertebereich, Legende aktualisieren, +Stilvorlagen laden/speichern). + +## 2. Festlegungen (mit Daniel abgestimmt, 02.10.2026) + +| # | Frage | Festlegung | +|---|---|---| +| 1 | Für welche Layouts? | Funktion layoutunabhängig bauen; jedes Layout kann sie per Kennzeichen nutzen. **Vorerst aktiv für § 38 und § 41**, nicht für „Anlage zur Baufreigabe". | +| 2 | Auswertebereich | **Standard Verfahrensgebiet.** Umschaltbar auf den Druckbereich (Kartenausschnitt der Vorlage) im Basemap-Bereich des Druck-Tabs. Ohne Verfahrensgebiet: Rückfall auf den Druckbereich mit Hinweis. | +| 3 | Feinheit der Einträge | Brücken-, Tunnel- und Fahrbahnvarianten gehen im Grundeintrag auf („Kreisstraße"). „Im Bau" und „Außer Betrieb" werden eigene Einträge. Beschriftungen (`Name_*`, `Nummer_*`), Hausnummern und `Hintergrund` kommen nie in die Legende. | +| 4 | Basemap-Layer in der Hauptlegende | Der Layer selbst (heute nur Name ohne Symbol) wird ausgeblendet und durch die Gruppe „Basemap" ersetzt. | +| 5 | Nachträglich aktualisieren | Knopf „Basemap-Legende aktualisieren" im Basemap-Bereich, für bestehende Vorlagen. | +| 6 | Stilvorlagen | Darstellung des Basemap-Layers als **lokale Stilvorlage** speichern und wieder laden, **mehrere Varianten** möglich, eine davon als Standard markierbar. | + +## 3. Technische Grundlagen (recherchiert 02.10.2026) + +- **QGIS bietet für Vektorkachel-Layer keine Legende.** `QgsVectorTileLayer` setzt keine `QgsMapLayerLegend` + (Quellcode geprüft für 3.40, 3.44, master). Die Legendeneinträge müssen wir selbst erzeugen. +- **Auswertung über `QgsVectorTileLayer.selectByGeometry()`** (seit QGIS 3.28): holt die Kacheln zur Zoomstufe + des übergebenen Maßstabs (`QgsSelectionContext.setScale`), dekodiert sie, schneidet sie mit der Geometrie (im + Layer-KBS EPSG:3857) und liefert über `selectedFeatures()` Features mit den Zusatzfeldern `tile_layer` und + `tile_zoom`. Mit übergebenem `QgsRenderContext` werden nur Features geliefert, die der Renderer auch zeichnet. + `getRawTile()` ist in Python nicht verfügbar (`SIP_SKIP`). Der Aufruf **verändert die Auswahl des Layers** + – vorher sichern, danach wiederherstellen. +- **Kacheln enden bei Zoomstufe 15** (`bm_web_de_3857.json`). Druckmaßstäbe 1:1.000 bis 1:5.000 werten Stufe 15 + aus. Eine Kachel der Stufe 15 deckt in Sachsen rund 0,6 km² ab → ein Verfahrensgebiet von 50 km² braucht + etwa 100 Kacheln. Bereits von der Kartenansicht geladene Kacheln kommen aus dem Netzwerkcache. +- **Der Signaturenkatalog ist die Style-JSON** `https://sgx.geodatenzentrum.de/gdz_basemapde_vektor/styles/bm_web_col.json` + (569 Style-Layer: 283 Linien, 210 Symbole, 66 Flächen, 7 Kreise, 3 Extrusionen; 29 Quell-Layer). + Style-IDs sind technische Namen (`Kontur_Kreisstr`, `Decker_Kreisstr`, `Bruecke_Kreisstr_getrennt_Decker` …), + **ein Legendeneintrag besteht aus mehreren Style-Layern** (Kreisstraße: 17). Lesbare Bezeichnungen liefern die + `klasse`-Werte der Filter (520 Werte über alle Quell-Layer). +- **Beim Import in QGIS wird jede Style-ID zum `styleName`** eines `QgsVectorTileBasicRendererStyle` (Layername, + Geometrietyp, Filterausdruck, Zoombereich, Symbol, aktiv/inaktiv). Beschriftungen landen in + `QgsVectorTileBasicLabeling`. Darüber verbinden wir Katalog und Projektlayer. +- **Zoomabhängige Strichbreiten** (Mapbox-`stops`) werden in QGIS zu datendefinierten Ausdrücken mit + `@vector_tile_zoom`. In der Legende gibt es diese Variable nicht → Werte für die Druck-Zoomstufe festschreiben + oder die Variable am Legendenelement setzen (Spike, Abschnitt 7). + +Quellen: [Datenmodell](https://basemap.de/data/produkte/web_vektor/meta/bm_web_vektor_datenmodell.html), +[Signaturenkatalog](https://basemap.de/data/produkte/web_vektor/meta/bm_web_vektor_col_signaturenkatalog.html), +[PyQGIS QgsVectorTileLayer](https://qgis.org/pyqgis/3.44/core/QgsVectorTileLayer.html), +QGIS-Quellcode `src/core/vectortile/qgsvectortilelayer.cpp` (`final-3_40_0`, `final-3_44_0`). + +## 4. Ablauf + +```mermaid +flowchart TD + A[Vorlage erstellen
oder Basemap-Legende aktualisieren] --> B{Layout nutzt
Basemap-Legende?} + B -- nein --> Z[Layout wie bisher] + B -- ja --> C{Sichtbarer Basemap-Layer
im Projekt?} + C -- nein --> Z + C -- ja --> D[Auswertebereich bestimmen
Verfahrensgebiet / Druckbereich] + D --> E[Geometrie nach EPSG:3857,
kachelweise selectByGeometry
mit Fortschritt und Abbruch] + E --> F[Feature → aktive Renderer-Styles
bei Druck-Zoomstufe] + F --> G[Styles → Legendeneinträge
über Legendenkatalog] + G --> H[Auswahldialog mit Symbolvorschau
Vorbelegung aus Projektvariable] + H --> I[Gruppe Basemap in der Legende
zusammengesetzte Symbole] + I --> J[Auswahl und Einstellungen
am Layout und im Projekt merken] +``` + +- **Layer-Erkennung:** Layertyp Vektorkachel **und** Quell-URL enthält `gdz_basemapde_vektor`; Name nur als + Rückfall (Layer werden sonst über den Namen gesucht, siehe `fallstricke.md`). Berücksichtigt werden nur Layer, + die in der Karte der Vorlage sichtbar sind (Thema bzw. aktuelle Sichtbarkeit). Mehrere Basemap-Layer + (z. B. Farbe und Grau): jeder bekommt eine eigene Gruppe. +- **Deaktivierte Styles** im Projektlayer erzeugen keine Legendeneinträge. Wer z. B. Hausnummern oder andere + Objekte über eine Stilvorlage ausschaltet, sieht sie automatisch auch nicht in der Legende. +- **Unbekannte Style-IDs** (eigene Ergänzungen im Projektlayer, neue Basemap-Version) erscheinen mit der + Bezeichnung „Quell-Layer: klasse" bzw. dem `styleName` in einer Gruppe „Sonstiges" und sind im Dialog + abgewählt vorbelegt. + +## 5. Bausteine + +### 5.1 `sn_basis` – neue und geänderte Dateien + +| Datei | Inhalt | +|---|---| +| `data/basemap_legende.json` (neu) | **Legendenkatalog:** je Eintrag Schlüssel, Bezeichnung, Gruppe, Reihenfolge, `treffer_stile` (alle Style-IDs, deren Vorkommen den Eintrag auslösen), `symbol_stile` (Style-IDs, die zusammen das Legendensymbol bilden, z. B. `Kontur_Kreisstr` + `Decker_Kreisstr`), dazu eine Liste `nie_in_legende`. Kopf mit Version der Style-JSON. | +| `modules/basemap_legende.py` (neu, < 600 Zeilen) | Layer finden, Auswertebereich bestimmen, Kacheln auswerten, Styles zuordnen (reine Logik, testbar), Symbole zusammensetzen, Gruppe in eine Legende schreiben bzw. ersetzen. Keine direkten QGIS-/Qt-Importe. | +| `modules/basemap_stilvorlagen.py` (neu) | Stilvorlagen auflisten, speichern, laden, löschen, Standard merken. | +| `ui/widgets/basemap_bereich.py` (neu) | Einklappbarer Bereich „Basemap" für den Druck-Tab (Abschnitt 5.2). Eigene Datei, weil `print_tab.py` schon 709 Zeilen hat. | +| `ui/tabs/print_tab.py` | Bereich einbinden (wenige Zeilen), `cleanup()` weiterreichen. | +| `modules/print_logic.py` | Nach der Vorlagenbezeichnung Basemap-Legende ermitteln, wenn das Layout sie nutzt; Ergebnis an das Layout übergeben. Menge der Layouts mit Basemap-Legende als Konstante (`{KARTENNAME_38, KARTENNAME_41}`). | +| `modules/print_layout.py` | Neuer optionaler Parameter für die Basemap-Einträge; Layout-Kennzeichen setzen; Basemap-Layer-Knoten der Hauptlegende ausblenden. | +| `functions/dialog_wrapper.py` | Neuer allgemeiner Dialog `ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)`: Liste mit Häkchen, Symbolvorschau, Gruppen, Suche, „Alle auswählen". Mock: gibt die Vorauswahl zurück. | +| `functions/qgiscore_wrapper.py`, `qgisui_wrapper.py`, `qt_wrapper.py` | Fehlende Symbole mit Mock-Fallback: `QgsVectorTileLayer`, `QgsSelectionContext`, `QgsRenderContext`, `QgsExpression`, `QgsExpressionContext(Scope)`, `QgsCategorizedSymbolRenderer`, `QgsRendererCategory`, `QgsLayerTreeGroup`/`QgsLayerTreeLayer`, `QgsSymbolLayerUtils`, `QgsApplication.qgisSettingsDirPath`, `QgsCollapsibleGroupBox`. | +| `changelog.txt` | Eintrag oben (nächstes Release). | + +**Layoutunabhängigkeit:** Ein Layout nutzt die Basemap-Legende, wenn es die Layout-Eigenschaft +`sn_basemap_legende = 1` trägt und ein Legendenelement mit bekannter ID hat (Standard `Legende`). Am Layout werden +außerdem Auswertebereich, Maßstab und gewählte Einträge gespeichert, damit „Aktualisieren" mit denselben +Einstellungen neu rechnen kann. Ein neues Layout schaltet die Funktion ein, indem es das Kennzeichen setzt bzw. +in die Konstante aufgenommen wird – ohne Änderung am Basemap-Modul. + +### 5.2 Basemap-Bereich im Druck-Tab + +Einklappbar (standardmäßig eingeklappt), unterhalb der bisherigen Einstellungen, oberhalb von „Vorlage erstellen". +Ohne Basemap-Layer im Projekt ist der Bereich deaktiviert und zeigt „Kein Basemap-Layer im Projekt". + +``` +▸ Basemap (Basemap.de - Farbe) + Legende + Auswertebereich [Verfahrensgebiet ▾] (Verfahrensgebiet | Druckbereich) + [x] Basemap-Legende beim Erstellen der Vorlage erzeugen + Vorlage [§41 Wegeplan ▾] [Basemap-Legende aktualisieren] + Stilvorlage + Variante [Ohne Hausnummern ★ ▾] + [Laden] [Speichern unter …] [Als Standard] [Löschen] +``` + +- „Vorlage" listet die Layouts des Projekts mit Kennzeichen `sn_basemap_legende`. +- „Aktualisieren" rechnet mit dem aktuell eingestellten Auswertebereich neu, zeigt den Auswahldialog + (vorbelegt mit der letzten Auswahl dieses Layouts) und ersetzt die Gruppe „Basemap" in der Legende. + +### 5.3 Stilvorlagen (lokal, mehrere Varianten) + +- **Inhalt einer Variante:** Darstellung des Basemap-Layers als QML (Kategorien Symbologie und Beschriftung, + also auch abgeschaltete Styles wie Hausnummern) und eine kleine JSON-Begleitdatei mit Anzeigename, + Erstellungsdatum, Quell-Stil (`bm_web_col` v2) und – optional – den in der Legende standardmäßig abgewählten + Einträgen. +- **Ablage:** lokal im QGIS-Profil, `/sn_basis/basemap_stile/.qml` + `.json`. + Dateiname über `sanitize_bezeichner`. Der Standard wird in `QSettings` gemerkt (`sn_basis/basemap_standardstil`). +- **Laden:** Rückfrage vor dem Überschreiben der aktuellen Darstellung. Danach Prüfung, ob die `styleName`s der + Vorlage zum Layer passen (Farbe vs. Grau, neue Basemap-Version); bei Abweichung Hinweis mit Anzahl unbekannter + Styles. Laden geschieht **nur über den Knopf**, nicht automatisch beim Öffnen eines Projekts. +- **Speichern unter …:** Name abfragen, vorhandene Variante nur nach Rückfrage überschreiben. +- **Später möglich, nicht Teil dieses Tickets:** mitgelieferte Varianten im Plugin (`styles/basemap/`) und ein + gemeinsamer Ordner (Netzlaufwerk) für die ganze Behörde. + +### 5.4 Werkzeug im Projektwissen + +`scripts/basemap_katalog.py` (neu, nur Standardbibliothek): lädt `bm_web_col.json`, gruppiert Style-IDs nach +Quell-Layer und `klasse` (Präfixe/Suffixe wie `Kontur_`, `Decker_`, `Bruecke_`, `Tunnel_`, `_getrennt`, +`_Fahrbahnachse`, `_FSZ1` werden zusammengefasst), schlägt `symbol_stile` vor und schreibt einen Entwurf von +`basemap_legende.json`. Bestehende fachliche Nachbearbeitung (Bezeichnungen, Gruppen, Reihenfolge) bleibt bei +einem erneuten Lauf erhalten; neue oder entfallene Style-IDs werden gemeldet. + +### 5.5 Neue Schnittstellen (in `schnittstellen.md` nachtragen) + +| Art | Schlüssel | Bedeutung | +|---|---|---| +| Projektvariable | `basemap_auswertebereich` | `verfahrensgebiet` (Standard) oder `druckbereich` | +| Projektvariable | `basemap_legende_erzeugen` | `1`/`0`, Standard `1` | +| Projektvariable | `basemap_legende_auswahl` | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` – zuletzt ab-/angewählte Einträge (Vorbelegung) | +| Layout-Eigenschaft | `sn_basemap_legende`, `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` | Kennzeichen und letzte Einstellungen je Layout | +| QSettings | `sn_basis/basemap_standardstil` | Name der Standard-Stilvorlage | +| Öffentliche Funktion | `dialog_wrapper.ask_mehrfachauswahl(...)` | neuer allgemeiner Dialog | + +## 6. Tests (Mock-Modus, ohne Netzwerk) + +Jede neue beobachtbare Funktion bekommt mindestens einen Test mit deutschem Docstring („Prüft, dass …"). + +| Bereich | Testfälle | +|---|---| +| Katalog | Laden und Schema; jede Style-ID höchstens einem Eintrag zugeordnet; `symbol_stile` ⊆ `treffer_stile`; Ausschlussliste greift | +| Zuordnung | Mehrere Styles → ein Eintrag; deaktivierte Styles ignoriert; Zoombereich berücksichtigt; Filterprüfung als übergebene Funktion; unbekannte Style-IDs → „Sonstiges", abgewählt vorbelegt; Reihenfolge nach Gruppe/Reihenfolge | +| Layer-Erkennung | Erkennung über Quell-URL; Name nur als Rückfall; unsichtbarer Layer ignoriert; kein Basemap-Layer → Druck unverändert | +| Auswertebereich | Verfahrensgebiet als Standard; Druckbereich bei Einstellung; ohne Verfahrensgebiet Rückfall mit Hinweis; Auswahl des Layers danach wiederhergestellt | +| Dialog | Mock gibt Vorauswahl zurück; Abbruch → keine Basemap-Gruppe, Vorlage trotzdem erzeugt | +| Layout | Kennzeichen nur bei § 38/§ 41; Gruppe „Basemap" wird beim Aktualisieren ersetzt, nicht verdoppelt; Basemap-Layer-Knoten ausgeblendet | +| Projektvariablen | Auswahl und Auswertebereich werden gespeichert und wieder gelesen | +| Stilvorlagen | Speichern, Auflisten, Laden, Löschen in temporärem Profilordner; Name über `sanitize_bezeichner`; Überschreiben nur nach Bestätigung; Standard in QSettings; fehlende Begleitdatei toleriert | +| Katalog-Skript | Gruppierung Kontur/Decker/Brücke zu einem Eintrag an einem Ausschnitt der Style-JSON (Testdaten im Repo, kein Download) | + +**Nur in QGIS prüfbar** (Ergebnis im PR dokumentieren): echte Kachelauswertung, Laufzeit, Aussehen der Symbole +in der Legende, QML-Laden/Speichern am Vektorkachel-Layer, Einklappen des Bereichs unter Qt5 und Qt6. + +## 7. Umsetzungsschritte + +Ein Feature-Branch `feature/88-basemap-legende` in `Plugin_SN_Basis` von `unstable`, Commits je Schritt. + +1. **Spike in QGIS** (Python-Konsole, echtes Verfahrensgebiet, QGIS 3.44 und 4.x). Ergebnis als ADR in + `docs/entscheidungen/`: + - Laufzeit und Kachelzahl von `selectByGeometry()` für ein großes Verfahrensgebiet; kachelweise Aufteilung. + - Auswertung der Style-Filter mit `@vector_tile_zoom`. + - **Darstellung in der Legende:** (a) leere Hilfslayer (Speicherlayer je Geometrietyp, kategorisiert, nicht im + Layerbaum) als Gruppe „Basemap" in der Hauptlegende – offen, ob `setLegendFilterByMapEnabled(True)` sie + wegfiltert; oder (b) eigenes Legendenelement „Basemap" ohne Kartenfilter direkt unter der Hauptlegende. + - Zusammengesetzte Symbole und festgeschriebene Strichbreiten in der Legende. + - `saveNamedStyle`/`loadNamedStyle` am Vektorkachel-Layer inklusive deaktivierter Styles und Beschriftung. + - `QgsCollapsibleGroupBox` unter Qt5/Qt6 verfügbar, sonst Muster des Atlas-Bereichs (Kontrollkästchen + blendet Inhalt ein). +2. **Legendenkatalog:** Skript, erster Entwurf, fachliche Durchsicht der Bezeichnungen und Gruppen. +3. **Auswertung und Zuordnung** (`basemap_legende.py`) mit Tests. +4. **Dialog** `ask_mehrfachauswahl` mit Test. +5. **Layout-Anbindung** in `print_logic`/`print_layout` für § 38/§ 41, Projektvariablen. +6. **Basemap-Bereich im Druck-Tab** mit Aktualisieren-Knopf. +7. **Stilvorlagen** (`basemap_stilvorlagen.py`, Teil des Bereichs). +8. `python scripts/run_tests.py sn_basis --baseline`, `python scripts/testkatalog.py`, + `python scripts/pruefe_umlaute.py Plugin_SN_Basis`, QGIS-Abnahme, `changelog.txt`, PR-Text, Übergabe: + `schnittstellen.md`, `modulkarte.md`, `fallstricke.md` (Katalog hängt an Stil-Version v2), `STATUS.md`. + +## 8. Abnahmekriterien + +- [ ] Beim Erstellen einer § 38- oder § 41-Vorlage mit sichtbarem Basemap-Layer erscheint der Auswahldialog mit + genau den Kategorien, die im Auswertebereich vorkommen und im Projektlayer aktiv sind. +- [ ] Die gewählten Kategorien stehen als Gruppe „Basemap" mit der Signatur des Projektlayers in der Legende; + zusammengesetzte Signaturen (Straßen) sind als ein Symbol dargestellt. +- [ ] Beschriftungen, Hausnummern und Hintergrund erscheinen nie. +- [ ] Ohne Basemap-Layer, bei Abbruch des Dialogs und bei „Anlage zur Baufreigabe" entsteht die Vorlage wie bisher. +- [ ] Auswertebereich ist im Basemap-Bereich auf Druckbereich umstellbar; die Einstellung bleibt im Projekt erhalten. +- [ ] „Basemap-Legende aktualisieren" ersetzt die Gruppe in einer bestehenden Vorlage, ohne sie zu verdoppeln. +- [ ] Stilvorlagen lassen sich unter eigenem Namen speichern, laden, löschen und als Standard markieren; mehrere + Varianten sind möglich; nach dem Laden einer Variante ohne Hausnummern fehlen diese in Karte und Legende. +- [ ] Neue Tests grün, Baseline nicht verschlechtert, Testkatalog aktualisiert, Ablauf in QGIS 3.44 und 4.x geprüft. + +## 9. Risiken + +- **Blockierender Netzwerkzugriff** im Hauptthread bei großen Gebieten → kachelweise mit Fortschrittsanzeige und + Abbruch; Hinweis bei mehr als z. B. 400 Kacheln. +- **Neue Basemap-Stilversion** (heute v2) → Katalog veraltet; Skript erneut laufen lassen, unbekannte IDs fallen + nach „Sonstiges". +- **Kachelränder:** Features sind an Kachelgrenzen beschnitten und können mehrfach vorkommen – für die Frage + „kommt vor" unerheblich. +- **Umfang:** Stilvorlagen sind fachlich eigenständig. Werden sie zu groß für einen PR, als eigenes Ticket + abtrennen (Schritte 1–6 bleiben #88). + +## 10. Umsetzung und Abweichungen (02.10.2026) + +Umgesetzt in `Plugin_SN_Basis`, Branch `feature/88-basemap-legende` (drei Commits). Spike-Ergebnisse und +Begründung der Legendenvariante: ADR 0010. + +| Thema | Umsetzung | Abweichung vom Konzept | +|---|---|---| +| Legendendarstellung | Variante (b): eigenes Element „Legende Basemap“, Titel „Kartengrundlage (basemap.de)“ | Spike entschieden | +| Platzierung | unten an der Quellenangabe verankert, wächst nach oben, 1–3 Spalten (nie über den Seitenrand), sonst Hinweis | statt „unter der Hauptlegende“: deren Höhe steht erst nach dem Rendern fest | +| Mehrere Basemap-Layer | ausgewertet wird der erste sichtbare, Hinweis an den Nutzer | statt „je Layer eine Gruppe“ (vereinfacht) | +| Katalog-Generator | `sn_basis/modules/basemap_katalog.py` (nur Standardbibliothek, eigenständig aufrufbar) | statt Skript im Projektwissen – so mit der sn_basis-Suite getestet | +| Auswahl im Projekt | `basemap_legende_auswahl` (ab- und angewählte Einträge) | statt nur `…_ausgeblendet`, damit angewählte „Sonstiges“-Einträge gemerkt werden | +| Module | `basemap_katalog`, `basemap_kacheln`, `basemap_legende`, `basemap_legende_layout`, `basemap_stilvorlagen`, `ui/widgets/basemap_bereich` | Kachelauswertung ausgelagert (Dateigröße) | +| Einklappen | `QToolButton`-Muster wie Atlas-Bereich | `QgsCollapsibleGroupBox` nicht nötig | +| Stilvorlagen | QML (rund 3,7 MB je Vorlage) + JSON mit Legendenauswahl, im QGIS-Profil | wie geplant | + +**Katalog:** 434 darstellende Styles in 190 Einträgen; Beschriftungen (`Name_*`, Labeling), Nummernschilder +(`Nummer_*`), Hintergrund, Mittellinien und kleinmaßstäbige Ortspunkte ausgeschlossen. Erster fachlicher +Durchgang der Bezeichnungen ist gemacht; **die fachliche Abnahme steht aus.** + +**Bekannte Punkte für die Durchsicht:** +- „Platz“ (Verkehr, Siedlung) hat im Basemap-Stil eine weiße Füllung ohne Rand und ist in der Legende kaum zu sehen. +- „Durchlass, Düker“ und „Baumreihe“ zeigen in der Legende kein sichtbares Symbol (Ursache noch nicht geklärt). +- Gewässerbreiten sind zu „Fließgewässer (Linie)“ zusammengefasst; Symbol ist die 6-m-Linie. + +**Nicht geprüft:** QGIS 4 (Qt6) – lokal nicht verfügbar. QGIS 3.44.4 (Qt5) offscreen geprüft: Auswertung, +Dialog (echte Widgets), Legende im §-41-Layout, Stilvorlage speichern/laden am echten Layer, Basemap-Bereich. diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md index cc08474..c566d27 100644 --- a/docs/status/STATUS.md +++ b/docs/status/STATUS.md @@ -1,7 +1,7 @@ # Status des Projekts Kurz und aktuell. Wird bei jedem Merge und jeder Übergabe gepflegt (Skill `sn-uebergabe`). Älteres wandert nach unten -in „Erledigt" oder wird gelöscht. **Stand: 04.09.2026.** +in „Erledigt" oder wird gelöscht. **Stand: 02.10.2026.** ## Aktuelle Phase @@ -13,7 +13,7 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek | Person / Behörde | Schwerpunkt | Stand | |---|---|---| -| Daniel Helbig (LK Meißen) | Fachfunktionen Verfahrensgebiet, Plan41, VLN-API | aktiv | +| Daniel Helbig (LK Meißen) | Fachfunktionen Verfahrensgebiet, Plan41, VLN-API; Basemap-Legende im Druck (Basis #88, PR vorbereitet) | aktiv | | Michael Otto (LK Mittelsachsen) | Release-Infrastruktur, Linkliste, zuletzt Verfahrensgebiet (Shape-Laden, Beteiligung) | aktiv | | 22munzke | Druck/Layout-Anforderungen (Issues Basis #37–#44) | Issues | | weitere Behörden | Einstieg über dieses Repository | ab 09/2026 | @@ -63,6 +63,10 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek (fest verdrahteter NAS-Namespace `gid/6.0` in `grenzpunkt_alkis.py`, abgeflachte WFS-Feldnamen in `sql=`-Filtern). Vergleich 6↔7 und Anpassungsliste: `docs/konzept/09_alkis7_umstellung.md`. Nächster Schritt: Gitea-Issue in `Plugin_SN_Verfahrensgebiet`. +15. **Basemap-Legende im Druck (Basis #88).** Umgesetzt auf `Plugin_SN_Basis:feature/88-basemap-legende` + (Konzept `docs/konzept/10_basemap_legende.md`, ADR 0010), PR steht aus. Offen: **fachliche Abnahme des + Legendenkatalogs** `sn_basis/data/basemap_legende.json` (Bezeichnungen, Gruppen, Zusammenfassungen; drei + Einträge ohne gut sichtbares Symbol, siehe Konzept Abschnitt 10) und Test unter QGIS 4. **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45) und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`. diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index d09fbcb..8de74cd 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,20 +119,20 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
346Testfälle
-
301grün
-
37rot (Baseline)
+
648Testfälle
+
605grün
+
35rot (Baseline)
-
33%mit Docstring
-
31Testdateien
+
78%mit Docstring
+
55Testdateien
Verteilung je Paket
-
sn_basis14 Dateien · 94 Testfälle
-
sn_verfahrensgebiet12 Dateien · 208 Testfälle
+
sn_basis38 Dateien · 396 Testfälle
+
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,16 +147,245 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 14 Testdateien · 94 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 38 Testdateien · 396 Testfälle

- -

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 6
flowchart TD
+  

Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).

CloseEventTests 5
flowchart TD
+  root(["test_base_dockwidget.py"])
+  class root rootnode
+  root --> g1["CloseEventTests (5)"]
+  class g1 groupnode
+  g1 --> t1("ruft_cleanup_aller_tabs_auf")
+  g1 --> t2("ignoriert_tabs_ohne_cleanup")
+  g1 --> t3("fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter")
+  g1 --> t4("setzt_toolbar_action_zurueck")
+  g1 --> t5("ohne_action_wird_nichts_zurueckgesetzt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.grün
CloseEventTests
test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter
Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert.grün
CloseEventTests
test_setzt_toolbar_action_zurueck
Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt.grün
CloseEventTests
test_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 8
flowchart TD
+  root(["test_basemap_anbindung.py"])
+  class root rootnode
+  root --> g1["TestMehrfachauswahl (1)"]
+  class g1 groupnode
+  g1 --> t1("mock_modus_liefert_vorauswahl_in_listenreihenfolge")
+  root --> g2["TestDruckanbindung (4)"]
+  class g2 groupnode
+  g2 --> t2("basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter")
+  g2 --> t3("layout_wird_markiert_und_legende_erzeugt")
+  g2 --> t4("fehler_der_basemap_legende_verhindert_die_vorlage_nicht")
+  g2 --> t5("druck_tab_aktualisiert_basemap_bereich_falls_vorhanden")
+  root --> g3["TestBasemapBereich (8)"]
+  class g3 groupnode
+  g3 --> t6("auswertebereich_wird_gespeichert")
+  g3 --> t7("aktualisieren_ohne_vorlage_warnt")
+  g3 --> t8("aktualisieren_wertet_gewaehlte_vorlage_aus")
+  g3 --> t9("stil_laden_uebernimmt_legendenauswahl")
+  g3 --> t10("stil_laden_bricht_bei_fremder_vorlage_ab")
+  g3 --> t11("vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt")
+  g3 --> t12("speichern_mit_aktueller_legendenauswahl")
+  g3 --> t13("stil_speichern_ohne_basemap_layer_warnt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestMehrfachauswahl
test_mock_modus_liefert_vorauswahl_in_listenreihenfolge
Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert.grün
TestDruckanbindung
test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter
Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist.grün
TestDruckanbindung
test_layout_wird_markiert_und_legende_erzeugt
Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt.grün
TestDruckanbindung
test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht
Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht.grün
TestDruckanbindung
test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden
Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut.grün
TestBasemapBereich
test_auswertebereich_wird_gespeichert
Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet.grün
TestBasemapBereich
test_aktualisieren_ohne_vorlage_warnt
Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet.grün
TestBasemapBereich
test_aktualisieren_wertet_gewaehlte_vorlage_aus
Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet.grün
TestBasemapBereich
test_stil_laden_uebernimmt_legendenauswahl
Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt.grün
TestBasemapBereich
test_stil_laden_bricht_bei_fremder_vorlage_ab
Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird.grün
TestBasemapBereich
test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt
Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird.grün
TestBasemapBereich
test_speichern_mit_aktueller_legendenauswahl
Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird.grün
TestBasemapBereich
test_stil_speichern_ohne_basemap_layer_warnt
Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird.grün

Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).

TestSammleObjekte 4TestZoomUndStile 2
flowchart TD
+  root(["test_basemap_kacheln.py"])
+  class root rootnode
+  root --> g1["TestSammleObjekte (4)"]
+  class g1 groupnode
+  g1 --> t1("gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt")
+  g1 --> t2("kacheln_ausserhalb_der_geometrie_werden_uebersprungen")
+  g1 --> t3("abbruch_liefert_none_und_hebt_auswahl_auf")
+  g1 --> t4("anzahl_kacheln_aus_kachelbereich")
+  root --> g2["TestZoomUndStile (2)"]
+  class g2 groupnode
+  g2 --> t5("zoomstufen_wie_im_qgis_renderer")
+  g2 --> t6("stile_aus_layer_liest_renderer_styles")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSammleObjekte
test_gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt
Prüft, dass Objekte mit gleichem Quell-Layer, Geometrietyp und Attributen über Kachelgrenzen hinweg nur einmal gezählt werden.grün
TestSammleObjekte
test_kacheln_ausserhalb_der_geometrie_werden_uebersprungen
Prüft, dass Kacheln ohne Schnitt mit dem Auswertebereich nicht abgefragt werden.grün
TestSammleObjekte
test_abbruch_liefert_none_und_hebt_auswahl_auf
Prüft, dass ein Abbruch über den Fortschritt None liefert und die Auswahl auf dem Basemap-Layer trotzdem aufgehoben wird.grün
TestSammleObjekte
test_anzahl_kacheln_aus_kachelbereich
Prüft, dass die Kachelzahl aus dem Kachelbereich der Geometrie berechnet wird.grün
TestZoomUndStile
test_zoomstufen_wie_im_qgis_renderer
Prüft, dass Kachelzoom begrenzt, Darstellungszoom unbegrenzt und der gebrochene Zoom aus dem Maßstab bestimmt werden.grün
TestZoomUndStile
test_stile_aus_layer_liest_renderer_styles
Prüft, dass Name, Quell-Layer, Geometrietyp, Zoombereich, Schalter und Filter der Renderer-Styles übernommen werden.grün

Tests für modules/basemap_katalog.py – Legendenkatalog aus der basemap.de-Style-JSON (Basis #88).

TestRendererStil 3TestErzeugeKatalog 6TestSymbolwahlUndPruefung 3
flowchart TD
+  root(["test_basemap_katalog.py"])
+  class root rootnode
+  root --> g1["TestRendererStil (3)"]
+  class g1 groupnode
+  g1 --> t1("beschriftung_und_3d_sind_keine_renderer_stile")
+  g1 --> t2("ausschluss_ueber_liste_und_praefix")
+  g1 --> t3("normalisierung_entfernt_variantenkennungen")
+  root --> g2["TestErzeugeKatalog (6)"]
+  class g2 groupnode
+  g2 --> t4("kontur_decker_und_bruecke_bilden_einen_eintrag")
+  g2 --> t5("zustand_im_bau_wird_eigener_eintrag")
+  g2 --> t6("punkt_und_flaeche_derselben_objektart_werden_zusammengefasst")
+  g2 --> t7("ausgeschlossene_stile_fehlen_im_katalog")
+  g2 --> t8("fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten")
+  g2 --> t9("entfallene_style_ids_werden_gemeldet")
+  root --> g3["TestSymbolwahlUndPruefung (3)"]
+  class g3 groupnode
+  g3 --> t10("symbol_stile_je_rolle_nur_grundvariante")
+  g3 --> t11("pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole")
+  g3 --> t12("ausgelieferter_katalog_ist_konsistent")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestRendererStil
test_beschriftung_und_3d_sind_keine_renderer_stile
Prüft, dass reine Beschriftungen und 3D-Gebäude nicht als Renderer-Style gelten, Icon-Symbole und Flächen schon.grün
TestRendererStil
test_ausschluss_ueber_liste_und_praefix
Prüft, dass Style-IDs über die Ausschlussliste und über Präfixe (Nummernschilder) ausgeschlossen werden.grün
TestRendererStil
test_normalisierung_entfernt_variantenkennungen
Prüft, dass Variantenkennungen wie Kontur, Brücke oder Breitenangaben beim Normalisieren einer Style-ID entfallen.grün
TestErzeugeKatalog
test_kontur_decker_und_bruecke_bilden_einen_eintrag
Prüft, dass Kontur-, Decker-, Fahrbahn- und Brückenvarianten derselben Straßenklasse genau einen Legendeneintrag bilden.grün
TestErzeugeKatalog
test_zustand_im_bau_wird_eigener_eintrag
Prüft, dass „im Bau“ einen eigenen Legendeneintrag mit Zustandsangabe in der Bezeichnung bekommt.grün
TestErzeugeKatalog
test_punkt_und_flaeche_derselben_objektart_werden_zusammengefasst
Prüft, dass das Kran-Symbol aus Bauwerkspunkt und Bauwerksfläche in einem Eintrag landet.grün
TestErzeugeKatalog
test_ausgeschlossene_stile_fehlen_im_katalog
Prüft, dass Hintergrund, Beschriftungen, Nummernschilder und 3D-Gebäude keinem Eintrag zugeordnet werden.grün
TestErzeugeKatalog
test_fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten
Prüft, dass Bezeichnung, Gruppe, Zusammenführung und Symbolwahl eines bestehenden Katalogs bei einem erneuten Lauf erhalten bleiben.grün
TestErzeugeKatalog
test_entfallene_style_ids_werden_gemeldet
Prüft, dass eine Style-ID, die in der neuen Style-JSON fehlt, als entfallen gemeldet und aus dem Katalog entfernt wird.grün
TestSymbolwahlUndPruefung
test_symbol_stile_je_rolle_nur_grundvariante
Prüft, dass für das Legendensymbol je Rolle (Kontur, Decker) nur die erste Grundvariante gewählt wird.grün
TestSymbolwahlUndPruefung
test_pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole
Prüft, dass die Katalogprüfung doppelt zugeordnete Style-IDs und Symbol-Styles außerhalb des Katalogs meldet.grün
TestSymbolwahlUndPruefung
test_ausgelieferter_katalog_ist_konsistent
Prüft, dass der ausgelieferte Katalog data/basemap_legende.json ohne Fehler geprüft wird und Kreisstraße mit Kontur und Decker enthält.grün

Tests für modules/basemap_legende.py – Zuordnung, Auswahl und Ablauf der Basemap-Legende (Basis #88).

TestStileUndEintraege 4TestAuswahlUndEinstellungen 6TestLayerErkennung 2TestAblauf 6
flowchart TD
+  root(["test_basemap_legende.py"])
+  class root rootnode
+  root --> g1["TestStileUndEintraege (4)"]
+  class g1 groupnode
+  g1 --> t1("stil_aktiv_nur_eingeschaltet_und_im_zoombereich")
+  g1 --> t2("getroffene_stile_pruefen_layer_geometrie_zoom_und_filter")
+  g1 --> t3("eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe")
+  g1 --> t4("ausgeschlossene_und_unbekannte_styles")
+  root --> g2["TestAuswahlUndEinstellungen (6)"]
+  class g2 groupnode
+  g2 --> t5("vorauswahl_bekannte_an_unbekannte_aus")
+  g2 --> t6("auswahl_wird_gemerkt_und_vorbelegt")
+  g2 --> t7("merken_ergaenzt_die_bisherige_auswahl")
+  g2 --> t8("kaputte_projektvariable_ergibt_leere_auswahl")
+  g2 --> t9("auswertebereich_standard_und_umstellung")
+  g2 --> t10("legende_erzeugen_standard_an")
+  root --> g3["TestLayerErkennung (2)"]
+  class g3 groupnode
+  g3 --> t11("erkennung_ueber_quelle_und_namen")
+  g3 --> t12("sichtbare_layer_aus_thema_feste_liste_oder_layerbaum")
+  root --> g4["TestAblauf (6)"]
+  class g4 groupnode
+  g4 --> t13("ohne_basemap_layer_passiert_nichts")
+  g4 --> t14("gewaehlte_eintraege_werden_geschrieben_und_gemerkt")
+  g4 --> t15("abbruch_im_dialog_aendert_nichts")
+  g4 --> t16("rueckfall_auf_druckbereich_wird_gemeldet")
+  g4 --> t17("ohne_objekte_keine_legende")
+  g4 --> t18("viele_kacheln_werden_vorher_bestaetigt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStileUndEintraege
test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich
Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt).grün
TestStileUndEintraege
test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter
Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe.grün
TestStileUndEintraege
test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe
Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind.grün
TestStileUndEintraege
test_ausgeschlossene_und_unbekannte_styles
Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen.grün
TestAuswahlUndEinstellungen
test_vorauswahl_bekannte_an_unbekannte_aus
Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind.grün
TestAuswahlUndEinstellungen
test_auswahl_wird_gemerkt_und_vorbelegt
Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden.grün
TestAuswahlUndEinstellungen
test_merken_ergaenzt_die_bisherige_auswahl
Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält.grün
TestAuswahlUndEinstellungen
test_kaputte_projektvariable_ergibt_leere_auswahl
Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt.grün
TestAuswahlUndEinstellungen
test_auswertebereich_standard_und_umstellung
Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden.grün
TestAuswahlUndEinstellungen
test_legende_erzeugen_standard_an
Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann.grün
TestLayerErkennung
test_erkennung_ueber_quelle_und_namen
Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen.grün
TestLayerErkennung
test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum
Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen.grün
TestAblauf
test_ohne_basemap_layer_passiert_nichts
Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen.grün
TestAblauf
test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt
Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird.grün
TestAblauf
test_abbruch_im_dialog_aendert_nichts
Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert.grün
TestAblauf
test_rueckfall_auf_druckbereich_wird_gemeldet
Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird.grün
TestAblauf
test_ohne_objekte_keine_legende
Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen.grün
TestAblauf
test_viele_kacheln_werden_vorher_bestaetigt
Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht.grün

Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).

TestKennzeichenUndKarte 2TestSymbolwahl 2TestHilfslayer 3TestPlatzierung 3
flowchart TD
+  root(["test_basemap_legende_layout.py"])
+  class root rootnode
+  root --> g1["TestKennzeichenUndKarte (2)"]
+  class g1 groupnode
+  g1 --> t1("layout_kennzeichen_setzen_und_erkennen")
+  g1 --> t2("hauptkarte_ueber_id")
+  root --> g2["TestSymbolwahl (2)"]
+  class g2 groupnode
+  g2 --> t3("symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge")
+  g2 --> t4("ohne_aktive_symbol_stile_erster_getroffener_style")
+  root --> g3["TestHilfslayer (3)"]
+  class g3 groupnode
+  g3 --> t5("je_gruppe_und_geometrietyp_ein_leerer_hilfslayer")
+  g3 --> t6("alte_hilfslayer_werden_entfernt")
+  g3 --> t7("ohne_eintraege_wird_vorhandene_basemap_legende_entfernt")
+  root --> g4["TestPlatzierung (3)"]
+  class g4 groupnode
+  g4 --> t8("einspaltig_ueber_der_quellenangabe")
+  g4 --> t9("mehrspaltig_wenn_hoehe_nicht_reicht")
+  g4 --> t10("nie_breiter_als_der_seitenrand_mit_hinweis")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestKennzeichenUndKarte
test_layout_kennzeichen_setzen_und_erkennen
Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird.grün
TestKennzeichenUndKarte
test_hauptkarte_ueber_id
Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird.grün
TestSymbolwahl
test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge
Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden.grün
TestSymbolwahl
test_ohne_aktive_symbol_stile_erster_getroffener_style
Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert.grün
TestHilfslayer
test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer
Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen.grün
TestHilfslayer
test_alte_hilfslayer_werden_entfernt
Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden.grün
TestHilfslayer
test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt
Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird.grün
TestPlatzierung
test_einspaltig_ueber_der_quellenangabe
Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht.grün
TestPlatzierung
test_mehrspaltig_wenn_hoehe_nicht_reicht
Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt.grün
TestPlatzierung
test_nie_breiter_als_der_seitenrand_mit_hinweis
Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.grün

Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).

TestStilvorlagen 8
flowchart TD
+  root(["test_basemap_stilvorlagen.py"])
+  class root rootnode
+  root --> g1["TestStilvorlagen (8)"]
+  class g1 groupnode
+  g1 --> t1("speichern_und_auflisten")
+  g1 --> t2("mehrere_varianten_alphabetisch")
+  g1 --> t3("leerer_name_oder_fehler_beim_schreiben")
+  g1 --> t4("laden_setzt_stil_und_zeichnet_neu")
+  g1 --> t5("passung_meldet_fremde_styles")
+  g1 --> t6("standard_setzen_und_beim_loeschen_entfernen")
+  g1 --> t7("fehlende_begleitdatei_wird_toleriert")
+  g1 --> t8("ohne_ordner_leere_liste")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilvorlagen
test_speichern_und_auflisten
Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird.grün
TestStilvorlagen
test_mehrere_varianten_alphabetisch
Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden.grün
TestStilvorlagen
test_leerer_name_oder_fehler_beim_schreiben
Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden.grün
TestStilvorlagen
test_laden_setzt_stil_und_zeichnet_neu
Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt.grün
TestStilvorlagen
test_passung_meldet_fremde_styles
Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt.grün
TestStilvorlagen
test_standard_setzen_und_beim_loeschen_entfernen
Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt.grün
TestStilvorlagen
test_fehlende_begleitdatei_wird_toleriert
Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird.grün
TestStilvorlagen
test_ohne_ordner_leere_liste
Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt.grün

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 8
flowchart TD
   root(["test_datagrabber.py"])
   class root rootnode
-  root --> g1["TestDataGrabber (6)"]
+  root --> g1["TestDataGrabber (8)"]
   class g1 groupnode
   g1 --> t1("analyze_source_type_detects_excel")
   g1 --> t2("analyze_source_type_returns_unknown_filetype")
@@ -164,20 +393,24 @@
   g1 --> t4("analyze_source_type_returns_unbekannte_quelle_on_full_failure")
   g1 --> t5("run_returns_success_summary_with_valid_rows")
   g1 --> t6("run_returns_failure_summary_without_rows")
+  g1 --> t7("aggregate_results_mit_zeilen_liefert_erfolgsergebnis")
+  g1 --> t8("aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
   class t6 ok
+  class t7 ok
+  class t8 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDataGrabber
test_analyze_source_type_detects_excel
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_returns_unknown_filetype
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_detects_service_when_file_check_fails
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_returns_unbekannte_quelle_on_full_failure
— (Docstring fehlt)grün
TestDataGrabber
test_run_returns_success_summary_with_valid_rows
— (Docstring fehlt)grün
TestDataGrabber
test_run_returns_failure_summary_without_rows
— (Docstring fehlt)grün

— (Docstring fehlt)

TestDateipruefer 6
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDataGrabber
test_analyze_source_type_detects_excel
Prüft, dass eine erreichbare .xlsx-Datei als Quelltyp 'excel' erkannt wird.grün
TestDataGrabber
test_analyze_source_type_returns_unknown_filetype
Prüft, dass eine erreichbare Datei mit unbekannter Endung als 'unbekannter_dateityp' eingestuft wird.grün
TestDataGrabber
test_analyze_source_type_detects_service_when_file_check_fails
Prüft, dass eine Quelle als 'dienst' erkannt wird, wenn die Dateiprüfung fehlschlägt, die URL aber erreichbar ist.grün
TestDataGrabber
test_analyze_source_type_returns_unbekannte_quelle_on_full_failure
Prüft, dass eine Quelle als 'unbekannte_quelle' eingestuft wird, wenn weder Datei- noch Linkprüfung erfolgreich sind.grün
TestDataGrabber
test_run_returns_success_summary_with_valid_rows
Prüft, dass run() bei erfolgreichem Excel-Import eine Erfolgsmeldung mit den geladenen Zeilen liefert.grün
TestDataGrabber
test_run_returns_failure_summary_without_rows
Prüft, dass run() bei nicht erkennbarer Quelle eine Fehlermeldung ohne Zeilen liefert.grün
TestDataGrabber
test_aggregate_results_mit_zeilen_liefert_erfolgsergebnis
Prüft, dass _aggregate_results bei vorhandenen Zeilen ein Erfolgsergebnis mit Zeilenzahl liefert.grün
TestDataGrabber
test_aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege
Prüft, dass _aggregate_results ohne Zeilen ok=False mit Aktion 'keine_validen_eintraege' liefert.grün

— (Docstring fehlt)

TestDateipruefer 17
flowchart TD
   root(["test_dateipruefer.py"])
   class root rootnode
-  root --> g1["TestDateipruefer (6)"]
+  root --> g1["TestDateipruefer (17)"]
   class g1 groupnode
   g1 --> t1("leereingabe_erlaubt")
   g1 --> t2("leereingabe_nicht_erlaubt")
@@ -185,6 +418,167 @@
   g1 --> t4("temporaer_erlaubt")
   g1 --> t5("datei_nicht_gefunden")
   g1 --> t6("datei_ok")
+  g1 --> t7("erkenne_dateityp_unterscheidet_excel_datenbank_und_unbekannt")
+  g1 --> t8("pfad_verbindet_relativen_pfad_mit_basispfad")
+  g1 --> t9("pfad_laesst_absoluten_pfad_unveraendert")
+  g1 --> t10("ist_gueltiger_gpkg_pfad_akzeptiert_vorhandenes_verzeichnis")
+  g1 --> t11("ist_gueltiger_gpkg_pfad_lehnt_falsche_endung_ab")
+  g1 --> t12("ist_gueltiger_gpkg_pfad_lehnt_fehlendes_verzeichnis_ab")
+  g1 --> t13("ist_leer_erkennt_none_und_nicht_string_als_leer")
+  g1 --> t14("ist_leer_erkennt_reinen_whitespace_als_leer")
+  g1 --> t15("ist_leer_meldet_pfad_mit_sichtbaren_zeichen_nicht_als_leer")
+  g1 --> t16("leere_eingabe_bevorzugt_leereingabe_erlaubt_vor_standarddatei")
+  g1 --> t17("leere_eingabe_bevorzugt_standarddatei_vor_temporaer_erlaubt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDateipruefer
test_leereingabe_erlaubt
Prüft, dass eine leere Pfadangabe bei leereingabe_erlaubt=True als 'leereingabe_erlaubt' ohne Kontext gemeldet wird.grün
TestDateipruefer
test_leereingabe_nicht_erlaubt
Prüft, dass eine leere Pfadangabe ohne leereingabe_erlaubt als 'leereingabe_nicht_erlaubt' abgelehnt wird.grün
TestDateipruefer
test_standarddatei_vorschlagen
Prüft, dass bei leerer Pfadangabe mit gesetzter Standarddatei 'standarddatei_vorschlagen' mit dem Standardpfad als Kontext geliefert wird.grün
TestDateipruefer
test_temporaer_erlaubt
Prüft, dass eine leere Pfadangabe bei temporaer_erlaubt=True als 'temporaer_erlaubt' ohne Kontext gemeldet wird.grün
TestDateipruefer
test_datei_nicht_gefunden
Prüft, dass ein nicht existierender Dateipfad als 'datei_nicht_gefunden' mit dem Pfad als Kontext abgelehnt wird.grün
TestDateipruefer
test_datei_ok
Prüft, dass ein existierender Dateipfad als 'ok' mit dem Pfad als Kontext bestätigt wird.grün
TestDateipruefer
test_erkenne_dateityp_unterscheidet_excel_datenbank_und_unbekannt
Prüft, dass erkenne_dateityp .xlsx als 'excel', .gpkg/.sqlite als 'datenbank' und Rest als 'unbekannt' erkennt.grün
TestDateipruefer
test_pfad_verbindet_relativen_pfad_mit_basispfad
Prüft, dass _pfad einen relativen Pfad mit dem Basisverzeichnis verbindet.grün
TestDateipruefer
test_pfad_laesst_absoluten_pfad_unveraendert
Prüft, dass _pfad einen bereits absoluten Pfad nicht mit dem Basisverzeichnis verbindet.grün
TestDateipruefer
test_ist_gueltiger_gpkg_pfad_akzeptiert_vorhandenes_verzeichnis
Prüft, dass ein .gpkg-Pfad mit vorhandenem Zielverzeichnis als gültig gilt.grün
TestDateipruefer
test_ist_gueltiger_gpkg_pfad_lehnt_falsche_endung_ab
Prüft, dass ein Pfad ohne .gpkg-Endung als ungültig gilt, auch bei vorhandenem Verzeichnis.grün
TestDateipruefer
test_ist_gueltiger_gpkg_pfad_lehnt_fehlendes_verzeichnis_ab
Prüft, dass ein .gpkg-Pfad in einem nicht vorhandenen Verzeichnis als ungültig gilt.grün
TestDateipruefer
test_ist_leer_erkennt_none_und_nicht_string_als_leer
Prüft, dass _ist_leer sowohl None als auch einen Nicht-String-Wert als leer meldet.grün
TestDateipruefer
test_ist_leer_erkennt_reinen_whitespace_als_leer
Prüft, dass _ist_leer eine Eingabe aus reinen Leerzeichen als leer meldet.grün
TestDateipruefer
test_ist_leer_meldet_pfad_mit_sichtbaren_zeichen_nicht_als_leer
Prüft, dass _ist_leer einen Pfad mit sichtbaren Zeichen nicht als leer meldet.grün
TestDateipruefer
test_leere_eingabe_bevorzugt_leereingabe_erlaubt_vor_standarddatei
Prüft, dass bei leerer Eingabe leereingabe_erlaubt Vorrang vor einer gesetzten Standarddatei hat.grün
TestDateipruefer
test_leere_eingabe_bevorzugt_standarddatei_vor_temporaer_erlaubt
Prüft, dass bei leerer Eingabe eine gesetzte Standarddatei Vorrang vor temporaer_erlaubt hat.grün

Tests für sn_basis/modules/Datenabruf.py (Datenabruf-Kernpipeline, Issue #63).

DetermineSpatialFilterTests 7BuildProviderUrlTests 9FetchFeaturesTests 5DatenabrufOrchestrierungTests 3
flowchart TD
+  root(["test_datenabruf.py"])
+  class root rootnode
+  root --> g1["DetermineSpatialFilterTests (7)"]
+  class g1 groupnode
+  g1 --> t1("raumfilter_ohne_liefert_none")
+  g1 --> t2("fehlender_layer_liefert_none")
+  g1 --> t3("unbekannter_raumfilter_liefert_none")
+  g1 --> t4("verfahrensgebiet_liefert_extent_und_crs")
+  g1 --> t5("verfahrensgebiet_ohne_extent_liefert_none")
+  g1 --> t6("pufferlayer_liefert_extent_des_pufferlayers")
+  g1 --> t7("pufferlayer_ohne_puffer_liefert_none")
+  root --> g2["BuildProviderUrlTests (9)"]
+  class g2 groupnode
+  g2 --> t8("wms_liefert_link_unveraendert")
+  g2 --> t9("url_praefix_wird_entfernt")
+  g2 --> t10("wfs_baut_standardparameter_und_bbox_mit_crs")
+  g2 --> t11("wfs_repariert_doppeltes_fragezeichen")
+  g2 --> t12("wfs_ohne_raumfilter_setzt_keine_bbox")
+  g2 --> t13("rest_provider_haengt_query_endpunkt_an")
+  g2 --> t14("rest_provider_mit_bbox_baut_geometry_envelope_und_srid")
+  g2 --> t15("default_provider_haengt_generischen_bbox_parameter_an")
+  g2 --> t16("default_provider_ohne_raumfilter_laesst_query_unveraendert")
+  root --> g3["FetchFeaturesTests (5)"]
+  class g3 groupnode
+  g3 --> t17("wms_liefert_keine_features_ohne_fehler")
+  g3 --> t18("abbruch_vor_abruf_liefert_fehlermeldung")
+  g3 --> t19("json_featurecollection_wird_geparst")
+  g3 --> t20("netzwerkfehler_wird_als_fehlermeldung_gemeldet")
+  g3 --> t21("wfs_xml_liefert_anzahl_aus_numberofmatched")
+  root --> g4["DatenabrufOrchestrierungTests (3)"]
+  class g4 groupnode
+  g4 --> t22("fehlende_pflichtfelder_werden_als_ungueltig_gemeldet")
+  g4 --> t23("wms_zeile_ohne_features_landet_in_ausserhalb")
+  g4 --> t24("abbruch_ueber_progress_stoppt_weitere_zeilen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
DetermineSpatialFilterTests
test_raumfilter_ohne_liefert_none
Prüft, dass Raumfilter 'ohne' unabhängig vom Layer immer None liefert.grün
DetermineSpatialFilterTests
test_fehlender_layer_liefert_none
Prüft, dass ohne Verfahrensgebiet-Layer kein Raumfilter gebaut wird.grün
DetermineSpatialFilterTests
test_unbekannter_raumfilter_liefert_none
Prüft, dass ein unbekannter Raumfilter-Wert None liefert, statt einen Fehler zu werfen.grün
DetermineSpatialFilterTests
test_verfahrensgebiet_liefert_extent_und_crs
Prüft, dass Raumfilter 'Verfahrensgebiet' Extent und CRS des Verfahrensgebiet-Layers liefert.grün
DetermineSpatialFilterTests
test_verfahrensgebiet_ohne_extent_liefert_none
Prüft, dass ein Verfahrensgebiet-Layer ohne ermittelbaren Extent None liefert.grün
DetermineSpatialFilterTests
test_pufferlayer_liefert_extent_des_pufferlayers
Prüft, dass Raumfilter 'Pufferlayer' Extent und CRS des erzeugten Pufferlayers liefert.grün
DetermineSpatialFilterTests
test_pufferlayer_ohne_puffer_liefert_none
Prüft, dass 'Pufferlayer' None liefert, wenn kein Pufferlayer erzeugt werden konnte.grün
BuildProviderUrlTests
test_wms_liefert_link_unveraendert
Prüft, dass bei Provider WMS der Link unverändert durchgereicht wird, auch mit Raumfilter.grün
BuildProviderUrlTests
test_url_praefix_wird_entfernt
Prüft, dass ein vorangestelltes 'url=' aus dem Link entfernt wird.grün
BuildProviderUrlTests
test_wfs_baut_standardparameter_und_bbox_mit_crs
Prüft, dass WFS-Abrufe Standardparameter, umbenanntes TYPENAMES und BBOX mit CRS-URN erhalten.grün
BuildProviderUrlTests
test_wfs_repariert_doppeltes_fragezeichen
Prüft, dass ein zweites '?' im WFS-Link zu '&' korrigiert wird, statt eine ungültige URL zu erzeugen.grün
BuildProviderUrlTests
test_wfs_ohne_raumfilter_setzt_keine_bbox
Prüft, dass WFS ohne Raumfilter (bbox_geom=None) keinen BBOX-Parameter setzt.grün
BuildProviderUrlTests
test_rest_provider_haengt_query_endpunkt_an
Prüft, dass REST/ArcGIS-Links ohne '/query'-Endpunkt automatisch ergänzt werden.grün
BuildProviderUrlTests
test_rest_provider_mit_bbox_baut_geometry_envelope_und_srid
Prüft, dass REST-Abrufe mit Raumfilter eine Geometry-Envelope und inSR/outSR aus dem CRS setzen.grün
BuildProviderUrlTests
test_default_provider_haengt_generischen_bbox_parameter_an
Prüft, dass ein unbekannter Provider mit Raumfilter einen generischen bbox-Parameter erhält.grün
BuildProviderUrlTests
test_default_provider_ohne_raumfilter_laesst_query_unveraendert
Prüft, dass ein unbekannter Provider ohne Raumfilter keinen bbox-Parameter ergänzt.grün
FetchFeaturesTests
test_wms_liefert_keine_features_ohne_fehler
Prüft, dass bei Provider WMS kein Featureabruf stattfindet und kein Fehler gemeldet wird.grün
FetchFeaturesTests
test_abbruch_vor_abruf_liefert_fehlermeldung
Prüft, dass ein bereits gesetzter Abbruch vor dem eigentlichen Abruf erkannt wird.grün
FetchFeaturesTests
test_json_featurecollection_wird_geparst
Prüft, dass eine JSON-FeatureCollection-Antwort in eine Feature-Liste umgewandelt wird.grün
FetchFeaturesTests
test_netzwerkfehler_wird_als_fehlermeldung_gemeldet
Prüft, dass ein fehlgeschlagener HTTP-Abruf eine Fehlermeldung statt einer Ausnahme liefert.grün
FetchFeaturesTests
test_wfs_xml_liefert_anzahl_aus_numberofmatched
Prüft, dass eine WFS-XML-Antwort ohne JSON die Trefferzahl aus numberOfMatched übernimmt.grün
DatenabrufOrchestrierungTests
test_fehlende_pflichtfelder_werden_als_ungueltig_gemeldet
Prüft, dass eine Zeile ohne Link/Provider mit Aktion 'pflichtfelder_fehlen' gemeldet und übersprungen wird.grün
DatenabrufOrchestrierungTests
test_wms_zeile_ohne_features_landet_in_ausserhalb
Prüft, dass eine erfolgreich abgerufene WMS-Zeile mit 0 Features als 'ausserhalb' gezählt wird.grün
DatenabrufOrchestrierungTests
test_abbruch_ueber_progress_stoppt_weitere_zeilen
Prüft, dass ein Abbruch über das Progress-Objekt die Verarbeitung weiterer Zeilen stoppt.grün

Tests für sn_basis/modules/Datenschreiber.py (Schreib-/Ladepipeline, Issue #63).

WriteLayerToGpkgTests 2ResolveStylePathTests 3SchreibeDatenTests 4LadeLayerTests 3SchreibeLogTests 2StoreStyleInGpkgTests 4
flowchart TD
+  root(["test_datenschreiber.py"])
+  class root rootnode
+  root --> g1["WriteLayerToGpkgTests (2)"]
+  class g1 groupnode
+  g1 --> t1("ungueltiger_layer_liefert_fehlermeldung")
+  g1 --> t2("gueltiger_layer_wird_ohne_fehler_geschrieben")
+  root --> g2["ResolveStylePathTests (3)"]
+  class g2 groupnode
+  g2 --> t3("leerer_stilpfad_liefert_none")
+  g2 --> t4("absoluter_vorhandener_stilpfad_wird_aufgeloest")
+  g2 --> t5("nicht_vorhandener_stilpfad_liefert_none")
+  root --> g3["SchreibeDatenTests (4)"]
+  class g3 groupnode
+  g3 --> t6("ohne_speicherort_wirft_valueerror")
+  g3 --> t7("eintrag_ohne_gueltigen_layer_wird_uebersprungen")
+  g3 --> t8("erfolgreicher_eintrag_liefert_layer_path_und_thema")
+  g3 --> t9("ohne_thema_wird_ident_als_layername_verwendet")
+  root --> g4["LadeLayerTests (3)"]
+  class g4 groupnode
+  g4 --> t10("ohne_layer_path_wird_uebersprungen")
+  g4 --> t11("gueltiger_layer_wird_dem_projekt_hinzugefuegt")
+  g4 --> t12("ungueltiger_layer_meldet_layer_nicht_gefunden")
+  root --> g5["SchreibeLogTests (2)"]
+  class g5 groupnode
+  g5 --> t13("ohne_speicherort_wirft_valueerror")
+  g5 --> t14("schreibt_json_datei_mit_ergebnissen_und_gibt_pfad_zurueck")
+  root --> g6["StoreStyleInGpkgTests (4)"]
+  class g6 groupnode
+  g6 --> t15("legt_tabelle_an_und_speichert_stil_als_default")
+  g6 --> t16("setzt_bisherigen_default_stil_desselben_layers_zurueck")
+  g6 --> t17("liest_geometriespalte_ueber_geometrycolumn_aus")
+  g6 --> t18("fehlender_stilpfad_meldet_fehler_ueber_pruefmanager_statt_absturz")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
WriteLayerToGpkgTests
test_ungueltiger_layer_liefert_fehlermeldung
Prüft, dass ein ungültiger Layer eine Fehlermeldung statt eines Absturzes liefert.grün
WriteLayerToGpkgTests
test_gueltiger_layer_wird_ohne_fehler_geschrieben
Prüft, dass ein gültiger Layer im Mock-Modus ohne Fehlermeldung geschrieben wird.grün
ResolveStylePathTests
test_leerer_stilpfad_liefert_none
Prüft, dass ein leerer oder fehlender Stilpfad None liefert.grün
ResolveStylePathTests
test_absoluter_vorhandener_stilpfad_wird_aufgeloest
Prüft, dass ein absoluter, vorhandener Stilpfad normalisiert und unverändert erkannt wird.grün
ResolveStylePathTests
test_nicht_vorhandener_stilpfad_liefert_none
Prüft, dass ein nicht vorhandener Stilpfad None liefert, statt einen ungültigen Pfad zurückzugeben.grün
SchreibeDatenTests
test_ohne_speicherort_wirft_valueerror
Prüft, dass schreibe_Daten ohne Speicherort einen ValueError wirft.grün
SchreibeDatenTests
test_eintrag_ohne_gueltigen_layer_wird_uebersprungen
Prüft, dass ein Eintrag ohne gültigen Layer übersprungen wird und ein Fehlerergebnis meldet.grün
SchreibeDatenTests
test_erfolgreicher_eintrag_liefert_layer_path_und_thema
Prüft, dass ein gültiger Layer einen Layer-Path mit GPKG-Pfad und Layername liefert.grün
SchreibeDatenTests
test_ohne_thema_wird_ident_als_layername_verwendet
Prüft, dass ohne passendes Thema in processed_results der ident als Layername verwendet wird.grün
LadeLayerTests
test_ohne_layer_path_wird_uebersprungen
Prüft, dass ein Eintrag ohne layer_path übersprungen wird, ohne einen Fehler zu melden.grün
LadeLayerTests
test_gueltiger_layer_wird_dem_projekt_hinzugefuegt
Prüft, dass ein gültig ladbarer Layer ohne Fehlermeldung verarbeitet wird.grün
LadeLayerTests
test_ungueltiger_layer_meldet_layer_nicht_gefunden
Prüft, dass ein ungültig ladbarer Layer als 'layer_nicht_gefunden' gemeldet wird.grün
SchreibeLogTests
test_ohne_speicherort_wirft_valueerror
Prüft, dass schreibe_log ohne Speicherort einen ValueError wirft.grün
SchreibeLogTests
test_schreibt_json_datei_mit_ergebnissen_und_gibt_pfad_zurueck
Prüft, dass schreibe_log eine lesbare JSON-Log-Datei erzeugt und deren Pfad zurückgibt.grün
StoreStyleInGpkgTests
test_legt_tabelle_an_und_speichert_stil_als_default
Prüft, dass _store_style_in_gpkg die layer_styles-Tabelle anlegt und den Stil als Default speichert.grün
StoreStyleInGpkgTests
test_setzt_bisherigen_default_stil_desselben_layers_zurueck
Prüft, dass _store_style_in_gpkg den useAsDefault-Wert vorhandener Stile desselben Layers zurücksetzt.grün
StoreStyleInGpkgTests
test_liest_geometriespalte_ueber_geometrycolumn_aus
Prüft, dass _store_style_in_gpkg die Geometriespalte eines Layers mit geometryColumn() übernimmt.grün
StoreStyleInGpkgTests
test_fehlender_stilpfad_meldet_fehler_ueber_pruefmanager_statt_absturz
Prüft, dass ein nicht lesbarer Stilpfad über den Pruefmanager gemeldet wird statt eine Exception zu werfen.grün

Tests für sn_basis/ui/dockmanager.py (nur ein Dock gleichzeitig sichtbar, Issue #65).

DockManagerShowTests 6
flowchart TD
+  root(["test_dockmanager.py"])
+  class root rootnode
+  root --> g1["DockManagerShowTests (6)"]
+  class g1 groupnode
+  g1 --> t1("none_dock_meldet_fehler_und_zeigt_nichts_an")
+  g1 --> t2("setzt_objectname_wenn_leer")
+  g1 --> t3("entfernt_andere_sn_docks_aber_nicht_sich_selbst")
+  g1 --> t4("zeigt_neues_dock_mit_default_area_an")
+  g1 --> t5("fehler_beim_entfernen_wird_als_warnung_gemeldet")
+  g1 --> t6("fehler_beim_anzeigen_wird_gemeldet")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -195,7 +589,32 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDateipruefer
test_leereingabe_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_leereingabe_nicht_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_standarddatei_vorschlagen
— (Docstring fehlt)grün
TestDateipruefer
test_temporaer_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_datei_nicht_gefunden
— (Docstring fehlt)grün
TestDateipruefer
test_datei_ok
— (Docstring fehlt)grün

Unit-Tests für `sn_basis.functions.laenderkennung`.

TestGetLaenderkennung 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
DockManagerShowTests
test_none_dock_meldet_fehler_und_zeigt_nichts_an
Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt.grün
DockManagerShowTests
test_setzt_objectname_wenn_leer
Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält.grün
DockManagerShowTests
test_entfernt_andere_sn_docks_aber_nicht_sich_selbst
Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben.grün
DockManagerShowTests
test_zeigt_neues_dock_mit_default_area_an
Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_entfernen_wird_als_warnung_gemeldet
Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird.grün
DockManagerShowTests
test_fehler_beim_anzeigen_wird_gemeldet
Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird.grün

Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).

ImportXlsxTests 5ExcelImporterKonstruktorTests 2
flowchart TD
+  root(["test_excel_importer.py"])
+  class root rootnode
+  root --> g1["ImportXlsxTests (5)"]
+  class g1 groupnode
+  g1 --> t1("erfolgreicher_import_liefert_zeilen_als_dicts")
+  g1 --> t2("fehlende_datei_liefert_leere_liste")
+  g1 --> t3("arbeitsblatt_ohne_header_liefert_leere_liste_und_meldet_kein_header")
+  g1 --> t4("header_zeile_nur_leere_werte_liefert_kein_header")
+  g1 --> t5("nicht_lesbare_datei_liefert_leere_liste_und_meldet_read_error")
+  root --> g2["ExcelImporterKonstruktorTests (2)"]
+  class g2 groupnode
+  g2 --> t6("ohne_dateipfad_wirft_valueerror")
+  g2 --> t7("ohne_pruefmanager_wirft_valueerror")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
ImportXlsxTests
test_erfolgreicher_import_liefert_zeilen_als_dicts
Prüft, dass eine gültige Excel-Datei als Liste von Dicts mit den Header-Spalten geliefert wird.grün
ImportXlsxTests
test_fehlende_datei_liefert_leere_liste
Prüft, dass eine nicht vorhandene Datei eine leere Liste liefert, statt eine Ausnahme zu werfen.grün
ImportXlsxTests
test_arbeitsblatt_ohne_header_liefert_leere_liste_und_meldet_kein_header
Prüft, dass ein leeres Arbeitsblatt ohne Header-Zeile 'kein_header' meldet und eine leere Liste liefert.grün
ImportXlsxTests
test_header_zeile_nur_leere_werte_liefert_kein_header
Prüft, dass eine Header-Zeile aus ausschließlich leeren Zellen ebenfalls als 'kein_header' gilt.grün
ImportXlsxTests
test_nicht_lesbare_datei_liefert_leere_liste_und_meldet_read_error
Prüft, dass eine Datei mit ungültigem Excel-Inhalt 'read_error' meldet und eine leere Liste liefert.grün
ExcelImporterKonstruktorTests
test_ohne_dateipfad_wirft_valueerror
Prüft, dass ExcelImporter ohne Dateipfad einen ValueError wirft.grün
ExcelImporterKonstruktorTests
test_ohne_pruefmanager_wirft_valueerror
Prüft, dass ExcelImporter ohne Pruefmanager einen ValueError wirft.grün

Unit-Tests für `sn_basis.functions.laenderkennung`.

TestGetLaenderkennung 5
flowchart TD
   root(["test_laenderkennung.py"])
   class root rootnode
   root --> g1["TestGetLaenderkennung (5)"]
@@ -214,7 +633,89 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetLaenderkennung
test_bekannter_praefix_gibt_kennung_zurueck
— (Docstring fehlt)grün
TestGetLaenderkennung
test_praefix_gross_klein_unabhaengig
— (Docstring fehlt)grün
TestGetLaenderkennung
test_unbekannter_praefix_wirft_keyerror
— (Docstring fehlt)grün
TestGetLaenderkennung
test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet
— (Docstring fehlt)grün
TestGetLaenderkennung
test_mapping_enthaelt_sachsen
— (Docstring fehlt)grün

— (Docstring fehlt)

TestLayerpruefer 10
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetLaenderkennung
test_bekannter_praefix_gibt_kennung_zurueck
Prüft, dass der Präfix 'sn' die ALKIS-Länderkennung '14' für Sachsen liefert.grün
TestGetLaenderkennung
test_praefix_gross_klein_unabhaengig
Prüft, dass die Präfixsuche groß-/kleinschreibungsunabhängig ist.grün
TestGetLaenderkennung
test_unbekannter_praefix_wirft_keyerror
Prüft, dass ein unbekannter Präfix einen KeyError auslöst.grün
TestGetLaenderkennung
test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet
Prüft, dass ohne Argument der Präfix aus dem Modulnamen des Aufrufers abgeleitet wird.grün
TestGetLaenderkennung
test_mapping_enthaelt_sachsen
Prüft, dass die Mapping-Tabelle LAENDERKENNUNGEN einen Eintrag für Sachsen ('sn' -> '14') enthält.grün

Tests für sn_basis/functions/landkreise.py::_lade_landkreise (Issue #65).

LadeLandkreiseTests 2
flowchart TD
+  root(["test_landkreise.py"])
+  class root rootnode
+  root --> g1["LadeLandkreiseTests (2)"]
+  class g1 groupnode
+  g1 --> t1("laedt_die_echte_landkreisliste_nicht_leer")
+  g1 --> t2("fehlende_datei_liefert_leere_liste")
+  class t1 ok
+  class t2 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
LadeLandkreiseTests
test_laedt_die_echte_landkreisliste_nicht_leer
Prüft, dass _lade_landkreise die echte landkreise.json als nicht-leere Liste von (Kennzahl, Name)-Tupeln liefert.grün
LadeLandkreiseTests
test_fehlende_datei_liefert_leere_liste
Prüft, dass eine nicht lesbare Datenquelle eine leere Liste liefert, statt eine Ausnahme zu werfen.grün

Tests für sn_basis/modules/LayerLoader.py (Layer erzeugen/filtern/laden, Issue #63).

CreateLayerTests 7TransformGeometryTests 3TransformExtentTests 2FilterByExtentTests 6FilterByLayerTests 4AddToProjectTests 3
flowchart TD
+  root(["test_layerloader.py"])
+  class root rootnode
+  root --> g1["CreateLayerTests (7)"]
+  class g1 groupnode
+  g1 --> t1("wfs_baut_uri_ohne_getfeature_parameter")
+  g1 --> t2("wfs_mit_url_praefix_wird_nicht_doppelt_gesetzt")
+  g1 --> t3("wms_baut_url_praefix_und_nutzt_rasterlayer")
+  g1 --> t4("rest_featureserver_ergaenzt_layer_index")
+  g1 --> t5("ogr_provider_nutzt_link_direkt")
+  g1 --> t6("ungueltiger_layer_meldet_layer_nicht_verfuegbar")
+  g1 --> t7("ausnahme_beim_erstellen_wird_abgefangen")
+  root --> g2["TransformGeometryTests (3)"]
+  class g2 groupnode
+  g2 --> t8("fehlender_layer_liefert_geometrie_unveraendert")
+  g2 --> t9("gleiche_crs_liefert_geometrie_unveraendert")
+  g2 --> t10("unterschiedliche_crs_klont_und_transformiert_geometrie")
+  root --> g3["TransformExtentTests (2)"]
+  class g3 groupnode
+  g3 --> t11("gleiche_crs_liefert_extent_unveraendert")
+  g3 --> t12("unterschiedliche_crs_nutzt_coordinate_transform")
+  root --> g4["FilterByExtentTests (6)"]
+  class g4 groupnode
+  g4 --> t13("ungueltiger_layer_wird_unveraendert_zurueckgegeben")
+  g4 --> t14("ohne_extent_wird_layer_unveraendert_zurueckgegeben")
+  g4 --> t15("falscher_layertyp_wird_unveraendert_zurueckgegeben")
+  g4 --> t16("keine_treffer_liefert_none")
+  g4 --> t17("abbruch_liefert_none_und_meldet_needs_user_action")
+  g4 --> t18("erfolgreicher_filter_liefert_neuen_layer_mit_features")
+  root --> g5["FilterByLayerTests (4)"]
+  class g5 groupnode
+  g5 --> t19("ungueltiger_layer_oder_filterlayer_wird_unveraendert_zurueckgegeben")
+  g5 --> t20("filterlayer_ohne_features_liefert_none")
+  g5 --> t21("erfolgreicher_geometriefilter_liefert_neuen_layer")
+  g5 --> t22("keine_ueberschneidung_liefert_none")
+  root --> g6["AddToProjectTests (3)"]
+  class g6 groupnode
+  g6 --> t23("gueltiger_layer_wird_dem_projekt_hinzugefuegt")
+  g6 --> t24("ungueltiger_layer_wird_nicht_hinzugefuegt")
+  g6 --> t25("none_layer_wird_nicht_hinzugefuegt")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CreateLayerTests
test_wfs_baut_uri_ohne_getfeature_parameter
Prüft, dass für WFS die GetFeature-spezifischen Parameter aus der URI entfernt werden.grün
CreateLayerTests
test_wfs_mit_url_praefix_wird_nicht_doppelt_gesetzt
Prüft, dass ein bereits vorhandenes 'url='-Präfix im WFS-Link nicht verdoppelt wird.grün
CreateLayerTests
test_wms_baut_url_praefix_und_nutzt_rasterlayer
Prüft, dass WMS-Layer über QgsRasterLayer mit 'url='-Präfix erzeugt werden.grün
CreateLayerTests
test_rest_featureserver_ergaenzt_layer_index
Prüft, dass ein REST-Link auf '.../FeatureServer' automatisch um '/0' ergänzt wird.grün
CreateLayerTests
test_ogr_provider_nutzt_link_direkt
Prüft, dass ein OGR-Provider den Link unverändert an QgsVectorLayer übergibt.grün
CreateLayerTests
test_ungueltiger_layer_meldet_layer_nicht_verfuegbar
Prüft, dass ein ungültig erzeugter Layer als 'layer_nicht_verfuegbar' gemeldet wird und None liefert.grün
CreateLayerTests
test_ausnahme_beim_erstellen_wird_abgefangen
Prüft, dass eine Ausnahme beim Layer-Erstellen abgefangen und als Fehlerergebnis gemeldet wird.grün
TransformGeometryTests
test_fehlender_layer_liefert_geometrie_unveraendert
Prüft, dass ohne Quell- oder Ziel-Layer die Geometrie unverändert zurückgegeben wird.grün
TransformGeometryTests
test_gleiche_crs_liefert_geometrie_unveraendert
Prüft, dass bei identischem CRS von Quelle und Ziel keine Transformation stattfindet.grün
TransformGeometryTests
test_unterschiedliche_crs_klont_und_transformiert_geometrie
Prüft, dass bei unterschiedlichem CRS eine geklonte, transformierte Geometrie geliefert wird.grün
TransformExtentTests
test_gleiche_crs_liefert_extent_unveraendert
Prüft, dass bei identischem CRS von Quelle und Ziel der Extent unverändert bleibt.grün
TransformExtentTests
test_unterschiedliche_crs_nutzt_coordinate_transform
Prüft, dass bei unterschiedlichem CRS der Extent über QgsCoordinateTransform umgerechnet wird.grün
FilterByExtentTests
test_ungueltiger_layer_wird_unveraendert_zurueckgegeben
Prüft, dass ein ungültiger Layer von filter_by_extent unverändert zurückgegeben wird.grün
FilterByExtentTests
test_ohne_extent_wird_layer_unveraendert_zurueckgegeben
Prüft, dass ohne Extent (None) der Layer unverändert zurückgegeben wird.grün
FilterByExtentTests
test_falscher_layertyp_wird_unveraendert_zurueckgegeben
Prüft, dass ein Nicht-Vektor-Layer (z. B. Rasterlayer) unverändert zurückgegeben wird.grün
FilterByExtentTests
test_keine_treffer_liefert_none
Prüft, dass ein Raumfilter ohne Treffer None statt eines leeren Layers liefert.grün
FilterByExtentTests
test_abbruch_liefert_none_und_meldet_needs_user_action
Prüft, dass ein Abbruch während des Raumfilters None liefert und gemeldet wird.grün
FilterByExtentTests
test_erfolgreicher_filter_liefert_neuen_layer_mit_features
Prüft, dass ein erfolgreicher BBOX-Filter einen neuen Speicherlayer mit den gefundenen Features liefert.grün
FilterByLayerTests
test_ungueltiger_layer_oder_filterlayer_wird_unveraendert_zurueckgegeben
Prüft, dass ein ungültiger Haupt- oder Filter-Layer unverändert zurückgegeben wird.grün
FilterByLayerTests
test_filterlayer_ohne_features_liefert_none
Prüft, dass ein Filter-Layer ohne Features (leere Vereinigungsgeometrie) None liefert.grün
FilterByLayerTests
test_erfolgreicher_geometriefilter_liefert_neuen_layer
Prüft, dass ein erfolgreicher Geometriefilter einen neuen Speicherlayer mit den passenden Features liefert.grün
FilterByLayerTests
test_keine_ueberschneidung_liefert_none
Prüft, dass ohne überschneidende Features None statt eines leeren Layers geliefert wird.grün
AddToProjectTests
test_gueltiger_layer_wird_dem_projekt_hinzugefuegt
Prüft, dass ein gültiger Layer über QgsProject.instance().addMapLayer hinzugefügt wird.grün
AddToProjectTests
test_ungueltiger_layer_wird_nicht_hinzugefuegt
Prüft, dass ein ungültiger Layer nicht ans Projekt übergeben wird.grün
AddToProjectTests
test_none_layer_wird_nicht_hinzugefuegt
Prüft, dass ein fehlender Layer (None) nicht ans Projekt übergeben wird.grün

— (Docstring fehlt)

TestLayerpruefer 10
flowchart TD
   root(["test_layerpruefer.py"])
   class root rootnode
   root --> g1["TestLayerpruefer (10)"]
@@ -243,26 +744,147 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLayerpruefer
test_layer_exists
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_unsichtbar
— (Docstring fehlt)grün
TestLayerpruefer
test_falscher_layertyp
— (Docstring fehlt)grün
TestLayerpruefer
test_falscher_geotyp
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_leer
— (Docstring fehlt)grün
TestLayerpruefer
test_falsches_crs
— (Docstring fehlt)grün
TestLayerpruefer
test_felder_fehlen
— (Docstring fehlt)grün
TestLayerpruefer
test_datenquelle_unerwartet
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_nicht_editierbar
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_ok
— (Docstring fehlt)grün

— (Docstring fehlt)

TestLinkpruefer 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLayerpruefer
test_layer_exists
Prüft, dass ein nicht vorhandener Layer als 'layer_nicht_gefunden' abgelehnt wird.grün
TestLayerpruefer
test_layer_unsichtbar
Prüft, dass ein unsichtbarer Layer bei geforderter Sichtbarkeit als 'layer_unsichtbar' abgelehnt wird.grün
TestLayerpruefer
test_falscher_layertyp
Prüft, dass ein Layer mit abweichendem Layertyp (Raster statt Vektor) als 'falscher_layertyp' abgelehnt wird.grün
TestLayerpruefer
test_falscher_geotyp
Prüft, dass ein Layer mit abweichendem Geometrietyp (Punkt statt Polygon) als 'falscher_geotyp' abgelehnt wird.grün
TestLayerpruefer
test_layer_leer
Prüft, dass ein Layer ohne Features als 'layer_leer' abgelehnt wird.grün
TestLayerpruefer
test_falsches_crs
Prüft, dass ein Layer mit abweichendem Koordinatenbezugssystem als 'falsches_crs' abgelehnt wird.grün
TestLayerpruefer
test_felder_fehlen
Prüft, dass ein Layer mit fehlenden Pflichtfeldern als 'felder_fehlen' abgelehnt wird.grün
TestLayerpruefer
test_datenquelle_unerwartet
Prüft, dass eine Datenquelle außerhalb der erlaubten Liste als 'datenquelle_unerwartet' abgelehnt wird.grün
TestLayerpruefer
test_layer_nicht_editierbar
Prüft, dass ein nicht editierbarer Layer bei geforderter Editierbarkeit als 'layer_nicht_editierbar' abgelehnt wird.grün
TestLayerpruefer
test_layer_ok
Prüft, dass ein Layer, der alle Anforderungen erfüllt, als 'ok' bestätigt wird.grün

— (Docstring fehlt)

TestLinkpruefer 6
flowchart TD
   root(["test_linkpruefer.py"])
   class root rootnode
-  root --> g1["TestLinkpruefer (5)"]
+  root --> g1["TestLinkpruefer (6)"]
   class g1 groupnode
-  g1 --> t1("remote_link_ok")
-  g1 --> t2("remote_link_error")
-  g1 --> t3("remote_link_network_error")
-  g1 --> t4("local_link_not_found")
-  g1 --> t5("local_link_ok")
+  g1 --> t1("leere_eingabe")
+  g1 --> t2("remote_link_ok")
+  g1 --> t3("remote_link_error")
+  g1 --> t4("remote_link_network_error")
+  g1 --> t5("local_link_not_found")
+  g1 --> t6("local_link_ok")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLinkpruefer
test_remote_link_ok
— (Docstring fehlt)grün
TestLinkpruefer
test_remote_link_error
— (Docstring fehlt)grün
TestLinkpruefer
test_remote_link_network_error
— (Docstring fehlt)grün
TestLinkpruefer
test_local_link_not_found
— (Docstring fehlt)grün
TestLinkpruefer
test_local_link_ok
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLinkpruefer
test_leere_eingabe
Prüft, dass eine leere Eingabe ohne Netzwerk-/Dateizugriff als 'leer' abgelehnt wird.grün
TestLinkpruefer
test_remote_link_ok
Prüft, dass eine erreichbare Remote-URL (HTTP HEAD ohne Fehler) als 'ok' bestätigt wird.grün
TestLinkpruefer
test_remote_link_error
Prüft, dass eine Remote-URL mit Fehlerantwort als 'url_nicht_erreichbar' abgelehnt wird.grün
TestLinkpruefer
test_remote_link_network_error
Prüft, dass eine ausbleibende Netzwerkantwort (None) als 'netzwerkfehler' gemeldet wird.grün
TestLinkpruefer
test_local_link_not_found
Prüft, dass ein nicht existierender lokaler Pfad als 'pfad_nicht_gefunden' abgelehnt wird.grün
TestLinkpruefer
test_local_link_ok
Prüft, dass ein existierender lokaler Pfad als 'ok' bestätigt wird.grün

Tests für sn_basis/functions/ly_existence_wrapper.py::layer_is_valid/layer_is_usable (Issue #65).

LayerIsValidTests 5LayerIsUsableTests 3
flowchart TD
+  root(["test_ly_existence_wrapper.py"])
+  class root rootnode
+  root --> g1["LayerIsValidTests (5)"]
+  class g1 groupnode
+  g1 --> t1("none_ist_nicht_gueltig")
+  g1 --> t2("objekt_ohne_isvalid_ist_nicht_gueltig")
+  g1 --> t3("gueltiger_layer_liefert_true")
+  g1 --> t4("ungueltiger_layer_liefert_false")
+  g1 --> t5("ausnahme_bei_isvalid_liefert_false")
+  root --> g2["LayerIsUsableTests (3)"]
+  class g2 groupnode
+  g2 --> t6("none_ist_nicht_nutzbar")
+  g2 --> t7("gueltiger_layer_ist_nutzbar")
+  g2 --> t8("ungueltiger_layer_ist_nicht_nutzbar")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
LayerIsValidTests
test_none_ist_nicht_gueltig
Prüft, dass layer_is_valid für None False liefert.grün
LayerIsValidTests
test_objekt_ohne_isvalid_ist_nicht_gueltig
Prüft, dass ein Objekt ohne isValid()-Methode als ungültig gilt.grün
LayerIsValidTests
test_gueltiger_layer_liefert_true
Prüft, dass ein Layer mit isValid()==True als gültig erkannt wird.grün
LayerIsValidTests
test_ungueltiger_layer_liefert_false
Prüft, dass ein Layer mit isValid()==False als ungültig erkannt wird.grün
LayerIsValidTests
test_ausnahme_bei_isvalid_liefert_false
Prüft, dass eine Ausnahme in isValid() abgefangen wird und False liefert.grün
LayerIsUsableTests
test_none_ist_nicht_nutzbar
Prüft, dass layer_is_usable für None False liefert.grün
LayerIsUsableTests
test_gueltiger_layer_ist_nutzbar
Prüft, dass ein vorhandener, gültiger Layer als nutzbar gilt.grün
LayerIsUsableTests
test_ungueltiger_layer_ist_nicht_nutzbar
Prüft, dass ein vorhandener, aber ungültiger Layer als nicht nutzbar gilt.grün

Tests für sn_basis/functions/ly_style_wrapper.py::apply_style_from_path (Issue #65).

ApplyStyleFromPathTests 4
flowchart TD
+  root(["test_ly_style_wrapper.py"])
+  class root rootnode
+  root --> g1["ApplyStyleFromPathTests (4)"]
+  class g1 groupnode
+  g1 --> t1("fehlender_layer_liefert_false")
+  g1 --> t2("erfolgreiches_laden_liefert_true_und_triggert_repaint")
+  g1 --> t3("fehlgeschlagenes_laden_liefert_false_ohne_repaint")
+  g1 --> t4("ausnahme_beim_laden_wird_abgefangen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
ApplyStyleFromPathTests
test_fehlender_layer_liefert_false
Prüft, dass ein fehlender Layer (None) False liefert, ohne einen Stil zu laden.grün
ApplyStyleFromPathTests
test_erfolgreiches_laden_liefert_true_und_triggert_repaint
Prüft, dass ein erfolgreich geladener Stil True liefert und den Layer neu zeichnet.grün
ApplyStyleFromPathTests
test_fehlgeschlagenes_laden_liefert_false_ohne_repaint
Prüft, dass ein fehlgeschlagenes Laden False liefert, ohne den Layer neu zu zeichnen.grün
ApplyStyleFromPathTests
test_ausnahme_beim_laden_wird_abgefangen
Prüft, dass eine Ausnahme beim Laden des Stils abgefangen wird und False liefert.grün

Tests für sn_basis/functions/message_wrapper.py (Issue #65).

GetMessageBarTests 2PushMessageTests 2
flowchart TD
+  root(["test_message_wrapper.py"])
+  class root rootnode
+  root --> g1["GetMessageBarTests (2)"]
+  class g1 groupnode
+  g1 --> t1("liefert_objekt_mit_pushmessage_methode")
+  g1 --> t2("faellt_bei_fehlerhaftem_iface_auf_mock_bar_zurueck")
+  root --> g2["PushMessageTests (2)"]
+  class g2 groupnode
+  g2 --> t3("liefert_dict_mit_titel_text_level_und_dauer")
+  g2 --> t4("fehler_in_der_message_bar_wird_abgefangen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
GetMessageBarTests
test_liefert_objekt_mit_pushmessage_methode
Prüft, dass _get_message_bar ein Objekt mit aufrufbarer pushMessage()-Methode liefert.grün
GetMessageBarTests
test_faellt_bei_fehlerhaftem_iface_auf_mock_bar_zurueck
Prüft, dass ein Fehler beim Zugriff auf iface.messageBar() auf eine funktionierende Mock-MessageBar zurückfällt.grün
PushMessageTests
test_liefert_dict_mit_titel_text_level_und_dauer
Prüft, dass push_message im Mock-Modus ein Dict mit den übergebenen Werten liefert.grün
PushMessageTests
test_fehler_in_der_message_bar_wird_abgefangen
Prüft, dass eine fehlschlagende MessageBar push_message nicht zum Absturz bringt, sondern None liefert.grün

Tests für sn_basis/ui/navigation.py::Navigation (Issue #67, ohne init_ui – s. Docstring dort).

AddActionTests 2FinalizeMenuAndToolbarTests 2SetActivePluginTests 2RemoveActionTests 3RemoveAllTests 3
flowchart TD
+  root(["test_navigation.py"])
+  class root rootnode
+  root --> g1["AddActionTests (2)"]
+  class g1 groupnode
+  g1 --> t1("ohne_plugin_group_liefert_none")
+  g1 --> t2("erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu")
+  root --> g2["FinalizeMenuAndToolbarTests (2)"]
+  class g2 groupnode
+  g2 --> t3("ohne_menu_oder_toolbar_tut_nichts")
+  g2 --> t4("sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu")
+  root --> g3["SetActivePluginTests (2)"]
+  class g3 groupnode
+  g3 --> t5("setzt_nur_aktive_action_auf_checked")
+  g3 --> t6("ohne_aktive_action_setzt_alle_auf_false")
+  root --> g4["RemoveActionTests (3)"]
+  class g4 groupnode
+  g4 --> t7("none_action_tut_nichts")
+  g4 --> t8("entfernt_action_aus_menu_toolbar_und_liste")
+  g4 --> t9("ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste")
+  root --> g5["RemoveAllTests (3)"]
+  class g5 groupnode
+  g5 --> t10("entfernt_menu_toolbar_und_setzt_zustand_zurueck")
+  g5 --> t11("loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt")
+  g5 --> t12("ohne_menu_und_toolbar_ruft_wrapper_nicht_auf")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
AddActionTests
test_ohne_plugin_group_liefert_none
Prüft, dass add_action ohne initialisierte Aktionsgruppe None liefert und nichts anlegt.grün
AddActionTests
test_erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu
Prüft, dass add_action eine Action erzeugt, ihr die Gruppe zuweist und sie priorisiert in der Liste ablegt.grün
FinalizeMenuAndToolbarTests
test_ohne_menu_oder_toolbar_tut_nichts
Prüft, dass finalize_menu_and_toolbar ohne Menü oder Toolbar keine Ausnahme wirft.grün
FinalizeMenuAndToolbarTests
test_sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu
Prüft, dass finalize_menu_and_toolbar Aktionen nach Priorität sortiert in Menü und Toolbar einträgt.grün
SetActivePluginTests
test_setzt_nur_aktive_action_auf_checked
Prüft, dass set_active_plugin nur die übergebene Action markiert, alle anderen abwählt.grün
SetActivePluginTests
test_ohne_aktive_action_setzt_alle_auf_false
Prüft, dass set_active_plugin ohne aktive Action alle bestehenden Actions abwählt.grün
RemoveActionTests
test_none_action_tut_nichts
Prüft, dass remove_action mit None keine Ausnahme wirft und die Liste unverändert lässt.grün
RemoveActionTests
test_entfernt_action_aus_menu_toolbar_und_liste
Prüft, dass remove_action die Action aus Menü, Toolbar und der internen Liste entfernt.grün
RemoveActionTests
test_ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste
Prüft, dass remove_action ohne Menü/Toolbar trotzdem aus der internen Liste entfernt.grün
RemoveAllTests
test_entfernt_menu_toolbar_und_setzt_zustand_zurueck
Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt.grün
RemoveAllTests
test_loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt
Prüft, dass remove_all deleteLater() auf Menü und Toolbar aufruft, damit sie den Qt-Objektbaum tatsächlich verlassen (Issue #72).grün
RemoveAllTests
test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf
Prüft, dass remove_all ohne vorhandenes Menü/Toolbar die Wrapper nicht aufruft, aber die Liste leert.grün

Tests für sn_basis/functions/os_wrapper.py::is_case_sensitive_fs (Issue #67).

IsCaseSensitiveFsTests 3
flowchart TD
+  root(["test_os_wrapper.py"])
+  class root rootnode
+  root --> g1["IsCaseSensitiveFsTests (3)"]
+  class g1 groupnode
+  g1 --> t1("windows_ist_nicht_case_sensitiv")
+  g1 --> t2("macos_ist_nicht_case_sensitiv")
+  g1 --> t3("linux_ist_case_sensitiv")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IsCaseSensitiveFsTests
test_windows_ist_nicht_case_sensitiv
Prüft, dass Windows als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_macos_ist_nicht_case_sensitiv
Prüft, dass macOS als nicht case-sensitives Dateisystem gilt.grün
IsCaseSensitiveFsTests
test_linux_ist_case_sensitiv
Prüft, dass Linux als case-sensitives Dateisystem gilt.grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
   root(["test_print_layout.py"])
   class root rootnode
   root --> g1["TestPrintLayout (5)"]
@@ -281,7 +903,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLayout
test_create_legend_item_links_map_and_enables_map_filtering
— (Docstring fehlt)grün
TestPrintLayout
test_create_legend_item_does_not_copy_project_layers_into_legend_model
— (Docstring fehlt)grün
TestPrintLayout
test_configure_legend_size_uses_content_adaptive_size
— (Docstring fehlt)grün
TestPrintLayout
test_configure_hauptkarte_grids_uses_wrapper_constants
— (Docstring fehlt)grün
TestPrintLayout
test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPrintLogic 17
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLayout
test_create_legend_item_links_map_and_enables_map_filtering
Prüft, dass eine erzeugte Legende mit der übergebenen Karte verknüpft ist und automatisch nach deren Sichtbarkeit gefiltert wird.grün
TestPrintLayout
test_create_legend_item_does_not_copy_project_layers_into_legend_model
Prüft, dass eine neu erzeugte Legende keine Projekt-Layer in ihr Legendenmodell übernimmt (leere Root-Gruppe).grün
TestPrintLayout
test_configure_legend_size_uses_content_adaptive_size
Prüft, dass die Legendengröße inhaltsadaptiv bestimmt wird (kein manuelles Resize) und die ermittelte Höhe zurückgegeben wird.grün
TestPrintLayout
test_configure_hauptkarte_grids_uses_wrapper_constants
Prüft, dass das Gitternetz der Hauptkarte mit den erwarteten Stil-, Rahmen- und Beschriftungskonstanten (nur rechts/unten beschriftet, Schriftgröße und Hintergrund) konfiguriert wird.grün
TestPrintLayout
test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme
Prüft, dass ein einseitiges Layout die Hauptkarte auf das übergebene Kartenthema festlegt und die Legende mit dieser Karte verknüpft bleibt.grün

— (Docstring fehlt)

TestPrintLogic 17
flowchart TD
   root(["test_print_logic.py"])
   class root rootnode
   root --> g1["TestPrintLogic (17)"]
@@ -324,10 +946,148 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLogic
test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_escalates_to_a3
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_uses_a4_as_minimum_sheet
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_limits_hauptkarte_to_a3
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_returns_none_if_a3_is_too_small
— (Docstring fehlt)grün
TestPrintLogic
test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent
— (Docstring fehlt)grün
TestPrintLogic
test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm
— (Docstring fehlt)grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_for_small_page
— (Docstring fehlt)grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_keeps_large_page
— (Docstring fehlt)grün
TestPrintLogic
test_does_plot_fit_page_true_for_standard_a4
— (Docstring fehlt)grün
TestPrintLogic
test_does_plot_fit_page_false_if_objects_exceed_page
— (Docstring fehlt)grün
TestPrintLogic
test_set_druckbereich_accepts_wie_kartenfenster
— (Docstring fehlt)grün
TestPrintLogic
test_get_canvas_extent_returns_valid_extent
— (Docstring fehlt)grün
TestPrintLogic
test_resolve_extent_uses_canvas_extent_without_buffer
— (Docstring fehlt)grün
TestPrintLogic
test_resolve_extent_uses_rectangle_extent_without_buffer
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPruefmanager 14
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLogic
test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent
Prüft, dass für einen kleinen Kartenausschnitt (Baufreigabe) das Format DIN A4 ohne Fallback gewählt wird.grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_escalates_to_a3
Prüft, dass ein größerer Kartenausschnitt (Baufreigabe) auf DIN A3 eskaliert wird.grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback
Prüft, dass bei auf DIN A4 begrenztem Höchstformat ein eigentlich A3-großer Ausschnitt mit Fallback-Kennzeichnung auf A4 reduziert wird.grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent
Prüft, dass ein Ausschnitt, der auch DIN A3 übersteigt, mit Fallback-Kennzeichnung auf DIN A3 begrenzt wird.grün
TestPrintLogic
test_select_standard_page_layout_uses_a4_as_minimum_sheet
Prüft, dass für einen kleinen Ausschnitt trotz Höchstgrenze DIN A3 mindestens DIN A4 als Blattgröße gewählt wird.grün
TestPrintLogic
test_select_standard_page_layout_limits_hauptkarte_to_a3
Prüft, dass ein die Fläche voll ausnutzender Ausschnitt auf DIN A3 als größtes zulässiges Format begrenzt wird.grün
TestPrintLogic
test_select_standard_page_layout_returns_none_if_a3_is_too_small
Prüft, dass kein Layout gewählt wird (None), wenn der Ausschnitt selbst DIN A3 als Höchstformat übersteigt.grün
TestPrintLogic
test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent
Prüft, dass für einen kleinen Ausschnitt DIN A4 gewählt und das Einpassen (force_fit) erzwungen wird.grün
TestPrintLogic
test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm
Prüft, dass bei aktiviertem Formfaktor (Endlosrolle) die Seitenbreite auf die maximale Rollenbreite (2000 mm) begrenzt wird.grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_for_small_page
Prüft, dass eine zu kleine Seite auf die A4-Mindestgröße angehoben und als erzwungen (forced) markiert wird.grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_keeps_large_page
Prüft, dass eine bereits ausreichend große Seite (A3) unverändert bleibt und nicht als erzwungen markiert wird.grün
TestPrintLogic
test_does_plot_fit_page_true_for_standard_a4
Prüft, dass ein Kartenobjekt, das in eine DIN A4-Seite passt, als passend erkannt wird.grün
TestPrintLogic
test_does_plot_fit_page_false_if_objects_exceed_page
Prüft, dass ein Kartenobjekt, das die Seitenbreite überschreitet, als nicht passend erkannt wird.grün
TestPrintLogic
test_set_druckbereich_accepts_wie_kartenfenster
Prüft, dass die Auswahl 'wie Kartenfenster' als Projektvariable 'druckbereich' gespeichert wird.grün
TestPrintLogic
test_get_canvas_extent_returns_valid_extent
Prüft, dass der aktuelle Kartenausschnitt des Kartenfensters unverändert zurückgegeben wird.grün
TestPrintLogic
test_resolve_extent_uses_canvas_extent_without_buffer
Prüft, dass bei Druckbereich 'wie Kartenfenster' der Kartenausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird.grün
TestPrintLogic
test_resolve_extent_uses_rectangle_extent_without_buffer
Prüft, dass bei Druckbereich 'Rechteck' der gezeichnete Ausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird.grün

Tests für sn_basis/ui/tabs/print_tab.py::PrintTab (Issue #67).

SetServicesTests 2RestoreStateTests 2OnDruckpufferClickedTests 3OnVorlageErstellenTests 2ProjectSignalsTests 3CleanupTests 1ToggleAtlasTests 2RestoreAtlasLayerSelectionTests 2ThemeCollectionSignalsTests 3RefreshThemaComboLiveTests 2GetGespeicherteThemenTests 2GetCurrentCanvasScaleTests 3OnAtlasLayerChangedTests 3OnAuswahlAsAtlasClickedTests 2PopulateAtlasLayerComboTests 2OnAtlasErzeugenChangedTests 3
flowchart TD
+  root(["test_print_tab.py"])
+  class root rootnode
+  root --> g1["SetServicesTests (2)"]
+  class g1 groupnode
+  g1 --> t1("ruft_alle_set_methoden_mit_combo_werten_auf")
+  g1 --> t2("ohne_combos_ruft_keine_set_methode_auf")
+  root --> g2["RestoreStateTests (2)"]
+  class g2 groupnode
+  g2 --> t3("gueltige_gespeicherte_werte_werden_uebernommen")
+  g2 --> t4("ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck")
+  root --> g3["OnDruckpufferClickedTests (3)"]
+  class g3 groupnode
+  g3 --> t5("gueltige_zahl_setzt_variable_und_button_text")
+  g3 --> t6("abgebrochen_aendert_nichts")
+  g3 --> t7("ungueltige_zahl_zeigt_hinweis_und_setzt_nichts")
+  root --> g4["OnVorlageErstellenTests (2)"]
+  class g4 groupnode
+  g4 --> t8("ruft_druckvorlage_anlegen_mit_combo_werten_auf")
+  g4 --> t9("ohne_logic_tut_nichts")
+  root --> g5["ProjectSignalsTests (3)"]
+  class g5 groupnode
+  g5 --> t10("connect_verbindet_alle_drei_signale")
+  g5 --> t11("disconnect_trennt_alle_drei_signale")
+  g5 --> t12("instance_wirft_ausnahme_wird_abgefangen")
+  root --> g6["CleanupTests (1)"]
+  class g6 groupnode
+  g6 --> t13("ruft_beide_disconnect_methoden_auf")
+  root --> g7["ToggleAtlasTests (2)"]
+  class g7 groupnode
+  g7 --> t14("checked_true_setzt_arrow_down_und_zeigt_inhalt")
+  g7 --> t15("checked_false_setzt_arrow_right_und_versteckt_inhalt")
+  root --> g8["RestoreAtlasLayerSelectionTests (2)"]
+  class g8 groupnode
+  g8 --> t16("vorhandene_layer_id_setzt_layer_in_combo")
+  g8 --> t17("leere_gespeicherte_layer_id_tut_nichts")
+  root --> g9["ThemeCollectionSignalsTests (3)"]
+  class g9 groupnode
+  g9 --> t18("connect_verbindet_alle_signale")
+  g9 --> t19("connect_ist_idempotent_wenn_schon_verbunden")
+  g9 --> t20("disconnect_trennt_alle_signale")
+  root --> g10["RefreshThemaComboLiveTests (2)"]
+  class g10 groupnode
+  g10 --> t21("erhaelt_vorherige_auswahl_wenn_noch_vorhanden")
+  g10 --> t22("faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden")
+  root --> g11["GetGespeicherteThemenTests (2)"]
+  class g11 groupnode
+  g11 --> t23("dedupliziert_und_filtert_leere_eintraege")
+  g11 --> t24("fehler_liefert_leere_liste")
+  root --> g12["GetCurrentCanvasScaleTests (3)"]
+  class g12 groupnode
+  g12 --> t25("liefert_skalierung_als_float")
+  g12 --> t26("ohne_canvas_liefert_none")
+  g12 --> t27("skalierung_null_liefert_none")
+  root --> g13["OnAtlasLayerChangedTests (3)"]
+  class g13 groupnode
+  g13 --> t28("none_oder_leer_setzt_variable_leer")
+  g13 --> t29("layer_objekt_mit_id_methode_setzt_dessen_id")
+  g13 --> t30("string_fallback_sucht_layer_ueber_namen")
+  root --> g14["OnAuswahlAsAtlasClickedTests (2)"]
+  class g14 groupnode
+  g14 --> t31("erfolgreicher_layer_aktualisiert_atlas_ui")
+  g14 --> t32("ohne_erzeugten_layer_tut_nichts_weiter")
+  root --> g15["PopulateAtlasLayerComboTests (2)"]
+  class g15 groupnode
+  g15 --> t33("befuellt_combo_ohne_druckrechteck_und_leere_namen")
+  g15 --> t34("ohne_atlas_layer_combo_tut_nichts")
+  root --> g16["OnAtlasErzeugenChangedTests (3)"]
+  class g16 groupnode
+  g16 --> t35("aktiviert_speichert_variable_und_aktiviert_widgets")
+  g16 --> t36("deaktiviert_speichert_variable_und_deaktiviert_widgets")
+  g16 --> t37("ohne_widgets_wirft_keine_ausnahme")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 ok
+  class t28 ok
+  class t29 ok
+  class t30 ok
+  class t31 ok
+  class t32 ok
+  class t33 ok
+  class t34 ok
+  class t35 ok
+  class t36 ok
+  class t37 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
SetServicesTests
test_ruft_alle_set_methoden_mit_combo_werten_auf
Prüft, dass set_services alle set_*-Methoden von PrintLogic mit den aktuellen Combo-Werten aufruft.grün
SetServicesTests
test_ohne_combos_ruft_keine_set_methode_auf
Prüft, dass set_services ohne vorhandene Combos keine set_*-Methode von PrintLogic aufruft.grün
RestoreStateTests
test_gueltige_gespeicherte_werte_werden_uebernommen
Prüft, dass _restore_state gültige gespeicherte Werte in die jeweiligen Widgets übernimmt.grün
RestoreStateTests
test_ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck
Prüft, dass _restore_state ohne (gültige) gespeicherte Werte auf die Default-Konstanten zurückfällt.grün
OnDruckpufferClickedTests
test_gueltige_zahl_setzt_variable_und_button_text
Prüft, dass eine bestätigte, gültige Zahl den Druckpuffer speichert und den Button-Text aktualisiert.grün
OnDruckpufferClickedTests
test_abgebrochen_aendert_nichts
Prüft, dass ein abgebrochener Dialog den Druckpuffer nicht verändert.grün
OnDruckpufferClickedTests
test_ungueltige_zahl_zeigt_hinweis_und_setzt_nichts
Prüft, dass eine nicht-numerische Eingabe einen Hinweis zeigt und den Druckpuffer nicht setzt.grün
OnVorlageErstellenTests
test_ruft_druckvorlage_anlegen_mit_combo_werten_auf
Prüft, dass _on_vorlage_erstellen druckvorlage_anlegen mit den aktuellen Combo-Werten aufruft.grün
OnVorlageErstellenTests
test_ohne_logic_tut_nichts
Prüft, dass _on_vorlage_erstellen ohne gesetzte PrintLogic keine Ausnahme wirft.grün
ProjectSignalsTests
test_connect_verbindet_alle_drei_signale
Prüft, dass _connect_project_signals alle drei Projektsignale mit _on_project_changed verbindet.grün
ProjectSignalsTests
test_disconnect_trennt_alle_drei_signale
Prüft, dass _disconnect_project_signals alle drei zuvor verbundenen Projektsignale trennt.grün
ProjectSignalsTests
test_instance_wirft_ausnahme_wird_abgefangen
Prüft, dass eine Ausnahme bei QgsProject.instance() in beiden Signalmethoden abgefangen wird.grün
CleanupTests
test_ruft_beide_disconnect_methoden_auf
Prüft, dass cleanup sowohl die Theme- als auch die Projektsignale trennt.grün
ToggleAtlasTests
test_checked_true_setzt_arrow_down_und_zeigt_inhalt
Prüft, dass _toggle_atlas bei checked=True den Pfeil nach unten dreht und den Inhalt einblendet.grün
ToggleAtlasTests
test_checked_false_setzt_arrow_right_und_versteckt_inhalt
Prüft, dass _toggle_atlas bei checked=False den Pfeil nach rechts dreht und den Inhalt ausblendet.grün
RestoreAtlasLayerSelectionTests
test_vorhandene_layer_id_setzt_layer_in_combo
Prüft, dass eine gespeicherte Layer-ID den zugehörigen Layer im Atlas-Dropdown auswählt.grün
RestoreAtlasLayerSelectionTests
test_leere_gespeicherte_layer_id_tut_nichts
Prüft, dass eine leere gespeicherte Layer-ID das Projekt gar nicht erst anfragt.grün
ThemeCollectionSignalsTests
test_connect_verbindet_alle_signale
Prüft, dass _connect_theme_collection_signals alle Signale der Theme-Collection verbindet.grün
ThemeCollectionSignalsTests
test_connect_ist_idempotent_wenn_schon_verbunden
Prüft, dass _connect_theme_collection_signals bei bereits verbundenem Signal nichts erneut tut.grün
ThemeCollectionSignalsTests
test_disconnect_trennt_alle_signale
Prüft, dass _disconnect_theme_collection_signals alle verbundenen Theme-Signale trennt und zurücksetzt.grün
RefreshThemaComboLiveTests
test_erhaelt_vorherige_auswahl_wenn_noch_vorhanden
Prüft, dass _refresh_thema_combo_live die vorherige Auswahl beibehält, wenn sie noch existiert.grün
RefreshThemaComboLiveTests
test_faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden
Prüft, dass _refresh_thema_combo_live auf 'wie Kartenfenster' zurückfällt, wenn die Auswahl verschwunden ist.grün
GetGespeicherteThemenTests
test_dedupliziert_und_filtert_leere_eintraege
Prüft, dass _get_gespeicherte_themen Duplikate und leere/None-Einträge entfernt.grün
GetGespeicherteThemenTests
test_fehler_liefert_leere_liste
Prüft, dass ein Fehler beim Zugriff auf die Theme-Collection eine leere Liste liefert.grün
GetCurrentCanvasScaleTests
test_liefert_skalierung_als_float
Prüft, dass _get_current_canvas_scale die Canvas-Skalierung als float liefert.grün
GetCurrentCanvasScaleTests
test_ohne_canvas_liefert_none
Prüft, dass _get_current_canvas_scale ohne verfügbares Kartenfenster None liefert.grün
GetCurrentCanvasScaleTests
test_skalierung_null_liefert_none
Prüft, dass eine Skalierung von 0 als None statt als 0.0 gilt.grün
OnAtlasLayerChangedTests
test_none_oder_leer_setzt_variable_leer
Prüft, dass None oder ein leerer Wert die Abdeckungslayer-Variable leeren.grün
OnAtlasLayerChangedTests
test_layer_objekt_mit_id_methode_setzt_dessen_id
Prüft, dass ein echtes Layer-Objekt (mit id()-Methode) dessen ID speichert.grün
OnAtlasLayerChangedTests
test_string_fallback_sucht_layer_ueber_namen
Prüft, dass ohne id()-Methode über den Layernamen im Projekt gesucht und dessen ID gespeichert wird.grün
OnAuswahlAsAtlasClickedTests
test_erfolgreicher_layer_aktualisiert_atlas_ui
Prüft, dass ein erfolgreich erzeugter Atlas-Layer den Atlas-Bereich öffnet und ins Dropdown übernimmt.grün
OnAuswahlAsAtlasClickedTests
test_ohne_erzeugten_layer_tut_nichts_weiter
Prüft, dass ohne erzeugten Atlas-Layer keine UI-Aktualisierung stattfindet.grün
PopulateAtlasLayerComboTests
test_befuellt_combo_ohne_druckrechteck_und_leere_namen
Prüft, dass _populate_atlas_layer_combo alle Layernamen außer 'Druckrechteck' und leeren Namen einträgt.grün
PopulateAtlasLayerComboTests
test_ohne_atlas_layer_combo_tut_nichts
Prüft, dass _populate_atlas_layer_combo ohne Atlas-Dropdown keine Ausnahme wirft.grün
OnAtlasErzeugenChangedTests
test_aktiviert_speichert_variable_und_aktiviert_widgets
Prüft, dass ein aktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button aktiviert.grün
OnAtlasErzeugenChangedTests
test_deaktiviert_speichert_variable_und_deaktiviert_widgets
Prüft, dass ein deaktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button deaktiviert.grün
OnAtlasErzeugenChangedTests
test_ohne_widgets_wirft_keine_ausnahme
Prüft, dass _on_atlas_erzeugen_changed ohne Atlas-Combo/-Button keine Ausnahme wirft.grün

Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung/dateipfad (Issue #65/#67).

IstVerfahrensDbAktionTests 2ErlaubtePersistierungTests 2DateipfadTests 2
flowchart TD
+  root(["test_pruef_ergebnis.py"])
+  class root rootnode
+  root --> g1["IstVerfahrensDbAktionTests (2)"]
+  class g1 groupnode
+  g1 --> t1("liefert_true_fuer_alle_verfahrens_db_aktionen")
+  g1 --> t2("liefert_false_fuer_andere_aktionen")
+  root --> g2["ErlaubtePersistierungTests (2)"]
+  class g2 groupnode
+  g2 --> t3("liefert_true_fuer_erzeugen_ueberschreiben_anhaengen")
+  g2 --> t4("liefert_false_fuer_ueberspringen_und_reines_existieren")
+  root --> g3["DateipfadTests (2)"]
+  class g3 groupnode
+  g3 --> t5("liefert_path_wenn_kontext_ein_path_ist")
+  g3 --> t6("liefert_none_wenn_kontext_kein_path_ist")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
IstVerfahrensDbAktionTests
test_liefert_true_fuer_alle_verfahrens_db_aktionen
Prüft, dass ist_verfahrens_db_aktion für alle vier Verfahrens-DB-Aktionscodes True liefert.grün
IstVerfahrensDbAktionTests
test_liefert_false_fuer_andere_aktionen
Prüft, dass ist_verfahrens_db_aktion für Nicht-Verfahrens-DB-Aktionen und ohne Aktion False liefert.grün
ErlaubtePersistierungTests
test_liefert_true_fuer_erzeugen_ueberschreiben_anhaengen
Prüft, dass erlaubte_persistierung für Erzeugen/Überschreiben/Anhängen True liefert.grün
ErlaubtePersistierungTests
test_liefert_false_fuer_ueberspringen_und_reines_existieren
Prüft, dass erlaubte_persistierung bei Überspringen und reinem 'datei_existiert' False liefert.grün
DateipfadTests
test_liefert_path_wenn_kontext_ein_path_ist
Prüft, dass dateipfad den Kontext liefert, wenn dieser bereits ein Path-Objekt ist.grün
DateipfadTests
test_liefert_none_wenn_kontext_kein_path_ist
Prüft, dass dateipfad None liefert, wenn der Kontext kein Path-Objekt ist (z. B. String oder Layer).grün

— (Docstring fehlt)

TestPruefmanager 26
flowchart TD
   root(["test_pruefmanager.py"])
   class root rootnode
-  root --> g1["TestPruefmanager (14)"]
+  root --> g1["TestPruefmanager (26)"]
   class g1 groupnode
   g1 --> t1("ok")
   g1 --> t2("leereingabe_erlaubt_ja")
@@ -343,6 +1103,71 @@
   g1 --> t12("datei_existiert_append")
   g1 --> t13("datei_existiert_cancel")
   g1 --> t14("datei_existiert_in_non_qgis_mode_returns_input")
+  g1 --> t15("verfahrensgebiet_layer_existiert_ergaenzen")
+  g1 --> t16("verfahrensgebiet_layer_existiert_abbrechen")
+  g1 --> t17("verfahrensgebiet_layer_existiert_entfernen")
+  g1 --> t18("verfahrensgebiet_layer_existiert_ersetzen")
+  g1 --> t19("verfahrensgebiet_layer_existiert_in_non_qgis_mode_returns_input")
+  g1 --> t20("report_error_kritische_aktion_ruft_error_auf")
+  g1 --> t21("report_error_warn_aktion_ruft_warning_auf")
+  g1 --> t22("report_error_unbekannte_aktion_faellt_auf_warning_zurueck")
+  g1 --> t23("frage_ja_nein_im_qgis_modus_fragt_nutzer")
+  g1 --> t24("frage_ja_nein_ausserhalb_qgis_liefert_default_ohne_rueckfrage")
+  g1 --> t25("frage_text_im_qgis_modus_fragt_nutzer")
+  g1 --> t26("frage_text_ausserhalb_qgis_liefert_default_ohne_rueckfrage")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPruefmanager
test_ok
Prüft, dass ein bereits erfolgreiches Prüfergebnis ohne Nutzerinteraktion als 'ok' durchgereicht wird.grün
TestPruefmanager
test_leereingabe_erlaubt_ja
Prüft, dass bei erlaubter leerer Eingabe eine Ja-Antwort des Nutzers die Entscheidung 'ok' liefert.grün
TestPruefmanager
test_leereingabe_erlaubt_nein
Prüft, dass bei erlaubter leerer Eingabe eine Nein-Antwort die Aktion 'leereingabe_erlaubt' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_standarddatei_vorschlagen_ja
Prüft, dass eine Ja-Antwort auf den Vorschlag der Standarddatei die Entscheidung 'ok' mit dem Standardpfad als Kontext liefert.grün
TestPruefmanager
test_standarddatei_vorschlagen_nein
Prüft, dass eine Nein-Antwort auf den Vorschlag der Standarddatei die Aktion 'standarddatei_vorschlagen' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_temporaer_erlaubt_ja
Prüft, dass eine Ja-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erzeugen' auslöst.grün
TestPruefmanager
test_temporaer_erlaubt_nein
Prüft, dass eine Nein-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erlaubt' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_layer_unsichtbar_ja
Prüft, dass eine Ja-Antwort bei unsichtbarem Layer den Layer sichtbar schaltet und die Entscheidung 'ok' liefert.grün
TestPruefmanager
test_layer_unsichtbar_nein
Prüft, dass eine Nein-Antwort bei unsichtbarem Layer die Aktion 'layer_unsichtbar' als abgelehnt bestehen lässt.grün
TestPruefmanager
test_unbekannte_aktion
Prüft, dass eine unbekannte Aktion eine Warnung auslöst und unverarbeitet als Fehlschlag zurückgegeben wird.grün
TestPruefmanager
test_datei_existiert_overwrite
Prüft, dass die Nutzerwahl 'überschreiben' bei bestehender Datei zur Aktion 'datei_existiert_ueberschreiben' führt.grün
TestPruefmanager
test_datei_existiert_append
Prüft, dass die Nutzerwahl 'anhängen' bei bestehender Datei zur Aktion 'datei_existiert_anhaengen' führt.grün
TestPruefmanager
test_datei_existiert_cancel
Prüft, dass die Nutzerwahl 'abbrechen' bei bestehender Datei zur Aktion 'datei_existiert_ueberspringen' führt.grün
TestPruefmanager
test_datei_existiert_in_non_qgis_mode_returns_input
Prüft, dass außerhalb des QGIS-Modus bei bestehender Datei keine Rückfrage erfolgt, sondern die ursprüngliche Aktion unentschieden zurückgegeben wird.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_ergaenzen
Prüft, dass die Nutzerwahl 'ergänzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ergaenzen' führt.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_abbrechen
Prüft, dass die Nutzerwahl 'abbrechen' bei bestehendem Verfahrensgebiet ok=False mit Aktion 'verfahrensgebiet_abbrechen' liefert.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_entfernen
Prüft, dass die Nutzerwahl 'entfernen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_entfernen' führt.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_ersetzen
Prüft, dass die Nutzerwahl 'ersetzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ersetzen' führt.grün
TestPruefmanager
test_verfahrensgebiet_layer_existiert_in_non_qgis_mode_returns_input
Prüft, dass außerhalb des QGIS-Modus bei bestehendem Verfahrensgebiet keine Rückfrage erfolgt.grün
TestPruefmanager
test_report_error_kritische_aktion_ruft_error_auf
Prüft, dass eine kritische Aktion (z. B. 'netzwerkfehler') über error() gemeldet wird, nicht über warning().grün
TestPruefmanager
test_report_error_warn_aktion_ruft_warning_auf
Prüft, dass eine Warn-Aktion (z. B. 'datei_nicht_gefunden') über warning() gemeldet wird, nicht über error().grün
TestPruefmanager
test_report_error_unbekannte_aktion_faellt_auf_warning_zurueck
Prüft, dass eine nicht eingestufte Aktion als Fallback über warning() gemeldet wird.grün
TestPruefmanager
test_frage_ja_nein_im_qgis_modus_fragt_nutzer
Prüft, dass frage_ja_nein im QGIS-Modus den Nutzer fragt und dessen Antwort liefert.grün
TestPruefmanager
test_frage_ja_nein_ausserhalb_qgis_liefert_default_ohne_rueckfrage
Prüft, dass frage_ja_nein außerhalb des QGIS-Modus den Default-Wert liefert, ohne den Nutzer zu fragen.grün
TestPruefmanager
test_frage_text_im_qgis_modus_fragt_nutzer
Prüft, dass frage_text im QGIS-Modus den Nutzer fragt und Text plus OK-Status liefert.grün
TestPruefmanager
test_frage_text_ausserhalb_qgis_liefert_default_ohne_rueckfrage
Prüft, dass frage_text außerhalb des QGIS-Modus den Standardtext mit OK=True liefert, ohne den Nutzer zu fragen.grün

Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg/create_buffer_layer (Issue #65/#67).

PolygonZuLinieTests 7LayerExistsInGpkgTests 4CreateBufferLayerTests 3
flowchart TD
+  root(["test_qgiscore_wrapper_helpers.py"])
+  class root rootnode
+  root --> g1["PolygonZuLinieTests (7)"]
+  class g1 groupnode
+  g1 --> t1("none_liefert_none")
+  g1 --> t2("leere_geometrie_liefert_none")
+  g1 --> t3("stufe1_nutzt_boundary_wenn_vorhanden")
+  g1 --> t4("stufe2_extrahiert_ringe_bei_einfachem_polygon")
+  g1 --> t5("stufe2_extrahiert_alle_ringe_bei_multipolygon")
+  g1 --> t6("stufe3_fallback_wenn_keine_ringe_extrahierbar")
+  g1 --> t7("alle_stufen_scheitern_liefert_none")
+  root --> g2["LayerExistsInGpkgTests (4)"]
+  class g2 groupnode
+  g2 --> t8("fehlende_datei_liefert_false")
+  g2 --> t9("leerer_pfad_oder_layername_liefert_false")
+  g2 --> t10("vorhandener_layer_liefert_true")
+  g2 --> t11("fehlender_layer_liefert_false")
+  root --> g3["CreateBufferLayerTests (3)"]
+  class g3 groupnode
+  g3 --> t12("ohne_qgis_liefert_none")
+  g3 --> t13("fehlender_source_layer_liefert_none")
+  g3 --> t14("source_layer_ohne_getfeatures_liefert_none")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -361,7 +1186,44 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPruefmanager
test_ok
— (Docstring fehlt)grün
TestPruefmanager
test_leereingabe_erlaubt_ja
— (Docstring fehlt)grün
TestPruefmanager
test_leereingabe_erlaubt_nein
— (Docstring fehlt)grün
TestPruefmanager
test_standarddatei_vorschlagen_ja
— (Docstring fehlt)grün
TestPruefmanager
test_standarddatei_vorschlagen_nein
— (Docstring fehlt)grün
TestPruefmanager
test_temporaer_erlaubt_ja
— (Docstring fehlt)grün
TestPruefmanager
test_temporaer_erlaubt_nein
— (Docstring fehlt)grün
TestPruefmanager
test_layer_unsichtbar_ja
— (Docstring fehlt)grün
TestPruefmanager
test_layer_unsichtbar_nein
— (Docstring fehlt)grün
TestPruefmanager
test_unbekannte_aktion
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_overwrite
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_append
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_cancel
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_in_non_qgis_mode_returns_input
— (Docstring fehlt)grün

— (Docstring fehlt)

TestSettingsLogic 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
PolygonZuLinieTests
test_none_liefert_none
Prüft, dass polygon_zu_linie für None-Eingabe None liefert.grün
PolygonZuLinieTests
test_leere_geometrie_liefert_none
Prüft, dass eine leere Geometrie None liefert.grün
PolygonZuLinieTests
test_stufe1_nutzt_boundary_wenn_vorhanden
Prüft, dass eine Geometrie mit boundary() (QGIS 3.x-Pfad) deren Ergebnis direkt liefert.grün
PolygonZuLinieTests
test_stufe2_extrahiert_ringe_bei_einfachem_polygon
Prüft, dass ohne boundary() die Ringe eines einfachen Polygons manuell extrahiert werden.grün
PolygonZuLinieTests
test_stufe2_extrahiert_alle_ringe_bei_multipolygon
Prüft, dass bei einem MultiPolygon die Ringe aller Teilpolygone extrahiert werden.grün
PolygonZuLinieTests
test_stufe3_fallback_wenn_keine_ringe_extrahierbar
Prüft, dass bei fehlgeschlagener Ringextraktion auf convertToType zurückgefallen wird.grün
PolygonZuLinieTests
test_alle_stufen_scheitern_liefert_none
Prüft, dass polygon_zu_linie None liefert, wenn Ringextraktion und convertToType beide scheitern.grün
LayerExistsInGpkgTests
test_fehlende_datei_liefert_false
Prüft, dass eine nicht vorhandene GPKG-Datei False liefert.grün
LayerExistsInGpkgTests
test_leerer_pfad_oder_layername_liefert_false
Prüft, dass ein leerer Pfad oder Layername False liefert.grün
LayerExistsInGpkgTests
test_vorhandener_layer_liefert_true
Prüft, dass ein in gpkg_contents eingetragener Layername als vorhanden erkannt wird.grün
LayerExistsInGpkgTests
test_fehlender_layer_liefert_false
Prüft, dass ein nicht in gpkg_contents eingetragener Layername als nicht vorhanden gilt.grün
CreateBufferLayerTests
test_ohne_qgis_liefert_none
Prüft, dass create_buffer_layer ohne verfügbares QGIS None liefert.grün
CreateBufferLayerTests
test_fehlender_source_layer_liefert_none
Prüft, dass ein fehlender Quelllayer (None) None liefert, auch wenn QGIS verfügbar wäre.grün
CreateBufferLayerTests
test_source_layer_ohne_getfeatures_liefert_none
Prüft, dass ein Quelllayer ohne getFeatures()-Methode None liefert.grün

Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project/find_dock_widgets/add_menu/remove_menu (Issue #65/#67).

AddLayerToProjectTests 4FindDockWidgetsTests 3AddRemoveMenuTests 5
flowchart TD
+  root(["test_qgisui_wrapper_helpers.py"])
+  class root rootnode
+  root --> g1["AddLayerToProjectTests (4)"]
+  class g1 groupnode
+  g1 --> t1("none_layer_liefert_false")
+  g1 --> t2("mock_modus_liefert_true_ohne_projektzugriff")
+  g1 --> t3("mit_qgis_fuegt_layer_dem_projekt_hinzu")
+  g1 --> t4("mit_qgis_fehler_liefert_false")
+  root --> g2["FindDockWidgetsTests (3)"]
+  class g2 groupnode
+  g2 --> t5("ohne_main_window_liefert_leere_liste")
+  g2 --> t6("liefert_dock_widgets_des_main_window")
+  g2 --> t7("fehler_beim_suchen_liefert_leere_liste")
+  root --> g3["AddRemoveMenuTests (5)"]
+  class g3 groupnode
+  g3 --> t8("add_menu_ohne_main_window_tut_nichts")
+  g3 --> t9("add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu")
+  g3 --> t10("add_menu_ohne_menuaction_wird_ignoriert")
+  g3 --> t11("remove_menu_entfernt_ueber_menuaction")
+  g3 --> t12("remove_menu_ohne_main_window_tut_nichts")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
AddLayerToProjectTests
test_none_layer_liefert_false
Prüft, dass add_layer_to_project für einen fehlenden Layer (None) False liefert.grün
AddLayerToProjectTests
test_mock_modus_liefert_true_ohne_projektzugriff
Prüft, dass im Mock-Modus (ohne QGIS) ein gültiger Layer True liefert, ohne das Projekt anzufassen.grün
AddLayerToProjectTests
test_mit_qgis_fuegt_layer_dem_projekt_hinzu
Prüft, dass bei verfügbarem QGIS der Layer über QgsProject.instance().addMapLayer hinzugefügt wird.grün
AddLayerToProjectTests
test_mit_qgis_fehler_liefert_false
Prüft, dass ein Fehler beim Hinzufügen zum Projekt abgefangen wird und False liefert.grün
FindDockWidgetsTests
test_ohne_main_window_liefert_leere_liste
Prüft, dass find_dock_widgets ohne verfügbares Hauptfenster eine leere Liste liefert.grün
FindDockWidgetsTests
test_liefert_dock_widgets_des_main_window
Prüft, dass find_dock_widgets die vom Hauptfenster gefundenen Dock-Widgets durchreicht.grün
FindDockWidgetsTests
test_fehler_beim_suchen_liefert_leere_liste
Prüft, dass ein Fehler beim Suchen der Dock-Widgets abgefangen wird und eine leere Liste liefert.grün
AddRemoveMenuTests
test_add_menu_ohne_main_window_tut_nichts
Prüft, dass add_menu ohne Hauptfenster keine Ausnahme wirft.grün
AddRemoveMenuTests
test_add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu
Prüft, dass ein echtes Qt-Menü (mit menuAction) der Menüleiste hinzugefügt wird.grün
AddRemoveMenuTests
test_add_menu_ohne_menuaction_wird_ignoriert
Prüft, dass ein Objekt ohne menuAction nicht an die Menüleiste übergeben wird.grün
AddRemoveMenuTests
test_remove_menu_entfernt_ueber_menuaction
Prüft, dass remove_menu das Menü über dessen menuAction() aus der Menüleiste entfernt.grün
AddRemoveMenuTests
test_remove_menu_ohne_main_window_tut_nichts
Prüft, dass remove_menu ohne Hauptfenster keine Ausnahme wirft.grün

— (Docstring fehlt)

TestSettingsLogic 4
flowchart TD
   root(["test_settings_logic.py"])
   class root rootnode
   root --> g1["TestSettingsLogic (4)"]
@@ -378,7 +1240,36 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSettingsLogic
test_load
— (Docstring fehlt)grün
TestSettingsLogic
test_save
— (Docstring fehlt)grün
TestSettingsLogic
test_load_handles_partial_values_with_empty_fallback
— (Docstring fehlt)grün
TestSettingsLogic
test_save_empty_string_value_is_persisted
— (Docstring fehlt)grün

— (Docstring fehlt)

TestStilpruefer 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSettingsLogic
test_load
Prüft, dass load() jede bekannte Projektvariable genau einmal über get_variable() liest.grün
TestSettingsLogic
test_save
Prüft, dass save() nur bekannte Variablen-Keys speichert und unbekannte Keys ignoriert.grün
TestSettingsLogic
test_load_handles_partial_values_with_empty_fallback
Prüft, dass load() für jede Variable den (auch leeren) Wert von get_variable() unverändert übernimmt, selbst wenn nicht alle Variablen im Mock konfiguriert sind.grün
TestSettingsLogic
test_save_empty_string_value_is_persisted
Prüft, dass save() einen leeren String als gültigen Wert speichert, statt ihn wie einen fehlenden Key zu behandeln.grün

Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data/_build_input (Issue #65/#67).

SaveDataTests 2LoadDataTests 4BuildInputTests 2
flowchart TD
+  root(["test_settings_tab.py"])
+  class root rootnode
+  root --> g1["SaveDataTests (2)"]
+  class g1 groupnode
+  g1 --> t1("sammelt_text_und_combobox_werte_inklusive_landkreiskennzahl")
+  g1 --> t2("ohne_landkreis_dropdown_wird_keine_kennzahl_ergaenzt")
+  root --> g2["LoadDataTests (4)"]
+  class g2 groupnode
+  g2 --> t3("befuellt_textfelder_aus_gespeicherten_daten")
+  g2 --> t4("ohne_gespeicherten_wert_bleibt_textfeld_leer")
+  g2 --> t5("waehlt_combobox_eintrag_bei_bekanntem_namen")
+  g2 --> t6("unbekannter_gespeicherter_name_waehlt_ersten_eintrag")
+  root --> g3["BuildInputTests (2)"]
+  class g3 groupnode
+  g3 --> t7("landkreis_feld_liefert_combobox_mit_allen_landkreisen")
+  g3 --> t8("anderes_feld_liefert_lineedit")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
SaveDataTests
test_sammelt_text_und_combobox_werte_inklusive_landkreiskennzahl
Prüft, dass save_data Text- und Dropdown-Werte sammelt und die Landkreiskennzahl separat ergänzt.grün
SaveDataTests
test_ohne_landkreis_dropdown_wird_keine_kennzahl_ergaenzt
Prüft, dass ohne Landkreis-Dropdown im Formular keine 'landkreis_kennzahl' ergänzt wird.grün
LoadDataTests
test_befuellt_textfelder_aus_gespeicherten_daten
Prüft, dass load_data ein Textfeld mit dem gespeicherten Wert befüllt.grün
LoadDataTests
test_ohne_gespeicherten_wert_bleibt_textfeld_leer
Prüft, dass load_data ohne gespeicherten Wert das Textfeld auf einen leeren Text setzt.grün
LoadDataTests
test_waehlt_combobox_eintrag_bei_bekanntem_namen
Prüft, dass load_data im Landkreis-Dropdown den gespeicherten Landkreisnamen auswählt.grün
LoadDataTests
test_unbekannter_gespeicherter_name_waehlt_ersten_eintrag
Prüft, dass load_data bei einem nicht mehr vorhandenen Landkreisnamen auf den ersten Eintrag zurückfällt.grün
BuildInputTests
test_landkreis_feld_liefert_combobox_mit_allen_landkreisen
Prüft, dass _build_input für das Landkreis-Feld eine ComboBox mit Platzhalter und allen Landkreisen liefert.grün
BuildInputTests
test_anderes_feld_liefert_lineedit
Prüft, dass _build_input für ein normales Feld ein leeres Textfeld liefert.grün

— (Docstring fehlt)

TestStilpruefer 4
flowchart TD
   root(["test_stilpruefer.py"])
   class root rootnode
   root --> g1["TestStilpruefer (4)"]
@@ -395,7 +1286,34 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilpruefer
test_keine_datei_angegeben
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_mit_qml
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_falsche_endung
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_nicht
— (Docstring fehlt)grün

Unit-Tests für `sn_basis.functions.text_wrapper`.

TestSanitizeBezeichner 7
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilpruefer
test_keine_datei_angegeben
Prüft, dass eine leere Pfadangabe als 'ok' ohne Kontext akzeptiert wird (Stil ist optional).grün
TestStilpruefer
test_datei_existiert_mit_qml
Prüft, dass eine existierende .qml-Datei als 'stil_anwendbar' bestätigt wird.grün
TestStilpruefer
test_datei_existiert_falsche_endung
Prüft, dass eine existierende Datei ohne .qml-Endung als 'falsche_endung' abgelehnt wird.grün
TestStilpruefer
test_datei_existiert_nicht
Prüft, dass ein nicht existierender .qml-Pfad als 'datei_nicht_gefunden' abgelehnt wird.grün

Unit-Tests für sn_basis.functions.sys_wrapper.get_plugin_root().

TestGetPluginRoot 1JoinPathTests 1EnsureDirTests 2GetattrSafeTests 2
flowchart TD
+  root(["test_sys_wrapper.py"])
+  class root rootnode
+  root --> g1["TestGetPluginRoot (1)"]
+  class g1 groupnode
+  g1 --> t1("get_plugin_root_folgt_keiner_verzeichnisverknuepfung")
+  root --> g2["JoinPathTests (1)"]
+  class g2 groupnode
+  g2 --> t2("verbindet_mehrere_pfadbestandteile")
+  root --> g3["EnsureDirTests (2)"]
+  class g3 groupnode
+  g3 --> t3("legt_fehlendes_verzeichnis_inklusive_eltern_an")
+  g3 --> t4("vorhandenes_verzeichnis_wirft_keinen_fehler")
+  root --> g4["GetattrSafeTests (2)"]
+  class g4 groupnode
+  g4 --> t5("liefert_vorhandenes_attribut")
+  g4 --> t6("liefert_default_bei_fehlendem_attribut")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetPluginRoot
test_get_plugin_root_folgt_keiner_verzeichnisverknuepfung
Prüft, dass get_plugin_root() bei einem über eine Verzeichnisverknüpfung (wie von link_qgis_profile.py angelegt) erreichten Modul den verlinkten Pfad liefert, statt zum echten Zielverzeichnis aufzulösen (Verfahrensgebiet #50).grün
JoinPathTests
test_verbindet_mehrere_pfadbestandteile
Prüft, dass join_path mehrere Bestandteile OS-sicher zu einem Path zusammenfügt.grün
EnsureDirTests
test_legt_fehlendes_verzeichnis_inklusive_eltern_an
Prüft, dass ensure_dir ein fehlendes Verzeichnis samt Elternverzeichnissen anlegt.grün
EnsureDirTests
test_vorhandenes_verzeichnis_wirft_keinen_fehler
Prüft, dass ensure_dir bei einem bereits vorhandenen Verzeichnis keinen Fehler wirft.grün
GetattrSafeTests
test_liefert_vorhandenes_attribut
Prüft, dass getattr_safe ein vorhandenes Attribut liefert.grün
GetattrSafeTests
test_liefert_default_bei_fehlendem_attribut
Prüft, dass getattr_safe bei fehlendem Attribut den Default-Wert liefert.grün

Unit-Tests für `sn_basis.functions.text_wrapper`.

TestSanitizeBezeichner 7
flowchart TD
   root(["test_text_wrapper.py"])
   class root rootnode
   root --> g1["TestSanitizeBezeichner (7)"]
@@ -418,7 +1336,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSanitizeBezeichner
test_umlaute_bleiben_erhalten
Prüft, dass Umlaute und ß nicht durch den Ersatz entfernt werden.grün
TestSanitizeBezeichner
test_bindestrich_bleibt_erhalten
Prüft, dass ein Bindestrich im Bezeichner erhalten bleibt (Issue #42).grün
TestSanitizeBezeichner
test_unterstrich_bleibt_erhalten
Prüft, dass ein Unterstrich im Bezeichner erhalten bleibt.grün
TestSanitizeBezeichner
test_leerzeichen_wird_durch_ersatz_zusammengefasst
Prüft, dass Leerzeichen (kein zulässiges Zeichen) zu einem Ersatzzeichen werden.grün
TestSanitizeBezeichner
test_fuehrender_und_folgender_ersatz_wird_entfernt
Prüft, dass ein Ersatzzeichen am Anfang oder Ende des Ergebnisses entfernt wird.grün
TestSanitizeBezeichner
test_eigenes_ersatzzeichen_wird_verwendet
Prüft, dass ein abweichendes Ersatzzeichen anstelle von Unterstrich genutzt wird.grün
TestSanitizeBezeichner
test_nur_unzulaessige_zeichen_ergibt_leeren_string
Prüft, dass ein Bezeichner ganz aus unzulässigen Zeichen zu einem leeren String wird.grün

— (Docstring fehlt)

TestVariableWrapper 6
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSanitizeBezeichner
test_umlaute_bleiben_erhalten
Prüft, dass Umlaute und ß nicht durch den Ersatz entfernt werden.grün
TestSanitizeBezeichner
test_bindestrich_bleibt_erhalten
Prüft, dass ein Bindestrich im Bezeichner erhalten bleibt (Issue #42).grün
TestSanitizeBezeichner
test_unterstrich_bleibt_erhalten
Prüft, dass ein Unterstrich im Bezeichner erhalten bleibt.grün
TestSanitizeBezeichner
test_leerzeichen_wird_durch_ersatz_zusammengefasst
Prüft, dass Leerzeichen (kein zulässiges Zeichen) zu einem Ersatzzeichen werden.grün
TestSanitizeBezeichner
test_fuehrender_und_folgender_ersatz_wird_entfernt
Prüft, dass ein Ersatzzeichen am Anfang oder Ende des Ergebnisses entfernt wird.grün
TestSanitizeBezeichner
test_eigenes_ersatzzeichen_wird_verwendet
Prüft, dass ein abweichendes Ersatzzeichen anstelle von Unterstrich genutzt wird.grün
TestSanitizeBezeichner
test_nur_unzulaessige_zeichen_ergibt_leeren_string
Prüft, dass ein Bezeichner ganz aus unzulässigen Zeichen zu einem leeren String wird.grün

— (Docstring fehlt)

TestVariableWrapper 6
flowchart TD
   root(["test_variable_wrapper.py"])
   class root rootnode
   root --> g1["TestVariableWrapper (6)"]
@@ -439,7 +1357,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVariableWrapper
test_set_variable_project_noop_write_is_skipped
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_project_changed_value_is_written
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_global_writes_prefixed_name
— (Docstring fehlt)grün
TestVariableWrapper
test_get_variable_global_returns_empty_string_fallback
— (Docstring fehlt)grün
TestVariableWrapper
test_get_variable_invalid_scope_raises
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_invalid_scope_raises
— (Docstring fehlt)grün

— (Docstring fehlt)

TestVerfahrensgebietManager 5
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVariableWrapper
test_set_variable_project_noop_write_is_skipped
Prüft, dass set_variable() im Projekt-Scope nicht schreibt, wenn der neue Wert dem bestehenden entspricht.grün
TestVariableWrapper
test_set_variable_project_changed_value_is_written
Prüft, dass set_variable() im Projekt-Scope bei geändertem Wert mit dem Präfix 'sn_' geschrieben wird.grün
TestVariableWrapper
test_set_variable_global_writes_prefixed_name
Prüft, dass set_variable() im globalen Scope den Variablennamen mit 'sn_' vorangestellt schreibt.grün
TestVariableWrapper
test_get_variable_global_returns_empty_string_fallback
Prüft, dass get_variable() im globalen Scope für eine nicht gesetzte Variable einen leeren String liefert.grün
TestVariableWrapper
test_get_variable_invalid_scope_raises
Prüft, dass get_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst.grün
TestVariableWrapper
test_set_variable_invalid_scope_raises
Prüft, dass set_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst.grün

Tests für sn_basis/functions/verfahrensgebiet_manager.py (zentrale Layer-Verfolgung, Issue #65).

TestVerfahrensgebietManager 5OnHandlerTests 5ListenerSetupTests 3
flowchart TD
   root(["test_verfahrensgebiet_manager.py"])
   class root rootnode
   root --> g1["TestVerfahrensgebietManager (5)"]
@@ -449,16 +1367,36 @@
   g1 --> t3("set_verfahrensgebiet_layer_id_writes_project_variable")
   g1 --> t4("update_without_layer_does_not_write_when_already_empty")
   g1 --> t5("update_with_layer_writes_variable")
+  root --> g2["OnHandlerTests (5)"]
+  class g2 groupnode
+  g2 --> t6("on_project_changed_ohne_layer_setzt_variable_leer")
+  g2 --> t7("on_project_changed_mit_layer_setzt_variable")
+  g2 --> t8("on_layers_added_findet_neuen_layer")
+  g2 --> t9("on_layer_removed_bei_aktuellem_layer_sucht_neu")
+  g2 --> t10("on_layer_removed_bei_anderem_layer_aendert_nichts")
+  root --> g3["ListenerSetupTests (3)"]
+  class g3 groupnode
+  g3 --> t11("setup_ohne_qgis_tut_nichts")
+  g3 --> t12("setup_verbindet_signale_und_ist_idempotent")
+  g3 --> t13("teardown_trennt_alle_signale_und_ist_idempotent")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_set_verfahrensgebiet_layer_id_writes_project_variable
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_update_without_layer_does_not_write_when_already_empty
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
— (Docstring fehlt)grün
+ classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert.grün
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value
Prüft, dass get_verfahrensgebiet_layer_id() bei leerer Projektvariable None statt eines leeren Strings liefert.grün
TestVerfahrensgebietManager
test_set_verfahrensgebiet_layer_id_writes_project_variable
Prüft, dass set_verfahrensgebiet_layer_id() die Layer-ID als Projektvariable 'verfahrensgebiet_layer' speichert.grün
TestVerfahrensgebietManager
test_update_without_layer_does_not_write_when_already_empty
Prüft, dass ohne gefundenen Verfahrensgebiet-Layer keine Schreibaktion erfolgt, wenn die Variable bereits leer ist.grün
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert.grün
OnHandlerTests
test_on_project_changed_ohne_layer_setzt_variable_leer
Prüft, dass _on_project_changed die Variable leert, wenn kein Verfahrensgebiet-Layer existiert.grün
OnHandlerTests
test_on_project_changed_mit_layer_setzt_variable
Prüft, dass _on_project_changed die Layer-ID eines gefundenen Verfahrensgebiet-Layers übernimmt.grün
OnHandlerTests
test_on_layers_added_findet_neuen_layer
Prüft, dass _on_layers_added nach neu hinzugefügten Layern sucht und die Variable aktualisiert.grün
OnHandlerTests
test_on_layer_removed_bei_aktuellem_layer_sucht_neu
Prüft, dass das Entfernen des aktuell aktiven Verfahrensgebiet-Layers eine Neusuche auslöst.grün
OnHandlerTests
test_on_layer_removed_bei_anderem_layer_aendert_nichts
Prüft, dass das Entfernen eines anderen Layers keine Neusuche auslöst.grün
ListenerSetupTests
test_setup_ohne_qgis_tut_nichts
Prüft, dass setup_verfahrensgebiet_listener ohne QGIS keine Signale registriert.grün
ListenerSetupTests
test_setup_verbindet_signale_und_ist_idempotent
Prüft, dass setup_verfahrensgebiet_listener die Projektsignale verbindet und ein zweiter Aufruf nichts doppelt registriert.grün
ListenerSetupTests
test_teardown_trennt_alle_signale_und_ist_idempotent
Prüft, dass teardown_verfahrensgebiet_listener alle Signale trennt und mehrfacher Aufruf nicht fehlschlägt.grün
@@ -468,8 +1406,8 @@

sn_verfahrensgebiet

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 12 Testdateien · 208 Testfälle

-

Unit-Tests für `sn_verfahrensgebiet.functions.beteiligung_pruefung`.

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
@@ -556,10 +1494,10 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDockWidget
test_init_tabs_with_services_injects_available_services
— (Docstring fehlt)grün
TestDockWidget
test_init_tabs_with_services_reports_errors
— (Docstring fehlt)grün

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 12TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDockWidget
test_init_tabs_with_services_injects_available_services
— (Docstring fehlt)grün
TestDockWidget
test_init_tabs_with_services_reports_errors
— (Docstring fehlt)grün

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 11TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
   root(["test_flurstueck_alkis.py"])
   class root rootnode
-  root --> g1["TestFlurstueckAlkis (12)"]
+  root --> g1["TestFlurstueckAlkis (11)"]
   class g1 groupnode
   g1 --> t1("user_cancel_returns_none")
   g1 --> t2("empty_text_after_confirmation_returns_none")
@@ -567,37 +1505,35 @@
   g1 --> t4("success_returns_layer_and_first_status")
   g1 --> t5("sql_filter_contains_all_schluessel")
   g1 --> t6("existing_layer_reload_yes")
-  g1 --> t7("existing_layer_keep_no")
+  g1 --> t7("existing_layer_abbrechen_returns_none")
   g1 --> t8("vorgabe_schluessel_prefills_default_text")
   g1 --> t9("vorgabe_namen_appears_in_label")
-  g1 --> t10("bbox_embedded_in_wfs_uri")
-  g1 --> t11("backwards_compatible_no_new_params")
-  g1 --> t12("ohne_schluessel_abfrage_ueberspringt_ask_text")
+  g1 --> t10("backwards_compatible_no_new_params")
+  g1 --> t11("ohne_schluessel_abfrage_ueberspringt_ask_text")
   root --> g2["TestFlurstueckAlkisAnhaengenDedup (3)"]
   class g2 groupnode
-  g2 --> t13("append_ueberspringt_bereits_vorhandene_flurstuecke")
-  g2 --> t14("append_ohne_kennzeichen_feld_haengt_alles_an")
-  g2 --> t15("ohne_schluessel_abfrage_append_dedup")
+  g2 --> t12("append_ueberspringt_bereits_vorhandene_flurstuecke")
+  g2 --> t13("append_ohne_kennzeichen_feld_haengt_alles_an")
+  g2 --> t14("ohne_schluessel_abfrage_append_dedup")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 warn
   class t5 warn
   class t6 warn
-  class t7 warn
+  class t7 ok
   class t8 warn
   class t9 warn
   class t10 warn
-  class t11 warn
+  class t11 ok
   class t12 ok
   class t13 ok
   class t14 ok
-  class t15 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
ask_text → ok=False, text="" → (None, NONE).grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
ask_text → ok=True, aber Text leer → (None, NONE).grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
WFS-Layer nicht valide → (None, NONE).grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Kein bestehender Layer, WFS valide → (layer, FIRST).rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Der WFS-URI enthält alle eingegebenen Gemarkungsschlüssel.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Bestehender Layer + Nutzer: 'Ja, neu laden' → LoadStatus.RELOAD.rot
TestFlurstueckAlkis
test_existing_layer_keep_no
Bestehender Layer + Nutzer: 'Nein, behalten' → LoadStatus.KEEP.rot
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
vorgabe_schluessel=['1234','5678'] → ask_text erhält '1234, 5678' als default_text.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
vorgabe_namen=['Musterstadt'] → ask_text-label enthält den Namen.rot
TestFlurstueckAlkis
test_bbox_embedded_in_wfs_uri
bbox-Parameter → WFS-URI enthält 'bbox=' Substring.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
flurstueck_alkis(tab) ohne neue Parameter → kein Fehler.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
ohne_schluessel_abfrage=True verwendet vorgabe_schluessel direkt, ohne ask_text aufzurufen.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert.grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird.rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird.rot
TestFlurstueckAlkis
test_existing_layer_abbrechen_returns_none
Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird.grün
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
   root(["test_gemarkung_alkis.py"])
   class root rootnode
   root --> g1["TestExtractSchluesselUndName (4)"]
@@ -640,7 +1576,7 @@
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtractSchluesselUndName
test_standard_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_fallback_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_missing_attribute_returns_empty
— (Docstring fehlt)grün
TestExtractSchluesselUndName
test_none_value_returns_empty
— (Docstring fehlt)grün
TestGemarkungSchluesselbbox
test_success_bbox_filter
WFS-BBox-Filter liefert Features → korrekte Tupel-Liste.übersp.
TestGemarkungSchluesselbbox
test_fallback_when_bbox_fails
BBox-Filter schlägt fehl → Fallback auf Volllade + client-seitige Filterung.übersp.
TestGemarkungSchluesselbbox
test_both_layers_fail_returns_empty
Beide Lade-Strategien scheitern → leere Liste.übersp.
TestGemarkungSchluesselbbox
test_deduplication_and_sorting
Doppelte Schlüssel werden dedupliziert, Ergebnis ist sortiert.übersp.
TestGemarkungSchluesselbbox
test_feature_without_schluessel_skipped
Feature ohne Schlüssel-Attribut wird übersprungen.übersp.
TestGemarkungSchluesselbbox
test_exception_returns_empty
Exception im Ladevorgang → leere Liste (kein Crash).übersp.
TestGemarkungSchluesselnurNummern
test_returns_only_nummern
— (Docstring fehlt)übersp.
TestGemarkungSchluesselnurNummern
test_empty_input_returns_empty
— (Docstring fehlt)übersp.
TestLadeGemarkungenlayer
test_returns_layer_when_service_reachable
Dienst erreichbar (isValid=True) → Layer-Objekt zurückgegeben.grün
TestLadeGemarkungenlayer
test_returns_none_when_service_unreachable
Dienst nicht erreichbar (isValid=False) → None zurückgegeben.grün

Unit-Tests für `sn_verfahrensgebiet.functions.grenzpunkt_alkis`.

TestExtrahiereGmlIds 5TestGrenzpunktAlkis 7
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtractSchluesselUndName
test_standard_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_fallback_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_missing_attribute_returns_empty
— (Docstring fehlt)grün
TestExtractSchluesselUndName
test_none_value_returns_empty
— (Docstring fehlt)grün
TestGemarkungSchluesselbbox
test_success_bbox_filter
WFS-BBox-Filter liefert Features → korrekte Tupel-Liste.übersp.
TestGemarkungSchluesselbbox
test_fallback_when_bbox_fails
BBox-Filter schlägt fehl → Fallback auf Volllade + client-seitige Filterung.übersp.
TestGemarkungSchluesselbbox
test_both_layers_fail_returns_empty
Beide Lade-Strategien scheitern → leere Liste.übersp.
TestGemarkungSchluesselbbox
test_deduplication_and_sorting
Doppelte Schlüssel werden dedupliziert, Ergebnis ist sortiert.übersp.
TestGemarkungSchluesselbbox
test_feature_without_schluessel_skipped
Feature ohne Schlüssel-Attribut wird übersprungen.übersp.
TestGemarkungSchluesselbbox
test_exception_returns_empty
Exception im Ladevorgang → leere Liste (kein Crash).übersp.
TestGemarkungSchluesselnurNummern
test_returns_only_nummern
— (Docstring fehlt)übersp.
TestGemarkungSchluesselnurNummern
test_empty_input_returns_empty
— (Docstring fehlt)übersp.
TestLadeGemarkungenlayer
test_returns_layer_when_service_reachable
Dienst erreichbar (isValid=True) → Layer-Objekt zurückgegeben.grün
TestLadeGemarkungenlayer
test_returns_none_when_service_unreachable
Dienst nicht erreichbar (isValid=False) → None zurückgegeben.grün

Unit-Tests für `sn_verfahrensgebiet.functions.grenzpunkt_alkis`.

TestExtrahiereGmlIds 5TestGrenzpunktAlkis 7TestGrenzpunkteGemarkungsweiseStil 1
flowchart TD
   root(["test_grenzpunkt_alkis.py"])
   class root rootnode
   root --> g1["TestExtrahiereGmlIds (5)"]
@@ -659,6 +1595,9 @@
   g2 --> t10("grenzpunkt_batch_empty")
   g2 --> t11("success_returns_both_layers")
   g2 --> t12("extent_derived_from_vg_layer")
+  root --> g3["TestGrenzpunkteGemarkungsweiseStil (1)"]
+  class g3 groupnode
+  g3 --> t13("stil_wird_nach_dem_laden_auf_den_ergebnis_layer_angewendet")
   class t1 ok
   class t2 ok
   class t3 ok
@@ -671,11 +1610,12 @@
   class t10 warn
   class t11 warn
   class t12 warn
+  class t13 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtrahiereGmlIds
test_xlink_href_format
URN-Format 'urn:adv:oid:DEABC123' → nur 'DEABC123' extrahiert.grün
TestExtrahiereGmlIds
test_plain_gml_id
Plain-GML-ID ohne Doppelpunkt → direkt übernommen.grün
TestExtrahiereGmlIds
test_empty_value_skipped
Leerer gehoertZu-Wert wird übersprungen.grün
TestExtrahiereGmlIds
test_deduplication
Doppelte Referenzen werden dedupliziert.grün
TestExtrahiereGmlIds
test_sorted_output
Rückgabe ist aufsteigend sortiert.grün
TestGrenzpunktAlkis
test_no_vg_layer_returns_none
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_invalid_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_empty_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_no_gml_ids_returns_pta_only
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_grenzpunkt_batch_empty
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_success_returns_both_layers
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_extent_derived_from_vg_layer
Wenn kein extent übergeben → Extent aus VG-Layer holen.rot

Unit-Tests für `sn_verfahrensgebiet.functions.knickpunkt_pruefung`.

TestExtrahiereKnickpunkte 4TestKnickpunktPruefungInputvalidierung 3TestKnickpunktPruefungKlassifizierung 2TestFehlendKnickpunkteViaVerbindungslinien 4
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtrahiereGmlIds
test_xlink_href_format
URN-Format 'urn:adv:oid:DEABC123' → nur 'DEABC123' extrahiert.grün
TestExtrahiereGmlIds
test_plain_gml_id
Plain-GML-ID ohne Doppelpunkt → direkt übernommen.grün
TestExtrahiereGmlIds
test_empty_value_skipped
Leerer gehoertZu-Wert wird übersprungen.grün
TestExtrahiereGmlIds
test_deduplication
Doppelte Referenzen werden dedupliziert.grün
TestExtrahiereGmlIds
test_sorted_output
Rückgabe ist aufsteigend sortiert.grün
TestGrenzpunktAlkis
test_no_vg_layer_returns_none
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_invalid_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_empty_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_no_gml_ids_returns_pta_only
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_grenzpunkt_batch_empty
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_success_returns_both_layers
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_extent_derived_from_vg_layer
Wenn kein extent übergeben → Extent aus VG-Layer holen.rot
TestGrenzpunkteGemarkungsweiseStil
test_stil_wird_nach_dem_laden_auf_den_ergebnis_layer_angewendet
Prüft, dass grenzpunkte_gemarkungsweise() nach dem Laden apply_style() mit dem neuen ALKIS-Grenzpunkt-Stil auf den fertigen Join-Layer anwendet (#53).grün

Unit-Tests für `sn_verfahrensgebiet.functions.knickpunkt_pruefung`.

TestExtrahiereKnickpunkte 4TestKnickpunktPruefungInputvalidierung 3TestKnickpunktPruefungKlassifizierung 2TestFehlendKnickpunkteViaVerbindungslinien 4
flowchart TD
   root(["test_knickpunkt_pruefung.py"])
   class root rootnode
   root --> g1["TestExtrahiereKnickpunkte (4)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index abcac7b..23d6be2 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
 
-Gesamt: 3 Pakete, 31 Testdateien, 346 Testfälle – 301 grün, 37 rot (Baseline), 8 übersprungen.
-Mit Beschreibung (Docstring): 114 von 346 (33 %).
+Gesamt: 3 Pakete, 55 Testdateien, 648 Testfälle – 605 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 503 von 648 (78 %).
 
 ## Zusammenfassung
 
 | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
 | --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| `sn_basis` | 14 | 94 | 94 | 0 | 0 | 7 von 94 (7 %) |
-| `sn_verfahrensgebiet` | 12 | 208 | 164 | 36 | 8 | 106 von 208 (51 %) |
+| `sn_basis` | 38 | 396 | 396 | 0 | 0 | 396 von 396 (100 %) |
+| `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
 | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
-| **Summe** | **31** | **346** | **301** | **37** | **8** | **114 von 346 (33 %)** |
+| **Summe** | **55** | **648** | **605** | **35** | **8** | **503 von 648 (78 %)** |
 
 ### Legende
 
@@ -31,7 +31,167 @@ Mit Beschreibung (Docstring): 114 von 346 (33 %).
 
 ## Paket `sn_basis`
 
-Plattform-Plugin, Ordner `Plugin_SN_Basis` – 14 Testdateien mit 94 Testfällen (94 grün, 0 rot, 0 übersprungen).
+Plattform-Plugin, Ordner `Plugin_SN_Basis` – 38 Testdateien mit 396 Testfällen (396 grün, 0 rot, 0 übersprungen).
+
+### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).
+
+Testgruppen:
+
+- `CloseEventTests` (5 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `CloseEventTests.test_ruft_cleanup_aller_tabs_auf` | Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft. | grün |
+| `CloseEventTests.test_ignoriert_tabs_ohne_cleanup` | Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt. | grün |
+| `CloseEventTests.test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter` | Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert. | grün |
+| `CloseEventTests.test_setzt_toolbar_action_zurueck` | Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt. | grün |
+| `CloseEventTests.test_ohne_action_wird_nichts_zurueckgesetzt` | Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_anbindung.py`
+
+Worum es in der Datei geht: Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):
+
+Testgruppen:
+
+- `TestMehrfachauswahl` (1 Testfälle): — (Docstring fehlt)
+- `TestDruckanbindung` (4 Testfälle): — (Docstring fehlt)
+- `TestBasemapBereich` (8 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestMehrfachauswahl.test_mock_modus_liefert_vorauswahl_in_listenreihenfolge` | Prüft, dass der Mehrfachauswahl-Dialog im Mock-Modus die vorausgewählten Schlüssel in Listenreihenfolge liefert. | grün |
+| `TestDruckanbindung.test_basemap_legende_nur_fuer_38_und_41_und_bei_eingeschaltetem_schalter` | Prüft, dass nur §-38- und §-41-Vorlagen eine Basemap-Legende bekommen und nur, wenn der Schalter im Druck-Tab an ist. | grün |
+| `TestDruckanbindung.test_layout_wird_markiert_und_legende_erzeugt` | Prüft, dass PrintLayout das Layout als Basemap-Nutzer kennzeichnet und die Basemap-Legende erzeugen lässt. | grün |
+| `TestDruckanbindung.test_fehler_der_basemap_legende_verhindert_die_vorlage_nicht` | Prüft, dass ein Fehler beim Erzeugen der Basemap-Legende nur als Warnung gemeldet wird und keine Ausnahme durchreicht. | grün |
+| `TestDruckanbindung.test_druck_tab_aktualisiert_basemap_bereich_falls_vorhanden` | Prüft, dass der Druck-Tab den Basemap-Bereich neu einliest und ohne Bereich nichts tut. | grün |
+| `TestBasemapBereich.test_auswertebereich_wird_gespeichert` | Prüft, dass die Wahl des Auswertebereichs in der Projektvariable landet. | grün |
+| `TestBasemapBereich.test_aktualisieren_ohne_vorlage_warnt` | Prüft, dass „Basemap-Legende aktualisieren“ ohne vorhandene Vorlage warnt und nichts auswertet. | grün |
+| `TestBasemapBereich.test_aktualisieren_wertet_gewaehlte_vorlage_aus` | Prüft, dass „Basemap-Legende aktualisieren“ die gewählte Vorlage auswertet und den Erfolg meldet. | grün |
+| `TestBasemapBereich.test_stil_laden_uebernimmt_legendenauswahl` | Prüft, dass das Laden einer Stilvorlage den Layerstil ersetzt und ihre Legendenauswahl in das Projekt übernimmt. | grün |
+| `TestBasemapBereich.test_stil_laden_bricht_bei_fremder_vorlage_ab` | Prüft, dass eine Vorlage mit unbekannten Styles nur nach Bestätigung geladen wird. | grün |
+| `TestBasemapBereich.test_vorhandene_vorlage_wird_nur_nach_rueckfrage_ersetzt` | Prüft, dass eine gleichnamige Stilvorlage ohne Bestätigung nicht überschrieben wird. | grün |
+| `TestBasemapBereich.test_speichern_mit_aktueller_legendenauswahl` | Prüft, dass eine neue Stilvorlage zusammen mit der aktuellen Legendenauswahl gespeichert wird. | grün |
+| `TestBasemapBereich.test_stil_speichern_ohne_basemap_layer_warnt` | Prüft, dass ohne Basemap-Layer keine Stilvorlage gespeichert, sondern gewarnt wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_kacheln.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_kacheln.py – kachelweise Auswertung des Basemap-Layers (Basis #88).
+
+Testgruppen:
+
+- `TestSammleObjekte` (4 Testfälle): — (Docstring fehlt)
+- `TestZoomUndStile` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestSammleObjekte.test_gleiche_objekte_aus_mehreren_kacheln_werden_entdoppelt` | Prüft, dass Objekte mit gleichem Quell-Layer, Geometrietyp und Attributen über Kachelgrenzen hinweg nur einmal gezählt werden. | grün |
+| `TestSammleObjekte.test_kacheln_ausserhalb_der_geometrie_werden_uebersprungen` | Prüft, dass Kacheln ohne Schnitt mit dem Auswertebereich nicht abgefragt werden. | grün |
+| `TestSammleObjekte.test_abbruch_liefert_none_und_hebt_auswahl_auf` | Prüft, dass ein Abbruch über den Fortschritt None liefert und die Auswahl auf dem Basemap-Layer trotzdem aufgehoben wird. | grün |
+| `TestSammleObjekte.test_anzahl_kacheln_aus_kachelbereich` | Prüft, dass die Kachelzahl aus dem Kachelbereich der Geometrie berechnet wird. | grün |
+| `TestZoomUndStile.test_zoomstufen_wie_im_qgis_renderer` | Prüft, dass Kachelzoom begrenzt, Darstellungszoom unbegrenzt und der gebrochene Zoom aus dem Maßstab bestimmt werden. | grün |
+| `TestZoomUndStile.test_stile_aus_layer_liest_renderer_styles` | Prüft, dass Name, Quell-Layer, Geometrietyp, Zoombereich, Schalter und Filter der Renderer-Styles übernommen werden. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_katalog.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_katalog.py – Legendenkatalog aus der basemap.de-Style-JSON (Basis #88).
+
+Testgruppen:
+
+- `TestRendererStil` (3 Testfälle): — (Docstring fehlt)
+- `TestErzeugeKatalog` (6 Testfälle): — (Docstring fehlt)
+- `TestSymbolwahlUndPruefung` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestRendererStil.test_beschriftung_und_3d_sind_keine_renderer_stile` | Prüft, dass reine Beschriftungen und 3D-Gebäude nicht als Renderer-Style gelten, Icon-Symbole und Flächen schon. | grün |
+| `TestRendererStil.test_ausschluss_ueber_liste_und_praefix` | Prüft, dass Style-IDs über die Ausschlussliste und über Präfixe (Nummernschilder) ausgeschlossen werden. | grün |
+| `TestRendererStil.test_normalisierung_entfernt_variantenkennungen` | Prüft, dass Variantenkennungen wie Kontur, Brücke oder Breitenangaben beim Normalisieren einer Style-ID entfallen. | grün |
+| `TestErzeugeKatalog.test_kontur_decker_und_bruecke_bilden_einen_eintrag` | Prüft, dass Kontur-, Decker-, Fahrbahn- und Brückenvarianten derselben Straßenklasse genau einen Legendeneintrag bilden. | grün |
+| `TestErzeugeKatalog.test_zustand_im_bau_wird_eigener_eintrag` | Prüft, dass „im Bau“ einen eigenen Legendeneintrag mit Zustandsangabe in der Bezeichnung bekommt. | grün |
+| `TestErzeugeKatalog.test_punkt_und_flaeche_derselben_objektart_werden_zusammengefasst` | Prüft, dass das Kran-Symbol aus Bauwerkspunkt und Bauwerksfläche in einem Eintrag landet. | grün |
+| `TestErzeugeKatalog.test_ausgeschlossene_stile_fehlen_im_katalog` | Prüft, dass Hintergrund, Beschriftungen, Nummernschilder und 3D-Gebäude keinem Eintrag zugeordnet werden. | grün |
+| `TestErzeugeKatalog.test_fachliche_nachbearbeitung_bleibt_beim_erneuten_lauf_erhalten` | Prüft, dass Bezeichnung, Gruppe, Zusammenführung und Symbolwahl eines bestehenden Katalogs bei einem erneuten Lauf erhalten bleiben. | grün |
+| `TestErzeugeKatalog.test_entfallene_style_ids_werden_gemeldet` | Prüft, dass eine Style-ID, die in der neuen Style-JSON fehlt, als entfallen gemeldet und aus dem Katalog entfernt wird. | grün |
+| `TestSymbolwahlUndPruefung.test_symbol_stile_je_rolle_nur_grundvariante` | Prüft, dass für das Legendensymbol je Rolle (Kontur, Decker) nur die erste Grundvariante gewählt wird. | grün |
+| `TestSymbolwahlUndPruefung.test_pruefung_findet_doppelte_zuordnung_und_unbekannte_symbole` | Prüft, dass die Katalogprüfung doppelt zugeordnete Style-IDs und Symbol-Styles außerhalb des Katalogs meldet. | grün |
+| `TestSymbolwahlUndPruefung.test_ausgelieferter_katalog_ist_konsistent` | Prüft, dass der ausgelieferte Katalog data/basemap_legende.json ohne Fehler geprüft wird und Kreisstraße mit Kontur und Decker enthält. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_legende.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_legende.py – Zuordnung, Auswahl und Ablauf der Basemap-Legende (Basis #88).
+
+Testgruppen:
+
+- `TestStileUndEintraege` (4 Testfälle): — (Docstring fehlt)
+- `TestAuswahlUndEinstellungen` (6 Testfälle): — (Docstring fehlt)
+- `TestLayerErkennung` (2 Testfälle): — (Docstring fehlt)
+- `TestAblauf` (6 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestStileUndEintraege.test_stil_aktiv_nur_eingeschaltet_und_im_zoombereich` | Prüft, dass ein Style nur aktiv ist, wenn er eingeschaltet ist und die Zoomstufe im Bereich liegt (-1 = unbegrenzt). | grün |
+| `TestStileUndEintraege.test_getroffene_stile_pruefen_layer_geometrie_zoom_und_filter` | Prüft, dass ein Style nur Objekte seines Quell-Layers und Geometrietyps zählt, deren Filter zutrifft, und nur bei passender Zoomstufe. | grün |
+| `TestStileUndEintraege.test_eintraege_fassen_styles_zusammen_und_sortieren_nach_gruppe` | Prüft, dass mehrere Styles eines Katalogeintrags einen Eintrag ergeben und die Einträge nach Gruppen- und Katalogreihenfolge sortiert sind. | grün |
+| `TestStileUndEintraege.test_ausgeschlossene_und_unbekannte_styles` | Prüft, dass ausgeschlossene Styles entfallen und unbekannte Styles als nicht bekannter Eintrag in „Sonstiges“ landen. | grün |
+| `TestAuswahlUndEinstellungen.test_vorauswahl_bekannte_an_unbekannte_aus` | Prüft, dass ohne gespeicherte Auswahl Katalogeinträge vorausgewählt und unbekannte Styles abgewählt sind. | grün |
+| `TestAuswahlUndEinstellungen.test_auswahl_wird_gemerkt_und_vorbelegt` | Prüft, dass ab- und angewählte Einträge in der Projektvariable gespeichert und beim nächsten Mal so vorbelegt werden. | grün |
+| `TestAuswahlUndEinstellungen.test_merken_ergaenzt_die_bisherige_auswahl` | Prüft, dass das Merken die Auswahl nicht angezeigter Einträge aus früheren Läufen behält. | grün |
+| `TestAuswahlUndEinstellungen.test_kaputte_projektvariable_ergibt_leere_auswahl` | Prüft, dass eine nicht lesbare Projektvariable als leere Auswahl gilt. | grün |
+| `TestAuswahlUndEinstellungen.test_auswertebereich_standard_und_umstellung` | Prüft, dass der Auswertebereich standardmäßig das Verfahrensgebiet ist, umgestellt werden kann und unbekannte Werte abgewiesen werden. | grün |
+| `TestAuswahlUndEinstellungen.test_legende_erzeugen_standard_an` | Prüft, dass das Erzeugen der Basemap-Legende standardmäßig eingeschaltet ist und abgeschaltet werden kann. | grün |
+| `TestLayerErkennung.test_erkennung_ueber_quelle_und_namen` | Prüft, dass der Basemap-Layer an Vektorkacheln mit basemap.de-Quelle erkannt wird, der Name nur als Rückfall dient und andere Layertypen nicht zählen. | grün |
+| `TestLayerErkennung.test_sichtbare_layer_aus_thema_feste_liste_oder_layerbaum` | Prüft, dass die Layer der Karte aus dem Kartenthema, sonst aus der festen Layerliste, sonst aus dem sichtbaren Layerbaum kommen. | grün |
+| `TestAblauf.test_ohne_basemap_layer_passiert_nichts` | Prüft, dass ohne sichtbaren Basemap-Layer weder Dialog noch Legende entstehen. | grün |
+| `TestAblauf.test_gewaehlte_eintraege_werden_geschrieben_und_gemerkt` | Prüft, dass nur die im Dialog gewählten Einträge in die Legende geschrieben und die Auswahl gespeichert wird. | grün |
+| `TestAblauf.test_abbruch_im_dialog_aendert_nichts` | Prüft, dass ein Abbruch im Auswahldialog die Legende nicht verändert und keine Auswahl speichert. | grün |
+| `TestAblauf.test_rueckfall_auf_druckbereich_wird_gemeldet` | Prüft, dass ohne Verfahrensgebiet auf den Druckbereich zurückgefallen und das gemeldet wird. | grün |
+| `TestAblauf.test_ohne_objekte_keine_legende` | Prüft, dass ohne Basemap-Objekte im Auswertebereich weder Dialog noch Legende entstehen. | grün |
+| `TestAblauf.test_viele_kacheln_werden_vorher_bestaetigt` | Prüft, dass vor einer Auswertung mit sehr vielen Kacheln nachgefragt wird und eine Ablehnung die Auswertung abbricht. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_legende_layout.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_legende_layout.py – Basemap-Legende im Drucklayout (Basis #88).
+
+Testgruppen:
+
+- `TestKennzeichenUndKarte` (2 Testfälle): — (Docstring fehlt)
+- `TestSymbolwahl` (2 Testfälle): — (Docstring fehlt)
+- `TestHilfslayer` (3 Testfälle): — (Docstring fehlt)
+- `TestPlatzierung` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestKennzeichenUndKarte.test_layout_kennzeichen_setzen_und_erkennen` | Prüft, dass ein markiertes Layout als Nutzer der Basemap-Legende erkannt und in der Projektliste geführt wird. | grün |
+| `TestKennzeichenUndKarte.test_hauptkarte_ueber_id` | Prüft, dass die Karte mit der ID „Hauptkarte“ gefunden wird. | grün |
+| `TestSymbolwahl.test_symbolnamen_aus_aktiven_symbol_stilen_in_renderer_reihenfolge` | Prüft, dass die Symbol-Styles des Katalogs in Zeichenreihenfolge genommen und inaktive übergangen werden. | grün |
+| `TestSymbolwahl.test_ohne_aktive_symbol_stile_erster_getroffener_style` | Prüft, dass ohne aktiven Symbol-Style der erste tatsächlich getroffene Style das Legendensymbol liefert. | grün |
+| `TestHilfslayer.test_je_gruppe_und_geometrietyp_ein_leerer_hilfslayer` | Prüft, dass je Gruppe und Geometrietyp ein Hilfslayer außerhalb des Layerbaums entsteht, Flächen vor Linien, ohne Rückfrage beim Schließen. | grün |
+| `TestHilfslayer.test_alte_hilfslayer_werden_entfernt` | Prüft, dass die Hilfslayer einer früheren Basemap-Legende des Layouts aus dem Projekt entfernt werden. | grün |
+| `TestHilfslayer.test_ohne_eintraege_wird_vorhandene_basemap_legende_entfernt` | Prüft, dass ohne gewählte Einträge eine vorhandene Basemap-Legende entfernt und die Auswahl am Layout vermerkt wird. | grün |
+| `TestPlatzierung.test_einspaltig_ueber_der_quellenangabe` | Prüft, dass die Basemap-Legende einspaltig unten an der Quellenangabe verankert wird, wenn der Platz reicht. | grün |
+| `TestPlatzierung.test_mehrspaltig_wenn_hoehe_nicht_reicht` | Prüft, dass die Legende zwei- oder dreispaltig wird, wenn sie einspaltig nicht zwischen Hauptlegende und Quellenangabe passt. | grün |
+| `TestPlatzierung.test_nie_breiter_als_der_seitenrand_mit_hinweis` | Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt. | grün |
+
+### `Plugin_SN_Basis/tests/test_basemap_stilvorlagen.py`
+
+Worum es in der Datei geht: Tests für modules/basemap_stilvorlagen.py – lokale Stilvorlagen des Basemap-Layers (Basis #88).
+
+Testgruppen:
+
+- `TestStilvorlagen` (8 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestStilvorlagen.test_speichern_und_auflisten` | Prüft, dass eine gespeicherte Vorlage mit Anzeigenamen, QML-Datei und Begleitdatei aufgelistet wird. | grün |
+| `TestStilvorlagen.test_mehrere_varianten_alphabetisch` | Prüft, dass mehrere Varianten nebeneinander bestehen und alphabetisch nach Anzeigename sortiert werden. | grün |
+| `TestStilvorlagen.test_leerer_name_oder_fehler_beim_schreiben` | Prüft, dass ein leerer Name und ein Schreibfehler von QGIS als ValueError gemeldet werden. | grün |
+| `TestStilvorlagen.test_laden_setzt_stil_und_zeichnet_neu` | Prüft, dass das Laden die QML-Datei der Vorlage in den Layer lädt und neu zeichnen lässt. | grün |
+| `TestStilvorlagen.test_passung_meldet_fremde_styles` | Prüft, dass Style-Namen der Vorlage gemeldet werden, die der Ziel-Layer nicht kennt. | grün |
+| `TestStilvorlagen.test_standard_setzen_und_beim_loeschen_entfernen` | Prüft, dass eine Vorlage als Standard markiert wird und die Markierung beim Löschen der Vorlage entfällt. | grün |
+| `TestStilvorlagen.test_fehlende_begleitdatei_wird_toleriert` | Prüft, dass eine QML-Datei ohne Begleitdatei mit ihrem Dateinamen aufgelistet wird. | grün |
+| `TestStilvorlagen.test_ohne_ordner_leere_liste` | Prüft, dass ohne angelegten Vorlagenordner eine leere Liste zurückkommt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_bootstrap.py`
 
@@ -45,16 +205,18 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestDataGrabber` (6 Testfälle): — (Docstring fehlt)
+- `TestDataGrabber` (8 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestDataGrabber.test_analyze_source_type_detects_excel` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_analyze_source_type_returns_unknown_filetype` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_analyze_source_type_detects_service_when_file_check_fails` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_analyze_source_type_returns_unbekannte_quelle_on_full_failure` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_run_returns_success_summary_with_valid_rows` | — (Docstring fehlt) | grün |
-| `TestDataGrabber.test_run_returns_failure_summary_without_rows` | — (Docstring fehlt) | grün |
+| `TestDataGrabber.test_analyze_source_type_detects_excel` | Prüft, dass eine erreichbare .xlsx-Datei als Quelltyp 'excel' erkannt wird. | grün |
+| `TestDataGrabber.test_analyze_source_type_returns_unknown_filetype` | Prüft, dass eine erreichbare Datei mit unbekannter Endung als 'unbekannter_dateityp' eingestuft wird. | grün |
+| `TestDataGrabber.test_analyze_source_type_detects_service_when_file_check_fails` | Prüft, dass eine Quelle als 'dienst' erkannt wird, wenn die Dateiprüfung fehlschlägt, die URL aber erreichbar ist. | grün |
+| `TestDataGrabber.test_analyze_source_type_returns_unbekannte_quelle_on_full_failure` | Prüft, dass eine Quelle als 'unbekannte_quelle' eingestuft wird, wenn weder Datei- noch Linkprüfung erfolgreich sind. | grün |
+| `TestDataGrabber.test_run_returns_success_summary_with_valid_rows` | Prüft, dass run() bei erfolgreichem Excel-Import eine Erfolgsmeldung mit den geladenen Zeilen liefert. | grün |
+| `TestDataGrabber.test_run_returns_failure_summary_without_rows` | Prüft, dass run() bei nicht erkennbarer Quelle eine Fehlermeldung ohne Zeilen liefert. | grün |
+| `TestDataGrabber.test_aggregate_results_mit_zeilen_liefert_erfolgsergebnis` | Prüft, dass _aggregate_results bei vorhandenen Zeilen ein Erfolgsergebnis mit Zeilenzahl liefert. | grün |
+| `TestDataGrabber.test_aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege` | Prüft, dass _aggregate_results ohne Zeilen ok=False mit Aktion 'keine_validen_eintraege' liefert. | grün |
 
 ### `Plugin_SN_Basis/tests/test_dateipruefer.py`
 
@@ -62,16 +224,135 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestDateipruefer` (6 Testfälle): — (Docstring fehlt)
+- `TestDateipruefer` (17 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestDateipruefer.test_leereingabe_erlaubt` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_leereingabe_nicht_erlaubt` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_standarddatei_vorschlagen` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_temporaer_erlaubt` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_datei_nicht_gefunden` | — (Docstring fehlt) | grün |
-| `TestDateipruefer.test_datei_ok` | — (Docstring fehlt) | grün |
+| `TestDateipruefer.test_leereingabe_erlaubt` | Prüft, dass eine leere Pfadangabe bei leereingabe_erlaubt=True als 'leereingabe_erlaubt' ohne Kontext gemeldet wird. | grün |
+| `TestDateipruefer.test_leereingabe_nicht_erlaubt` | Prüft, dass eine leere Pfadangabe ohne leereingabe_erlaubt als 'leereingabe_nicht_erlaubt' abgelehnt wird. | grün |
+| `TestDateipruefer.test_standarddatei_vorschlagen` | Prüft, dass bei leerer Pfadangabe mit gesetzter Standarddatei 'standarddatei_vorschlagen' mit dem Standardpfad als Kontext geliefert wird. | grün |
+| `TestDateipruefer.test_temporaer_erlaubt` | Prüft, dass eine leere Pfadangabe bei temporaer_erlaubt=True als 'temporaer_erlaubt' ohne Kontext gemeldet wird. | grün |
+| `TestDateipruefer.test_datei_nicht_gefunden` | Prüft, dass ein nicht existierender Dateipfad als 'datei_nicht_gefunden' mit dem Pfad als Kontext abgelehnt wird. | grün |
+| `TestDateipruefer.test_datei_ok` | Prüft, dass ein existierender Dateipfad als 'ok' mit dem Pfad als Kontext bestätigt wird. | grün |
+| `TestDateipruefer.test_erkenne_dateityp_unterscheidet_excel_datenbank_und_unbekannt` | Prüft, dass erkenne_dateityp .xlsx als 'excel', .gpkg/.sqlite als 'datenbank' und Rest als 'unbekannt' erkennt. | grün |
+| `TestDateipruefer.test_pfad_verbindet_relativen_pfad_mit_basispfad` | Prüft, dass _pfad einen relativen Pfad mit dem Basisverzeichnis verbindet. | grün |
+| `TestDateipruefer.test_pfad_laesst_absoluten_pfad_unveraendert` | Prüft, dass _pfad einen bereits absoluten Pfad nicht mit dem Basisverzeichnis verbindet. | grün |
+| `TestDateipruefer.test_ist_gueltiger_gpkg_pfad_akzeptiert_vorhandenes_verzeichnis` | Prüft, dass ein .gpkg-Pfad mit vorhandenem Zielverzeichnis als gültig gilt. | grün |
+| `TestDateipruefer.test_ist_gueltiger_gpkg_pfad_lehnt_falsche_endung_ab` | Prüft, dass ein Pfad ohne .gpkg-Endung als ungültig gilt, auch bei vorhandenem Verzeichnis. | grün |
+| `TestDateipruefer.test_ist_gueltiger_gpkg_pfad_lehnt_fehlendes_verzeichnis_ab` | Prüft, dass ein .gpkg-Pfad in einem nicht vorhandenen Verzeichnis als ungültig gilt. | grün |
+| `TestDateipruefer.test_ist_leer_erkennt_none_und_nicht_string_als_leer` | Prüft, dass _ist_leer sowohl None als auch einen Nicht-String-Wert als leer meldet. | grün |
+| `TestDateipruefer.test_ist_leer_erkennt_reinen_whitespace_als_leer` | Prüft, dass _ist_leer eine Eingabe aus reinen Leerzeichen als leer meldet. | grün |
+| `TestDateipruefer.test_ist_leer_meldet_pfad_mit_sichtbaren_zeichen_nicht_als_leer` | Prüft, dass _ist_leer einen Pfad mit sichtbaren Zeichen nicht als leer meldet. | grün |
+| `TestDateipruefer.test_leere_eingabe_bevorzugt_leereingabe_erlaubt_vor_standarddatei` | Prüft, dass bei leerer Eingabe leereingabe_erlaubt Vorrang vor einer gesetzten Standarddatei hat. | grün |
+| `TestDateipruefer.test_leere_eingabe_bevorzugt_standarddatei_vor_temporaer_erlaubt` | Prüft, dass bei leerer Eingabe eine gesetzte Standarddatei Vorrang vor temporaer_erlaubt hat. | grün |
+
+### `Plugin_SN_Basis/tests/test_datenabruf.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/Datenabruf.py (Datenabruf-Kernpipeline, Issue #63).
+
+Testgruppen:
+
+- `DetermineSpatialFilterTests` (7 Testfälle): — (Docstring fehlt)
+- `BuildProviderUrlTests` (9 Testfälle): — (Docstring fehlt)
+- `FetchFeaturesTests` (5 Testfälle): — (Docstring fehlt)
+- `DatenabrufOrchestrierungTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `DetermineSpatialFilterTests.test_raumfilter_ohne_liefert_none` | Prüft, dass Raumfilter 'ohne' unabhängig vom Layer immer None liefert. | grün |
+| `DetermineSpatialFilterTests.test_fehlender_layer_liefert_none` | Prüft, dass ohne Verfahrensgebiet-Layer kein Raumfilter gebaut wird. | grün |
+| `DetermineSpatialFilterTests.test_unbekannter_raumfilter_liefert_none` | Prüft, dass ein unbekannter Raumfilter-Wert None liefert, statt einen Fehler zu werfen. | grün |
+| `DetermineSpatialFilterTests.test_verfahrensgebiet_liefert_extent_und_crs` | Prüft, dass Raumfilter 'Verfahrensgebiet' Extent und CRS des Verfahrensgebiet-Layers liefert. | grün |
+| `DetermineSpatialFilterTests.test_verfahrensgebiet_ohne_extent_liefert_none` | Prüft, dass ein Verfahrensgebiet-Layer ohne ermittelbaren Extent None liefert. | grün |
+| `DetermineSpatialFilterTests.test_pufferlayer_liefert_extent_des_pufferlayers` | Prüft, dass Raumfilter 'Pufferlayer' Extent und CRS des erzeugten Pufferlayers liefert. | grün |
+| `DetermineSpatialFilterTests.test_pufferlayer_ohne_puffer_liefert_none` | Prüft, dass 'Pufferlayer' None liefert, wenn kein Pufferlayer erzeugt werden konnte. | grün |
+| `BuildProviderUrlTests.test_wms_liefert_link_unveraendert` | Prüft, dass bei Provider WMS der Link unverändert durchgereicht wird, auch mit Raumfilter. | grün |
+| `BuildProviderUrlTests.test_url_praefix_wird_entfernt` | Prüft, dass ein vorangestelltes 'url=' aus dem Link entfernt wird. | grün |
+| `BuildProviderUrlTests.test_wfs_baut_standardparameter_und_bbox_mit_crs` | Prüft, dass WFS-Abrufe Standardparameter, umbenanntes TYPENAMES und BBOX mit CRS-URN erhalten. | grün |
+| `BuildProviderUrlTests.test_wfs_repariert_doppeltes_fragezeichen` | Prüft, dass ein zweites '?' im WFS-Link zu '&' korrigiert wird, statt eine ungültige URL zu erzeugen. | grün |
+| `BuildProviderUrlTests.test_wfs_ohne_raumfilter_setzt_keine_bbox` | Prüft, dass WFS ohne Raumfilter (bbox_geom=None) keinen BBOX-Parameter setzt. | grün |
+| `BuildProviderUrlTests.test_rest_provider_haengt_query_endpunkt_an` | Prüft, dass REST/ArcGIS-Links ohne '/query'-Endpunkt automatisch ergänzt werden. | grün |
+| `BuildProviderUrlTests.test_rest_provider_mit_bbox_baut_geometry_envelope_und_srid` | Prüft, dass REST-Abrufe mit Raumfilter eine Geometry-Envelope und inSR/outSR aus dem CRS setzen. | grün |
+| `BuildProviderUrlTests.test_default_provider_haengt_generischen_bbox_parameter_an` | Prüft, dass ein unbekannter Provider mit Raumfilter einen generischen bbox-Parameter erhält. | grün |
+| `BuildProviderUrlTests.test_default_provider_ohne_raumfilter_laesst_query_unveraendert` | Prüft, dass ein unbekannter Provider ohne Raumfilter keinen bbox-Parameter ergänzt. | grün |
+| `FetchFeaturesTests.test_wms_liefert_keine_features_ohne_fehler` | Prüft, dass bei Provider WMS kein Featureabruf stattfindet und kein Fehler gemeldet wird. | grün |
+| `FetchFeaturesTests.test_abbruch_vor_abruf_liefert_fehlermeldung` | Prüft, dass ein bereits gesetzter Abbruch vor dem eigentlichen Abruf erkannt wird. | grün |
+| `FetchFeaturesTests.test_json_featurecollection_wird_geparst` | Prüft, dass eine JSON-FeatureCollection-Antwort in eine Feature-Liste umgewandelt wird. | grün |
+| `FetchFeaturesTests.test_netzwerkfehler_wird_als_fehlermeldung_gemeldet` | Prüft, dass ein fehlgeschlagener HTTP-Abruf eine Fehlermeldung statt einer Ausnahme liefert. | grün |
+| `FetchFeaturesTests.test_wfs_xml_liefert_anzahl_aus_numberofmatched` | Prüft, dass eine WFS-XML-Antwort ohne JSON die Trefferzahl aus numberOfMatched übernimmt. | grün |
+| `DatenabrufOrchestrierungTests.test_fehlende_pflichtfelder_werden_als_ungueltig_gemeldet` | Prüft, dass eine Zeile ohne Link/Provider mit Aktion 'pflichtfelder_fehlen' gemeldet und übersprungen wird. | grün |
+| `DatenabrufOrchestrierungTests.test_wms_zeile_ohne_features_landet_in_ausserhalb` | Prüft, dass eine erfolgreich abgerufene WMS-Zeile mit 0 Features als 'ausserhalb' gezählt wird. | grün |
+| `DatenabrufOrchestrierungTests.test_abbruch_ueber_progress_stoppt_weitere_zeilen` | Prüft, dass ein Abbruch über das Progress-Objekt die Verarbeitung weiterer Zeilen stoppt. | grün |
+
+### `Plugin_SN_Basis/tests/test_datenschreiber.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/Datenschreiber.py (Schreib-/Ladepipeline, Issue #63).
+
+Testgruppen:
+
+- `WriteLayerToGpkgTests` (2 Testfälle): — (Docstring fehlt)
+- `ResolveStylePathTests` (3 Testfälle): — (Docstring fehlt)
+- `SchreibeDatenTests` (4 Testfälle): — (Docstring fehlt)
+- `LadeLayerTests` (3 Testfälle): — (Docstring fehlt)
+- `SchreibeLogTests` (2 Testfälle): — (Docstring fehlt)
+- `StoreStyleInGpkgTests` (4 Testfälle): Tests für Datenschreiber._store_style_in_gpkg (Issue #70, nachgezogen aus #63).
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `WriteLayerToGpkgTests.test_ungueltiger_layer_liefert_fehlermeldung` | Prüft, dass ein ungültiger Layer eine Fehlermeldung statt eines Absturzes liefert. | grün |
+| `WriteLayerToGpkgTests.test_gueltiger_layer_wird_ohne_fehler_geschrieben` | Prüft, dass ein gültiger Layer im Mock-Modus ohne Fehlermeldung geschrieben wird. | grün |
+| `ResolveStylePathTests.test_leerer_stilpfad_liefert_none` | Prüft, dass ein leerer oder fehlender Stilpfad None liefert. | grün |
+| `ResolveStylePathTests.test_absoluter_vorhandener_stilpfad_wird_aufgeloest` | Prüft, dass ein absoluter, vorhandener Stilpfad normalisiert und unverändert erkannt wird. | grün |
+| `ResolveStylePathTests.test_nicht_vorhandener_stilpfad_liefert_none` | Prüft, dass ein nicht vorhandener Stilpfad None liefert, statt einen ungültigen Pfad zurückzugeben. | grün |
+| `SchreibeDatenTests.test_ohne_speicherort_wirft_valueerror` | Prüft, dass schreibe_Daten ohne Speicherort einen ValueError wirft. | grün |
+| `SchreibeDatenTests.test_eintrag_ohne_gueltigen_layer_wird_uebersprungen` | Prüft, dass ein Eintrag ohne gültigen Layer übersprungen wird und ein Fehlerergebnis meldet. | grün |
+| `SchreibeDatenTests.test_erfolgreicher_eintrag_liefert_layer_path_und_thema` | Prüft, dass ein gültiger Layer einen Layer-Path mit GPKG-Pfad und Layername liefert. | grün |
+| `SchreibeDatenTests.test_ohne_thema_wird_ident_als_layername_verwendet` | Prüft, dass ohne passendes Thema in processed_results der ident als Layername verwendet wird. | grün |
+| `LadeLayerTests.test_ohne_layer_path_wird_uebersprungen` | Prüft, dass ein Eintrag ohne layer_path übersprungen wird, ohne einen Fehler zu melden. | grün |
+| `LadeLayerTests.test_gueltiger_layer_wird_dem_projekt_hinzugefuegt` | Prüft, dass ein gültig ladbarer Layer ohne Fehlermeldung verarbeitet wird. | grün |
+| `LadeLayerTests.test_ungueltiger_layer_meldet_layer_nicht_gefunden` | Prüft, dass ein ungültig ladbarer Layer als 'layer_nicht_gefunden' gemeldet wird. | grün |
+| `SchreibeLogTests.test_ohne_speicherort_wirft_valueerror` | Prüft, dass schreibe_log ohne Speicherort einen ValueError wirft. | grün |
+| `SchreibeLogTests.test_schreibt_json_datei_mit_ergebnissen_und_gibt_pfad_zurueck` | Prüft, dass schreibe_log eine lesbare JSON-Log-Datei erzeugt und deren Pfad zurückgibt. | grün |
+| `StoreStyleInGpkgTests.test_legt_tabelle_an_und_speichert_stil_als_default` | Prüft, dass _store_style_in_gpkg die layer_styles-Tabelle anlegt und den Stil als Default speichert. | grün |
+| `StoreStyleInGpkgTests.test_setzt_bisherigen_default_stil_desselben_layers_zurueck` | Prüft, dass _store_style_in_gpkg den useAsDefault-Wert vorhandener Stile desselben Layers zurücksetzt. | grün |
+| `StoreStyleInGpkgTests.test_liest_geometriespalte_ueber_geometrycolumn_aus` | Prüft, dass _store_style_in_gpkg die Geometriespalte eines Layers mit geometryColumn() übernimmt. | grün |
+| `StoreStyleInGpkgTests.test_fehlender_stilpfad_meldet_fehler_ueber_pruefmanager_statt_absturz` | Prüft, dass ein nicht lesbarer Stilpfad über den Pruefmanager gemeldet wird statt eine Exception zu werfen. | grün |
+
+### `Plugin_SN_Basis/tests/test_dockmanager.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/dockmanager.py (nur ein Dock gleichzeitig sichtbar, Issue #65).
+
+Testgruppen:
+
+- `DockManagerShowTests` (6 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `DockManagerShowTests.test_none_dock_meldet_fehler_und_zeigt_nichts_an` | Prüft, dass ein fehlendes Dock-Widget (None) einen Fehler meldet und nichts anzeigt. | grün |
+| `DockManagerShowTests.test_setzt_objectname_wenn_leer` | Prüft, dass ein Dock ohne Namen einen technischen Namen mit dem Präfix 'sn_dock_' erhält. | grün |
+| `DockManagerShowTests.test_entfernt_andere_sn_docks_aber_nicht_sich_selbst` | Prüft, dass andere sn_basis-Docks entfernt werden, das neue Dock und fremde Widgets aber unangetastet bleiben. | grün |
+| `DockManagerShowTests.test_zeigt_neues_dock_mit_default_area_an` | Prüft, dass ein neues Dock mit der Standard-Area (DockAreaRight) angezeigt wird. | grün |
+| `DockManagerShowTests.test_fehler_beim_entfernen_wird_als_warnung_gemeldet` | Prüft, dass ein Fehler beim Entfernen alter Docks nur gewarnt und das neue Dock trotzdem angezeigt wird. | grün |
+| `DockManagerShowTests.test_fehler_beim_anzeigen_wird_gemeldet` | Prüft, dass ein Fehler beim Anzeigen des Docks als Fehler gemeldet wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_excel_importer.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/excel_importer.py (Excel-Import für Linklisten, Issue #63).
+
+Testgruppen:
+
+- `ImportXlsxTests` (5 Testfälle): — (Docstring fehlt)
+- `ExcelImporterKonstruktorTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `ImportXlsxTests.test_erfolgreicher_import_liefert_zeilen_als_dicts` | Prüft, dass eine gültige Excel-Datei als Liste von Dicts mit den Header-Spalten geliefert wird. | grün |
+| `ImportXlsxTests.test_fehlende_datei_liefert_leere_liste` | Prüft, dass eine nicht vorhandene Datei eine leere Liste liefert, statt eine Ausnahme zu werfen. | grün |
+| `ImportXlsxTests.test_arbeitsblatt_ohne_header_liefert_leere_liste_und_meldet_kein_header` | Prüft, dass ein leeres Arbeitsblatt ohne Header-Zeile 'kein_header' meldet und eine leere Liste liefert. | grün |
+| `ImportXlsxTests.test_header_zeile_nur_leere_werte_liefert_kein_header` | Prüft, dass eine Header-Zeile aus ausschließlich leeren Zellen ebenfalls als 'kein_header' gilt. | grün |
+| `ImportXlsxTests.test_nicht_lesbare_datei_liefert_leere_liste_und_meldet_read_error` | Prüft, dass eine Datei mit ungültigem Excel-Inhalt 'read_error' meldet und eine leere Liste liefert. | grün |
+| `ExcelImporterKonstruktorTests.test_ohne_dateipfad_wirft_valueerror` | Prüft, dass ExcelImporter ohne Dateipfad einen ValueError wirft. | grün |
+| `ExcelImporterKonstruktorTests.test_ohne_pruefmanager_wirft_valueerror` | Prüft, dass ExcelImporter ohne Pruefmanager einen ValueError wirft. | grün |
 
 ### `Plugin_SN_Basis/tests/test_laenderkennung.py`
 
@@ -83,11 +364,65 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestGetLaenderkennung.test_bekannter_praefix_gibt_kennung_zurueck` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_praefix_gross_klein_unabhaengig` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_unbekannter_praefix_wirft_keyerror` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet` | — (Docstring fehlt) | grün |
-| `TestGetLaenderkennung.test_mapping_enthaelt_sachsen` | — (Docstring fehlt) | grün |
+| `TestGetLaenderkennung.test_bekannter_praefix_gibt_kennung_zurueck` | Prüft, dass der Präfix 'sn' die ALKIS-Länderkennung '14' für Sachsen liefert. | grün |
+| `TestGetLaenderkennung.test_praefix_gross_klein_unabhaengig` | Prüft, dass die Präfixsuche groß-/kleinschreibungsunabhängig ist. | grün |
+| `TestGetLaenderkennung.test_unbekannter_praefix_wirft_keyerror` | Prüft, dass ein unbekannter Präfix einen KeyError auslöst. | grün |
+| `TestGetLaenderkennung.test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet` | Prüft, dass ohne Argument der Präfix aus dem Modulnamen des Aufrufers abgeleitet wird. | grün |
+| `TestGetLaenderkennung.test_mapping_enthaelt_sachsen` | Prüft, dass die Mapping-Tabelle LAENDERKENNUNGEN einen Eintrag für Sachsen ('sn' -> '14') enthält. | grün |
+
+### `Plugin_SN_Basis/tests/test_landkreise.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/landkreise.py::_lade_landkreise (Issue #65).
+
+Testgruppen:
+
+- `LadeLandkreiseTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `LadeLandkreiseTests.test_laedt_die_echte_landkreisliste_nicht_leer` | Prüft, dass _lade_landkreise die echte landkreise.json als nicht-leere Liste von (Kennzahl, Name)-Tupeln liefert. | grün |
+| `LadeLandkreiseTests.test_fehlende_datei_liefert_leere_liste` | Prüft, dass eine nicht lesbare Datenquelle eine leere Liste liefert, statt eine Ausnahme zu werfen. | grün |
+
+### `Plugin_SN_Basis/tests/test_layerloader.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/LayerLoader.py (Layer erzeugen/filtern/laden, Issue #63).
+
+Testgruppen:
+
+- `CreateLayerTests` (7 Testfälle): — (Docstring fehlt)
+- `TransformGeometryTests` (3 Testfälle): — (Docstring fehlt)
+- `TransformExtentTests` (2 Testfälle): — (Docstring fehlt)
+- `FilterByExtentTests` (6 Testfälle): — (Docstring fehlt)
+- `FilterByLayerTests` (4 Testfälle): — (Docstring fehlt)
+- `AddToProjectTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `CreateLayerTests.test_wfs_baut_uri_ohne_getfeature_parameter` | Prüft, dass für WFS die GetFeature-spezifischen Parameter aus der URI entfernt werden. | grün |
+| `CreateLayerTests.test_wfs_mit_url_praefix_wird_nicht_doppelt_gesetzt` | Prüft, dass ein bereits vorhandenes 'url='-Präfix im WFS-Link nicht verdoppelt wird. | grün |
+| `CreateLayerTests.test_wms_baut_url_praefix_und_nutzt_rasterlayer` | Prüft, dass WMS-Layer über QgsRasterLayer mit 'url='-Präfix erzeugt werden. | grün |
+| `CreateLayerTests.test_rest_featureserver_ergaenzt_layer_index` | Prüft, dass ein REST-Link auf '.../FeatureServer' automatisch um '/0' ergänzt wird. | grün |
+| `CreateLayerTests.test_ogr_provider_nutzt_link_direkt` | Prüft, dass ein OGR-Provider den Link unverändert an QgsVectorLayer übergibt. | grün |
+| `CreateLayerTests.test_ungueltiger_layer_meldet_layer_nicht_verfuegbar` | Prüft, dass ein ungültig erzeugter Layer als 'layer_nicht_verfuegbar' gemeldet wird und None liefert. | grün |
+| `CreateLayerTests.test_ausnahme_beim_erstellen_wird_abgefangen` | Prüft, dass eine Ausnahme beim Layer-Erstellen abgefangen und als Fehlerergebnis gemeldet wird. | grün |
+| `TransformGeometryTests.test_fehlender_layer_liefert_geometrie_unveraendert` | Prüft, dass ohne Quell- oder Ziel-Layer die Geometrie unverändert zurückgegeben wird. | grün |
+| `TransformGeometryTests.test_gleiche_crs_liefert_geometrie_unveraendert` | Prüft, dass bei identischem CRS von Quelle und Ziel keine Transformation stattfindet. | grün |
+| `TransformGeometryTests.test_unterschiedliche_crs_klont_und_transformiert_geometrie` | Prüft, dass bei unterschiedlichem CRS eine geklonte, transformierte Geometrie geliefert wird. | grün |
+| `TransformExtentTests.test_gleiche_crs_liefert_extent_unveraendert` | Prüft, dass bei identischem CRS von Quelle und Ziel der Extent unverändert bleibt. | grün |
+| `TransformExtentTests.test_unterschiedliche_crs_nutzt_coordinate_transform` | Prüft, dass bei unterschiedlichem CRS der Extent über QgsCoordinateTransform umgerechnet wird. | grün |
+| `FilterByExtentTests.test_ungueltiger_layer_wird_unveraendert_zurueckgegeben` | Prüft, dass ein ungültiger Layer von filter_by_extent unverändert zurückgegeben wird. | grün |
+| `FilterByExtentTests.test_ohne_extent_wird_layer_unveraendert_zurueckgegeben` | Prüft, dass ohne Extent (None) der Layer unverändert zurückgegeben wird. | grün |
+| `FilterByExtentTests.test_falscher_layertyp_wird_unveraendert_zurueckgegeben` | Prüft, dass ein Nicht-Vektor-Layer (z. B. Rasterlayer) unverändert zurückgegeben wird. | grün |
+| `FilterByExtentTests.test_keine_treffer_liefert_none` | Prüft, dass ein Raumfilter ohne Treffer None statt eines leeren Layers liefert. | grün |
+| `FilterByExtentTests.test_abbruch_liefert_none_und_meldet_needs_user_action` | Prüft, dass ein Abbruch während des Raumfilters None liefert und gemeldet wird. | grün |
+| `FilterByExtentTests.test_erfolgreicher_filter_liefert_neuen_layer_mit_features` | Prüft, dass ein erfolgreicher BBOX-Filter einen neuen Speicherlayer mit den gefundenen Features liefert. | grün |
+| `FilterByLayerTests.test_ungueltiger_layer_oder_filterlayer_wird_unveraendert_zurueckgegeben` | Prüft, dass ein ungültiger Haupt- oder Filter-Layer unverändert zurückgegeben wird. | grün |
+| `FilterByLayerTests.test_filterlayer_ohne_features_liefert_none` | Prüft, dass ein Filter-Layer ohne Features (leere Vereinigungsgeometrie) None liefert. | grün |
+| `FilterByLayerTests.test_erfolgreicher_geometriefilter_liefert_neuen_layer` | Prüft, dass ein erfolgreicher Geometriefilter einen neuen Speicherlayer mit den passenden Features liefert. | grün |
+| `FilterByLayerTests.test_keine_ueberschneidung_liefert_none` | Prüft, dass ohne überschneidende Features None statt eines leeren Layers geliefert wird. | grün |
+| `AddToProjectTests.test_gueltiger_layer_wird_dem_projekt_hinzugefuegt` | Prüft, dass ein gültiger Layer über QgsProject.instance().addMapLayer hinzugefügt wird. | grün |
+| `AddToProjectTests.test_ungueltiger_layer_wird_nicht_hinzugefuegt` | Prüft, dass ein ungültiger Layer nicht ans Projekt übergeben wird. | grün |
+| `AddToProjectTests.test_none_layer_wird_nicht_hinzugefuegt` | Prüft, dass ein fehlender Layer (None) nicht ans Projekt übergeben wird. | grün |
 
 ### `Plugin_SN_Basis/tests/test_layerpruefer.py`
 
@@ -99,16 +434,16 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestLayerpruefer.test_layer_exists` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_unsichtbar` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_falscher_layertyp` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_falscher_geotyp` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_leer` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_falsches_crs` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_felder_fehlen` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_datenquelle_unerwartet` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_nicht_editierbar` | — (Docstring fehlt) | grün |
-| `TestLayerpruefer.test_layer_ok` | — (Docstring fehlt) | grün |
+| `TestLayerpruefer.test_layer_exists` | Prüft, dass ein nicht vorhandener Layer als 'layer_nicht_gefunden' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_unsichtbar` | Prüft, dass ein unsichtbarer Layer bei geforderter Sichtbarkeit als 'layer_unsichtbar' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_falscher_layertyp` | Prüft, dass ein Layer mit abweichendem Layertyp (Raster statt Vektor) als 'falscher_layertyp' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_falscher_geotyp` | Prüft, dass ein Layer mit abweichendem Geometrietyp (Punkt statt Polygon) als 'falscher_geotyp' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_leer` | Prüft, dass ein Layer ohne Features als 'layer_leer' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_falsches_crs` | Prüft, dass ein Layer mit abweichendem Koordinatenbezugssystem als 'falsches_crs' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_felder_fehlen` | Prüft, dass ein Layer mit fehlenden Pflichtfeldern als 'felder_fehlen' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_datenquelle_unerwartet` | Prüft, dass eine Datenquelle außerhalb der erlaubten Liste als 'datenquelle_unerwartet' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_nicht_editierbar` | Prüft, dass ein nicht editierbarer Layer bei geforderter Editierbarkeit als 'layer_nicht_editierbar' abgelehnt wird. | grün |
+| `TestLayerpruefer.test_layer_ok` | Prüft, dass ein Layer, der alle Anforderungen erfüllt, als 'ok' bestätigt wird. | grün |
 
 ### `Plugin_SN_Basis/tests/test_linkpruefer.py`
 
@@ -116,15 +451,108 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestLinkpruefer` (5 Testfälle): — (Docstring fehlt)
+- `TestLinkpruefer` (6 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestLinkpruefer.test_remote_link_ok` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_remote_link_error` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_remote_link_network_error` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_local_link_not_found` | — (Docstring fehlt) | grün |
-| `TestLinkpruefer.test_local_link_ok` | — (Docstring fehlt) | grün |
+| `TestLinkpruefer.test_leere_eingabe` | Prüft, dass eine leere Eingabe ohne Netzwerk-/Dateizugriff als 'leer' abgelehnt wird. | grün |
+| `TestLinkpruefer.test_remote_link_ok` | Prüft, dass eine erreichbare Remote-URL (HTTP HEAD ohne Fehler) als 'ok' bestätigt wird. | grün |
+| `TestLinkpruefer.test_remote_link_error` | Prüft, dass eine Remote-URL mit Fehlerantwort als 'url_nicht_erreichbar' abgelehnt wird. | grün |
+| `TestLinkpruefer.test_remote_link_network_error` | Prüft, dass eine ausbleibende Netzwerkantwort (None) als 'netzwerkfehler' gemeldet wird. | grün |
+| `TestLinkpruefer.test_local_link_not_found` | Prüft, dass ein nicht existierender lokaler Pfad als 'pfad_nicht_gefunden' abgelehnt wird. | grün |
+| `TestLinkpruefer.test_local_link_ok` | Prüft, dass ein existierender lokaler Pfad als 'ok' bestätigt wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_ly_existence_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/ly_existence_wrapper.py::layer_is_valid/layer_is_usable (Issue #65).
+
+Testgruppen:
+
+- `LayerIsValidTests` (5 Testfälle): — (Docstring fehlt)
+- `LayerIsUsableTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `LayerIsValidTests.test_none_ist_nicht_gueltig` | Prüft, dass layer_is_valid für None False liefert. | grün |
+| `LayerIsValidTests.test_objekt_ohne_isvalid_ist_nicht_gueltig` | Prüft, dass ein Objekt ohne isValid()-Methode als ungültig gilt. | grün |
+| `LayerIsValidTests.test_gueltiger_layer_liefert_true` | Prüft, dass ein Layer mit isValid()==True als gültig erkannt wird. | grün |
+| `LayerIsValidTests.test_ungueltiger_layer_liefert_false` | Prüft, dass ein Layer mit isValid()==False als ungültig erkannt wird. | grün |
+| `LayerIsValidTests.test_ausnahme_bei_isvalid_liefert_false` | Prüft, dass eine Ausnahme in isValid() abgefangen wird und False liefert. | grün |
+| `LayerIsUsableTests.test_none_ist_nicht_nutzbar` | Prüft, dass layer_is_usable für None False liefert. | grün |
+| `LayerIsUsableTests.test_gueltiger_layer_ist_nutzbar` | Prüft, dass ein vorhandener, gültiger Layer als nutzbar gilt. | grün |
+| `LayerIsUsableTests.test_ungueltiger_layer_ist_nicht_nutzbar` | Prüft, dass ein vorhandener, aber ungültiger Layer als nicht nutzbar gilt. | grün |
+
+### `Plugin_SN_Basis/tests/test_ly_style_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/ly_style_wrapper.py::apply_style_from_path (Issue #65).
+
+Testgruppen:
+
+- `ApplyStyleFromPathTests` (4 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `ApplyStyleFromPathTests.test_fehlender_layer_liefert_false` | Prüft, dass ein fehlender Layer (None) False liefert, ohne einen Stil zu laden. | grün |
+| `ApplyStyleFromPathTests.test_erfolgreiches_laden_liefert_true_und_triggert_repaint` | Prüft, dass ein erfolgreich geladener Stil True liefert und den Layer neu zeichnet. | grün |
+| `ApplyStyleFromPathTests.test_fehlgeschlagenes_laden_liefert_false_ohne_repaint` | Prüft, dass ein fehlgeschlagenes Laden False liefert, ohne den Layer neu zu zeichnen. | grün |
+| `ApplyStyleFromPathTests.test_ausnahme_beim_laden_wird_abgefangen` | Prüft, dass eine Ausnahme beim Laden des Stils abgefangen wird und False liefert. | grün |
+
+### `Plugin_SN_Basis/tests/test_message_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/message_wrapper.py (Issue #65).
+
+Testgruppen:
+
+- `GetMessageBarTests` (2 Testfälle): — (Docstring fehlt)
+- `PushMessageTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `GetMessageBarTests.test_liefert_objekt_mit_pushmessage_methode` | Prüft, dass _get_message_bar ein Objekt mit aufrufbarer pushMessage()-Methode liefert. | grün |
+| `GetMessageBarTests.test_faellt_bei_fehlerhaftem_iface_auf_mock_bar_zurueck` | Prüft, dass ein Fehler beim Zugriff auf iface.messageBar() auf eine funktionierende Mock-MessageBar zurückfällt. | grün |
+| `PushMessageTests.test_liefert_dict_mit_titel_text_level_und_dauer` | Prüft, dass push_message im Mock-Modus ein Dict mit den übergebenen Werten liefert. | grün |
+| `PushMessageTests.test_fehler_in_der_message_bar_wird_abgefangen` | Prüft, dass eine fehlschlagende MessageBar push_message nicht zum Absturz bringt, sondern None liefert. | grün |
+
+### `Plugin_SN_Basis/tests/test_navigation.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/navigation.py::Navigation (Issue #67, ohne init_ui – s. Docstring dort).
+
+Testgruppen:
+
+- `AddActionTests` (2 Testfälle): — (Docstring fehlt)
+- `FinalizeMenuAndToolbarTests` (2 Testfälle): — (Docstring fehlt)
+- `SetActivePluginTests` (2 Testfälle): — (Docstring fehlt)
+- `RemoveActionTests` (3 Testfälle): — (Docstring fehlt)
+- `RemoveAllTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `AddActionTests.test_ohne_plugin_group_liefert_none` | Prüft, dass add_action ohne initialisierte Aktionsgruppe None liefert und nichts anlegt. | grün |
+| `AddActionTests.test_erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu` | Prüft, dass add_action eine Action erzeugt, ihr die Gruppe zuweist und sie priorisiert in der Liste ablegt. | grün |
+| `FinalizeMenuAndToolbarTests.test_ohne_menu_oder_toolbar_tut_nichts` | Prüft, dass finalize_menu_and_toolbar ohne Menü oder Toolbar keine Ausnahme wirft. | grün |
+| `FinalizeMenuAndToolbarTests.test_sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu` | Prüft, dass finalize_menu_and_toolbar Aktionen nach Priorität sortiert in Menü und Toolbar einträgt. | grün |
+| `SetActivePluginTests.test_setzt_nur_aktive_action_auf_checked` | Prüft, dass set_active_plugin nur die übergebene Action markiert, alle anderen abwählt. | grün |
+| `SetActivePluginTests.test_ohne_aktive_action_setzt_alle_auf_false` | Prüft, dass set_active_plugin ohne aktive Action alle bestehenden Actions abwählt. | grün |
+| `RemoveActionTests.test_none_action_tut_nichts` | Prüft, dass remove_action mit None keine Ausnahme wirft und die Liste unverändert lässt. | grün |
+| `RemoveActionTests.test_entfernt_action_aus_menu_toolbar_und_liste` | Prüft, dass remove_action die Action aus Menü, Toolbar und der internen Liste entfernt. | grün |
+| `RemoveActionTests.test_ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste` | Prüft, dass remove_action ohne Menü/Toolbar trotzdem aus der internen Liste entfernt. | grün |
+| `RemoveAllTests.test_entfernt_menu_toolbar_und_setzt_zustand_zurueck` | Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt. | grün |
+| `RemoveAllTests.test_loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt` | Prüft, dass remove_all deleteLater() auf Menü und Toolbar aufruft, damit sie den Qt-Objektbaum tatsächlich verlassen (Issue #72). | grün |
+| `RemoveAllTests.test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf` | Prüft, dass remove_all ohne vorhandenes Menü/Toolbar die Wrapper nicht aufruft, aber die Liste leert. | grün |
+
+### `Plugin_SN_Basis/tests/test_os_wrapper.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/os_wrapper.py::is_case_sensitive_fs (Issue #67).
+
+Testgruppen:
+
+- `IsCaseSensitiveFsTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `IsCaseSensitiveFsTests.test_windows_ist_nicht_case_sensitiv` | Prüft, dass Windows als nicht case-sensitives Dateisystem gilt. | grün |
+| `IsCaseSensitiveFsTests.test_macos_ist_nicht_case_sensitiv` | Prüft, dass macOS als nicht case-sensitives Dateisystem gilt. | grün |
+| `IsCaseSensitiveFsTests.test_linux_ist_case_sensitiv` | Prüft, dass Linux als case-sensitives Dateisystem gilt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_print_layout.py`
 
@@ -136,11 +564,11 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestPrintLayout.test_create_legend_item_links_map_and_enables_map_filtering` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_create_legend_item_does_not_copy_project_layers_into_legend_model` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_configure_legend_size_uses_content_adaptive_size` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_configure_hauptkarte_grids_uses_wrapper_constants` | — (Docstring fehlt) | grün |
-| `TestPrintLayout.test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme` | — (Docstring fehlt) | grün |
+| `TestPrintLayout.test_create_legend_item_links_map_and_enables_map_filtering` | Prüft, dass eine erzeugte Legende mit der übergebenen Karte verknüpft ist und automatisch nach deren Sichtbarkeit gefiltert wird. | grün |
+| `TestPrintLayout.test_create_legend_item_does_not_copy_project_layers_into_legend_model` | Prüft, dass eine neu erzeugte Legende keine Projekt-Layer in ihr Legendenmodell übernimmt (leere Root-Gruppe). | grün |
+| `TestPrintLayout.test_configure_legend_size_uses_content_adaptive_size` | Prüft, dass die Legendengröße inhaltsadaptiv bestimmt wird (kein manuelles Resize) und die ermittelte Höhe zurückgegeben wird. | grün |
+| `TestPrintLayout.test_configure_hauptkarte_grids_uses_wrapper_constants` | Prüft, dass das Gitternetz der Hauptkarte mit den erwarteten Stil-, Rahmen- und Beschriftungskonstanten (nur rechts/unten beschriftet, Schriftgröße und Hintergrund) konfiguriert wird. | grün |
+| `TestPrintLayout.test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme` | Prüft, dass ein einseitiges Layout die Hauptkarte auf das übergebene Kartenthema festlegt und die Legende mit dieser Karte verknüpft bleibt. | grün |
 
 ### `Plugin_SN_Basis/tests/test_print_logic.py`
 
@@ -152,23 +580,105 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_escalates_to_a3` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_select_standard_page_layout_uses_a4_as_minimum_sheet` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_select_standard_page_layout_limits_hauptkarte_to_a3` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_select_standard_page_layout_returns_none_if_a3_is_too_small` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_for_small_page` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_keeps_large_page` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_does_plot_fit_page_true_for_standard_a4` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_does_plot_fit_page_false_if_objects_exceed_page` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_set_druckbereich_accepts_wie_kartenfenster` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_get_canvas_extent_returns_valid_extent` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_resolve_extent_uses_canvas_extent_without_buffer` | — (Docstring fehlt) | grün |
-| `TestPrintLogic.test_resolve_extent_uses_rectangle_extent_without_buffer` | — (Docstring fehlt) | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent` | Prüft, dass für einen kleinen Kartenausschnitt (Baufreigabe) das Format DIN A4 ohne Fallback gewählt wird. | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_escalates_to_a3` | Prüft, dass ein größerer Kartenausschnitt (Baufreigabe) auf DIN A3 eskaliert wird. | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback` | Prüft, dass bei auf DIN A4 begrenztem Höchstformat ein eigentlich A3-großer Ausschnitt mit Fallback-Kennzeichnung auf A4 reduziert wird. | grün |
+| `TestPrintLogic.test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent` | Prüft, dass ein Ausschnitt, der auch DIN A3 übersteigt, mit Fallback-Kennzeichnung auf DIN A3 begrenzt wird. | grün |
+| `TestPrintLogic.test_select_standard_page_layout_uses_a4_as_minimum_sheet` | Prüft, dass für einen kleinen Ausschnitt trotz Höchstgrenze DIN A3 mindestens DIN A4 als Blattgröße gewählt wird. | grün |
+| `TestPrintLogic.test_select_standard_page_layout_limits_hauptkarte_to_a3` | Prüft, dass ein die Fläche voll ausnutzender Ausschnitt auf DIN A3 als größtes zulässiges Format begrenzt wird. | grün |
+| `TestPrintLogic.test_select_standard_page_layout_returns_none_if_a3_is_too_small` | Prüft, dass kein Layout gewählt wird (None), wenn der Ausschnitt selbst DIN A3 als Höchstformat übersteigt. | grün |
+| `TestPrintLogic.test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent` | Prüft, dass für einen kleinen Ausschnitt DIN A4 gewählt und das Einpassen (force_fit) erzwungen wird. | grün |
+| `TestPrintLogic.test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm` | Prüft, dass bei aktiviertem Formfaktor (Endlosrolle) die Seitenbreite auf die maximale Rollenbreite (2000 mm) begrenzt wird. | grün |
+| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_for_small_page` | Prüft, dass eine zu kleine Seite auf die A4-Mindestgröße angehoben und als erzwungen (forced) markiert wird. | grün |
+| `TestPrintLogic.test_apply_standard_minimum_a4_fallback_keeps_large_page` | Prüft, dass eine bereits ausreichend große Seite (A3) unverändert bleibt und nicht als erzwungen markiert wird. | grün |
+| `TestPrintLogic.test_does_plot_fit_page_true_for_standard_a4` | Prüft, dass ein Kartenobjekt, das in eine DIN A4-Seite passt, als passend erkannt wird. | grün |
+| `TestPrintLogic.test_does_plot_fit_page_false_if_objects_exceed_page` | Prüft, dass ein Kartenobjekt, das die Seitenbreite überschreitet, als nicht passend erkannt wird. | grün |
+| `TestPrintLogic.test_set_druckbereich_accepts_wie_kartenfenster` | Prüft, dass die Auswahl 'wie Kartenfenster' als Projektvariable 'druckbereich' gespeichert wird. | grün |
+| `TestPrintLogic.test_get_canvas_extent_returns_valid_extent` | Prüft, dass der aktuelle Kartenausschnitt des Kartenfensters unverändert zurückgegeben wird. | grün |
+| `TestPrintLogic.test_resolve_extent_uses_canvas_extent_without_buffer` | Prüft, dass bei Druckbereich 'wie Kartenfenster' der Kartenausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird. | grün |
+| `TestPrintLogic.test_resolve_extent_uses_rectangle_extent_without_buffer` | Prüft, dass bei Druckbereich 'Rechteck' der gezeichnete Ausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_print_tab.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/tabs/print_tab.py::PrintTab (Issue #67).
+
+Testgruppen:
+
+- `SetServicesTests` (2 Testfälle): — (Docstring fehlt)
+- `RestoreStateTests` (2 Testfälle): — (Docstring fehlt)
+- `OnDruckpufferClickedTests` (3 Testfälle): — (Docstring fehlt)
+- `OnVorlageErstellenTests` (2 Testfälle): — (Docstring fehlt)
+- `ProjectSignalsTests` (3 Testfälle): — (Docstring fehlt)
+- `CleanupTests` (1 Testfälle): — (Docstring fehlt)
+- `ToggleAtlasTests` (2 Testfälle): — (Docstring fehlt)
+- `RestoreAtlasLayerSelectionTests` (2 Testfälle): — (Docstring fehlt)
+- `ThemeCollectionSignalsTests` (3 Testfälle): — (Docstring fehlt)
+- `RefreshThemaComboLiveTests` (2 Testfälle): — (Docstring fehlt)
+- `GetGespeicherteThemenTests` (2 Testfälle): — (Docstring fehlt)
+- `GetCurrentCanvasScaleTests` (3 Testfälle): — (Docstring fehlt)
+- `OnAtlasLayerChangedTests` (3 Testfälle): — (Docstring fehlt)
+- `OnAuswahlAsAtlasClickedTests` (2 Testfälle): — (Docstring fehlt)
+- `PopulateAtlasLayerComboTests` (2 Testfälle): — (Docstring fehlt)
+- `OnAtlasErzeugenChangedTests` (3 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `SetServicesTests.test_ruft_alle_set_methoden_mit_combo_werten_auf` | Prüft, dass set_services alle set_*-Methoden von PrintLogic mit den aktuellen Combo-Werten aufruft. | grün |
+| `SetServicesTests.test_ohne_combos_ruft_keine_set_methode_auf` | Prüft, dass set_services ohne vorhandene Combos keine set_*-Methode von PrintLogic aufruft. | grün |
+| `RestoreStateTests.test_gueltige_gespeicherte_werte_werden_uebernommen` | Prüft, dass _restore_state gültige gespeicherte Werte in die jeweiligen Widgets übernimmt. | grün |
+| `RestoreStateTests.test_ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck` | Prüft, dass _restore_state ohne (gültige) gespeicherte Werte auf die Default-Konstanten zurückfällt. | grün |
+| `OnDruckpufferClickedTests.test_gueltige_zahl_setzt_variable_und_button_text` | Prüft, dass eine bestätigte, gültige Zahl den Druckpuffer speichert und den Button-Text aktualisiert. | grün |
+| `OnDruckpufferClickedTests.test_abgebrochen_aendert_nichts` | Prüft, dass ein abgebrochener Dialog den Druckpuffer nicht verändert. | grün |
+| `OnDruckpufferClickedTests.test_ungueltige_zahl_zeigt_hinweis_und_setzt_nichts` | Prüft, dass eine nicht-numerische Eingabe einen Hinweis zeigt und den Druckpuffer nicht setzt. | grün |
+| `OnVorlageErstellenTests.test_ruft_druckvorlage_anlegen_mit_combo_werten_auf` | Prüft, dass _on_vorlage_erstellen druckvorlage_anlegen mit den aktuellen Combo-Werten aufruft. | grün |
+| `OnVorlageErstellenTests.test_ohne_logic_tut_nichts` | Prüft, dass _on_vorlage_erstellen ohne gesetzte PrintLogic keine Ausnahme wirft. | grün |
+| `ProjectSignalsTests.test_connect_verbindet_alle_drei_signale` | Prüft, dass _connect_project_signals alle drei Projektsignale mit _on_project_changed verbindet. | grün |
+| `ProjectSignalsTests.test_disconnect_trennt_alle_drei_signale` | Prüft, dass _disconnect_project_signals alle drei zuvor verbundenen Projektsignale trennt. | grün |
+| `ProjectSignalsTests.test_instance_wirft_ausnahme_wird_abgefangen` | Prüft, dass eine Ausnahme bei QgsProject.instance() in beiden Signalmethoden abgefangen wird. | grün |
+| `CleanupTests.test_ruft_beide_disconnect_methoden_auf` | Prüft, dass cleanup sowohl die Theme- als auch die Projektsignale trennt. | grün |
+| `ToggleAtlasTests.test_checked_true_setzt_arrow_down_und_zeigt_inhalt` | Prüft, dass _toggle_atlas bei checked=True den Pfeil nach unten dreht und den Inhalt einblendet. | grün |
+| `ToggleAtlasTests.test_checked_false_setzt_arrow_right_und_versteckt_inhalt` | Prüft, dass _toggle_atlas bei checked=False den Pfeil nach rechts dreht und den Inhalt ausblendet. | grün |
+| `RestoreAtlasLayerSelectionTests.test_vorhandene_layer_id_setzt_layer_in_combo` | Prüft, dass eine gespeicherte Layer-ID den zugehörigen Layer im Atlas-Dropdown auswählt. | grün |
+| `RestoreAtlasLayerSelectionTests.test_leere_gespeicherte_layer_id_tut_nichts` | Prüft, dass eine leere gespeicherte Layer-ID das Projekt gar nicht erst anfragt. | grün |
+| `ThemeCollectionSignalsTests.test_connect_verbindet_alle_signale` | Prüft, dass _connect_theme_collection_signals alle Signale der Theme-Collection verbindet. | grün |
+| `ThemeCollectionSignalsTests.test_connect_ist_idempotent_wenn_schon_verbunden` | Prüft, dass _connect_theme_collection_signals bei bereits verbundenem Signal nichts erneut tut. | grün |
+| `ThemeCollectionSignalsTests.test_disconnect_trennt_alle_signale` | Prüft, dass _disconnect_theme_collection_signals alle verbundenen Theme-Signale trennt und zurücksetzt. | grün |
+| `RefreshThemaComboLiveTests.test_erhaelt_vorherige_auswahl_wenn_noch_vorhanden` | Prüft, dass _refresh_thema_combo_live die vorherige Auswahl beibehält, wenn sie noch existiert. | grün |
+| `RefreshThemaComboLiveTests.test_faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden` | Prüft, dass _refresh_thema_combo_live auf 'wie Kartenfenster' zurückfällt, wenn die Auswahl verschwunden ist. | grün |
+| `GetGespeicherteThemenTests.test_dedupliziert_und_filtert_leere_eintraege` | Prüft, dass _get_gespeicherte_themen Duplikate und leere/None-Einträge entfernt. | grün |
+| `GetGespeicherteThemenTests.test_fehler_liefert_leere_liste` | Prüft, dass ein Fehler beim Zugriff auf die Theme-Collection eine leere Liste liefert. | grün |
+| `GetCurrentCanvasScaleTests.test_liefert_skalierung_als_float` | Prüft, dass _get_current_canvas_scale die Canvas-Skalierung als float liefert. | grün |
+| `GetCurrentCanvasScaleTests.test_ohne_canvas_liefert_none` | Prüft, dass _get_current_canvas_scale ohne verfügbares Kartenfenster None liefert. | grün |
+| `GetCurrentCanvasScaleTests.test_skalierung_null_liefert_none` | Prüft, dass eine Skalierung von 0 als None statt als 0.0 gilt. | grün |
+| `OnAtlasLayerChangedTests.test_none_oder_leer_setzt_variable_leer` | Prüft, dass None oder ein leerer Wert die Abdeckungslayer-Variable leeren. | grün |
+| `OnAtlasLayerChangedTests.test_layer_objekt_mit_id_methode_setzt_dessen_id` | Prüft, dass ein echtes Layer-Objekt (mit id()-Methode) dessen ID speichert. | grün |
+| `OnAtlasLayerChangedTests.test_string_fallback_sucht_layer_ueber_namen` | Prüft, dass ohne id()-Methode über den Layernamen im Projekt gesucht und dessen ID gespeichert wird. | grün |
+| `OnAuswahlAsAtlasClickedTests.test_erfolgreicher_layer_aktualisiert_atlas_ui` | Prüft, dass ein erfolgreich erzeugter Atlas-Layer den Atlas-Bereich öffnet und ins Dropdown übernimmt. | grün |
+| `OnAuswahlAsAtlasClickedTests.test_ohne_erzeugten_layer_tut_nichts_weiter` | Prüft, dass ohne erzeugten Atlas-Layer keine UI-Aktualisierung stattfindet. | grün |
+| `PopulateAtlasLayerComboTests.test_befuellt_combo_ohne_druckrechteck_und_leere_namen` | Prüft, dass _populate_atlas_layer_combo alle Layernamen außer 'Druckrechteck' und leeren Namen einträgt. | grün |
+| `PopulateAtlasLayerComboTests.test_ohne_atlas_layer_combo_tut_nichts` | Prüft, dass _populate_atlas_layer_combo ohne Atlas-Dropdown keine Ausnahme wirft. | grün |
+| `OnAtlasErzeugenChangedTests.test_aktiviert_speichert_variable_und_aktiviert_widgets` | Prüft, dass ein aktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button aktiviert. | grün |
+| `OnAtlasErzeugenChangedTests.test_deaktiviert_speichert_variable_und_deaktiviert_widgets` | Prüft, dass ein deaktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button deaktiviert. | grün |
+| `OnAtlasErzeugenChangedTests.test_ohne_widgets_wirft_keine_ausnahme` | Prüft, dass _on_atlas_erzeugen_changed ohne Atlas-Combo/-Button keine Ausnahme wirft. | grün |
+
+### `Plugin_SN_Basis/tests/test_pruef_ergebnis.py`
+
+Worum es in der Datei geht: Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung/dateipfad (Issue #65/#67).
+
+Testgruppen:
+
+- `IstVerfahrensDbAktionTests` (2 Testfälle): — (Docstring fehlt)
+- `ErlaubtePersistierungTests` (2 Testfälle): — (Docstring fehlt)
+- `DateipfadTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `IstVerfahrensDbAktionTests.test_liefert_true_fuer_alle_verfahrens_db_aktionen` | Prüft, dass ist_verfahrens_db_aktion für alle vier Verfahrens-DB-Aktionscodes True liefert. | grün |
+| `IstVerfahrensDbAktionTests.test_liefert_false_fuer_andere_aktionen` | Prüft, dass ist_verfahrens_db_aktion für Nicht-Verfahrens-DB-Aktionen und ohne Aktion False liefert. | grün |
+| `ErlaubtePersistierungTests.test_liefert_true_fuer_erzeugen_ueberschreiben_anhaengen` | Prüft, dass erlaubte_persistierung für Erzeugen/Überschreiben/Anhängen True liefert. | grün |
+| `ErlaubtePersistierungTests.test_liefert_false_fuer_ueberspringen_und_reines_existieren` | Prüft, dass erlaubte_persistierung bei Überspringen und reinem 'datei_existiert' False liefert. | grün |
+| `DateipfadTests.test_liefert_path_wenn_kontext_ein_path_ist` | Prüft, dass dateipfad den Kontext liefert, wenn dieser bereits ein Path-Objekt ist. | grün |
+| `DateipfadTests.test_liefert_none_wenn_kontext_kein_path_ist` | Prüft, dass dateipfad None liefert, wenn der Kontext kein Path-Objekt ist (z. B. String oder Layer). | grün |
 
 ### `Plugin_SN_Basis/tests/test_pruefmanager.py`
 
@@ -176,24 +686,88 @@ Worum es in der Datei geht: — (Docstring fehlt)
 
 Testgruppen:
 
-- `TestPruefmanager` (14 Testfälle): — (Docstring fehlt)
+- `TestPruefmanager` (26 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestPruefmanager.test_ok` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_leereingabe_erlaubt_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_leereingabe_erlaubt_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_standarddatei_vorschlagen_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_standarddatei_vorschlagen_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_temporaer_erlaubt_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_temporaer_erlaubt_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_layer_unsichtbar_ja` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_layer_unsichtbar_nein` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_unbekannte_aktion` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_overwrite` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_append` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_cancel` | — (Docstring fehlt) | grün |
-| `TestPruefmanager.test_datei_existiert_in_non_qgis_mode_returns_input` | — (Docstring fehlt) | grün |
+| `TestPruefmanager.test_ok` | Prüft, dass ein bereits erfolgreiches Prüfergebnis ohne Nutzerinteraktion als 'ok' durchgereicht wird. | grün |
+| `TestPruefmanager.test_leereingabe_erlaubt_ja` | Prüft, dass bei erlaubter leerer Eingabe eine Ja-Antwort des Nutzers die Entscheidung 'ok' liefert. | grün |
+| `TestPruefmanager.test_leereingabe_erlaubt_nein` | Prüft, dass bei erlaubter leerer Eingabe eine Nein-Antwort die Aktion 'leereingabe_erlaubt' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_standarddatei_vorschlagen_ja` | Prüft, dass eine Ja-Antwort auf den Vorschlag der Standarddatei die Entscheidung 'ok' mit dem Standardpfad als Kontext liefert. | grün |
+| `TestPruefmanager.test_standarddatei_vorschlagen_nein` | Prüft, dass eine Nein-Antwort auf den Vorschlag der Standarddatei die Aktion 'standarddatei_vorschlagen' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_temporaer_erlaubt_ja` | Prüft, dass eine Ja-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erzeugen' auslöst. | grün |
+| `TestPruefmanager.test_temporaer_erlaubt_nein` | Prüft, dass eine Nein-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erlaubt' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_layer_unsichtbar_ja` | Prüft, dass eine Ja-Antwort bei unsichtbarem Layer den Layer sichtbar schaltet und die Entscheidung 'ok' liefert. | grün |
+| `TestPruefmanager.test_layer_unsichtbar_nein` | Prüft, dass eine Nein-Antwort bei unsichtbarem Layer die Aktion 'layer_unsichtbar' als abgelehnt bestehen lässt. | grün |
+| `TestPruefmanager.test_unbekannte_aktion` | Prüft, dass eine unbekannte Aktion eine Warnung auslöst und unverarbeitet als Fehlschlag zurückgegeben wird. | grün |
+| `TestPruefmanager.test_datei_existiert_overwrite` | Prüft, dass die Nutzerwahl 'überschreiben' bei bestehender Datei zur Aktion 'datei_existiert_ueberschreiben' führt. | grün |
+| `TestPruefmanager.test_datei_existiert_append` | Prüft, dass die Nutzerwahl 'anhängen' bei bestehender Datei zur Aktion 'datei_existiert_anhaengen' führt. | grün |
+| `TestPruefmanager.test_datei_existiert_cancel` | Prüft, dass die Nutzerwahl 'abbrechen' bei bestehender Datei zur Aktion 'datei_existiert_ueberspringen' führt. | grün |
+| `TestPruefmanager.test_datei_existiert_in_non_qgis_mode_returns_input` | Prüft, dass außerhalb des QGIS-Modus bei bestehender Datei keine Rückfrage erfolgt, sondern die ursprüngliche Aktion unentschieden zurückgegeben wird. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_ergaenzen` | Prüft, dass die Nutzerwahl 'ergänzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ergaenzen' führt. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_abbrechen` | Prüft, dass die Nutzerwahl 'abbrechen' bei bestehendem Verfahrensgebiet ok=False mit Aktion 'verfahrensgebiet_abbrechen' liefert. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_entfernen` | Prüft, dass die Nutzerwahl 'entfernen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_entfernen' führt. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_ersetzen` | Prüft, dass die Nutzerwahl 'ersetzen' bei bestehendem Verfahrensgebiet zur Aktion 'verfahrensgebiet_ersetzen' führt. | grün |
+| `TestPruefmanager.test_verfahrensgebiet_layer_existiert_in_non_qgis_mode_returns_input` | Prüft, dass außerhalb des QGIS-Modus bei bestehendem Verfahrensgebiet keine Rückfrage erfolgt. | grün |
+| `TestPruefmanager.test_report_error_kritische_aktion_ruft_error_auf` | Prüft, dass eine kritische Aktion (z. B. 'netzwerkfehler') über error() gemeldet wird, nicht über warning(). | grün |
+| `TestPruefmanager.test_report_error_warn_aktion_ruft_warning_auf` | Prüft, dass eine Warn-Aktion (z. B. 'datei_nicht_gefunden') über warning() gemeldet wird, nicht über error(). | grün |
+| `TestPruefmanager.test_report_error_unbekannte_aktion_faellt_auf_warning_zurueck` | Prüft, dass eine nicht eingestufte Aktion als Fallback über warning() gemeldet wird. | grün |
+| `TestPruefmanager.test_frage_ja_nein_im_qgis_modus_fragt_nutzer` | Prüft, dass frage_ja_nein im QGIS-Modus den Nutzer fragt und dessen Antwort liefert. | grün |
+| `TestPruefmanager.test_frage_ja_nein_ausserhalb_qgis_liefert_default_ohne_rueckfrage` | Prüft, dass frage_ja_nein außerhalb des QGIS-Modus den Default-Wert liefert, ohne den Nutzer zu fragen. | grün |
+| `TestPruefmanager.test_frage_text_im_qgis_modus_fragt_nutzer` | Prüft, dass frage_text im QGIS-Modus den Nutzer fragt und Text plus OK-Status liefert. | grün |
+| `TestPruefmanager.test_frage_text_ausserhalb_qgis_liefert_default_ohne_rueckfrage` | Prüft, dass frage_text außerhalb des QGIS-Modus den Standardtext mit OK=True liefert, ohne den Nutzer zu fragen. | grün |
+
+### `Plugin_SN_Basis/tests/test_qgiscore_wrapper_helpers.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg/create_buffer_layer (Issue #65/#67).
+
+Testgruppen:
+
+- `PolygonZuLinieTests` (7 Testfälle): — (Docstring fehlt)
+- `LayerExistsInGpkgTests` (4 Testfälle): — (Docstring fehlt)
+- `CreateBufferLayerTests` (3 Testfälle): Deckt nur die Wächter ab: die eigentliche Pufferbildung importiert `qgis.core.QgsFeature`
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `PolygonZuLinieTests.test_none_liefert_none` | Prüft, dass polygon_zu_linie für None-Eingabe None liefert. | grün |
+| `PolygonZuLinieTests.test_leere_geometrie_liefert_none` | Prüft, dass eine leere Geometrie None liefert. | grün |
+| `PolygonZuLinieTests.test_stufe1_nutzt_boundary_wenn_vorhanden` | Prüft, dass eine Geometrie mit boundary() (QGIS 3.x-Pfad) deren Ergebnis direkt liefert. | grün |
+| `PolygonZuLinieTests.test_stufe2_extrahiert_ringe_bei_einfachem_polygon` | Prüft, dass ohne boundary() die Ringe eines einfachen Polygons manuell extrahiert werden. | grün |
+| `PolygonZuLinieTests.test_stufe2_extrahiert_alle_ringe_bei_multipolygon` | Prüft, dass bei einem MultiPolygon die Ringe aller Teilpolygone extrahiert werden. | grün |
+| `PolygonZuLinieTests.test_stufe3_fallback_wenn_keine_ringe_extrahierbar` | Prüft, dass bei fehlgeschlagener Ringextraktion auf convertToType zurückgefallen wird. | grün |
+| `PolygonZuLinieTests.test_alle_stufen_scheitern_liefert_none` | Prüft, dass polygon_zu_linie None liefert, wenn Ringextraktion und convertToType beide scheitern. | grün |
+| `LayerExistsInGpkgTests.test_fehlende_datei_liefert_false` | Prüft, dass eine nicht vorhandene GPKG-Datei False liefert. | grün |
+| `LayerExistsInGpkgTests.test_leerer_pfad_oder_layername_liefert_false` | Prüft, dass ein leerer Pfad oder Layername False liefert. | grün |
+| `LayerExistsInGpkgTests.test_vorhandener_layer_liefert_true` | Prüft, dass ein in gpkg_contents eingetragener Layername als vorhanden erkannt wird. | grün |
+| `LayerExistsInGpkgTests.test_fehlender_layer_liefert_false` | Prüft, dass ein nicht in gpkg_contents eingetragener Layername als nicht vorhanden gilt. | grün |
+| `CreateBufferLayerTests.test_ohne_qgis_liefert_none` | Prüft, dass create_buffer_layer ohne verfügbares QGIS None liefert. | grün |
+| `CreateBufferLayerTests.test_fehlender_source_layer_liefert_none` | Prüft, dass ein fehlender Quelllayer (None) None liefert, auch wenn QGIS verfügbar wäre. | grün |
+| `CreateBufferLayerTests.test_source_layer_ohne_getfeatures_liefert_none` | Prüft, dass ein Quelllayer ohne getFeatures()-Methode None liefert. | grün |
+
+### `Plugin_SN_Basis/tests/test_qgisui_wrapper_helpers.py`
+
+Worum es in der Datei geht: Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project/find_dock_widgets/add_menu/remove_menu (Issue #65/#67).
+
+Testgruppen:
+
+- `AddLayerToProjectTests` (4 Testfälle): — (Docstring fehlt)
+- `FindDockWidgetsTests` (3 Testfälle): — (Docstring fehlt)
+- `AddRemoveMenuTests` (5 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `AddLayerToProjectTests.test_none_layer_liefert_false` | Prüft, dass add_layer_to_project für einen fehlenden Layer (None) False liefert. | grün |
+| `AddLayerToProjectTests.test_mock_modus_liefert_true_ohne_projektzugriff` | Prüft, dass im Mock-Modus (ohne QGIS) ein gültiger Layer True liefert, ohne das Projekt anzufassen. | grün |
+| `AddLayerToProjectTests.test_mit_qgis_fuegt_layer_dem_projekt_hinzu` | Prüft, dass bei verfügbarem QGIS der Layer über QgsProject.instance().addMapLayer hinzugefügt wird. | grün |
+| `AddLayerToProjectTests.test_mit_qgis_fehler_liefert_false` | Prüft, dass ein Fehler beim Hinzufügen zum Projekt abgefangen wird und False liefert. | grün |
+| `FindDockWidgetsTests.test_ohne_main_window_liefert_leere_liste` | Prüft, dass find_dock_widgets ohne verfügbares Hauptfenster eine leere Liste liefert. | grün |
+| `FindDockWidgetsTests.test_liefert_dock_widgets_des_main_window` | Prüft, dass find_dock_widgets die vom Hauptfenster gefundenen Dock-Widgets durchreicht. | grün |
+| `FindDockWidgetsTests.test_fehler_beim_suchen_liefert_leere_liste` | Prüft, dass ein Fehler beim Suchen der Dock-Widgets abgefangen wird und eine leere Liste liefert. | grün |
+| `AddRemoveMenuTests.test_add_menu_ohne_main_window_tut_nichts` | Prüft, dass add_menu ohne Hauptfenster keine Ausnahme wirft. | grün |
+| `AddRemoveMenuTests.test_add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu` | Prüft, dass ein echtes Qt-Menü (mit menuAction) der Menüleiste hinzugefügt wird. | grün |
+| `AddRemoveMenuTests.test_add_menu_ohne_menuaction_wird_ignoriert` | Prüft, dass ein Objekt ohne menuAction nicht an die Menüleiste übergeben wird. | grün |
+| `AddRemoveMenuTests.test_remove_menu_entfernt_ueber_menuaction` | Prüft, dass remove_menu das Menü über dessen menuAction() aus der Menüleiste entfernt. | grün |
+| `AddRemoveMenuTests.test_remove_menu_ohne_main_window_tut_nichts` | Prüft, dass remove_menu ohne Hauptfenster keine Ausnahme wirft. | grün |
 
 ### `Plugin_SN_Basis/tests/test_settings_logic.py`
 
@@ -205,10 +779,31 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestSettingsLogic.test_load` | — (Docstring fehlt) | grün |
-| `TestSettingsLogic.test_save` | — (Docstring fehlt) | grün |
-| `TestSettingsLogic.test_load_handles_partial_values_with_empty_fallback` | — (Docstring fehlt) | grün |
-| `TestSettingsLogic.test_save_empty_string_value_is_persisted` | — (Docstring fehlt) | grün |
+| `TestSettingsLogic.test_load` | Prüft, dass load() jede bekannte Projektvariable genau einmal über get_variable() liest. | grün |
+| `TestSettingsLogic.test_save` | Prüft, dass save() nur bekannte Variablen-Keys speichert und unbekannte Keys ignoriert. | grün |
+| `TestSettingsLogic.test_load_handles_partial_values_with_empty_fallback` | Prüft, dass load() für jede Variable den (auch leeren) Wert von get_variable() unverändert übernimmt, selbst wenn nicht alle Variablen im Mock konfiguriert sind. | grün |
+| `TestSettingsLogic.test_save_empty_string_value_is_persisted` | Prüft, dass save() einen leeren String als gültigen Wert speichert, statt ihn wie einen fehlenden Key zu behandeln. | grün |
+
+### `Plugin_SN_Basis/tests/test_settings_tab.py`
+
+Worum es in der Datei geht: Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data/_build_input (Issue #65/#67).
+
+Testgruppen:
+
+- `SaveDataTests` (2 Testfälle): — (Docstring fehlt)
+- `LoadDataTests` (4 Testfälle): — (Docstring fehlt)
+- `BuildInputTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `SaveDataTests.test_sammelt_text_und_combobox_werte_inklusive_landkreiskennzahl` | Prüft, dass save_data Text- und Dropdown-Werte sammelt und die Landkreiskennzahl separat ergänzt. | grün |
+| `SaveDataTests.test_ohne_landkreis_dropdown_wird_keine_kennzahl_ergaenzt` | Prüft, dass ohne Landkreis-Dropdown im Formular keine 'landkreis_kennzahl' ergänzt wird. | grün |
+| `LoadDataTests.test_befuellt_textfelder_aus_gespeicherten_daten` | Prüft, dass load_data ein Textfeld mit dem gespeicherten Wert befüllt. | grün |
+| `LoadDataTests.test_ohne_gespeicherten_wert_bleibt_textfeld_leer` | Prüft, dass load_data ohne gespeicherten Wert das Textfeld auf einen leeren Text setzt. | grün |
+| `LoadDataTests.test_waehlt_combobox_eintrag_bei_bekanntem_namen` | Prüft, dass load_data im Landkreis-Dropdown den gespeicherten Landkreisnamen auswählt. | grün |
+| `LoadDataTests.test_unbekannter_gespeicherter_name_waehlt_ersten_eintrag` | Prüft, dass load_data bei einem nicht mehr vorhandenen Landkreisnamen auf den ersten Eintrag zurückfällt. | grün |
+| `BuildInputTests.test_landkreis_feld_liefert_combobox_mit_allen_landkreisen` | Prüft, dass _build_input für das Landkreis-Feld eine ComboBox mit Platzhalter und allen Landkreisen liefert. | grün |
+| `BuildInputTests.test_anderes_feld_liefert_lineedit` | Prüft, dass _build_input für ein normales Feld ein leeres Textfeld liefert. | grün |
 
 ### `Plugin_SN_Basis/tests/test_stilpruefer.py`
 
@@ -220,10 +815,30 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestStilpruefer.test_keine_datei_angegeben` | — (Docstring fehlt) | grün |
-| `TestStilpruefer.test_datei_existiert_mit_qml` | — (Docstring fehlt) | grün |
-| `TestStilpruefer.test_datei_existiert_falsche_endung` | — (Docstring fehlt) | grün |
-| `TestStilpruefer.test_datei_existiert_nicht` | — (Docstring fehlt) | grün |
+| `TestStilpruefer.test_keine_datei_angegeben` | Prüft, dass eine leere Pfadangabe als 'ok' ohne Kontext akzeptiert wird (Stil ist optional). | grün |
+| `TestStilpruefer.test_datei_existiert_mit_qml` | Prüft, dass eine existierende .qml-Datei als 'stil_anwendbar' bestätigt wird. | grün |
+| `TestStilpruefer.test_datei_existiert_falsche_endung` | Prüft, dass eine existierende Datei ohne .qml-Endung als 'falsche_endung' abgelehnt wird. | grün |
+| `TestStilpruefer.test_datei_existiert_nicht` | Prüft, dass ein nicht existierender .qml-Pfad als 'datei_nicht_gefunden' abgelehnt wird. | grün |
+
+### `Plugin_SN_Basis/tests/test_sys_wrapper.py`
+
+Worum es in der Datei geht: Unit-Tests für sn_basis.functions.sys_wrapper.get_plugin_root().
+
+Testgruppen:
+
+- `TestGetPluginRoot` (1 Testfälle): — (Docstring fehlt)
+- `JoinPathTests` (1 Testfälle): — (Docstring fehlt)
+- `EnsureDirTests` (2 Testfälle): — (Docstring fehlt)
+- `GetattrSafeTests` (2 Testfälle): — (Docstring fehlt)
+
+| Testfall | prüft | Status |
+| --- | --- | --- |
+| `TestGetPluginRoot.test_get_plugin_root_folgt_keiner_verzeichnisverknuepfung` | Prüft, dass get_plugin_root() bei einem über eine Verzeichnisverknüpfung (wie von link_qgis_profile.py angelegt) erreichten Modul den verlinkten Pfad liefert, statt zum echten Zielverzeichnis aufzulösen (Verfahrensgebiet #50). | grün |
+| `JoinPathTests.test_verbindet_mehrere_pfadbestandteile` | Prüft, dass join_path mehrere Bestandteile OS-sicher zu einem Path zusammenfügt. | grün |
+| `EnsureDirTests.test_legt_fehlendes_verzeichnis_inklusive_eltern_an` | Prüft, dass ensure_dir ein fehlendes Verzeichnis samt Elternverzeichnissen anlegt. | grün |
+| `EnsureDirTests.test_vorhandenes_verzeichnis_wirft_keinen_fehler` | Prüft, dass ensure_dir bei einem bereits vorhandenen Verzeichnis keinen Fehler wirft. | grün |
+| `GetattrSafeTests.test_liefert_vorhandenes_attribut` | Prüft, dass getattr_safe ein vorhandenes Attribut liefert. | grün |
+| `GetattrSafeTests.test_liefert_default_bei_fehlendem_attribut` | Prüft, dass getattr_safe bei fehlendem Attribut den Default-Wert liefert. | grün |
 
 ### `Plugin_SN_Basis/tests/test_text_wrapper.py`
 
@@ -253,32 +868,42 @@ Testgruppen:
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestVariableWrapper.test_set_variable_project_noop_write_is_skipped` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_set_variable_project_changed_value_is_written` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_set_variable_global_writes_prefixed_name` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_get_variable_global_returns_empty_string_fallback` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_get_variable_invalid_scope_raises` | — (Docstring fehlt) | grün |
-| `TestVariableWrapper.test_set_variable_invalid_scope_raises` | — (Docstring fehlt) | grün |
+| `TestVariableWrapper.test_set_variable_project_noop_write_is_skipped` | Prüft, dass set_variable() im Projekt-Scope nicht schreibt, wenn der neue Wert dem bestehenden entspricht. | grün |
+| `TestVariableWrapper.test_set_variable_project_changed_value_is_written` | Prüft, dass set_variable() im Projekt-Scope bei geändertem Wert mit dem Präfix 'sn_' geschrieben wird. | grün |
+| `TestVariableWrapper.test_set_variable_global_writes_prefixed_name` | Prüft, dass set_variable() im globalen Scope den Variablennamen mit 'sn_' vorangestellt schreibt. | grün |
+| `TestVariableWrapper.test_get_variable_global_returns_empty_string_fallback` | Prüft, dass get_variable() im globalen Scope für eine nicht gesetzte Variable einen leeren String liefert. | grün |
+| `TestVariableWrapper.test_get_variable_invalid_scope_raises` | Prüft, dass get_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst. | grün |
+| `TestVariableWrapper.test_set_variable_invalid_scope_raises` | Prüft, dass set_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst. | grün |
 
 ### `Plugin_SN_Basis/tests/test_verfahrensgebiet_manager.py`
 
-Worum es in der Datei geht: — (Docstring fehlt)
+Worum es in der Datei geht: Tests für sn_basis/functions/verfahrensgebiet_manager.py (zentrale Layer-Verfolgung, Issue #65).
 
 Testgruppen:
 
 - `TestVerfahrensgebietManager` (5 Testfälle): — (Docstring fehlt)
+- `OnHandlerTests` (5 Testfälle): — (Docstring fehlt)
+- `ListenerSetupTests` (3 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_value` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_set_verfahrensgebiet_layer_id_writes_project_variable` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_update_without_layer_does_not_write_when_already_empty` | — (Docstring fehlt) | grün |
-| `TestVerfahrensgebietManager.test_update_with_layer_writes_variable` | — (Docstring fehlt) | grün |
+| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_value` | Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert. | grün |
+| `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value` | Prüft, dass get_verfahrensgebiet_layer_id() bei leerer Projektvariable None statt eines leeren Strings liefert. | grün |
+| `TestVerfahrensgebietManager.test_set_verfahrensgebiet_layer_id_writes_project_variable` | Prüft, dass set_verfahrensgebiet_layer_id() die Layer-ID als Projektvariable 'verfahrensgebiet_layer' speichert. | grün |
+| `TestVerfahrensgebietManager.test_update_without_layer_does_not_write_when_already_empty` | Prüft, dass ohne gefundenen Verfahrensgebiet-Layer keine Schreibaktion erfolgt, wenn die Variable bereits leer ist. | grün |
+| `TestVerfahrensgebietManager.test_update_with_layer_writes_variable` | Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert. | grün |
+| `OnHandlerTests.test_on_project_changed_ohne_layer_setzt_variable_leer` | Prüft, dass _on_project_changed die Variable leert, wenn kein Verfahrensgebiet-Layer existiert. | grün |
+| `OnHandlerTests.test_on_project_changed_mit_layer_setzt_variable` | Prüft, dass _on_project_changed die Layer-ID eines gefundenen Verfahrensgebiet-Layers übernimmt. | grün |
+| `OnHandlerTests.test_on_layers_added_findet_neuen_layer` | Prüft, dass _on_layers_added nach neu hinzugefügten Layern sucht und die Variable aktualisiert. | grün |
+| `OnHandlerTests.test_on_layer_removed_bei_aktuellem_layer_sucht_neu` | Prüft, dass das Entfernen des aktuell aktiven Verfahrensgebiet-Layers eine Neusuche auslöst. | grün |
+| `OnHandlerTests.test_on_layer_removed_bei_anderem_layer_aendert_nichts` | Prüft, dass das Entfernen eines anderen Layers keine Neusuche auslöst. | grün |
+| `ListenerSetupTests.test_setup_ohne_qgis_tut_nichts` | Prüft, dass setup_verfahrensgebiet_listener ohne QGIS keine Signale registriert. | grün |
+| `ListenerSetupTests.test_setup_verbindet_signale_und_ist_idempotent` | Prüft, dass setup_verfahrensgebiet_listener die Projektsignale verbindet und ein zweiter Aufruf nichts doppelt registriert. | grün |
+| `ListenerSetupTests.test_teardown_trennt_alle_signale_und_ist_idempotent` | Prüft, dass teardown_verfahrensgebiet_listener alle Signale trennt und mehrfacher Aufruf nicht fehlschlägt. | grün |
 
 ## Paket `sn_verfahrensgebiet`
 
-Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 208 Testfällen (164 grün, 36 rot, 8 übersprungen).
+Fachplugin, Ordner `Plugin_SN_Verfahrensgebiet` – 12 Testdateien mit 208 Testfällen (166 grün, 34 rot, 8 übersprungen).
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_beteiligung_pruefung.py`
 
@@ -341,23 +966,22 @@ Worum es in der Datei geht: Unit-Tests für das flurstueck_alkis-Modul.
 
 Testgruppen:
 
-- `TestFlurstueckAlkis` (12 Testfälle): — (Docstring fehlt)
+- `TestFlurstueckAlkis` (11 Testfälle): — (Docstring fehlt)
 - `TestFlurstueckAlkisAnhaengenDedup` (3 Testfälle): Anhängen an einen bestehenden Flurstück-Layer (Issue #28).
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
-| `TestFlurstueckAlkis.test_user_cancel_returns_none` | ask_text → ok=False, text="" → (None, NONE). | grün |
-| `TestFlurstueckAlkis.test_empty_text_after_confirmation_returns_none` | ask_text → ok=True, aber Text leer → (None, NONE). | grün |
-| `TestFlurstueckAlkis.test_invalid_wfs_layer_returns_none` | WFS-Layer nicht valide → (None, NONE). | grün |
-| `TestFlurstueckAlkis.test_success_returns_layer_and_first_status` | Kein bestehender Layer, WFS valide → (layer, FIRST). | rot (Baseline) |
-| `TestFlurstueckAlkis.test_sql_filter_contains_all_schluessel` | Der WFS-URI enthält alle eingegebenen Gemarkungsschlüssel. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_existing_layer_reload_yes` | Bestehender Layer + Nutzer: 'Ja, neu laden' → LoadStatus.RELOAD. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_existing_layer_keep_no` | Bestehender Layer + Nutzer: 'Nein, behalten' → LoadStatus.KEEP. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_vorgabe_schluessel_prefills_default_text` | vorgabe_schluessel=['1234','5678'] → ask_text erhält '1234, 5678' als default_text. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_vorgabe_namen_appears_in_label` | vorgabe_namen=['Musterstadt'] → ask_text-label enthält den Namen. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_bbox_embedded_in_wfs_uri` | bbox-Parameter → WFS-URI enthält 'bbox=' Substring. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_backwards_compatible_no_new_params` | flurstueck_alkis(tab) ohne neue Parameter → kein Fehler. | rot (Baseline) |
-| `TestFlurstueckAlkis.test_ohne_schluessel_abfrage_ueberspringt_ask_text` | ohne_schluessel_abfrage=True verwendet vorgabe_schluessel direkt, ohne ask_text aufzurufen. | grün |
+| `TestFlurstueckAlkis.test_user_cancel_returns_none` | Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert. | grün |
+| `TestFlurstueckAlkis.test_empty_text_after_confirmation_returns_none` | Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert. | grün |
+| `TestFlurstueckAlkis.test_invalid_wfs_layer_returns_none` | Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert. | grün |
+| `TestFlurstueckAlkis.test_success_returns_layer_and_first_status` | Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_sql_filter_contains_all_schluessel` | Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_existing_layer_reload_yes` | Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_existing_layer_abbrechen_returns_none` | Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird. | grün |
+| `TestFlurstueckAlkis.test_vorgabe_schluessel_prefills_default_text` | Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_vorgabe_namen_appears_in_label` | Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_backwards_compatible_no_new_params` | Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert. | rot (Baseline) |
+| `TestFlurstueckAlkis.test_ohne_schluessel_abfrage_ueberspringt_ask_text` | Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird. | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_append_ueberspringt_bereits_vorhandene_flurstuecke` | Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden. | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_append_ohne_kennzeichen_feld_haengt_alles_an` | Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten). | grün |
 | `TestFlurstueckAlkisAnhaengenDedup.test_ohne_schluessel_abfrage_append_dedup` | ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47). | grün |
@@ -398,6 +1022,7 @@ Testgruppen:
 
 - `TestExtrahiereGmlIds` (5 Testfälle): — (Docstring fehlt)
 - `TestGrenzpunktAlkis` (7 Testfälle): — (Docstring fehlt)
+- `TestGrenzpunkteGemarkungsweiseStil` (1 Testfälle): — (Docstring fehlt)
 
 | Testfall | prüft | Status |
 | --- | --- | --- |
@@ -413,6 +1038,7 @@ Testgruppen:
 | `TestGrenzpunktAlkis.test_grenzpunkt_batch_empty` | — (Docstring fehlt) | rot (Baseline) |
 | `TestGrenzpunktAlkis.test_success_returns_both_layers` | — (Docstring fehlt) | rot (Baseline) |
 | `TestGrenzpunktAlkis.test_extent_derived_from_vg_layer` | Wenn kein extent übergeben → Extent aus VG-Layer holen. | rot (Baseline) |
+| `TestGrenzpunkteGemarkungsweiseStil.test_stil_wird_nach_dem_laden_auf_den_ergebnis_layer_angewendet` | Prüft, dass grenzpunkte_gemarkungsweise() nach dem Laden apply_style() mit dem neuen ALKIS-Grenzpunkt-Stil auf den fertigen Join-Layer anwendet (#53). | grün |
 
 ### `Plugin_SN_Verfahrensgebiet/tests/test_knickpunkt_pruefung.py`
 
@@ -762,130 +1388,9 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
 aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
 dann verschwindet er hier von selbst.
 
-### `sn_basis` – 87 von 94 Testfällen ohne Beschreibung
+### `sn_basis` – 0 von 396 Testfällen ohne Beschreibung
 
-#### `Plugin_SN_Basis/tests/test_datagrabber.py` (6 von 6) – auch der Modul-Docstring fehlt
-
-- `TestDataGrabber.test_analyze_source_type_detects_excel`
-- `TestDataGrabber.test_analyze_source_type_returns_unknown_filetype`
-- `TestDataGrabber.test_analyze_source_type_detects_service_when_file_check_fails`
-- `TestDataGrabber.test_analyze_source_type_returns_unbekannte_quelle_on_full_failure`
-- `TestDataGrabber.test_run_returns_success_summary_with_valid_rows`
-- `TestDataGrabber.test_run_returns_failure_summary_without_rows`
-
-#### `Plugin_SN_Basis/tests/test_dateipruefer.py` (6 von 6) – auch der Modul-Docstring fehlt
-
-- `TestDateipruefer.test_leereingabe_erlaubt`
-- `TestDateipruefer.test_leereingabe_nicht_erlaubt`
-- `TestDateipruefer.test_standarddatei_vorschlagen`
-- `TestDateipruefer.test_temporaer_erlaubt`
-- `TestDateipruefer.test_datei_nicht_gefunden`
-- `TestDateipruefer.test_datei_ok`
-
-#### `Plugin_SN_Basis/tests/test_laenderkennung.py` (5 von 5)
-
-- `TestGetLaenderkennung.test_bekannter_praefix_gibt_kennung_zurueck`
-- `TestGetLaenderkennung.test_praefix_gross_klein_unabhaengig`
-- `TestGetLaenderkennung.test_unbekannter_praefix_wirft_keyerror`
-- `TestGetLaenderkennung.test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet`
-- `TestGetLaenderkennung.test_mapping_enthaelt_sachsen`
-
-#### `Plugin_SN_Basis/tests/test_layerpruefer.py` (10 von 10) – auch der Modul-Docstring fehlt
-
-- `TestLayerpruefer.test_layer_exists`
-- `TestLayerpruefer.test_layer_unsichtbar`
-- `TestLayerpruefer.test_falscher_layertyp`
-- `TestLayerpruefer.test_falscher_geotyp`
-- `TestLayerpruefer.test_layer_leer`
-- `TestLayerpruefer.test_falsches_crs`
-- `TestLayerpruefer.test_felder_fehlen`
-- `TestLayerpruefer.test_datenquelle_unerwartet`
-- `TestLayerpruefer.test_layer_nicht_editierbar`
-- `TestLayerpruefer.test_layer_ok`
-
-#### `Plugin_SN_Basis/tests/test_linkpruefer.py` (5 von 5) – auch der Modul-Docstring fehlt
-
-- `TestLinkpruefer.test_remote_link_ok`
-- `TestLinkpruefer.test_remote_link_error`
-- `TestLinkpruefer.test_remote_link_network_error`
-- `TestLinkpruefer.test_local_link_not_found`
-- `TestLinkpruefer.test_local_link_ok`
-
-#### `Plugin_SN_Basis/tests/test_print_layout.py` (5 von 5) – auch der Modul-Docstring fehlt
-
-- `TestPrintLayout.test_create_legend_item_links_map_and_enables_map_filtering`
-- `TestPrintLayout.test_create_legend_item_does_not_copy_project_layers_into_legend_model`
-- `TestPrintLayout.test_configure_legend_size_uses_content_adaptive_size`
-- `TestPrintLayout.test_configure_hauptkarte_grids_uses_wrapper_constants`
-- `TestPrintLayout.test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme`
-
-#### `Plugin_SN_Basis/tests/test_print_logic.py` (17 von 17) – auch der Modul-Docstring fehlt
-
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent`
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_escalates_to_a3`
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback`
-- `TestPrintLogic.test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent`
-- `TestPrintLogic.test_select_standard_page_layout_uses_a4_as_minimum_sheet`
-- `TestPrintLogic.test_select_standard_page_layout_limits_hauptkarte_to_a3`
-- `TestPrintLogic.test_select_standard_page_layout_returns_none_if_a3_is_too_small`
-- `TestPrintLogic.test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent`
-- `TestPrintLogic.test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm`
-- `TestPrintLogic.test_apply_standard_minimum_a4_fallback_for_small_page`
-- `TestPrintLogic.test_apply_standard_minimum_a4_fallback_keeps_large_page`
-- `TestPrintLogic.test_does_plot_fit_page_true_for_standard_a4`
-- `TestPrintLogic.test_does_plot_fit_page_false_if_objects_exceed_page`
-- `TestPrintLogic.test_set_druckbereich_accepts_wie_kartenfenster`
-- `TestPrintLogic.test_get_canvas_extent_returns_valid_extent`
-- `TestPrintLogic.test_resolve_extent_uses_canvas_extent_without_buffer`
-- `TestPrintLogic.test_resolve_extent_uses_rectangle_extent_without_buffer`
-
-#### `Plugin_SN_Basis/tests/test_pruefmanager.py` (14 von 14) – auch der Modul-Docstring fehlt
-
-- `TestPruefmanager.test_ok`
-- `TestPruefmanager.test_leereingabe_erlaubt_ja`
-- `TestPruefmanager.test_leereingabe_erlaubt_nein`
-- `TestPruefmanager.test_standarddatei_vorschlagen_ja`
-- `TestPruefmanager.test_standarddatei_vorschlagen_nein`
-- `TestPruefmanager.test_temporaer_erlaubt_ja`
-- `TestPruefmanager.test_temporaer_erlaubt_nein`
-- `TestPruefmanager.test_layer_unsichtbar_ja`
-- `TestPruefmanager.test_layer_unsichtbar_nein`
-- `TestPruefmanager.test_unbekannte_aktion`
-- `TestPruefmanager.test_datei_existiert_overwrite`
-- `TestPruefmanager.test_datei_existiert_append`
-- `TestPruefmanager.test_datei_existiert_cancel`
-- `TestPruefmanager.test_datei_existiert_in_non_qgis_mode_returns_input`
-
-#### `Plugin_SN_Basis/tests/test_settings_logic.py` (4 von 4) – auch der Modul-Docstring fehlt
-
-- `TestSettingsLogic.test_load`
-- `TestSettingsLogic.test_save`
-- `TestSettingsLogic.test_load_handles_partial_values_with_empty_fallback`
-- `TestSettingsLogic.test_save_empty_string_value_is_persisted`
-
-#### `Plugin_SN_Basis/tests/test_stilpruefer.py` (4 von 4) – auch der Modul-Docstring fehlt
-
-- `TestStilpruefer.test_keine_datei_angegeben`
-- `TestStilpruefer.test_datei_existiert_mit_qml`
-- `TestStilpruefer.test_datei_existiert_falsche_endung`
-- `TestStilpruefer.test_datei_existiert_nicht`
-
-#### `Plugin_SN_Basis/tests/test_variable_wrapper.py` (6 von 6) – auch der Modul-Docstring fehlt
-
-- `TestVariableWrapper.test_set_variable_project_noop_write_is_skipped`
-- `TestVariableWrapper.test_set_variable_project_changed_value_is_written`
-- `TestVariableWrapper.test_set_variable_global_writes_prefixed_name`
-- `TestVariableWrapper.test_get_variable_global_returns_empty_string_fallback`
-- `TestVariableWrapper.test_get_variable_invalid_scope_raises`
-- `TestVariableWrapper.test_set_variable_invalid_scope_raises`
-
-#### `Plugin_SN_Basis/tests/test_verfahrensgebiet_manager.py` (5 von 5) – auch der Modul-Docstring fehlt
-
-- `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_value`
-- `TestVerfahrensgebietManager.test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value`
-- `TestVerfahrensgebietManager.test_set_verfahrensgebiet_layer_id_writes_project_variable`
-- `TestVerfahrensgebietManager.test_update_without_layer_does_not_write_when_already_empty`
-- `TestVerfahrensgebietManager.test_update_with_layer_writes_variable`
+Keine Lücken.
 
 ### `sn_verfahrensgebiet` – 102 von 208 Testfällen ohne Beschreibung
 
@@ -931,7 +1436,7 @@ dann verschwindet er hier von selbst.
 - `TestGemarkungSchluesselnurNummern.test_returns_only_nummern`
 - `TestGemarkungSchluesselnurNummern.test_empty_input_returns_empty`
 
-#### `Plugin_SN_Verfahrensgebiet/tests/test_grenzpunkt_alkis.py` (6 von 12)
+#### `Plugin_SN_Verfahrensgebiet/tests/test_grenzpunkt_alkis.py` (6 von 13)
 
 - `TestGrenzpunktAlkis.test_no_vg_layer_returns_none`
 - `TestGrenzpunktAlkis.test_invalid_punktort_layer`
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index fa5e6db..1c11907 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -5,6 +5,13 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Umgebung und Werkzeuge
 
+- **`qt_wrapper` meldet unter QGIS 3.44 `QT_VERSION == 6`, obwohl Qt5 läuft.** Der „PyQt6“-Zweig importiert
+  `qgis.PyQt`, das unter QGIS 3 PyQt5 ist; die Qt6-Schreibweise der Enums (`Qt.ItemFlag.ItemIsUserCheckable`)
+  funktioniert mit PyQt 5.15 trotzdem. `QT_VERSION` also nicht als Qt-Hauptversion verstehen, nur als
+  „echtes Qt (≠ 0)“. (02.10.2026, QGIS 3.44.4 geprüft, Basis #88)
+- **Lücken im Mock-Modus:** `QgsGeometry` hat im Mock kein `fromRect`, `QgsLayoutPoint`/`QgsLayoutSize` haben
+  `x`/`width` als Attribute statt als Methoden; `QgsRectangle` hatte bis Basis #88 gar keinen Mock. Tests patchen
+  solche Stellen gezielt, statt die Produktionsfunktion umzubauen. (02.10.2026)
 - **QGIS 3.40 ist seit Januar 2026 End of Life.** Aktueller LTR ist 3.44, Stable 4.2 (wird 25.09.2026 LTR). `workspace.toml` und alle `metadata.template` stehen noch auf `3.40` – offene Produktentscheidung, siehe `docs/konzept/07_qgis_tests_plan.md` Abschnitt 2. (04.09.2026, QGIS-Release-Fahrplan)
 - **Zwei Qt-Welten.** QGIS 3.40/3.44 laufen mit Qt5/PyQt5, QGIS 4 mit Qt6/PyQt6. `qt_wrapper` lädt PyQt6 zuerst, dann PyQt5, sonst Mock. Enum-Zugriffe unterscheiden sich (`Qt.AlignmentFlag.AlignTop` statt `Qt.AlignTop`), deshalb nur Wrapper-Aliase verwenden. (09/2026, Code)
 - **Mock-Modus ist kein QGIS.** `python scripts/run_tests.py` läuft in Sekunden, prüft aber nur Logik gegen Mock-Klassen. Geometrieoperationen, WFS, Layouts und Signale müssen in QGIS geprüft werden (`tests/test_qgis.bat` unter Windows). Manuelle QGIS-Tests im PR dokumentieren. (09/2026)
@@ -19,6 +26,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## QGIS-Tests (geplant, siehe `docs/konzept/07_qgis_tests_plan.md`)
 
+- **Spike/Abnahme ohne QGIS-Oberfläche:** Skript mit `QT_QPA_PLATFORM=offscreen` über `python-qgis.bat` starten,
+  das Plugin-Repo per Junction (`mklink /J \sn_basis `) unter dem Paketnamen in `sys.path`
+  legen. Für Widgets und Dialoge `QgsApplication([], True)` (mit GUI) – ohne GUI stürzt z. B.
+  `QgsSymbolLayerUtils.symbolPreviewIcon` ab. Modale Dialoge: `exec_dialog` patchen und im Ersatz die Widgets
+  prüfen. Layout-Ergebnis mit `QgsLayoutExporter.renderRegionToImage` als PNG ansehen; ohne installierte Schriften
+  erscheinen Texte als Kästchen. `exportToImage` liefert bei Erfolg `0`. (02.10.2026, Basis #88)
 - **`QT_QPA_PLATFORM=offscreen` muss gesetzt sein, bevor die `QgsApplication` erzeugt wird.** Danach wirkt es nicht mehr. (04.09.2026, Recherche)
 - **`qgis.utils.iface` ist nach `start_app()` `None`.** Entweder `pytest-qgis` verwenden (patcht automatisch) oder selbst `qgis.utils.iface = get_iface()` setzen – und keine Module zu früh importieren. (04.09.2026)
 - **`QgsProject.instance()` ist global.** Ohne Aufräumen zwischen den Tests beeinflussen sie sich gegenseitig. (04.09.2026)
@@ -40,6 +53,15 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Daten und Dienste
 
+- **basemap.de-Vektorkacheln (Basis #88):** QGIS zeigt für Vektorkachel-Layer keine Legende. Objekte im Gebiet
+  liefert `QgsVectorTileLayer.selectByGeometry()` (lädt die Kacheln selbst; `getRawTile` ist in Python gesperrt).
+  Kacheln enden bei Zoom 15. Der Renderer prüft Styles mit `scaleToZoomLevel(maßstab, False)` und setzt
+  `@vector_tile_zoom = scaleToZoom(maßstab)` – diese Variable fehlt in der Legende, zoomabhängige Strichmuster
+  sind dort sonst unsichtbar. Beim Import der Style-JSON werden Beschriftungen zu Labeling (Hausnummern =
+  Labeling-Style `Hauskoordinate`), `Nummer_*`-Schilder zu Renderer-Symbolen, `fill-extrusion` (3D) entfällt.
+  Der Legendenkatalog `sn_basis/data/basemap_legende.json` gehört zur Style-Version `bm_web_col` v2; nach einer
+  neuen Version `python modules/basemap_katalog.py` laufen lassen und Meldungen prüfen. Eine QML-Stilvorlage des
+  Layers ist rund 3,7 MB groß. (02.10.2026)
 - **WFS-Beschränkungen der sächsischen ALKIS-Dienste:** POST/GML-Filter werden blockiert, URL-Länge ist begrenzt, serverseitige Feature-Limits. Deshalb arbeiten Flurstücks- und Grenzpunktabrufe mit BBOX, gemarkungsweisen Batches und Nutzerdialogen. Nicht „vereinfachen“, ohne das gegen den Dienst zu testen. Vollständig mit erprobten Anfragen: `docs/wissen/wfs_dienste.md`, Objektarten/Attribute: `docs/wissen/alkis_objektarten.md`. (Verfahrensgebiet, Code)
 - **Zwei Linklisten.** Zentrale `Linkliste/linkliste.csv` (Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) und die vom Plugin genutzte `Plugin_SN_Plan41/assets/Linkliste.xlsx` (`ident, gruppe, kartenebene, inhalt, link, provider, stildatei`) sind nicht verbunden. Änderungen an Diensten in **beiden** nachziehen, bis die Zusammenführung entschieden ist. (09/2026)
 - **ArcGIS-REST-Layer nicht nach Nummer deuten.** Gleiche Layer-URLs werden in der Linkliste und im Alternativansatz unterschiedlich benannt; Bedeutung und Geometrie immer gegen die Dienstmetadaten prüfen. (Vergleich Alternativansatz 09/2026)
@@ -50,6 +72,12 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## Code-Konventionen, die man kennen muss
 
+- **Legendenelemente im Layout (QGIS 3.44):** `QgsLayoutItemLegend.adjustBoxSize()` wirkt erst, nachdem das
+  Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über
+  `QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))`
+  berechnen; für mehrere Spalten zusätzlich `setSplitLayer(True)`, sonst bleibt ein Layer in einer Spalte.
+  Eine Legende mit `setLegendFilterByMapEnabled(True)` blendet Layer aus, die nicht in der Karte liegen, und ihre
+  Höhe steht erst nach dem Rendern fest. (02.10.2026, Spike Basis #88)
 - **Projektvariablen-Schlüssel ohne `sn_` übergeben.** `set_variable("sn_abdeckungslayer", ...)` erzeugt `sn_sn_abdeckungslayer` (Altlast in Druckmodulen; funktioniert nur, weil Lesen und Schreiben denselben Fehler machen).
 - **Zwei Schreibweisen für den Verfahrensgebiet-Layer:** `verfahrensgebiet_layer` (Manager, Fachplugins) und `verfahrensgebietslayer` (`print_logic.py`). Bis zur Bereinigung nicht auf Gleichheit vertrauen.
 - **Layer werden über Anzeigenamen gefunden** (`BROR_LAYER_NAME` usw.). Umbenennungen in der UI brechen Abläufe.