diff --git a/docs/leitfaden/06_tests.md b/docs/leitfaden/06_tests.md index 3a49eb1..a60e778 100644 --- a/docs/leitfaden/06_tests.md +++ b/docs/leitfaden/06_tests.md @@ -119,6 +119,14 @@ per `ast`, ohne Import). Das ist bewusst **keine** Zeilen-Coverage in Prozent verworfen, weil sie durchlaufene Zeilen statt fachliche Vollständigkeit misst. Es ist eine Lückenliste wie die fehlenden Docstrings: ein Hinweis, wo ein Blick lohnt, kein Ausführungsnachweis und kein Ziel in Prozent. +Jede so gefundene Funktion gilt zusätzlich als **beabsichtigt** oder **ungeklärt**: Der Katalog liest dafür +den Docstring der Funktion und erkennt zwei Vermerke — `"""von Test abgesehen, weil …; Kürzel Datum"""` für +eine bewusst als vernachlässigbar eingestufte Funktion, `"""Test ausgesetzt, zunächst Refactor"""` für eine +Funktion, die erst nach einer geplanten Überarbeitung getestet wird. Fehlt der Vermerk, gilt die Funktion als +**ungeklärt** — das ist die eigentliche Arbeitsliste, nicht die beabsichtigten. Im Prüfstand +(`docs/tests/testkatalog.html`) erscheint das je Paket als eigene, aufklappbare Karte mit Kennzahlen +(„X ungeklärt, Y beabsichtigt"), genau wie bei den Testdateien. + Nach dem Hinzufügen von Tests den Katalog neu erzeugen und mit committen. `python scripts/testkatalog.py --pruefen` meldet, wenn er veraltet ist. diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index a81b2f3..910af04 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -75,6 +75,10 @@ .pkg-bar{ width:220px; height:9px; border-radius:6px; overflow:hidden; display:flex; background:var(--surface-2); border:1px solid var(--line); } .pkg-bar .seg{ height:100%; display:block; } .seg-ok{ background:var(--ok); } .seg-warn{ background:var(--warn); } .seg-skip{ background:var(--skip); } + .pkg-summary{ max-width:100%; margin:0 0 16px; display:grid; gap:8px; grid-template-columns:repeat(auto-fit,minmax(108px,1fr)); } + .stat-sm{ padding:8px 10px; } + .stat-sm .n{ font-size:17px; } + .stat-sm .l{ font-size:10px; } .card-grid{ display:grid; gap:12px; } .filecard{ background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow); overflow:hidden; } .card-toggle{ width:100%; display:flex; flex-wrap:wrap; align-items:center; gap:8px 14px; justify-content:space-between; @@ -153,12 +157,13 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 32 Testdateien · 317 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis

+
32Testdateien
317Testfälle
317grün
0rot (Baseline)
100%mit Docstring
17%geprüft
76ohne Testbezug

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

CloseEventTests 5
flowchart TD
   root(["test_base_dockwidget.py"])
   class root rootnode
@@ -1167,88 +1172,7 @@
   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üftStatusgeprüft
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert.grünnie geprüft
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ünnie geprüft
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ünnie geprüft
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ünnie geprüft
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert.grünnie geprüft
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ünnie geprüft
OnHandlerTests
test_on_project_changed_mit_layer_setzt_variable
Prüft, dass _on_project_changed die Layer-ID eines gefundenen Verfahrensgebiet-Layers übernimmt.grünnie geprüft
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ünnie geprüft
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ünnie geprüft
OnHandlerTests
test_on_layer_removed_bei_anderem_layer_aendert_nichts
Prüft, dass das Entfernen eines anderen Layers keine Neusuche auslöst.grünnie geprüft
ListenerSetupTests
test_setup_ohne_qgis_tut_nichts
Prüft, dass setup_verfahrensgebiet_listener ohne QGIS keine Signale registriert.grünnie geprüft
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ünnie geprüft
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ünnie geprüft
- -
-

Ungetestete Funktionen (strukturell) – 76 von 229

-

Heuristik: Name kommt in keiner Testdatei des Pakets vor. Details siehe docs/tests/testkatalog.md.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DateiFunktion/MethodeZeile
functions/dialog_wrapper.pyshow_info_dialog42
functions/dialog_wrapper.pyask_detailpruefung_oder_vg_laden192
functions/dialog_wrapper.pyask_detailpruefung_oder_abbrechen244
functions/dialog_wrapper.pyProgressDialog.set_total339
functions/dialog_wrapper.pyProgressDialog.set_value348
functions/dialog_wrapper.pyProgressDialog.set_label361
functions/dialog_wrapper.pyProgressDialog.is_canceled371
functions/dialog_wrapper.pyProgressDialog.raise_to_front389
functions/dialog_wrapper.pycreate_progress_dialog408
functions/message_wrapper.pyinfo67
functions/message_wrapper.pysuccess84
functions/os_wrapper.pynormalize_path43
functions/os_wrapper.pyget_home_dir55
functions/os_wrapper.pyis_absolute_path64
functions/os_wrapper.pybasename75
functions/os_wrapper.pypath_suffix106
functions/qgisui_wrapper.pyadd_toolbar204
functions/qt_wrapper.pyexec_dialog60
functions/qt_wrapper.pydebug_qt_status67
modules/DataGrabber.pyDataGrabber.set_source68
modules/DataGrabber.pyDataGrabber._process_database_source312
modules/DataGrabber.pyDataGrabber._process_service_source324
modules/Dateipruefer.pyDateipruefer._ist_leer98
modules/Dateipruefer.pyDateipruefer._handle_leere_eingabe227
modules/Datenschreiber.pyDatenschreiber._store_style_in_gpkg77
modules/LayerLoader.pyLayerLoader._was_canceled42
modules/LayerLoader.pyLayerLoader._process_events51
modules/linkpruefer.pyLinkpruefer._ist_url41
modules/linkpruefer.pyLinkpruefer._pruefe_url81
modules/linkpruefer.pyLinkpruefer._pruefe_dateipfad115
modules/print_layout.pyPrintLayout._create_map_item98
modules/print_layout.pyPrintLayout._set_item_frame_stroke_width120
modules/print_layout.pyPrintLayout._fit_extent_to_map_item156
modules/print_layout.pyPrintLayout.add_text_label214
modules/print_layout.pyPrintLayout._setup_layout_objects246
modules/print_layout.pyPrintLayout._create_label_item378
modules/print_layout.pyPrintLayout._resolve_visible_map_layers476
modules/print_layout.pyPrintLayout._apply_grid_common_settings798
modules/print_layout.pyPrintLayout._safe_grid_call805
modules/print_layout.pyPrintLayout._apply_grid_annotation_text_style817
modules/print_layout.pyPrintLayout._create_rectangle_box_item918
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe.create_baufreigabe_layout90
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._setup_layout_objects_baufreigabe264
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._calc_atlas_margin_percent398
modules/print_logic.pyPrintLogic._failed_result123
modules/print_logic.pyPrintLogic._show_target_size_too_small127
modules/print_logic.pyPrintLogic._is_enabled_flag135
modules/print_logic.pyPrintLogic._resolve_layer_from_project141
modules/print_logic.pyPrintLogic._resolve_active_layer162
modules/print_logic.pyPrintLogic._get_druckpuffer_cm172
modules/print_logic.pyPrintLogic._get_druckpuffer_m181
modules/print_logic.pyPrintLogic._import_qgis_symbol185
modules/print_logic.pyPrintLogic._build_rectangle193
modules/print_logic.pyPrintLogic._clone_extent203
modules/print_logic.pyPrintLogic._get_existing_layer_by_name270
modules/print_logic.pyPrintLogic._create_druckrechteck_layer284
modules/print_logic.pyPrintLogic._clear_layer_features313
modules/print_logic.pyPrintLogic.configure_atlas_for_baufreigabe_layout483
modules/print_logic.pyPrintLogic._store_druckrechteck631
modules/print_logic.pyPrintLogic.activate_druckrechteck_capture680
modules/print_logic.pyPrintLogic._resolve_selection_layer811
modules/print_logic.pyPrintLogic._get_selected_extent825
modules/print_logic.pyPrintLogic._get_plot_dimensions_mm1078
modules/print_logic.pyPrintLogic._get_required_map_dimensions_mm1088
modules/Pruefmanager.pyPruefmanager.report_summary45
modules/Pruefmanager.pyPruefmanager.zeige_hinweis63
modules/Pruefmanager.pyPruefmanager._handle_datei_existiert87
modules/Pruefmanager.pyPruefmanager._handle_verfahrensgebiet_layer_existiert234
modules/Pruefmanager.pyPruefmanager._ask_use_or_replace_pufferlayer277
ui/tabs/print_tab.pyPrintTab._build_ui115
ui/tabs/print_tab.pyPrintTab._on_kartenname_changed335
ui/tabs/print_tab.pyPrintTab._on_massstab_changed349
ui/tabs/print_tab.pyPrintTab._on_zielgroesse_changed361
ui/tabs/print_tab.pyPrintTab._on_formfaktor_changed367
ui/tabs/print_tab.pyPrintTab._on_druckbereich_changed374
ui/tabs/print_tab.pyPrintTab._format_druckpuffer_label432
-
- +

Heuristik: Name kommt in keiner Testdatei des Pakets vor (Details siehe docs/tests/testkatalog.md). beabsichtigt liest den Docstring der Funktion – ein Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“ gilt als bewusste Entscheidung. ungeklärt hat weder Test noch Vermerk.

DateiFunktion/MethodeZeileStatus
functions/dialog_wrapper.pyshow_info_dialog42beabsichtigt
functions/dialog_wrapper.pyask_detailpruefung_oder_vg_laden192beabsichtigt
functions/dialog_wrapper.pyask_detailpruefung_oder_abbrechen244beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_total339beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_value348beabsichtigt
functions/dialog_wrapper.pyProgressDialog.set_label361beabsichtigt
functions/dialog_wrapper.pyProgressDialog.is_canceled371beabsichtigt
functions/dialog_wrapper.pyProgressDialog.raise_to_front389beabsichtigt
functions/dialog_wrapper.pycreate_progress_dialog408beabsichtigt
functions/message_wrapper.pyinfo67beabsichtigt
functions/message_wrapper.pysuccess84beabsichtigt
functions/os_wrapper.pynormalize_path43beabsichtigt
functions/os_wrapper.pyget_home_dir55beabsichtigt
functions/os_wrapper.pyis_absolute_path64beabsichtigt
functions/os_wrapper.pybasename75beabsichtigt
functions/os_wrapper.pypath_suffix106beabsichtigt
functions/qgisui_wrapper.pyadd_toolbar204beabsichtigt
functions/qt_wrapper.pyexec_dialog60beabsichtigt
functions/qt_wrapper.pydebug_qt_status67beabsichtigt
modules/DataGrabber.pyDataGrabber.set_source68beabsichtigt
modules/DataGrabber.pyDataGrabber._process_database_source312beabsichtigt
modules/DataGrabber.pyDataGrabber._process_service_source324beabsichtigt
modules/Dateipruefer.pyDateipruefer._ist_leer98ungeklärt
modules/Dateipruefer.pyDateipruefer._handle_leere_eingabe227ungeklärt
modules/Datenschreiber.pyDatenschreiber._store_style_in_gpkg77ungeklärt
modules/LayerLoader.pyLayerLoader._was_canceled42beabsichtigt
modules/LayerLoader.pyLayerLoader._process_events51beabsichtigt
modules/Pruefmanager.pyPruefmanager.report_summary45beabsichtigt
modules/Pruefmanager.pyPruefmanager.zeige_hinweis63beabsichtigt
modules/Pruefmanager.pyPruefmanager._handle_datei_existiert87ungeklärt
modules/Pruefmanager.pyPruefmanager._handle_verfahrensgebiet_layer_existiert234ungeklärt
modules/Pruefmanager.pyPruefmanager._ask_use_or_replace_pufferlayer277ungeklärt
modules/linkpruefer.pyLinkpruefer._ist_url41ungeklärt
modules/linkpruefer.pyLinkpruefer._pruefe_url81ungeklärt
modules/linkpruefer.pyLinkpruefer._pruefe_dateipfad115ungeklärt
modules/print_layout.pyPrintLayout._create_map_item98beabsichtigt
modules/print_layout.pyPrintLayout._set_item_frame_stroke_width120beabsichtigt
modules/print_layout.pyPrintLayout._fit_extent_to_map_item156beabsichtigt
modules/print_layout.pyPrintLayout.add_text_label214beabsichtigt
modules/print_layout.pyPrintLayout._setup_layout_objects246beabsichtigt
modules/print_layout.pyPrintLayout._create_label_item378beabsichtigt
modules/print_layout.pyPrintLayout._resolve_visible_map_layers476beabsichtigt
modules/print_layout.pyPrintLayout._apply_grid_common_settings798beabsichtigt
modules/print_layout.pyPrintLayout._safe_grid_call805beabsichtigt
modules/print_layout.pyPrintLayout._apply_grid_annotation_text_style817beabsichtigt
modules/print_layout.pyPrintLayout._create_rectangle_box_item918beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe.create_baufreigabe_layout90beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._setup_layout_objects_baufreigabe264beabsichtigt
modules/print_layout_baufreigabe.pyPrintLayoutBaufreigabe._calc_atlas_margin_percent398beabsichtigt
modules/print_logic.pyPrintLogic._failed_result123beabsichtigt
modules/print_logic.pyPrintLogic._show_target_size_too_small127beabsichtigt
modules/print_logic.pyPrintLogic._is_enabled_flag135beabsichtigt
modules/print_logic.pyPrintLogic._resolve_layer_from_project141beabsichtigt
modules/print_logic.pyPrintLogic._resolve_active_layer162beabsichtigt
modules/print_logic.pyPrintLogic._get_druckpuffer_cm172beabsichtigt
modules/print_logic.pyPrintLogic._get_druckpuffer_m181beabsichtigt
modules/print_logic.pyPrintLogic._import_qgis_symbol185beabsichtigt
modules/print_logic.pyPrintLogic._build_rectangle193beabsichtigt
modules/print_logic.pyPrintLogic._clone_extent203beabsichtigt
modules/print_logic.pyPrintLogic._get_existing_layer_by_name270beabsichtigt
modules/print_logic.pyPrintLogic._create_druckrechteck_layer284beabsichtigt
modules/print_logic.pyPrintLogic._clear_layer_features313beabsichtigt
modules/print_logic.pyPrintLogic.configure_atlas_for_baufreigabe_layout483beabsichtigt
modules/print_logic.pyPrintLogic._store_druckrechteck631beabsichtigt
modules/print_logic.pyPrintLogic.activate_druckrechteck_capture680beabsichtigt
modules/print_logic.pyPrintLogic._resolve_selection_layer811beabsichtigt
modules/print_logic.pyPrintLogic._get_selected_extent825beabsichtigt
modules/print_logic.pyPrintLogic._get_plot_dimensions_mm1078beabsichtigt
modules/print_logic.pyPrintLogic._get_required_map_dimensions_mm1088beabsichtigt
ui/tabs/print_tab.pyPrintTab._build_ui115beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_kartenname_changed335beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_massstab_changed349beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_zielgroesse_changed361beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_formfaktor_changed367beabsichtigt
ui/tabs/print_tab.pyPrintTab._on_druckbereich_changed374beabsichtigt
ui/tabs/print_tab.pyPrintTab._format_druckpuffer_label432beabsichtigt
@@ -1256,12 +1180,13 @@

sn_verfahrensgebiet

-

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

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet

+
12Testdateien
208Testfälle
166grün
34rot (Baseline)
51%mit Docstring
0%geprüft
34ohne Testbezug

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

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
   root(["test_beteiligung_pruefung.py"])
   class root rootnode
@@ -1843,46 +1768,7 @@
   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üftStatusgeprüft
TestWorkingTab
test_set_busy_updates_button_state
— (Docstring fehlt)grünnie geprüft
TestWorkingTab
test_on_verf_layer_changed_valid_sets_variable
— (Docstring fehlt)grünnie geprüft
TestWorkingTab
test_on_verf_layer_changed_none_clears_variable
— (Docstring fehlt)grünnie geprüft
TestWorkingTab
test_handle_verf_alkis_success_path
verfahrensgebiet_alkis_komplett → 4-Tupel (enriched, flst, bror, FIRST).rotnie geprüft
TestWorkingTab
test_handle_verf_alkis_failure_clears_layer_var
Status NONE → Layer-Variable wird auf '' gesetzt.grünnie geprüft
TestWorkingTab
test_handle_flurst_alkis_first_load_uebernimmt_layer
Erstmaliges Laden über 'Flurstücke laden' fügt den Layer dem Projekt hinzu.grünnie geprüft
TestWorkingTab
test_handle_flurst_aus_gemarkungen_ruft_flurstueck_alkis_ohne_schluessel_abfrage
Der Gemarkungen-Auswahl-Button ruft dieselbe Ladefunktion mit ohne_schluessel_abfrage=True auf (#47).grünnie geprüft
TestWorkingTab
test_handle_flurst_aus_gemarkungen_abbruch_zeigt_keine_zusaetzliche_meldung
Bricht flurstueck_alkis() ab (None, NONE), zeigt der Button keine eigene Meldung – flurstueck_alkis() hat das schon getan (#47).grünnie geprüft
TestWorkingTab
test_handle_grenz_alkis_success_sets_haken
Grenzpunkte erfolgreich geladen → haken_grenz wird gesetzt.rotnie geprüft
TestWorkingTab
test_handle_grenz_alkis_none_result_sets_no_haken
grenzpunkt_alkis liefert (None, None) → kein Haken.rotnie geprüft
TestWorkingTab
test_handle_flurst_alkis_success_sets_haken_and_style
— (Docstring fehlt)grünnie geprüft
TestWorkingTab
test_handle_flurst_alkis_existing_layer_keep_path
— (Docstring fehlt)grünnie geprüft
TestWorkingTab
test_handle_flurst_alkis_invalid_input_returns_early
Status NONE (z. B. Nutzerabbruch) → kein Layer, kein Haken.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_active_layer
Kein aktiver Layer → warning, kein Layer wird erstellt.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_required_fields
Layer ohne Pflichtfelder → warning, kein Layer wird erstellt.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_selection
Keine Flurstück-Auswahl → warning, kein Layer erstellt.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_new_layer_success
Erstlauf ohne bestehenden Layer → Layer wird erstellt und hinzugefügt.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ergaenzen_dispatch
Entscheidung 'ergaenzen' → ergaenze_verfahrensgebiet wird aufgerufen.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_entfernen_dispatch
Entscheidung 'entfernen' → entferne_aus_verfahrensgebiet wird aufgerufen.grünnie geprüft
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ersetzen_dispatch
Entscheidung 'ersetzen' → alter Layer entfernt, neuer hinzugefügt.grünnie geprüft
TestWorkingTab
test_aus_liste_button_shows_not_implemented_hint
— (Docstring fehlt)grünnie geprüft
TestWorkingTab
test_handle_gem_alkis_service_reachable_adds_layer
Dienst erreichbar → Layer ins Projekt, Stil angewendet, Haken gesetzt.grünnie geprüft
TestWorkingTab
test_handle_gem_alkis_service_unreachable_shows_warning
Dienst nicht erreichbar (None) → warning, kein addMapLayer.grünnie geprüft
TestWorkingTab
test_handle_gem_alkis_layer_already_exists_shows_info
Layer 'Gemarkungen' bereits vorhanden → info, kein erneutes Laden.grünnie geprüft
TestHandleVerfShape
test_status_none_clears_layer_variable
Status NONE → Layer-Variable wird auf '' gesetzt.grünnie geprüft
TestHandleVerfShape
test_button_always_re_enabled_on_failure
Button muss nach Status NONE wieder freigegeben sein (finally).grünnie geprüft
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_nein_bricht_ab
Verfahrensnummer leer + Nutzer wählt 'Nein' → Vorgang abgebrochen.rotnie geprüft
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_ja_fuehrt_fort
Verfahrensnummer leer + Nutzer wählt 'Ja' → Pipeline wird aufgerufen.rotnie geprüft
TestHandleVerfShape
test_verfahrensnummer_gesetzt_kein_rueckfrage_dialog
Verfahrensnummer gesetzt → kein ask_yes_no-Dialog.grünnie geprüft
TestHandleVerfShape
test_success_path_applies_style_and_sets_haken
Erfolg → verfahrensgebiet.qml angewendet, haken_verf gesetzt, success aufgerufen.grünnie geprüft
TestHandleVerfShape
test_bror_layer_erhaelt_qml_stil
Wenn bror_layer zurückgegeben → BauRaumOderBodenordnungsrecht.qml angewendet.grünnie geprüft
TestHandleVerfShape
test_enriched_layer_already_in_project_not_added_again
Layer bereits im Projekt → kein erneutes addMapLayer für VG-Layer.grünnie geprüft
TestHandleVerfShape
test_busy_state_set_during_execution
Button wird während der Ausführung auf busy gesetzt.grünnie geprüft
- -
-

Ungetestete Funktionen (strukturell) – 34 von 104

-

Heuristik: Name kommt in keiner Testdatei des Pakets vor. Details siehe docs/tests/testkatalog.md.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DateiFunktion/MethodeZeile
functions/beteiligung_pruefung.py_baue_nenner_kennzeichen139
functions/beteiligung_pruefung.py_hole_flurstuecke_layer253
functions/beteiligung_pruefung.py_flurstueckskennzeichen_geometrien258
functions/beteiligung_pruefung.py_try1_listenabgleich279
functions/beteiligung_pruefung.py_lese_vef_vkzf380
functions/flurstueck_alkis.py_vorhandene_kennzeichen80
functions/gemarkung_alkis.pygemarkung_schluessel_von_vg_geometrie212
functions/grenzpunkt_alkis.py_punktort_ta_bbox_param115
functions/grenzpunkt_alkis.py_hat_geometrie573
functions/grenzpunkt_alkis.py_lade_gemarkung_extents_mit_puffer637
functions/grenzpunkt_alkis.py_lade_punktort_ta_batches_per_gemarkung784
functions/knickpunkt_pruefung.py_setze_kategorien_symbologie158
functions/knickpunkt_pruefung.pyerstelle_korrigiertes_polygon604
functions/umringspruefung.py_zerlege_in_teile55
functions/umringspruefung.py_setze_kategorien_symbologie112
functions/umringspruefung.pyumringspruefung152
functions/umringstatistik_grenzpunkte.py_setze_kategorien_symbologie74
functions/umringstatistik_grenzpunkte.py_klassifiziere119
functions/umringstatistik_grenzpunkte.pyumringstatistik_grenzpunkte164
functions/verfahrensgebiet_alkis.py_baue_layer_aus_kombinationen144
functions/verfahrensgebiet_alkis_komplett.py_entferne_hilfs_layer100
functions/verfahrensgebiet_alkis_komplett.py_flaeche_des_layers210
functions/verfahrensgebiet_dialoge.pyask_landkreis_objekte101
functions/verfahrensgebiet_shape_komplett.py_normalisiere_feldname110
functions/verfahrensgebiet_shape_komplett.py_finde_fbschl_index132
functions/verfahrensgebiet_shape_komplett.py_ermittle_gebietstyp_aus_fbschl161
functions/verfahrensgebiet_shape_komplett.py_wende_vergleich_stil_an211
functions/verfahrensgebiet_shape_komplett.py_erstelle_vergleich_layer251
functions/verfahrensgebiet_shape_komplett.py_finde_alle_shp342
ui/tabs/working_tab.pyWorkingTab.handle_beteiligung_pruefen297
ui/tabs/working_tab.pyWorkingTab._haken_label349
ui/tabs/working_tab.pyWorkingTab._restore_verf_layer_selection374
ui/tabs/working_tab.pyWorkingTab._uebernimm_flurstuecke_ergebnis390
ui/tabs/working_tab.pyWorkingTab.handle_umringstatistik777
-
- +

Heuristik: Name kommt in keiner Testdatei des Pakets vor (Details siehe docs/tests/testkatalog.md). beabsichtigt liest den Docstring der Funktion – ein Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“ gilt als bewusste Entscheidung. ungeklärt hat weder Test noch Vermerk.

DateiFunktion/MethodeZeileStatus
functions/beteiligung_pruefung.py_baue_nenner_kennzeichen139ungeklärt
functions/beteiligung_pruefung.py_hole_flurstuecke_layer253ungeklärt
functions/beteiligung_pruefung.py_flurstueckskennzeichen_geometrien258ungeklärt
functions/beteiligung_pruefung.py_try1_listenabgleich279ungeklärt
functions/beteiligung_pruefung.py_lese_vef_vkzf380ungeklärt
functions/flurstueck_alkis.py_vorhandene_kennzeichen80ungeklärt
functions/gemarkung_alkis.pygemarkung_schluessel_von_vg_geometrie212ungeklärt
functions/grenzpunkt_alkis.py_punktort_ta_bbox_param115ungeklärt
functions/grenzpunkt_alkis.py_hat_geometrie573ungeklärt
functions/grenzpunkt_alkis.py_lade_gemarkung_extents_mit_puffer637ungeklärt
functions/grenzpunkt_alkis.py_lade_punktort_ta_batches_per_gemarkung784ungeklärt
functions/knickpunkt_pruefung.py_setze_kategorien_symbologie158ungeklärt
functions/knickpunkt_pruefung.pyerstelle_korrigiertes_polygon604ungeklärt
functions/umringspruefung.py_zerlege_in_teile55ungeklärt
functions/umringspruefung.py_setze_kategorien_symbologie112ungeklärt
functions/umringspruefung.pyumringspruefung152ungeklärt
functions/umringstatistik_grenzpunkte.py_setze_kategorien_symbologie74ungeklärt
functions/umringstatistik_grenzpunkte.py_klassifiziere119ungeklärt
functions/umringstatistik_grenzpunkte.pyumringstatistik_grenzpunkte164ungeklärt
functions/verfahrensgebiet_alkis.py_baue_layer_aus_kombinationen144ungeklärt
functions/verfahrensgebiet_alkis_komplett.py_entferne_hilfs_layer100ungeklärt
functions/verfahrensgebiet_alkis_komplett.py_flaeche_des_layers210ungeklärt
functions/verfahrensgebiet_dialoge.pyask_landkreis_objekte101ungeklärt
functions/verfahrensgebiet_shape_komplett.py_normalisiere_feldname110ungeklärt
functions/verfahrensgebiet_shape_komplett.py_finde_fbschl_index132ungeklärt
functions/verfahrensgebiet_shape_komplett.py_ermittle_gebietstyp_aus_fbschl161ungeklärt
functions/verfahrensgebiet_shape_komplett.py_wende_vergleich_stil_an211ungeklärt
functions/verfahrensgebiet_shape_komplett.py_erstelle_vergleich_layer251ungeklärt
functions/verfahrensgebiet_shape_komplett.py_finde_alle_shp342ungeklärt
ui/tabs/working_tab.pyWorkingTab.handle_beteiligung_pruefen297ungeklärt
ui/tabs/working_tab.pyWorkingTab._haken_label349ungeklärt
ui/tabs/working_tab.pyWorkingTab._restore_verf_layer_selection374ungeklärt
ui/tabs/working_tab.pyWorkingTab._uebernimm_flurstuecke_ergebnis390ungeklärt
ui/tabs/working_tab.pyWorkingTab.handle_umringstatistik777ungeklärt
@@ -1890,12 +1776,13 @@

sn_plan41

-

Fachplugin · Ordner Plugin_SN_Plan41 · 5 Testdateien · 44 Testfälle

+

Fachplugin · Ordner Plugin_SN_Plan41

+
5Testdateien
44Testfälle
43grün
1rot (Baseline)
2%mit Docstring
0%geprüft
39ohne Testbezug

— (Docstring fehlt)

TestListenauswerter 4
flowchart TD
   root(["test_listenauswerter.py"])
   class root rootnode
@@ -2042,51 +1929,7 @@
   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üftStatusgeprüft
TestVlnApiLogicAvailability
test_is_available_reflects_import_state
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicAvailability
test_is_available_false_when_vln_karten_missing
Wenn VLN_KARTEN_AVAILABLE=False, liefert is_available() False.grünnie geprüft
TestVlnApiLogicCredentials
test_save_api_key_calls_qsettings
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicCredentials
test_clear_api_key_calls_remove
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicCredentials
test_load_stored_credentials_returns_api_key_and_mail
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicCredentials
test_load_stored_credentials_returns_none_for_empty
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLogin
test_login_sets_client_and_saves_key
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLogin
test_login_raises_runtime_error_without_qgis_modules
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLogin
test_login_propagates_api_error
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLogin
test_logout_clears_client
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLogin
test_is_authenticated_false_after_logout
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLogin
test_handle_session_expired_clears_key_and_client
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicVerfahren
test_get_verfahren_raises_when_not_authenticated
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicVerfahren
test_get_verfahren_delegates_to_client
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicVkz
test_load_stored_vkz_reads_from_project
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicVkz
test_load_stored_vkz_returns_none_for_empty
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicVkz
test_save_vkz_writes_to_project
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicVkz
test_save_vkz_does_nothing_for_none
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLoadP41
test_load_p41_raises_without_vln_karten
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLoadP41
test_load_p41_applies_styles_to_polygon_and_line_layers
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicLoadP41
test_load_p41_no_style_for_point_layer
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicUpload
test_upload_returns_error_for_layer_without_api_path
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicUpload
test_upload_returns_error_for_none_layer
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicUpload
test_upload_returns_false_without_qgis_modules
— (Docstring fehlt)grünnie geprüft
TestVlnApiLogicUpload
test_upload_calls_save_feature_collection
— (Docstring fehlt)grünnie geprüft
- -
-

Ungetestete Funktionen (strukturell) – 39 von 80

-

Heuristik: Name kommt in keiner Testdatei des Pakets vor. Details siehe docs/tests/testkatalog.md.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DateiFunktion/MethodeZeile
modules/vln_api_client.pyKartenApiClient.load_feature_collection154
modules/vln_api_client.pyKartenApiClient._request176
modules/vln_api_client.pyKartenApiClient._error_detail235
modules/vln_api_logic.pyVlnApiLogic.get_p41_layers233
modules/vln_api_logic.pyVlnApiLogic.remove_p41_layers237
modules/vln_api_logic.pyVlnApiLogic._apply_p41_styles277
modules/vln_api_logic.pyVlnApiLogic.upload_summary347
modules/vln_layer_manager.py_family58
modules/vln_layer_manager.pymemory_layer_from_features73
ui/dockwidget.pyDockWidget._init_tabs_with_services42
ui/tab_a_logic.pyTabALogic._log90
ui/tab_a_logic.pyTabALogic._create_silent_pruefmanager93
ui/tab_a_logic.pyTabALogic._zeige_verfahrensgebiet_hinweis127
ui/tab_a_logic.pyTabALogic._close_progress_dialog139
ui/tab_a_logic.pyTabALogic.write_layer_to_verfahrens_db169
ui/tab_a_logic.pyTabALogic.is_valid_verfahrensgebiet_layer242
ui/tab_a_logic.pyTabALogic._load_dienste_aus_result_dict391
ui/tab_a_logic.pyTabALogic._create_local_layer_from_fetched_features639
ui/tab_a_logic.pyTabALogic._write_html_log779
ui/tab_a_logic.pyTabALogic._clone_layer_with_extent934
ui/tab_a_logic.pyTabALogic._handle_pufferlayer1062
ui/tab_a_logic.pyTabALogic._load_existing_pufferlayer1084
ui/tab_a_logic.pyTabALogic._create_pufferlayer1092
ui/tab_a_logic.pyTabALogic._pufferlayer_exists_in_source1119
ui/tab_a_logic.pyTabALogic.Pufferlayer_erstellen1133
ui/tab_a_ui.pyTabA._build_ui113
ui/tab_a_ui.pyTabA._restore_state274
ui/tab_a_ui.pyTabA._restore_vln_state296
ui/tab_a_ui.pyTabA._toggle_vln_group307
ui/tab_a_ui.pyTabA._update_vln_ui_state311
ui/tab_a_ui.pyTabA._populate_verfahren_combo347
ui/tab_a_ui.pyTabA._on_vln_login374
ui/tab_a_ui.pyTabA._on_vln_logout405
ui/tab_a_ui.pyTabA._on_vln_verfahren_changed416
ui/tab_a_ui.pyTabA._on_p41_laden424
ui/tab_a_ui.pyTabA._on_upload_active498
ui/tab_a_ui.pyTabA._create_new_gpkg595
ui/tab_a_ui.pyTabA._update_group_color608
ui/tab_a_ui.pyTabA._on_load_fachdaten618
-
- +

Heuristik: Name kommt in keiner Testdatei des Pakets vor (Details siehe docs/tests/testkatalog.md). beabsichtigt liest den Docstring der Funktion – ein Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“ gilt als bewusste Entscheidung. ungeklärt hat weder Test noch Vermerk.

DateiFunktion/MethodeZeileStatus
modules/vln_api_client.pyKartenApiClient.load_feature_collection154ungeklärt
modules/vln_api_client.pyKartenApiClient._request176ungeklärt
modules/vln_api_client.pyKartenApiClient._error_detail235ungeklärt
modules/vln_api_logic.pyVlnApiLogic.get_p41_layers233ungeklärt
modules/vln_api_logic.pyVlnApiLogic.remove_p41_layers237ungeklärt
modules/vln_api_logic.pyVlnApiLogic._apply_p41_styles277ungeklärt
modules/vln_api_logic.pyVlnApiLogic.upload_summary347ungeklärt
modules/vln_layer_manager.py_family58ungeklärt
modules/vln_layer_manager.pymemory_layer_from_features73ungeklärt
ui/dockwidget.pyDockWidget._init_tabs_with_services42ungeklärt
ui/tab_a_logic.pyTabALogic._log90ungeklärt
ui/tab_a_logic.pyTabALogic._create_silent_pruefmanager93ungeklärt
ui/tab_a_logic.pyTabALogic._zeige_verfahrensgebiet_hinweis127ungeklärt
ui/tab_a_logic.pyTabALogic._close_progress_dialog139ungeklärt
ui/tab_a_logic.pyTabALogic.write_layer_to_verfahrens_db169ungeklärt
ui/tab_a_logic.pyTabALogic.is_valid_verfahrensgebiet_layer242ungeklärt
ui/tab_a_logic.pyTabALogic._load_dienste_aus_result_dict391ungeklärt
ui/tab_a_logic.pyTabALogic._create_local_layer_from_fetched_features639ungeklärt
ui/tab_a_logic.pyTabALogic._write_html_log779ungeklärt
ui/tab_a_logic.pyTabALogic._clone_layer_with_extent934ungeklärt
ui/tab_a_logic.pyTabALogic._handle_pufferlayer1062ungeklärt
ui/tab_a_logic.pyTabALogic._load_existing_pufferlayer1084ungeklärt
ui/tab_a_logic.pyTabALogic._create_pufferlayer1092ungeklärt
ui/tab_a_logic.pyTabALogic._pufferlayer_exists_in_source1119ungeklärt
ui/tab_a_logic.pyTabALogic.Pufferlayer_erstellen1133ungeklärt
ui/tab_a_ui.pyTabA._build_ui113ungeklärt
ui/tab_a_ui.pyTabA._restore_state274ungeklärt
ui/tab_a_ui.pyTabA._restore_vln_state296ungeklärt
ui/tab_a_ui.pyTabA._toggle_vln_group307ungeklärt
ui/tab_a_ui.pyTabA._update_vln_ui_state311ungeklärt
ui/tab_a_ui.pyTabA._populate_verfahren_combo347ungeklärt
ui/tab_a_ui.pyTabA._on_vln_login374ungeklärt
ui/tab_a_ui.pyTabA._on_vln_logout405ungeklärt
ui/tab_a_ui.pyTabA._on_vln_verfahren_changed416ungeklärt
ui/tab_a_ui.pyTabA._on_p41_laden424ungeklärt
ui/tab_a_ui.pyTabA._on_upload_active498ungeklärt
ui/tab_a_ui.pyTabA._create_new_gpkg595ungeklärt
ui/tab_a_ui.pyTabA._update_group_color608ungeklärt
ui/tab_a_ui.pyTabA._on_load_fachdaten618ungeklärt
@@ -2123,13 +1966,13 @@ function applyFilters(){ var active = document.querySelector('.chip[aria-pressed="true"]').dataset.filter; var q = document.getElementById('searchInput').value.trim().toLowerCase(); - document.querySelectorAll('.filecard').forEach(function(card){ + document.querySelectorAll('.filecard:not(.gapcard)').forEach(function(card){ var passFilter = active === 'all' || card.dataset[active] === '1'; var passSearch = !q || card.dataset.search.indexOf(q) !== -1; card.hidden = !(passFilter && passSearch); }); document.querySelectorAll('.pkg-section').forEach(function(sec){ - sec.hidden = !sec.querySelector('.filecard:not([hidden])'); + sec.hidden = !sec.querySelector('.filecard:not(.gapcard):not([hidden])'); }); } document.querySelectorAll('.chip').forEach(function(chip){ diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md index 1be0c78..eeaf1f6 100644 --- a/docs/tests/testkatalog.md +++ b/docs/tests/testkatalog.md @@ -32,7 +32,7 @@ Strukturelle Lücken (heuristisch, Namensabgleich): 149 von 413 (36 %) Funktione - **übersprungen** – per `@unittest.skip`/`skipIf`/`skipUnless` abgeschaltet; der Grund steht dabei. - Ein Testfall heißt `Testgruppe.test_name`; die Testgruppe fasst zusammengehörige Testfälle einer Datei zusammen. - **geprüft** – Prüf-Audit (`scripts/test_audit.py`): **nie geprüft** kein Eintrag in `docs/status/testpruefungen.json`; **geprüft am …** ein Mensch hat den Testfall bestätigt und der Testcode hat sich seither nicht geändert; **veraltet seit Änderung …** der Testcode wurde nach der letzten Prüfung geändert. -- **ohne Testbezug** – Funktionen/Methoden im Quellcode, deren Name in keiner Testdatei des Pakets vorkommt (weder als Aufruf noch als `mock.patch("…")`-Ziel). Ein Namensabgleich, kein Ausführungsnachweis – siehe Abschnitt „Ungetestete Funktionen“ für die Grenzen dieser Heuristik. Das ist bewusst **keine** Zeilen-Coverage in Prozent (siehe ADR 0009, „Verworfene Alternativen“), sondern eine Lückenliste wie bei fehlenden Beschreibungen. +- **ohne Testbezug** – Funktionen/Methoden im Quellcode, deren Name in keiner Testdatei des Pakets vorkommt (weder als Aufruf noch als `mock.patch("…")`-Ziel). Ein Namensabgleich, kein Ausführungsnachweis – siehe Abschnitt „Ungetestete Funktionen“ für die Grenzen dieser Heuristik. Das ist bewusst **keine** Zeilen-Coverage in Prozent (siehe ADR 0009, „Verworfene Alternativen“), sondern eine Lückenliste wie bei fehlenden Beschreibungen. Davon **beabsichtigt** – der Docstring dokumentiert den Testverzicht; **ungeklärt** – weder Test noch Vermerk, die eigentliche Arbeitsliste. ## Paket `sn_basis` @@ -1440,258 +1440,259 @@ Keine Lücken. ## Ungetestete Funktionen (strukturelle Lücke) Heuristik, kein Beweis: Diese Funktionen und Methoden kommen unter ihrem Namen in keiner Testdatei des Pakets vor. Das schließt nicht aus, dass sie indirekt über eine andere, getestete Funktion mitlaufen – umgekehrt gilt: Taucht der Name irgendwo in einer Testdatei auf, gilt die Funktion schon als referenziert, auch bei zufälliger Namensgleichheit mit etwas anderem. `main.py`/`__init__.py` (QGIS-Einstiegspunkte wie `classFactory`, `initGui`, `unload`) und Dunder-Methoden sind ausgenommen. Diese Liste ersetzt keine fachliche Prüfung, ob ein Test sinnvoll wäre – dafür braucht es einen Menschen, der die Funktion kennt. +„beabsichtigt“ liest den Docstring der Funktion: Enthält er einen Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“, gilt der fehlende Test als bewusste Entscheidung. Alles andere ist „ungeklärt“ – die eigentliche Arbeitsliste. -### `sn_basis` – 76 von 229 (33 %) ohne Testbezug +### `sn_basis` – 76 von 229 (33 %) ohne Testbezug (9 ungeklärt, 67 beabsichtigt) #### `functions/dialog_wrapper.py` (9) -- `show_info_dialog` (Zeile 42) -- `ask_detailpruefung_oder_vg_laden` (Zeile 192) -- `ask_detailpruefung_oder_abbrechen` (Zeile 244) -- `ProgressDialog.set_total` (Zeile 339) -- `ProgressDialog.set_value` (Zeile 348) -- `ProgressDialog.set_label` (Zeile 361) -- `ProgressDialog.is_canceled` (Zeile 371) -- `ProgressDialog.raise_to_front` (Zeile 389) -- `create_progress_dialog` (Zeile 408) +- `show_info_dialog` (Zeile 42) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ask_detailpruefung_oder_vg_laden` (Zeile 192) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ask_detailpruefung_oder_abbrechen` (Zeile 244) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.set_total` (Zeile 339) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.set_value` (Zeile 348) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.set_label` (Zeile 361) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.is_canceled` (Zeile 371) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `ProgressDialog.raise_to_front` (Zeile 389) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `create_progress_dialog` (Zeile 408) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `functions/message_wrapper.py` (2) -- `info` (Zeile 67) -- `success` (Zeile 84) +- `info` (Zeile 67) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `success` (Zeile 84) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `functions/os_wrapper.py` (5) -- `normalize_path` (Zeile 43) -- `get_home_dir` (Zeile 55) -- `is_absolute_path` (Zeile 64) -- `basename` (Zeile 75) -- `path_suffix` (Zeile 106) +- `normalize_path` (Zeile 43) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `get_home_dir` (Zeile 55) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `is_absolute_path` (Zeile 64) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `basename` (Zeile 75) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `path_suffix` (Zeile 106) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `functions/qgisui_wrapper.py` (1) -- `add_toolbar` (Zeile 204) +- `add_toolbar` (Zeile 204) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `functions/qt_wrapper.py` (2) -- `exec_dialog` (Zeile 60) -- `debug_qt_status` (Zeile 67) +- `exec_dialog` (Zeile 60) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `debug_qt_status` (Zeile 67) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `modules/DataGrabber.py` (3) -- `DataGrabber.set_source` (Zeile 68) -- `DataGrabber._process_database_source` (Zeile 312) -- `DataGrabber._process_service_source` (Zeile 324) +- `DataGrabber.set_source` (Zeile 68) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `DataGrabber._process_database_source` (Zeile 312) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `DataGrabber._process_service_source` (Zeile 324) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `modules/Dateipruefer.py` (2) -- `Dateipruefer._ist_leer` (Zeile 98) -- `Dateipruefer._handle_leere_eingabe` (Zeile 227) +- `Dateipruefer._ist_leer` (Zeile 98) — ungeklärt +- `Dateipruefer._handle_leere_eingabe` (Zeile 227) — ungeklärt #### `modules/Datenschreiber.py` (1) -- `Datenschreiber._store_style_in_gpkg` (Zeile 77) +- `Datenschreiber._store_style_in_gpkg` (Zeile 77) — ungeklärt #### `modules/LayerLoader.py` (2) -- `LayerLoader._was_canceled` (Zeile 42) -- `LayerLoader._process_events` (Zeile 51) +- `LayerLoader._was_canceled` (Zeile 42) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `LayerLoader._process_events` (Zeile 51) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 #### `modules/linkpruefer.py` (3) -- `Linkpruefer._ist_url` (Zeile 41) -- `Linkpruefer._pruefe_url` (Zeile 81) -- `Linkpruefer._pruefe_dateipfad` (Zeile 115) +- `Linkpruefer._ist_url` (Zeile 41) — ungeklärt +- `Linkpruefer._pruefe_url` (Zeile 81) — ungeklärt +- `Linkpruefer._pruefe_dateipfad` (Zeile 115) — ungeklärt #### `modules/print_layout.py` (11) -- `PrintLayout._create_map_item` (Zeile 98) -- `PrintLayout._set_item_frame_stroke_width` (Zeile 120) -- `PrintLayout._fit_extent_to_map_item` (Zeile 156) -- `PrintLayout.add_text_label` (Zeile 214) -- `PrintLayout._setup_layout_objects` (Zeile 246) -- `PrintLayout._create_label_item` (Zeile 378) -- `PrintLayout._resolve_visible_map_layers` (Zeile 476) -- `PrintLayout._apply_grid_common_settings` (Zeile 798) -- `PrintLayout._safe_grid_call` (Zeile 805) -- `PrintLayout._apply_grid_annotation_text_style` (Zeile 817) -- `PrintLayout._create_rectangle_box_item` (Zeile 918) +- `PrintLayout._create_map_item` (Zeile 98) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._set_item_frame_stroke_width` (Zeile 120) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._fit_extent_to_map_item` (Zeile 156) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout.add_text_label` (Zeile 214) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._setup_layout_objects` (Zeile 246) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._create_label_item` (Zeile 378) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._resolve_visible_map_layers` (Zeile 476) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._apply_grid_common_settings` (Zeile 798) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._safe_grid_call` (Zeile 805) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._apply_grid_annotation_text_style` (Zeile 817) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayout._create_rectangle_box_item` (Zeile 918) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor #### `modules/print_layout_baufreigabe.py` (3) -- `PrintLayoutBaufreigabe.create_baufreigabe_layout` (Zeile 90) -- `PrintLayoutBaufreigabe._setup_layout_objects_baufreigabe` (Zeile 264) -- `PrintLayoutBaufreigabe._calc_atlas_margin_percent` (Zeile 398) +- `PrintLayoutBaufreigabe.create_baufreigabe_layout` (Zeile 90) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayoutBaufreigabe._setup_layout_objects_baufreigabe` (Zeile 264) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLayoutBaufreigabe._calc_atlas_margin_percent` (Zeile 398) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor #### `modules/print_logic.py` (20) -- `PrintLogic._failed_result` (Zeile 123) -- `PrintLogic._show_target_size_too_small` (Zeile 127) -- `PrintLogic._is_enabled_flag` (Zeile 135) -- `PrintLogic._resolve_layer_from_project` (Zeile 141) -- `PrintLogic._resolve_active_layer` (Zeile 162) -- `PrintLogic._get_druckpuffer_cm` (Zeile 172) -- `PrintLogic._get_druckpuffer_m` (Zeile 181) -- `PrintLogic._import_qgis_symbol` (Zeile 185) -- `PrintLogic._build_rectangle` (Zeile 193) -- `PrintLogic._clone_extent` (Zeile 203) -- `PrintLogic._get_existing_layer_by_name` (Zeile 270) -- `PrintLogic._create_druckrechteck_layer` (Zeile 284) -- `PrintLogic._clear_layer_features` (Zeile 313) -- `PrintLogic.configure_atlas_for_baufreigabe_layout` (Zeile 483) -- `PrintLogic._store_druckrechteck` (Zeile 631) -- `PrintLogic.activate_druckrechteck_capture` (Zeile 680) -- `PrintLogic._resolve_selection_layer` (Zeile 811) -- `PrintLogic._get_selected_extent` (Zeile 825) -- `PrintLogic._get_plot_dimensions_mm` (Zeile 1078) -- `PrintLogic._get_required_map_dimensions_mm` (Zeile 1088) +- `PrintLogic._failed_result` (Zeile 123) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._show_target_size_too_small` (Zeile 127) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._is_enabled_flag` (Zeile 135) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._resolve_layer_from_project` (Zeile 141) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._resolve_active_layer` (Zeile 162) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_druckpuffer_cm` (Zeile 172) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_druckpuffer_m` (Zeile 181) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._import_qgis_symbol` (Zeile 185) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._build_rectangle` (Zeile 193) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._clone_extent` (Zeile 203) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_existing_layer_by_name` (Zeile 270) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._create_druckrechteck_layer` (Zeile 284) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._clear_layer_features` (Zeile 313) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic.configure_atlas_for_baufreigabe_layout` (Zeile 483) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._store_druckrechteck` (Zeile 631) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic.activate_druckrechteck_capture` (Zeile 680) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._resolve_selection_layer` (Zeile 811) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_selected_extent` (Zeile 825) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_plot_dimensions_mm` (Zeile 1078) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor +- `PrintLogic._get_required_map_dimensions_mm` (Zeile 1088) — **beabsichtigt:** Test ausgesetzt, zunächst Refactor #### `modules/Pruefmanager.py` (5) -- `Pruefmanager.report_summary` (Zeile 45) -- `Pruefmanager.zeige_hinweis` (Zeile 63) -- `Pruefmanager._handle_datei_existiert` (Zeile 87) -- `Pruefmanager._handle_verfahrensgebiet_layer_existiert` (Zeile 234) -- `Pruefmanager._ask_use_or_replace_pufferlayer` (Zeile 277) +- `Pruefmanager.report_summary` (Zeile 45) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `Pruefmanager.zeige_hinweis` (Zeile 63) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `Pruefmanager._handle_datei_existiert` (Zeile 87) — ungeklärt +- `Pruefmanager._handle_verfahrensgebiet_layer_existiert` (Zeile 234) — ungeklärt +- `Pruefmanager._ask_use_or_replace_pufferlayer` (Zeile 277) — ungeklärt #### `ui/tabs/print_tab.py` (7) -- `PrintTab._build_ui` (Zeile 115) -- `PrintTab._on_kartenname_changed` (Zeile 335) -- `PrintTab._on_massstab_changed` (Zeile 349) -- `PrintTab._on_zielgroesse_changed` (Zeile 361) -- `PrintTab._on_formfaktor_changed` (Zeile 367) -- `PrintTab._on_druckbereich_changed` (Zeile 374) -- `PrintTab._format_druckpuffer_label` (Zeile 432) +- `PrintTab._build_ui` (Zeile 115) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_kartenname_changed` (Zeile 335) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_massstab_changed` (Zeile 349) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_zielgroesse_changed` (Zeile 361) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_formfaktor_changed` (Zeile 367) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._on_druckbereich_changed` (Zeile 374) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 +- `PrintTab._format_druckpuffer_label` (Zeile 432) — **beabsichtigt:** von Test abgesehen, weil vernachlässigbar; HD 18.09.2026 -### `sn_verfahrensgebiet` – 34 von 104 (33 %) ohne Testbezug +### `sn_verfahrensgebiet` – 34 von 104 (33 %) ohne Testbezug (34 ungeklärt, 0 beabsichtigt) #### `functions/beteiligung_pruefung.py` (5) -- `_baue_nenner_kennzeichen` (Zeile 139) -- `_hole_flurstuecke_layer` (Zeile 253) -- `_flurstueckskennzeichen_geometrien` (Zeile 258) -- `_try1_listenabgleich` (Zeile 279) -- `_lese_vef_vkzf` (Zeile 380) +- `_baue_nenner_kennzeichen` (Zeile 139) — ungeklärt +- `_hole_flurstuecke_layer` (Zeile 253) — ungeklärt +- `_flurstueckskennzeichen_geometrien` (Zeile 258) — ungeklärt +- `_try1_listenabgleich` (Zeile 279) — ungeklärt +- `_lese_vef_vkzf` (Zeile 380) — ungeklärt #### `functions/flurstueck_alkis.py` (1) -- `_vorhandene_kennzeichen` (Zeile 80) +- `_vorhandene_kennzeichen` (Zeile 80) — ungeklärt #### `functions/gemarkung_alkis.py` (1) -- `gemarkung_schluessel_von_vg_geometrie` (Zeile 212) +- `gemarkung_schluessel_von_vg_geometrie` (Zeile 212) — ungeklärt #### `functions/grenzpunkt_alkis.py` (4) -- `_punktort_ta_bbox_param` (Zeile 115) -- `_hat_geometrie` (Zeile 573) -- `_lade_gemarkung_extents_mit_puffer` (Zeile 637) -- `_lade_punktort_ta_batches_per_gemarkung` (Zeile 784) +- `_punktort_ta_bbox_param` (Zeile 115) — ungeklärt +- `_hat_geometrie` (Zeile 573) — ungeklärt +- `_lade_gemarkung_extents_mit_puffer` (Zeile 637) — ungeklärt +- `_lade_punktort_ta_batches_per_gemarkung` (Zeile 784) — ungeklärt #### `functions/knickpunkt_pruefung.py` (2) -- `_setze_kategorien_symbologie` (Zeile 158) -- `erstelle_korrigiertes_polygon` (Zeile 604) +- `_setze_kategorien_symbologie` (Zeile 158) — ungeklärt +- `erstelle_korrigiertes_polygon` (Zeile 604) — ungeklärt #### `functions/umringspruefung.py` (3) -- `_zerlege_in_teile` (Zeile 55) -- `_setze_kategorien_symbologie` (Zeile 112) -- `umringspruefung` (Zeile 152) +- `_zerlege_in_teile` (Zeile 55) — ungeklärt +- `_setze_kategorien_symbologie` (Zeile 112) — ungeklärt +- `umringspruefung` (Zeile 152) — ungeklärt #### `functions/umringstatistik_grenzpunkte.py` (3) -- `_setze_kategorien_symbologie` (Zeile 74) -- `_klassifiziere` (Zeile 119) -- `umringstatistik_grenzpunkte` (Zeile 164) +- `_setze_kategorien_symbologie` (Zeile 74) — ungeklärt +- `_klassifiziere` (Zeile 119) — ungeklärt +- `umringstatistik_grenzpunkte` (Zeile 164) — ungeklärt #### `functions/verfahrensgebiet_alkis.py` (1) -- `_baue_layer_aus_kombinationen` (Zeile 144) +- `_baue_layer_aus_kombinationen` (Zeile 144) — ungeklärt #### `functions/verfahrensgebiet_alkis_komplett.py` (2) -- `_entferne_hilfs_layer` (Zeile 100) -- `_flaeche_des_layers` (Zeile 210) +- `_entferne_hilfs_layer` (Zeile 100) — ungeklärt +- `_flaeche_des_layers` (Zeile 210) — ungeklärt #### `functions/verfahrensgebiet_dialoge.py` (1) -- `ask_landkreis_objekte` (Zeile 101) +- `ask_landkreis_objekte` (Zeile 101) — ungeklärt #### `functions/verfahrensgebiet_shape_komplett.py` (6) -- `_normalisiere_feldname` (Zeile 110) -- `_finde_fbschl_index` (Zeile 132) -- `_ermittle_gebietstyp_aus_fbschl` (Zeile 161) -- `_wende_vergleich_stil_an` (Zeile 211) -- `_erstelle_vergleich_layer` (Zeile 251) -- `_finde_alle_shp` (Zeile 342) +- `_normalisiere_feldname` (Zeile 110) — ungeklärt +- `_finde_fbschl_index` (Zeile 132) — ungeklärt +- `_ermittle_gebietstyp_aus_fbschl` (Zeile 161) — ungeklärt +- `_wende_vergleich_stil_an` (Zeile 211) — ungeklärt +- `_erstelle_vergleich_layer` (Zeile 251) — ungeklärt +- `_finde_alle_shp` (Zeile 342) — ungeklärt #### `ui/tabs/working_tab.py` (5) -- `WorkingTab.handle_beteiligung_pruefen` (Zeile 297) -- `WorkingTab._haken_label` (Zeile 349) -- `WorkingTab._restore_verf_layer_selection` (Zeile 374) -- `WorkingTab._uebernimm_flurstuecke_ergebnis` (Zeile 390) -- `WorkingTab.handle_umringstatistik` (Zeile 777) +- `WorkingTab.handle_beteiligung_pruefen` (Zeile 297) — ungeklärt +- `WorkingTab._haken_label` (Zeile 349) — ungeklärt +- `WorkingTab._restore_verf_layer_selection` (Zeile 374) — ungeklärt +- `WorkingTab._uebernimm_flurstuecke_ergebnis` (Zeile 390) — ungeklärt +- `WorkingTab.handle_umringstatistik` (Zeile 777) — ungeklärt -### `sn_plan41` – 39 von 80 (49 %) ohne Testbezug +### `sn_plan41` – 39 von 80 (49 %) ohne Testbezug (39 ungeklärt, 0 beabsichtigt) #### `modules/vln_api_client.py` (3) -- `KartenApiClient.load_feature_collection` (Zeile 154) -- `KartenApiClient._request` (Zeile 176) -- `KartenApiClient._error_detail` (Zeile 235) +- `KartenApiClient.load_feature_collection` (Zeile 154) — ungeklärt +- `KartenApiClient._request` (Zeile 176) — ungeklärt +- `KartenApiClient._error_detail` (Zeile 235) — ungeklärt #### `modules/vln_api_logic.py` (4) -- `VlnApiLogic.get_p41_layers` (Zeile 233) -- `VlnApiLogic.remove_p41_layers` (Zeile 237) -- `VlnApiLogic._apply_p41_styles` (Zeile 277) -- `VlnApiLogic.upload_summary` (Zeile 347) +- `VlnApiLogic.get_p41_layers` (Zeile 233) — ungeklärt +- `VlnApiLogic.remove_p41_layers` (Zeile 237) — ungeklärt +- `VlnApiLogic._apply_p41_styles` (Zeile 277) — ungeklärt +- `VlnApiLogic.upload_summary` (Zeile 347) — ungeklärt #### `modules/vln_layer_manager.py` (2) -- `_family` (Zeile 58) -- `memory_layer_from_features` (Zeile 73) +- `_family` (Zeile 58) — ungeklärt +- `memory_layer_from_features` (Zeile 73) — ungeklärt #### `ui/dockwidget.py` (1) -- `DockWidget._init_tabs_with_services` (Zeile 42) +- `DockWidget._init_tabs_with_services` (Zeile 42) — ungeklärt #### `ui/tab_a_logic.py` (15) -- `TabALogic._log` (Zeile 90) -- `TabALogic._create_silent_pruefmanager` (Zeile 93) -- `TabALogic._zeige_verfahrensgebiet_hinweis` (Zeile 127) -- `TabALogic._close_progress_dialog` (Zeile 139) -- `TabALogic.write_layer_to_verfahrens_db` (Zeile 169) -- `TabALogic.is_valid_verfahrensgebiet_layer` (Zeile 242) -- `TabALogic._load_dienste_aus_result_dict` (Zeile 391) -- `TabALogic._create_local_layer_from_fetched_features` (Zeile 639) -- `TabALogic._write_html_log` (Zeile 779) -- `TabALogic._clone_layer_with_extent` (Zeile 934) -- `TabALogic._handle_pufferlayer` (Zeile 1062) -- `TabALogic._load_existing_pufferlayer` (Zeile 1084) -- `TabALogic._create_pufferlayer` (Zeile 1092) -- `TabALogic._pufferlayer_exists_in_source` (Zeile 1119) -- `TabALogic.Pufferlayer_erstellen` (Zeile 1133) +- `TabALogic._log` (Zeile 90) — ungeklärt +- `TabALogic._create_silent_pruefmanager` (Zeile 93) — ungeklärt +- `TabALogic._zeige_verfahrensgebiet_hinweis` (Zeile 127) — ungeklärt +- `TabALogic._close_progress_dialog` (Zeile 139) — ungeklärt +- `TabALogic.write_layer_to_verfahrens_db` (Zeile 169) — ungeklärt +- `TabALogic.is_valid_verfahrensgebiet_layer` (Zeile 242) — ungeklärt +- `TabALogic._load_dienste_aus_result_dict` (Zeile 391) — ungeklärt +- `TabALogic._create_local_layer_from_fetched_features` (Zeile 639) — ungeklärt +- `TabALogic._write_html_log` (Zeile 779) — ungeklärt +- `TabALogic._clone_layer_with_extent` (Zeile 934) — ungeklärt +- `TabALogic._handle_pufferlayer` (Zeile 1062) — ungeklärt +- `TabALogic._load_existing_pufferlayer` (Zeile 1084) — ungeklärt +- `TabALogic._create_pufferlayer` (Zeile 1092) — ungeklärt +- `TabALogic._pufferlayer_exists_in_source` (Zeile 1119) — ungeklärt +- `TabALogic.Pufferlayer_erstellen` (Zeile 1133) — ungeklärt #### `ui/tab_a_ui.py` (14) -- `TabA._build_ui` (Zeile 113) -- `TabA._restore_state` (Zeile 274) -- `TabA._restore_vln_state` (Zeile 296) -- `TabA._toggle_vln_group` (Zeile 307) -- `TabA._update_vln_ui_state` (Zeile 311) -- `TabA._populate_verfahren_combo` (Zeile 347) -- `TabA._on_vln_login` (Zeile 374) -- `TabA._on_vln_logout` (Zeile 405) -- `TabA._on_vln_verfahren_changed` (Zeile 416) -- `TabA._on_p41_laden` (Zeile 424) -- `TabA._on_upload_active` (Zeile 498) -- `TabA._create_new_gpkg` (Zeile 595) -- `TabA._update_group_color` (Zeile 608) -- `TabA._on_load_fachdaten` (Zeile 618) +- `TabA._build_ui` (Zeile 113) — ungeklärt +- `TabA._restore_state` (Zeile 274) — ungeklärt +- `TabA._restore_vln_state` (Zeile 296) — ungeklärt +- `TabA._toggle_vln_group` (Zeile 307) — ungeklärt +- `TabA._update_vln_ui_state` (Zeile 311) — ungeklärt +- `TabA._populate_verfahren_combo` (Zeile 347) — ungeklärt +- `TabA._on_vln_login` (Zeile 374) — ungeklärt +- `TabA._on_vln_logout` (Zeile 405) — ungeklärt +- `TabA._on_vln_verfahren_changed` (Zeile 416) — ungeklärt +- `TabA._on_p41_laden` (Zeile 424) — ungeklärt +- `TabA._on_upload_active` (Zeile 498) — ungeklärt +- `TabA._create_new_gpkg` (Zeile 595) — ungeklärt +- `TabA._update_group_color` (Zeile 608) — ungeklärt +- `TabA._on_load_fachdaten` (Zeile 618) — ungeklärt diff --git a/scripts/testkatalog.py b/scripts/testkatalog.py index bfba14b..ffc6e79 100644 --- a/scripts/testkatalog.py +++ b/scripts/testkatalog.py @@ -136,6 +136,7 @@ class Quellfunktion: name: str zeile: int referenziert: bool = False + absichtlich_grund: str = "" @dataclass @@ -304,6 +305,26 @@ def _ist_dunder(name: str) -> bool: return name.startswith("__") and name.endswith("__") +ABSICHTLICH_MARKER = ("von Test abgesehen", "Test ausgesetzt") + + +def absichtlicher_verzicht(docstring: str | None) -> str: + """Docstring-Zeile, die einen bewusst dokumentierten Testverzicht enthält, sonst "". + + Erkennt die beiden im Projekt verwendeten Vermerke: "von Test abgesehen, weil …" (bewusst + vernachlässigbar) und "Test ausgesetzt, zunächst Refactor" (zurückgestellt bis zu einer geplanten + Überarbeitung). Ein Namensabgleich wie bei `sammle_referenzierte_namen` – reicht der Vermerk nicht + wörtlich, wird die Funktion weiterhin als offen (ungeklärt) geführt. + """ + if not docstring: + return "" + for zeile in docstring.splitlines(): + text = " ".join(zeile.split()) + if text and any(marker in text for marker in ABSICHTLICH_MARKER): + return text + return "" + + def sammle_quellcode(ordner: Path) -> list[Quellfunktion]: """Listet Top-Level-Funktionen und Methoden im Quellcode eines Pakets (ohne tests/). @@ -323,12 +344,24 @@ def sammle_quellcode(ordner: Path) -> list[Quellfunktion]: baum = ast.parse(pfad.read_text(encoding="utf-8"), filename=str(pfad)) for knoten in baum.body: if isinstance(knoten, (ast.FunctionDef, ast.AsyncFunctionDef)): - ergebnisse.append(Quellfunktion(datei=anzeigepfad, name=knoten.name, zeile=knoten.lineno)) + ergebnisse.append( + Quellfunktion( + datei=anzeigepfad, + name=knoten.name, + zeile=knoten.lineno, + absichtlich_grund=absichtlicher_verzicht(ast.get_docstring(knoten)), + ) + ) elif isinstance(knoten, ast.ClassDef): for glied in knoten.body: if isinstance(glied, (ast.FunctionDef, ast.AsyncFunctionDef)) and not _ist_dunder(glied.name): ergebnisse.append( - Quellfunktion(datei=anzeigepfad, name=f"{knoten.name}.{glied.name}", zeile=glied.lineno) + Quellfunktion( + datei=anzeigepfad, + name=f"{knoten.name}.{glied.name}", + zeile=glied.lineno, + absichtlich_grund=absichtlicher_verzicht(ast.get_docstring(glied)), + ) ) return ergebnisse @@ -364,10 +397,13 @@ def setze_referenzstatus(katalog: Paketkatalog) -> None: def zaehle_quellfunktionen(funktionen: list[Quellfunktion]) -> dict[str, int]: + ohne_bezug = [f for f in funktionen if not f.referenziert] return { "gesamt": len(funktionen), "referenziert": sum(1 for f in funktionen if f.referenziert), - "ohne_bezug": sum(1 for f in funktionen if not f.referenziert), + "ohne_bezug": len(ohne_bezug), + "absichtlich": sum(1 for f in ohne_bezug if f.absichtlich_grund), + "ungeklaert": sum(1 for f in ohne_bezug if not f.absichtlich_grund), } @@ -565,7 +601,8 @@ def erzeuge_markdown( "vorkommt (weder als Aufruf noch als `mock.patch(\"…\")`-Ziel). Ein Namensabgleich, kein Ausführungsnachweis " "– siehe Abschnitt „Ungetestete Funktionen“ für die Grenzen dieser Heuristik. Das ist bewusst **keine** " "Zeilen-Coverage in Prozent (siehe ADR 0009, „Verworfene Alternativen“), sondern eine Lückenliste wie bei " - "fehlenden Beschreibungen." + "fehlenden Beschreibungen. Davon **beabsichtigt** – der Docstring dokumentiert den Testverzicht; " + "**ungeklärt** – weder Test noch Vermerk, die eigentliche Arbeitsliste." ) z.append("") @@ -643,10 +680,18 @@ def erzeuge_markdown( " Diese Liste ersetzt keine fachliche Prüfung, ob ein Test sinnvoll wäre – dafür braucht es einen" " Menschen, der die Funktion kennt." ) + z.append( + "„beabsichtigt“ liest den Docstring der Funktion: Enthält er einen Vermerk wie „von Test abgesehen," + " weil …“ oder „Test ausgesetzt, zunächst Refactor“, gilt der fehlende Test als bewusste Entscheidung." + " Alles andere ist „ungeklärt“ – die eigentliche Arbeitsliste." + ) z.append("") for katalog in kataloge: qf = zaehle_quellfunktionen(katalog.quellfunktionen) - z.append(f"### `{katalog.paket}` – {anteil(qf['ohne_bezug'], qf['gesamt'])} ohne Testbezug") + z.append( + f"### `{katalog.paket}` – {anteil(qf['ohne_bezug'], qf['gesamt'])} ohne Testbezug " + f"({qf['ungeklaert']} ungeklärt, {qf['absichtlich']} beabsichtigt)" + ) z.append("") luecken_qf = [f for f in katalog.quellfunktionen if not f.referenziert] if not luecken_qf: @@ -660,7 +705,10 @@ def erzeuge_markdown( z.append(f"#### `{datei}` ({len(funktionen)})") z.append("") for funktion in funktionen: - z.append(f"- `{funktion.name}` (Zeile {funktion.zeile})") + if funktion.absichtlich_grund: + z.append(f"- `{funktion.name}` (Zeile {funktion.zeile}) — **beabsichtigt:** {zelle(funktion.absichtlich_grund)}") + else: + z.append(f"- `{funktion.name}` (Zeile {funktion.zeile}) — ungeklärt") z.append("") return "\n".join(z) + "\n" @@ -796,51 +844,94 @@ def _html_karte_fuer_datei(datei: Testdatei) -> str: def _html_luecken_fuer_paket(katalog: Paketkatalog) -> str: - """Statischer Block je Paket: Quellfunktionen ohne erkennbaren Testbezug.""" + """Ausklappbare Karte je Paket (wie eine Testdatei-Karte): Quellfunktionen ohne erkennbaren Testbezug.""" qf = zaehle_quellfunktionen(katalog.quellfunktionen) if not qf["gesamt"]: return "" - luecken = [f for f in katalog.quellfunktionen if not f.referenziert] + fid = _slug(f"{katalog.paket}-luecken") + luecken = sorted([f for f in katalog.quellfunktionen if not f.referenziert], key=lambda f: (f.datei, f.zeile)) if not luecken: - koerper = '

Keine Lücken.

' + tabelle = '

Keine Lücken.

' else: - zeilen = "\n".join( - f'{html.escape(f.datei)}{html.escape(f.name)}' - f"{f.zeile}" - for f in luecken + zeilen = [] + for f in luecken: + if f.absichtlich_grund: + status = f'beabsichtigt' + else: + status = 'ungeklärt' + zeilen.append( + f'{html.escape(f.datei)}{html.escape(f.name)}' + f"{f.zeile}{status}" + ) + tabelle = ( + '' + "" + f"{''.join(zeilen)}
DateiFunktion/MethodeZeileStatus
" ) - koerper = ( - '' - f"{zeilen}
DateiFunktion/MethodeZeile
" - ) - return f""" -
-

Ungetestete Funktionen (strukturell) – {qf['ohne_bezug']} von {qf['gesamt']}

-

Heuristik: Name kommt in keiner Testdatei des Pakets vor. Details siehe docs/tests/testkatalog.md.

- {koerper} -
-""" + beschreibung = ( + '

Heuristik: Name kommt in keiner Testdatei des Pakets vor (Details siehe ' + 'docs/tests/testkatalog.md). beabsichtigt liest den Docstring der ' + "Funktion – ein Vermerk wie „von Test abgesehen, weil …“ oder „Test ausgesetzt, zunächst Refactor“ " + "gilt als bewusste Entscheidung. ungeklärt hat weder Test noch Vermerk.

" + ) + suchtext = html.escape(f"ungetestete funktionen {katalog.paket}".lower()) + header = ( + f'' + ) + return ( + f'
' + f"{header}" + f'
{beschreibung}{tabelle}
' + "
" + ) def _html_abschnitt_fuer_paket(katalog: Paketkatalog) -> str: k = zaehle(katalog.faelle) + qf = zaehle_quellfunktionen(katalog.quellfunktionen) rolle = ROLLENTEXT.get(katalog.rolle, katalog.rolle or "Plugin") karten = "".join(_html_karte_fuer_datei(d) for d in katalog.dateien) or "

Keine Testdateien gefunden.

" gesamt = k["gesamt"] or 1 ok_pct = round(100 * k["gruen"] / gesamt) warn_pct = round(100 * k["rot"] / gesamt) skip_pct = max(0, 100 - ok_pct - warn_pct) + doc_pct = round(100 * k["mit_docstring"] / k["gesamt"]) if k["gesamt"] else 0 + audit_pct = round(100 * k["aktuell_geprueft"] / k["gesamt"]) if k["gesamt"] else 0 + + kacheln = [ + ("", len(katalog.dateien), "Testdateien"), + ("", k["gesamt"], "Testfälle"), + ("ok", k["gruen"], "grün"), + ("warn", k["rot"], "rot (Baseline)"), + ("skip", k["uebersprungen"], "übersprungen"), + ("", f"{doc_pct}%", "mit Docstring"), + ("", f"{audit_pct}%", "geprüft"), + ("warn" if qf["ungeklaert"] else "", qf["ohne_bezug"], "ohne Testbezug"), + ] + tiles = "".join( + f'
{wert}' + f'{label}
' + for klasse, wert, label in kacheln + ) + return f"""

{html.escape(katalog.paket)}

-

{html.escape(rolle)} · Ordner {html.escape(katalog.ordner)} · {len(katalog.dateien)} Testdateien · {k['gesamt']} Testfälle

+

{html.escape(rolle)} · Ordner {html.escape(katalog.ordner)}

+
{tiles}
{karten}
{_html_luecken_fuer_paket(katalog)}
@@ -924,6 +1015,10 @@ _HTML_KOPF = """Prüfstand .pkg-bar{ width:220px; height:9px; border-radius:6px; overflow:hidden; display:flex; background:var(--surface-2); border:1px solid var(--line); } .pkg-bar .seg{ height:100%; display:block; } .seg-ok{ background:var(--ok); } .seg-warn{ background:var(--warn); } .seg-skip{ background:var(--skip); } + .pkg-summary{ max-width:100%; margin:0 0 16px; display:grid; gap:8px; grid-template-columns:repeat(auto-fit,minmax(108px,1fr)); } + .stat-sm{ padding:8px 10px; } + .stat-sm .n{ font-size:17px; } + .stat-sm .l{ font-size:10px; } .card-grid{ display:grid; gap:12px; } .filecard{ background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow); overflow:hidden; } .card-toggle{ width:100%; display:flex; flex-wrap:wrap; align-items:center; gap:8px 14px; justify-content:space-between; @@ -1000,13 +1095,13 @@ _HTML_SKRIPT = """ function applyFilters(){ var active = document.querySelector('.chip[aria-pressed="true"]').dataset.filter; var q = document.getElementById('searchInput').value.trim().toLowerCase(); - document.querySelectorAll('.filecard').forEach(function(card){ + document.querySelectorAll('.filecard:not(.gapcard)').forEach(function(card){ var passFilter = active === 'all' || card.dataset[active] === '1'; var passSearch = !q || card.dataset.search.indexOf(q) !== -1; card.hidden = !(passFilter && passSearch); }); document.querySelectorAll('.pkg-section').forEach(function(sec){ - sec.hidden = !sec.querySelector('.filecard:not([hidden])'); + sec.hidden = !sec.querySelector('.filecard:not(.gapcard):not([hidden])'); }); } document.querySelectorAll('.chip').forEach(function(chip){ diff --git a/scripts/tests/test_testkatalog_quellfunktionen.py b/scripts/tests/test_testkatalog_quellfunktionen.py index dba050e..9407f69 100644 --- a/scripts/tests/test_testkatalog_quellfunktionen.py +++ b/scripts/tests/test_testkatalog_quellfunktionen.py @@ -38,6 +38,10 @@ class Beispielklasse: def ungetestete_methode(self): return self.wert + + def bewusst_ausgelassene_methode(self): + """von Test abgesehen, weil vernachlässigbar; XY 01.01.2026""" + return self.wert ''' TESTDATEI_MIT_AUFRUF = '''"""Testet Beispielklasse.""" @@ -96,6 +100,35 @@ class SammleQuellcodeTests(unittest.TestCase): namen = {f.name for f in tk.sammle_quellcode(self.ordner)} self.assertNotIn("Beispielklasse.__init__", namen) + def test_docstring_mit_vermerk_setzt_absichtlich_grund(self): + """Prüft, dass eine Methode mit „von Test abgesehen“-Vermerk im Docstring absichtlich_grund gesetzt bekommt.""" + funktionen = {f.name: f for f in tk.sammle_quellcode(self.ordner)} + self.assertIn("von Test abgesehen", funktionen["Beispielklasse.bewusst_ausgelassene_methode"].absichtlich_grund) + + def test_docstring_ohne_vermerk_laesst_absichtlich_grund_leer(self): + """Prüft, dass eine Methode ohne Vermerk im Docstring ein leeres absichtlich_grund behält.""" + funktionen = {f.name: f for f in tk.sammle_quellcode(self.ordner)} + self.assertEqual(funktionen["Beispielklasse.ungetestete_methode"].absichtlich_grund, "") + + +class AbsichtlicherVerzichtTests(unittest.TestCase): + def test_erkennt_von_test_abgesehen_marker(self): + """Prüft, dass absichtlicher_verzicht den 'von Test abgesehen'-Vermerk erkennt.""" + text = tk.absichtlicher_verzicht("Kurzbeschreibung.\n\nvon Test abgesehen, weil vernachlässigbar; XY 01.01.2026") + self.assertIn("von Test abgesehen", text) + + def test_erkennt_test_ausgesetzt_marker(self): + """Prüft, dass absichtlicher_verzicht den 'Test ausgesetzt'-Vermerk erkennt.""" + self.assertEqual(tk.absichtlicher_verzicht("Test ausgesetzt, zunächst Refactor"), "Test ausgesetzt, zunächst Refactor") + + def test_liefert_leeren_string_ohne_marker(self): + """Prüft, dass absichtlicher_verzicht ohne bekannten Vermerk einen leeren String liefert.""" + self.assertEqual(tk.absichtlicher_verzicht("Ganz normale Beschreibung ohne Vermerk."), "") + + def test_liefert_leeren_string_fuer_fehlenden_docstring(self): + """Prüft, dass absichtlicher_verzicht für None einen leeren String liefert.""" + self.assertEqual(tk.absichtlicher_verzicht(None), "") + class SammleReferenzierteNamenTests(unittest.TestCase): def test_erkennt_aufruf_attribut_und_string_ziel(self): @@ -131,15 +164,18 @@ class SetzeReferenzstatusTests(unittest.TestCase): class ZaehleQuellfunktionenTests(unittest.TestCase): - def test_zaehlt_referenzierte_und_ungetestete_funktionen_korrekt(self): - """Prüft, dass zaehle_quellfunktionen Gesamtzahl, referenziert und ohne_bezug korrekt zählt.""" + def test_zaehlt_referenzierte_beabsichtigte_und_ungeklaerte_funktionen_korrekt(self): + """Prüft, dass zaehle_quellfunktionen zwischen referenziert, beabsichtigt und ungeklärt unterscheidet.""" funktionen = [ tk.Quellfunktion(datei="modul.py", name="a", zeile=1, referenziert=True), - tk.Quellfunktion(datei="modul.py", name="b", zeile=2, referenziert=False), + tk.Quellfunktion(datei="modul.py", name="b", zeile=2, referenziert=False, absichtlich_grund="von Test abgesehen, weil x"), tk.Quellfunktion(datei="modul.py", name="c", zeile=3, referenziert=False), ] ergebnis = tk.zaehle_quellfunktionen(funktionen) - self.assertEqual(ergebnis, {"gesamt": 3, "referenziert": 1, "ohne_bezug": 2}) + self.assertEqual( + ergebnis, + {"gesamt": 3, "referenziert": 1, "ohne_bezug": 2, "absichtlich": 1, "ungeklaert": 1}, + ) if __name__ == "__main__":