Kategorie "vielleicht testen" in sn_basis abgeschlossen (#67)
37 neue Testfälle für die 31 verbleibenden "vielleicht testen"- Funktionen: os_wrapper.is_case_sensitive_fs, qgiscore_wrapper. create_buffer_layer (nur die Wächter, echte Pufferlogik importiert qgis.core direkt), qgisui_wrapper.find_dock_widgets/add_menu/remove_menu, DataGrabber._aggregate_results, pruef_ergebnis.dateipfad, Navigation (add_action, finalize_menu_and_toolbar, set_active_plugin, remove_action, remove_all), SettingsTab._build_input und PrintTab (18 Methoden: Zustandslogik, Signal-Handling, Atlas-/Theme-Verwaltung). PrintTab._build_ui bekommt stattdessen den Vermerk "von Test abgesehen, weil vernachlässigbar; HD 18.09.2026" - reine Qt-Widget-Konstruktion ohne eigenständig prüfbare Logik. Damit ist die Kategorie "vielleicht testen" für sn_basis aufgelöst: jede Funktion ist jetzt getestet oder mit einem begründeten Vermerk versehen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
- Kategorie „vielleicht testen" abgeschlossen: 30 weitere Funktionen bekamen Tests (os_wrapper, qgiscore_wrapper, qgisui_wrapper, DataGrabber, pruef_ergebnis, Navigation, SettingsTab, PrintTab – Zustandslogik, Signal-Handling, Atlas-/Theme-Verwaltung), `PrintTab._build_ui` bekam den Vermerk „Test ausgesetzt, zunächst Refactor" (#67)
|
||||
- Testkatalog-Klassifizierung in Docstrings festgehalten: 43 als „vernachlässigbar" eingestufte Funktionen tragen jetzt den Vermerk „von Test abgesehen, weil vernachlässigbar", 41 Print-Bereich-Funktionen (`print_logic.py`, `print_layout.py`, `print_layout_baufreigabe.py`) den Vermerk „Test ausgesetzt, zunächst Refactor" – keine Verhaltensänderung
|
||||
- Tests für weitere zentrale Funktionen ergänzt: Verfahrensgebiet-Zustandsverwaltung, Dock-Verwaltung, Pruefmanager-/Dateipruefer-Entscheidungswege, Fundament-Wrapper (Pfade, Landkreise, Nachrichten, Layer-Existenz/-Stil, GPKG-Layer-Prüfung, QGIS-3/4-Geometriekompatibilität) und Projekteigenschaften-Tab (#65)
|
||||
- Tests für die Kernpipeline ergänzt: Datenabruf (WFS/REST/OGR-URL-Bau, Raumfilter), Datenschreiber (GPKG-Schreiben, Laden, Log), LayerLoader (CRS-Transformation, Raum-/Geometriefilter) und excel_importer (Linklisten-Import) waren zuvor ohne Testdatei (#63)
|
||||
|
||||
@@ -128,6 +128,26 @@ class TestDataGrabber(unittest.TestCase):
|
||||
self.assertFalse(summary.ok)
|
||||
self.assertEqual(summary.aktion, "keine_validen_eintraege")
|
||||
|
||||
def test_aggregate_results_mit_zeilen_liefert_erfolgsergebnis(self):
|
||||
"""Prüft, dass _aggregate_results bei vorhandenen Zeilen ein Erfolgsergebnis mit Zeilenzahl liefert."""
|
||||
grabber = self._make_grabber()
|
||||
|
||||
ergebnis = grabber._aggregate_results("quelle.xlsx", {"rows": [{"a": 1}, {"b": 2}]}, [])
|
||||
|
||||
self.assertTrue(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "ok")
|
||||
self.assertEqual(ergebnis.kontext["valid_entries"], 2)
|
||||
|
||||
def test_aggregate_results_ohne_zeilen_liefert_keine_validen_eintraege(self):
|
||||
"""Prüft, dass _aggregate_results ohne Zeilen ok=False mit Aktion 'keine_validen_eintraege' liefert."""
|
||||
grabber = self._make_grabber()
|
||||
|
||||
ergebnis = grabber._aggregate_results("quelle.xlsx", {"rows": []}, [])
|
||||
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "keine_validen_eintraege")
|
||||
self.assertEqual(ergebnis.kontext["eintraege_gesamt"], 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
"""Tests für sn_basis/ui/navigation.py::Navigation (Issue #67, ohne init_ui – s. Docstring dort)."""
|
||||
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from sn_basis.ui.navigation import Navigation
|
||||
|
||||
|
||||
class _FakeQAction:
|
||||
def __init__(self, text, parent=None) -> None:
|
||||
self.text = text
|
||||
self.parent = parent
|
||||
self.tooltip = None
|
||||
self.checkable = None
|
||||
self.checked = None
|
||||
self.triggered = SimpleNamespace(connect=Mock())
|
||||
|
||||
def setToolTip(self, text) -> None:
|
||||
self.tooltip = text
|
||||
|
||||
def setCheckable(self, value: bool) -> None:
|
||||
self.checkable = value
|
||||
|
||||
def setChecked(self, value: bool) -> None:
|
||||
self.checked = value
|
||||
|
||||
|
||||
class _FakeGroup:
|
||||
def __init__(self) -> None:
|
||||
self.added = []
|
||||
|
||||
def addAction(self, action) -> None:
|
||||
self.added.append(action)
|
||||
|
||||
|
||||
class _FakeMenuOrToolbar:
|
||||
def __init__(self) -> None:
|
||||
self.cleared = False
|
||||
self.added = []
|
||||
self.removed = []
|
||||
|
||||
def clear(self) -> None:
|
||||
self.cleared = True
|
||||
self.added = []
|
||||
|
||||
def addAction(self, action) -> None:
|
||||
self.added.append(action)
|
||||
|
||||
def removeAction(self, action) -> None:
|
||||
self.removed.append(action)
|
||||
|
||||
|
||||
class AddActionTests(unittest.TestCase):
|
||||
def test_ohne_plugin_group_liefert_none(self):
|
||||
"""Prüft, dass add_action ohne initialisierte Aktionsgruppe None liefert und nichts anlegt."""
|
||||
nav = Navigation()
|
||||
|
||||
ergebnis = nav.add_action("Text", callback=lambda: None)
|
||||
|
||||
self.assertIsNone(ergebnis)
|
||||
self.assertEqual(nav.actions, [])
|
||||
|
||||
@patch("sn_basis.ui.navigation.get_main_window", return_value=object())
|
||||
@patch("sn_basis.ui.navigation.QAction", _FakeQAction)
|
||||
def test_erzeugt_action_und_fuegt_sie_gruppe_und_liste_hinzu(self, _mock_window):
|
||||
"""Prüft, dass add_action eine Action erzeugt, ihr die Gruppe zuweist und sie priorisiert in der Liste ablegt."""
|
||||
nav = Navigation()
|
||||
nav.plugin_group = _FakeGroup()
|
||||
callback = Mock()
|
||||
|
||||
action = nav.add_action("Mein Text", callback, tooltip="Tooltip", priority=5)
|
||||
|
||||
self.assertEqual(action.text, "Mein Text")
|
||||
self.assertEqual(action.tooltip, "Tooltip")
|
||||
self.assertTrue(action.checkable)
|
||||
action.triggered.connect.assert_called_once_with(callback)
|
||||
self.assertIn(action, nav.plugin_group.added)
|
||||
self.assertEqual(nav.actions, [(5, action)])
|
||||
|
||||
|
||||
class FinalizeMenuAndToolbarTests(unittest.TestCase):
|
||||
def test_ohne_menu_oder_toolbar_tut_nichts(self):
|
||||
"""Prüft, dass finalize_menu_and_toolbar ohne Menü oder Toolbar keine Ausnahme wirft."""
|
||||
nav = Navigation()
|
||||
|
||||
nav.finalize_menu_and_toolbar() # darf keine Ausnahme werfen
|
||||
|
||||
def test_sortiert_und_fuegt_aktionen_in_prioritaetsreihenfolge_hinzu(self):
|
||||
"""Prüft, dass finalize_menu_and_toolbar Aktionen nach Priorität sortiert in Menü und Toolbar einträgt."""
|
||||
nav = Navigation()
|
||||
nav.menu = _FakeMenuOrToolbar()
|
||||
nav.toolbar = _FakeMenuOrToolbar()
|
||||
nav.actions = [(20, "a2"), (10, "a1")]
|
||||
|
||||
nav.finalize_menu_and_toolbar()
|
||||
|
||||
self.assertTrue(nav.menu.cleared)
|
||||
self.assertTrue(nav.toolbar.cleared)
|
||||
self.assertEqual(nav.menu.added, ["a1", "a2"])
|
||||
self.assertEqual(nav.toolbar.added, ["a1", "a2"])
|
||||
|
||||
|
||||
class SetActivePluginTests(unittest.TestCase):
|
||||
def test_setzt_nur_aktive_action_auf_checked(self):
|
||||
"""Prüft, dass set_active_plugin nur die übergebene Action markiert, alle anderen abwählt."""
|
||||
action1, action2 = _FakeQAction("a1"), _FakeQAction("a2")
|
||||
nav = Navigation()
|
||||
nav.actions = [(1, action1), (2, action2)]
|
||||
|
||||
nav.set_active_plugin(action2)
|
||||
|
||||
self.assertFalse(action1.checked)
|
||||
self.assertTrue(action2.checked)
|
||||
|
||||
def test_ohne_aktive_action_setzt_alle_auf_false(self):
|
||||
"""Prüft, dass set_active_plugin ohne aktive Action alle bestehenden Actions abwählt."""
|
||||
action1 = _FakeQAction("a1")
|
||||
nav = Navigation()
|
||||
nav.actions = [(1, action1)]
|
||||
|
||||
nav.set_active_plugin(None) # darf keine Ausnahme werfen
|
||||
|
||||
self.assertFalse(action1.checked)
|
||||
|
||||
|
||||
class RemoveActionTests(unittest.TestCase):
|
||||
def test_none_action_tut_nichts(self):
|
||||
"""Prüft, dass remove_action mit None keine Ausnahme wirft und die Liste unverändert lässt."""
|
||||
nav = Navigation()
|
||||
nav.actions = [(1, "a1")]
|
||||
|
||||
nav.remove_action(None)
|
||||
|
||||
self.assertEqual(nav.actions, [(1, "a1")])
|
||||
|
||||
def test_entfernt_action_aus_menu_toolbar_und_liste(self):
|
||||
"""Prüft, dass remove_action die Action aus Menü, Toolbar und der internen Liste entfernt."""
|
||||
nav = Navigation()
|
||||
nav.menu = _FakeMenuOrToolbar()
|
||||
nav.toolbar = _FakeMenuOrToolbar()
|
||||
nav.actions = [(1, "a1"), (2, "a2")]
|
||||
|
||||
nav.remove_action("a1")
|
||||
|
||||
self.assertEqual(nav.menu.removed, ["a1"])
|
||||
self.assertEqual(nav.toolbar.removed, ["a1"])
|
||||
self.assertEqual(nav.actions, [(2, "a2")])
|
||||
|
||||
def test_ohne_menu_oder_toolbar_entfernt_trotzdem_aus_liste(self):
|
||||
"""Prüft, dass remove_action ohne Menü/Toolbar trotzdem aus der internen Liste entfernt."""
|
||||
nav = Navigation()
|
||||
nav.actions = [(1, "a1")]
|
||||
|
||||
nav.remove_action("a1")
|
||||
|
||||
self.assertEqual(nav.actions, [])
|
||||
|
||||
|
||||
class RemoveAllTests(unittest.TestCase):
|
||||
@patch("sn_basis.ui.navigation.remove_toolbar")
|
||||
@patch("sn_basis.ui.navigation.remove_menu")
|
||||
def test_entfernt_menu_toolbar_und_setzt_zustand_zurueck(self, mock_remove_menu, mock_remove_toolbar):
|
||||
"""Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt."""
|
||||
nav = Navigation()
|
||||
fake_menu, fake_toolbar = object(), object()
|
||||
nav.menu = fake_menu
|
||||
nav.toolbar = fake_toolbar
|
||||
nav.actions = [(1, "a1")]
|
||||
nav.plugin_group = object()
|
||||
|
||||
nav.remove_all()
|
||||
|
||||
mock_remove_menu.assert_called_once_with(fake_menu)
|
||||
mock_remove_toolbar.assert_called_once_with(fake_toolbar)
|
||||
self.assertIsNone(nav.menu)
|
||||
self.assertIsNone(nav.toolbar)
|
||||
self.assertEqual(nav.actions, [])
|
||||
self.assertIsNone(nav.plugin_group)
|
||||
|
||||
@patch("sn_basis.ui.navigation.remove_toolbar")
|
||||
@patch("sn_basis.ui.navigation.remove_menu")
|
||||
def test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf(self, mock_remove_menu, mock_remove_toolbar):
|
||||
"""Prüft, dass remove_all ohne vorhandenes Menü/Toolbar die Wrapper nicht aufruft, aber die Liste leert."""
|
||||
nav = Navigation()
|
||||
nav.actions = [(1, "a1")]
|
||||
|
||||
nav.remove_all()
|
||||
|
||||
mock_remove_menu.assert_not_called()
|
||||
mock_remove_toolbar.assert_not_called()
|
||||
self.assertEqual(nav.actions, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Tests für sn_basis/functions/os_wrapper.py::is_case_sensitive_fs (Issue #67)."""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from sn_basis.functions import os_wrapper
|
||||
|
||||
|
||||
class IsCaseSensitiveFsTests(unittest.TestCase):
|
||||
def test_windows_ist_nicht_case_sensitiv(self):
|
||||
"""Prüft, dass Windows als nicht case-sensitives Dateisystem gilt."""
|
||||
with patch.object(os_wrapper, "IS_WINDOWS", True), patch.object(os_wrapper, "IS_MACOS", False):
|
||||
self.assertFalse(os_wrapper.is_case_sensitive_fs())
|
||||
|
||||
def test_macos_ist_nicht_case_sensitiv(self):
|
||||
"""Prüft, dass macOS als nicht case-sensitives Dateisystem gilt."""
|
||||
with patch.object(os_wrapper, "IS_WINDOWS", False), patch.object(os_wrapper, "IS_MACOS", True):
|
||||
self.assertFalse(os_wrapper.is_case_sensitive_fs())
|
||||
|
||||
def test_linux_ist_case_sensitiv(self):
|
||||
"""Prüft, dass Linux als case-sensitives Dateisystem gilt."""
|
||||
with patch.object(os_wrapper, "IS_WINDOWS", False), patch.object(os_wrapper, "IS_MACOS", False):
|
||||
self.assertTrue(os_wrapper.is_case_sensitive_fs())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,649 @@
|
||||
"""Tests für sn_basis/ui/tabs/print_tab.py::PrintTab (Issue #67).
|
||||
|
||||
self.logic ist eine PrintLogic-Instanz (modules/print_logic.py) - die Klasse steht wegen einer
|
||||
geplanten Grundüberarbeitung komplett unter "Test ausgesetzt, zunächst Refactor" (#63/#65). Wo eine
|
||||
PrintTab-Methode self.logic.irgendwas(...) aufruft, wird self.logic hier durch ein Fake/Mock ersetzt;
|
||||
geprüft wird nur, dass PrintTab die richtige Methode mit den richtigen Argumenten aufruft, nie das
|
||||
tatsächliche PrintLogic-Verhalten.
|
||||
|
||||
PrintTab(QWidget) erbt von der im Mock-Modus leeren _MockWidget-Klasse (kein setLayout() usw.) - wie bei
|
||||
SettingsTab/BaseDockWidget wird nie PrintTab() echt konstruiert, sondern PrintTab.__new__(PrintTab) mit
|
||||
den je Test nötigen Instanzattributen von Hand.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from sn_basis.functions.qt_wrapper import ArrowDown, ArrowRight
|
||||
from sn_basis.ui.tabs.print_tab import (
|
||||
ATLAS_ERZEUGEN_VAR,
|
||||
DRUCKBEREICH_VAR,
|
||||
DRUCKBEREICH_VERFAHRENSGEBIET,
|
||||
DRUCKPUFFER_STANDARD_CM,
|
||||
DRUCKPUFFER_VAR,
|
||||
FORMFAKTOR_UI_VAR,
|
||||
KARTENNAME_38,
|
||||
KARTENNAME_PLACEHOLDER,
|
||||
KARTENNAME_VAR,
|
||||
MASSSTAB_VAR,
|
||||
MASSSTAB_WIE_KARTENFENSTER,
|
||||
PLOTMASSSTAB_BY_AUSWAHL,
|
||||
THEMA_VAR,
|
||||
THEMA_WIE_KARTENFENSTER,
|
||||
DIN_STANDARD,
|
||||
PrintTab,
|
||||
ZIELGROESSE_UI_VAR,
|
||||
)
|
||||
|
||||
|
||||
def _neuer_tab() -> PrintTab:
|
||||
return PrintTab.__new__(PrintTab)
|
||||
|
||||
|
||||
class _FakeCombo:
|
||||
def __init__(self, text: str = "") -> None:
|
||||
self._text = text
|
||||
self.added_items: list = []
|
||||
|
||||
def currentText(self) -> str:
|
||||
return self._text
|
||||
|
||||
def setCurrentText(self, value: str) -> None:
|
||||
self._text = value
|
||||
|
||||
def clear(self) -> None:
|
||||
self.added_items = []
|
||||
|
||||
def addItems(self, items) -> None:
|
||||
self.added_items.extend(items)
|
||||
|
||||
def blockSignals(self, value: bool) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class _FakeCheckbox:
|
||||
def __init__(self, checked: bool = False) -> None:
|
||||
self._checked = checked
|
||||
|
||||
def isChecked(self) -> bool:
|
||||
return self._checked
|
||||
|
||||
def setChecked(self, value: bool) -> None:
|
||||
self._checked = value
|
||||
|
||||
|
||||
class _FakeButton:
|
||||
def __init__(self, text: str = "") -> None:
|
||||
self._text = text
|
||||
|
||||
def setText(self, value: str) -> None:
|
||||
self._text = value
|
||||
|
||||
def text(self) -> str:
|
||||
return self._text
|
||||
|
||||
|
||||
class _FakePruefmanager:
|
||||
def __init__(self, frage_text_ergebnis) -> None:
|
||||
self._frage_text_ergebnis = frage_text_ergebnis
|
||||
self.zeige_hinweis_calls: list = []
|
||||
|
||||
def frage_text(self, titel, meldung, default_text=""):
|
||||
return self._frage_text_ergebnis
|
||||
|
||||
def zeige_hinweis(self, titel, meldung) -> None:
|
||||
self.zeige_hinweis_calls.append((titel, meldung))
|
||||
|
||||
|
||||
class _FakeSignal:
|
||||
def __init__(self) -> None:
|
||||
self.connected: list = []
|
||||
|
||||
def connect(self, fn) -> None:
|
||||
self.connected.append(fn)
|
||||
|
||||
def disconnect(self, fn) -> None:
|
||||
self.connected.remove(fn)
|
||||
|
||||
|
||||
class _FakeProjectSignals:
|
||||
def __init__(self) -> None:
|
||||
self.readProject = _FakeSignal()
|
||||
self.newProjectCreated = _FakeSignal()
|
||||
self.cleared = _FakeSignal()
|
||||
|
||||
|
||||
class _FakeThemeCollection:
|
||||
def __init__(self) -> None:
|
||||
self.mapThemesChanged = _FakeSignal()
|
||||
self.changed = _FakeSignal()
|
||||
self.themeChanged = _FakeSignal()
|
||||
|
||||
|
||||
class SetServicesTests(unittest.TestCase):
|
||||
@patch("sn_basis.ui.tabs.print_tab.PrintLogic")
|
||||
def test_ruft_alle_set_methoden_mit_combo_werten_auf(self, mock_logic_cls):
|
||||
"""Prüft, dass set_services alle set_*-Methoden von PrintLogic mit den aktuellen Combo-Werten aufruft."""
|
||||
tab = _neuer_tab()
|
||||
tab._kartenname_combo = _FakeCombo("§38")
|
||||
tab._massstab_combo = _FakeCombo("1:1000")
|
||||
tab._thema_combo = _FakeCombo("Thema A")
|
||||
tab._zielgroesse_combo = _FakeCombo("A3")
|
||||
tab._druckbereich_combo = _FakeCombo("Verfahrensgebiet")
|
||||
tab._endlosrolle_cb = _FakeCheckbox(True)
|
||||
tab._get_current_canvas_scale = lambda: 5000.0
|
||||
mock_logic = mock_logic_cls.return_value
|
||||
|
||||
pruefmanager = object()
|
||||
tab.set_services(pruefmanager, object())
|
||||
|
||||
self.assertIs(tab.pruefmanager, pruefmanager)
|
||||
mock_logic.set_kartenname_for_auswahl.assert_called_once_with("§38")
|
||||
mock_logic.set_plotmassstab_for_auswahl.assert_called_once_with("1:1000", 5000.0)
|
||||
mock_logic.set_view_for_auswahl.assert_called_once_with("Thema A")
|
||||
mock_logic.set_zielgroesse_for_auswahl.assert_called_once_with("A3")
|
||||
mock_logic.set_druckbereich_for_auswahl.assert_called_once_with("Verfahrensgebiet")
|
||||
mock_logic.set_formfaktor.assert_called_once_with(True)
|
||||
|
||||
@patch("sn_basis.ui.tabs.print_tab.PrintLogic")
|
||||
def test_ohne_combos_ruft_keine_set_methode_auf(self, mock_logic_cls):
|
||||
"""Prüft, dass set_services ohne vorhandene Combos keine set_*-Methode von PrintLogic aufruft."""
|
||||
tab = _neuer_tab()
|
||||
tab._kartenname_combo = None
|
||||
tab._massstab_combo = None
|
||||
tab._thema_combo = None
|
||||
tab._zielgroesse_combo = None
|
||||
tab._druckbereich_combo = None
|
||||
tab._endlosrolle_cb = None
|
||||
mock_logic = mock_logic_cls.return_value
|
||||
|
||||
tab.set_services(object(), object())
|
||||
|
||||
mock_logic.set_kartenname_for_auswahl.assert_not_called()
|
||||
mock_logic.set_plotmassstab_for_auswahl.assert_not_called()
|
||||
mock_logic.set_view_for_auswahl.assert_not_called()
|
||||
mock_logic.set_zielgroesse_for_auswahl.assert_not_called()
|
||||
mock_logic.set_druckbereich_for_auswahl.assert_not_called()
|
||||
mock_logic.set_formfaktor.assert_not_called()
|
||||
self.assertIs(tab.logic, mock_logic)
|
||||
|
||||
|
||||
class RestoreStateTests(unittest.TestCase):
|
||||
def _neuer_tab_mit_widgets(self) -> PrintTab:
|
||||
tab = _neuer_tab()
|
||||
tab._kartenname_combo = _FakeCombo()
|
||||
tab._massstab_combo = _FakeCombo()
|
||||
tab._thema_combo = _FakeCombo()
|
||||
tab._zielgroesse_combo = _FakeCombo()
|
||||
tab._endlosrolle_cb = _FakeCheckbox()
|
||||
tab._druckbereich_combo = _FakeCombo()
|
||||
tab._druckpuffer_button = _FakeButton()
|
||||
tab._atlas_erzeugen_cb = None
|
||||
tab._atlas_layer_combo = None
|
||||
tab._get_gespeicherte_themen = lambda: ["Thema A"]
|
||||
return tab
|
||||
|
||||
def test_gueltige_gespeicherte_werte_werden_uebernommen(self):
|
||||
"""Prüft, dass _restore_state gültige gespeicherte Werte in die jeweiligen Widgets übernimmt."""
|
||||
tab = self._neuer_tab_mit_widgets()
|
||||
massstab_gueltig = next(iter(PLOTMASSSTAB_BY_AUSWAHL.keys()))
|
||||
werte = {
|
||||
KARTENNAME_VAR: KARTENNAME_38,
|
||||
MASSSTAB_VAR: massstab_gueltig,
|
||||
THEMA_VAR: THEMA_WIE_KARTENFENSTER,
|
||||
ZIELGROESSE_UI_VAR: DIN_STANDARD,
|
||||
FORMFAKTOR_UI_VAR: "Blatt",
|
||||
DRUCKBEREICH_VAR: DRUCKBEREICH_VERFAHRENSGEBIET,
|
||||
DRUCKPUFFER_VAR: "7",
|
||||
}
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", side_effect=lambda key, scope=None: werte.get(key)):
|
||||
tab._restore_state()
|
||||
|
||||
self.assertEqual(tab._kartenname_combo.currentText(), KARTENNAME_38)
|
||||
self.assertEqual(tab._massstab_combo.currentText(), massstab_gueltig)
|
||||
self.assertEqual(tab._druckbereich_combo.currentText(), DRUCKBEREICH_VERFAHRENSGEBIET)
|
||||
self.assertEqual(tab._druckpuffer_button.text(), "7 cm")
|
||||
|
||||
def test_ungueltige_oder_fehlende_werte_fallen_auf_defaults_zurueck(self):
|
||||
"""Prüft, dass _restore_state ohne (gültige) gespeicherte Werte auf die Default-Konstanten zurückfällt."""
|
||||
tab = self._neuer_tab_mit_widgets()
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None):
|
||||
tab._restore_state()
|
||||
|
||||
self.assertEqual(tab._kartenname_combo.currentText(), KARTENNAME_PLACEHOLDER)
|
||||
self.assertEqual(tab._massstab_combo.currentText(), MASSSTAB_WIE_KARTENFENSTER)
|
||||
self.assertEqual(tab._druckbereich_combo.currentText(), DRUCKBEREICH_VERFAHRENSGEBIET)
|
||||
self.assertEqual(tab._druckpuffer_button.text(), f"{DRUCKPUFFER_STANDARD_CM} cm")
|
||||
|
||||
|
||||
class OnDruckpufferClickedTests(unittest.TestCase):
|
||||
def test_gueltige_zahl_setzt_variable_und_button_text(self):
|
||||
"""Prüft, dass eine bestätigte, gültige Zahl den Druckpuffer speichert und den Button-Text aktualisiert."""
|
||||
tab = _neuer_tab()
|
||||
tab.pruefmanager = _FakePruefmanager(("5", True))
|
||||
tab._druckpuffer_button = _FakeButton()
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None), \
|
||||
patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
|
||||
tab._on_druckpuffer_clicked()
|
||||
|
||||
mock_set.assert_called_once_with(DRUCKPUFFER_VAR, "5", scope="project")
|
||||
self.assertEqual(tab._druckpuffer_button.text(), "5 cm")
|
||||
|
||||
def test_abgebrochen_aendert_nichts(self):
|
||||
"""Prüft, dass ein abgebrochener Dialog den Druckpuffer nicht verändert."""
|
||||
tab = _neuer_tab()
|
||||
tab.pruefmanager = _FakePruefmanager(("5", False))
|
||||
tab._druckpuffer_button = _FakeButton()
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None), \
|
||||
patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
|
||||
tab._on_druckpuffer_clicked()
|
||||
|
||||
mock_set.assert_not_called()
|
||||
|
||||
def test_ungueltige_zahl_zeigt_hinweis_und_setzt_nichts(self):
|
||||
"""Prüft, dass eine nicht-numerische Eingabe einen Hinweis zeigt und den Druckpuffer nicht setzt."""
|
||||
tab = _neuer_tab()
|
||||
pruefmanager = _FakePruefmanager(("abc", True))
|
||||
tab.pruefmanager = pruefmanager
|
||||
tab._druckpuffer_button = _FakeButton()
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=None), \
|
||||
patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
|
||||
tab._on_druckpuffer_clicked()
|
||||
|
||||
mock_set.assert_not_called()
|
||||
self.assertEqual(len(pruefmanager.zeige_hinweis_calls), 1)
|
||||
|
||||
|
||||
class OnVorlageErstellenTests(unittest.TestCase):
|
||||
def test_ruft_druckvorlage_anlegen_mit_combo_werten_auf(self):
|
||||
"""Prüft, dass _on_vorlage_erstellen druckvorlage_anlegen mit den aktuellen Combo-Werten aufruft."""
|
||||
tab = _neuer_tab()
|
||||
mock_logic = MagicMock()
|
||||
tab.logic = mock_logic
|
||||
tab.pruefmanager = object()
|
||||
tab._kartenname_combo = _FakeCombo("§38")
|
||||
tab._massstab_combo = _FakeCombo("1:1000")
|
||||
tab._zielgroesse_combo = _FakeCombo("A3")
|
||||
tab._endlosrolle_cb = _FakeCheckbox(True)
|
||||
tab._druckbereich_combo = _FakeCombo("Verfahrensgebiet")
|
||||
|
||||
tab._on_vorlage_erstellen()
|
||||
|
||||
mock_logic.druckvorlage_anlegen.assert_called_once_with(
|
||||
layer=None,
|
||||
kartenname_auswahl="§38",
|
||||
massstab_auswahl="1:1000",
|
||||
zielgroesse="A3",
|
||||
formfaktor=True,
|
||||
druckbereich_auswahl="Verfahrensgebiet",
|
||||
)
|
||||
|
||||
def test_ohne_logic_tut_nichts(self):
|
||||
"""Prüft, dass _on_vorlage_erstellen ohne gesetzte PrintLogic keine Ausnahme wirft."""
|
||||
tab = _neuer_tab()
|
||||
tab.logic = None
|
||||
tab.pruefmanager = object()
|
||||
|
||||
tab._on_vorlage_erstellen() # darf keine Ausnahme werfen
|
||||
|
||||
|
||||
class ProjectSignalsTests(unittest.TestCase):
|
||||
def test_connect_verbindet_alle_drei_signale(self):
|
||||
"""Prüft, dass _connect_project_signals alle drei Projektsignale mit _on_project_changed verbindet."""
|
||||
tab = _neuer_tab()
|
||||
projekt = _FakeProjectSignals()
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value = projekt
|
||||
tab._connect_project_signals()
|
||||
|
||||
self.assertEqual(projekt.readProject.connected, [tab._on_project_changed])
|
||||
self.assertEqual(projekt.newProjectCreated.connected, [tab._on_project_changed])
|
||||
self.assertEqual(projekt.cleared.connected, [tab._on_project_changed])
|
||||
|
||||
def test_disconnect_trennt_alle_drei_signale(self):
|
||||
"""Prüft, dass _disconnect_project_signals alle drei zuvor verbundenen Projektsignale trennt."""
|
||||
tab = _neuer_tab()
|
||||
projekt = _FakeProjectSignals()
|
||||
projekt.readProject.connect(tab._on_project_changed)
|
||||
projekt.newProjectCreated.connect(tab._on_project_changed)
|
||||
projekt.cleared.connect(tab._on_project_changed)
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value = projekt
|
||||
tab._disconnect_project_signals()
|
||||
|
||||
self.assertEqual(projekt.readProject.connected, [])
|
||||
self.assertEqual(projekt.newProjectCreated.connected, [])
|
||||
self.assertEqual(projekt.cleared.connected, [])
|
||||
|
||||
def test_instance_wirft_ausnahme_wird_abgefangen(self):
|
||||
"""Prüft, dass eine Ausnahme bei QgsProject.instance() in beiden Signalmethoden abgefangen wird."""
|
||||
tab = _neuer_tab()
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.side_effect = RuntimeError("kaputt")
|
||||
tab._connect_project_signals() # darf keine Ausnahme werfen
|
||||
tab._disconnect_project_signals() # darf keine Ausnahme werfen
|
||||
|
||||
|
||||
class CleanupTests(unittest.TestCase):
|
||||
def test_ruft_beide_disconnect_methoden_auf(self):
|
||||
"""Prüft, dass cleanup sowohl die Theme- als auch die Projektsignale trennt."""
|
||||
tab = _neuer_tab()
|
||||
tab._disconnect_theme_collection_signals = MagicMock()
|
||||
tab._disconnect_project_signals = MagicMock()
|
||||
|
||||
tab.cleanup()
|
||||
|
||||
tab._disconnect_theme_collection_signals.assert_called_once()
|
||||
tab._disconnect_project_signals.assert_called_once()
|
||||
|
||||
|
||||
class ToggleAtlasTests(unittest.TestCase):
|
||||
def test_checked_true_setzt_arrow_down_und_zeigt_inhalt(self):
|
||||
"""Prüft, dass _toggle_atlas bei checked=True den Pfeil nach unten dreht und den Inhalt einblendet."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_button = MagicMock()
|
||||
tab._atlas_content = MagicMock()
|
||||
|
||||
tab._toggle_atlas(True)
|
||||
|
||||
tab._atlas_button.setArrowType.assert_called_once_with(ArrowDown)
|
||||
tab._atlas_content.setVisible.assert_called_once_with(True)
|
||||
|
||||
def test_checked_false_setzt_arrow_right_und_versteckt_inhalt(self):
|
||||
"""Prüft, dass _toggle_atlas bei checked=False den Pfeil nach rechts dreht und den Inhalt ausblendet."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_button = MagicMock()
|
||||
tab._atlas_content = MagicMock()
|
||||
|
||||
tab._toggle_atlas(False)
|
||||
|
||||
tab._atlas_button.setArrowType.assert_called_once_with(ArrowRight)
|
||||
tab._atlas_content.setVisible.assert_called_once_with(False)
|
||||
|
||||
|
||||
class RestoreAtlasLayerSelectionTests(unittest.TestCase):
|
||||
def test_vorhandene_layer_id_setzt_layer_in_combo(self):
|
||||
"""Prüft, dass eine gespeicherte Layer-ID den zugehörigen Layer im Atlas-Dropdown auswählt."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_layer_combo = MagicMock(spec=["setLayer"])
|
||||
fake_layer = object()
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value="layer_1"), \
|
||||
patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value.mapLayer.return_value = fake_layer
|
||||
tab._restore_atlas_layer_selection()
|
||||
|
||||
tab._atlas_layer_combo.setLayer.assert_called_once_with(fake_layer)
|
||||
|
||||
def test_leere_gespeicherte_layer_id_tut_nichts(self):
|
||||
"""Prüft, dass eine leere gespeicherte Layer-ID das Projekt gar nicht erst anfragt."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_layer_combo = MagicMock(spec=["setLayer"])
|
||||
with patch("sn_basis.ui.tabs.print_tab.get_variable", return_value=""), \
|
||||
patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
tab._restore_atlas_layer_selection()
|
||||
|
||||
tab._atlas_layer_combo.setLayer.assert_not_called()
|
||||
mock_project.instance.assert_not_called()
|
||||
|
||||
|
||||
class ThemeCollectionSignalsTests(unittest.TestCase):
|
||||
def test_connect_verbindet_alle_signale(self):
|
||||
"""Prüft, dass _connect_theme_collection_signals alle Signale der Theme-Collection verbindet."""
|
||||
tab = _neuer_tab()
|
||||
tab._theme_signal_connected = False
|
||||
collection = _FakeThemeCollection()
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value.mapThemeCollection.return_value = collection
|
||||
tab._connect_theme_collection_signals()
|
||||
|
||||
self.assertEqual(collection.mapThemesChanged.connected, [tab._refresh_thema_combo_live])
|
||||
self.assertTrue(tab._theme_signal_connected)
|
||||
self.assertIs(tab._connected_theme_collection, collection)
|
||||
|
||||
def test_connect_ist_idempotent_wenn_schon_verbunden(self):
|
||||
"""Prüft, dass _connect_theme_collection_signals bei bereits verbundenem Signal nichts erneut tut."""
|
||||
tab = _neuer_tab()
|
||||
tab._theme_signal_connected = True
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
tab._connect_theme_collection_signals()
|
||||
|
||||
mock_project.instance.assert_not_called()
|
||||
|
||||
def test_disconnect_trennt_alle_signale(self):
|
||||
"""Prüft, dass _disconnect_theme_collection_signals alle verbundenen Theme-Signale trennt und zurücksetzt."""
|
||||
tab = _neuer_tab()
|
||||
collection = _FakeThemeCollection()
|
||||
collection.mapThemesChanged.connect(tab._refresh_thema_combo_live)
|
||||
collection.changed.connect(tab._refresh_thema_combo_live)
|
||||
collection.themeChanged.connect(tab._refresh_thema_combo_live)
|
||||
tab._connected_theme_collection = collection
|
||||
tab._theme_signal_connected = True
|
||||
|
||||
tab._disconnect_theme_collection_signals()
|
||||
|
||||
self.assertEqual(collection.mapThemesChanged.connected, [])
|
||||
self.assertIsNone(tab._connected_theme_collection)
|
||||
self.assertFalse(tab._theme_signal_connected)
|
||||
|
||||
|
||||
class RefreshThemaComboLiveTests(unittest.TestCase):
|
||||
def test_erhaelt_vorherige_auswahl_wenn_noch_vorhanden(self):
|
||||
"""Prüft, dass _refresh_thema_combo_live die vorherige Auswahl beibehält, wenn sie noch existiert."""
|
||||
tab = _neuer_tab()
|
||||
combo = _FakeCombo("Thema A")
|
||||
tab._thema_combo = combo
|
||||
tab._get_gespeicherte_themen = lambda: ["Thema A", "Thema B"]
|
||||
tab._on_thema_changed = MagicMock()
|
||||
|
||||
tab._refresh_thema_combo_live()
|
||||
|
||||
self.assertEqual(combo.currentText(), "Thema A")
|
||||
tab._on_thema_changed.assert_called_once_with("Thema A")
|
||||
|
||||
def test_faellt_auf_wie_kartenfenster_zurueck_wenn_auswahl_verschwunden(self):
|
||||
"""Prüft, dass _refresh_thema_combo_live auf 'wie Kartenfenster' zurückfällt, wenn die Auswahl verschwunden ist."""
|
||||
tab = _neuer_tab()
|
||||
combo = _FakeCombo("Alte Auswahl")
|
||||
tab._thema_combo = combo
|
||||
tab._get_gespeicherte_themen = lambda: ["Thema B"]
|
||||
tab._on_thema_changed = MagicMock()
|
||||
|
||||
tab._refresh_thema_combo_live()
|
||||
|
||||
self.assertEqual(combo.currentText(), THEMA_WIE_KARTENFENSTER)
|
||||
tab._on_thema_changed.assert_called_once_with(THEMA_WIE_KARTENFENSTER)
|
||||
|
||||
|
||||
class GetGespeicherteThemenTests(unittest.TestCase):
|
||||
def test_dedupliziert_und_filtert_leere_eintraege(self):
|
||||
"""Prüft, dass _get_gespeicherte_themen Duplikate und leere/None-Einträge entfernt."""
|
||||
tab = _neuer_tab()
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value.mapThemeCollection.return_value.mapThemes.return_value = [
|
||||
"A", "A", "", "B", None,
|
||||
]
|
||||
ergebnis = tab._get_gespeicherte_themen()
|
||||
|
||||
self.assertEqual(ergebnis, ["A", "B"])
|
||||
|
||||
def test_fehler_liefert_leere_liste(self):
|
||||
"""Prüft, dass ein Fehler beim Zugriff auf die Theme-Collection eine leere Liste liefert."""
|
||||
tab = _neuer_tab()
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.side_effect = RuntimeError("kaputt")
|
||||
ergebnis = tab._get_gespeicherte_themen()
|
||||
|
||||
self.assertEqual(ergebnis, [])
|
||||
|
||||
|
||||
class GetCurrentCanvasScaleTests(unittest.TestCase):
|
||||
def test_liefert_skalierung_als_float(self):
|
||||
"""Prüft, dass _get_current_canvas_scale die Canvas-Skalierung als float liefert."""
|
||||
tab = _neuer_tab()
|
||||
fake_iface = MagicMock()
|
||||
fake_iface.mapCanvas.return_value.scale.return_value = 5000
|
||||
with patch("sn_basis.ui.tabs.print_tab.iface", fake_iface):
|
||||
self.assertEqual(tab._get_current_canvas_scale(), 5000.0)
|
||||
|
||||
def test_ohne_canvas_liefert_none(self):
|
||||
"""Prüft, dass _get_current_canvas_scale ohne verfügbares Kartenfenster None liefert."""
|
||||
tab = _neuer_tab()
|
||||
fake_iface = MagicMock()
|
||||
fake_iface.mapCanvas.return_value = None
|
||||
with patch("sn_basis.ui.tabs.print_tab.iface", fake_iface):
|
||||
self.assertIsNone(tab._get_current_canvas_scale())
|
||||
|
||||
def test_skalierung_null_liefert_none(self):
|
||||
"""Prüft, dass eine Skalierung von 0 als None statt als 0.0 gilt."""
|
||||
tab = _neuer_tab()
|
||||
fake_iface = MagicMock()
|
||||
fake_iface.mapCanvas.return_value.scale.return_value = 0
|
||||
with patch("sn_basis.ui.tabs.print_tab.iface", fake_iface):
|
||||
self.assertIsNone(tab._get_current_canvas_scale())
|
||||
|
||||
|
||||
class OnAtlasLayerChangedTests(unittest.TestCase):
|
||||
def test_none_oder_leer_setzt_variable_leer(self):
|
||||
"""Prüft, dass None oder ein leerer Wert die Abdeckungslayer-Variable leeren."""
|
||||
tab = _neuer_tab()
|
||||
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
|
||||
tab._on_atlas_layer_changed(None)
|
||||
tab._on_atlas_layer_changed("")
|
||||
|
||||
mock_set.assert_has_calls([
|
||||
call("sn_abdeckungslayer", "", scope="project"),
|
||||
call("sn_abdeckungslayer", "", scope="project"),
|
||||
])
|
||||
|
||||
def test_layer_objekt_mit_id_methode_setzt_dessen_id(self):
|
||||
"""Prüft, dass ein echtes Layer-Objekt (mit id()-Methode) dessen ID speichert."""
|
||||
tab = _neuer_tab()
|
||||
fake_layer = MagicMock()
|
||||
fake_layer.id.return_value = "layer_42"
|
||||
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set:
|
||||
tab._on_atlas_layer_changed(fake_layer)
|
||||
|
||||
mock_set.assert_called_once_with("sn_abdeckungslayer", "layer_42", scope="project")
|
||||
|
||||
def test_string_fallback_sucht_layer_ueber_namen(self):
|
||||
"""Prüft, dass ohne id()-Methode über den Layernamen im Projekt gesucht und dessen ID gespeichert wird."""
|
||||
tab = _neuer_tab()
|
||||
fake_layer = MagicMock(spec=["name"])
|
||||
fake_layer.name.return_value = "Mein Layer"
|
||||
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set, \
|
||||
patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value.mapLayers.return_value = {"layer_7": fake_layer}
|
||||
tab._on_atlas_layer_changed("Mein Layer")
|
||||
|
||||
mock_set.assert_called_once_with("sn_abdeckungslayer", "layer_7", scope="project")
|
||||
|
||||
|
||||
class OnAuswahlAsAtlasClickedTests(unittest.TestCase):
|
||||
def test_erfolgreicher_layer_aktualisiert_atlas_ui(self):
|
||||
"""Prüft, dass ein erfolgreich erzeugter Atlas-Layer den Atlas-Bereich öffnet und ins Dropdown übernimmt."""
|
||||
tab = _neuer_tab()
|
||||
fake_layer = object()
|
||||
mock_logic = MagicMock()
|
||||
mock_logic.create_atlas_layer_from_selection.return_value = (fake_layer, "layer_9")
|
||||
tab.logic = mock_logic
|
||||
tab.pruefmanager = object()
|
||||
tab._atlas_button = MagicMock()
|
||||
tab._atlas_content = MagicMock()
|
||||
tab._atlas_layer_combo = MagicMock(spec=["setLayer"])
|
||||
tab._toggle_atlas = MagicMock()
|
||||
|
||||
tab._on_auswahl_as_atlas_clicked()
|
||||
|
||||
tab._atlas_button.setChecked.assert_called_once_with(True)
|
||||
tab._toggle_atlas.assert_called_once_with(True)
|
||||
tab._atlas_layer_combo.setLayer.assert_called_once_with(fake_layer)
|
||||
|
||||
def test_ohne_erzeugten_layer_tut_nichts_weiter(self):
|
||||
"""Prüft, dass ohne erzeugten Atlas-Layer keine UI-Aktualisierung stattfindet."""
|
||||
tab = _neuer_tab()
|
||||
mock_logic = MagicMock()
|
||||
mock_logic.create_atlas_layer_from_selection.return_value = (None, None)
|
||||
tab.logic = mock_logic
|
||||
tab.pruefmanager = object()
|
||||
tab._atlas_button = MagicMock()
|
||||
tab._atlas_content = MagicMock()
|
||||
tab._atlas_layer_combo = MagicMock()
|
||||
|
||||
tab._on_auswahl_as_atlas_clicked() # darf keine Ausnahme werfen
|
||||
|
||||
tab._atlas_button.setChecked.assert_not_called()
|
||||
|
||||
|
||||
class PopulateAtlasLayerComboTests(unittest.TestCase):
|
||||
def test_befuellt_combo_ohne_druckrechteck_und_leere_namen(self):
|
||||
"""Prüft, dass _populate_atlas_layer_combo alle Layernamen außer 'Druckrechteck' und leeren Namen einträgt."""
|
||||
tab = _neuer_tab()
|
||||
combo = MagicMock(spec=["clear", "addItem", "blockSignals"])
|
||||
tab._atlas_layer_combo = combo
|
||||
|
||||
layer_normal = MagicMock()
|
||||
layer_normal.name.return_value = "Flurstuecke"
|
||||
layer_druckrechteck = MagicMock()
|
||||
layer_druckrechteck.name.return_value = "Druckrechteck"
|
||||
layer_leer = MagicMock()
|
||||
layer_leer.name.return_value = ""
|
||||
|
||||
with patch("sn_basis.ui.tabs.print_tab.QgsProject") as mock_project:
|
||||
mock_project.instance.return_value.mapLayers.return_value = {
|
||||
"1": layer_normal, "2": layer_druckrechteck, "3": layer_leer,
|
||||
}
|
||||
tab._populate_atlas_layer_combo()
|
||||
|
||||
alle_aufrufe = [c.args[0] for c in combo.addItem.call_args_list]
|
||||
self.assertIn("", alle_aufrufe)
|
||||
self.assertIn("Flurstuecke", alle_aufrufe)
|
||||
self.assertNotIn("Druckrechteck", alle_aufrufe)
|
||||
self.assertEqual(alle_aufrufe.count(""), 1)
|
||||
|
||||
def test_ohne_atlas_layer_combo_tut_nichts(self):
|
||||
"""Prüft, dass _populate_atlas_layer_combo ohne Atlas-Dropdown keine Ausnahme wirft."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_layer_combo = None
|
||||
|
||||
tab._populate_atlas_layer_combo() # darf keine Ausnahme werfen
|
||||
|
||||
|
||||
class OnAtlasErzeugenChangedTests(unittest.TestCase):
|
||||
def test_aktiviert_speichert_variable_und_aktiviert_widgets(self):
|
||||
"""Prüft, dass ein aktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button aktiviert."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_layer_combo = MagicMock()
|
||||
tab._btn_auswahl_as_atlas = MagicMock()
|
||||
|
||||
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set_variable:
|
||||
tab._on_atlas_erzeugen_changed(1)
|
||||
|
||||
mock_set_variable.assert_called_once_with(ATLAS_ERZEUGEN_VAR, "1", scope="project")
|
||||
tab._atlas_layer_combo.setEnabled.assert_called_once_with(True)
|
||||
tab._btn_auswahl_as_atlas.setEnabled.assert_called_once_with(True)
|
||||
|
||||
def test_deaktiviert_speichert_variable_und_deaktiviert_widgets(self):
|
||||
"""Prüft, dass ein deaktivierter Atlas-Schalter die Variable setzt und Layer-Combo/Button deaktiviert."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_layer_combo = MagicMock()
|
||||
tab._btn_auswahl_as_atlas = MagicMock()
|
||||
|
||||
with patch("sn_basis.ui.tabs.print_tab.set_variable") as mock_set_variable:
|
||||
tab._on_atlas_erzeugen_changed(0)
|
||||
|
||||
mock_set_variable.assert_called_once_with(ATLAS_ERZEUGEN_VAR, "0", scope="project")
|
||||
tab._atlas_layer_combo.setEnabled.assert_called_once_with(False)
|
||||
tab._btn_auswahl_as_atlas.setEnabled.assert_called_once_with(False)
|
||||
|
||||
def test_ohne_widgets_wirft_keine_ausnahme(self):
|
||||
"""Prüft, dass _on_atlas_erzeugen_changed ohne Atlas-Combo/-Button keine Ausnahme wirft."""
|
||||
tab = _neuer_tab()
|
||||
tab._atlas_layer_combo = None
|
||||
tab._btn_auswahl_as_atlas = None
|
||||
|
||||
with patch("sn_basis.ui.tabs.print_tab.set_variable"):
|
||||
tab._on_atlas_erzeugen_changed(1) # darf keine Ausnahme werfen
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung (Issue #65)."""
|
||||
"""Tests für sn_basis/modules/pruef_ergebnis.py::ist_verfahrens_db_aktion/erlaubte_persistierung/dateipfad (Issue #65/#67)."""
|
||||
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||
|
||||
@@ -43,5 +44,19 @@ class ErlaubtePersistierungTests(unittest.TestCase):
|
||||
self.assertFalse(ergebnis.erlaubte_persistierung)
|
||||
|
||||
|
||||
class DateipfadTests(unittest.TestCase):
|
||||
def test_liefert_path_wenn_kontext_ein_path_ist(self):
|
||||
"""Prüft, dass dateipfad den Kontext liefert, wenn dieser bereits ein Path-Objekt ist."""
|
||||
ergebnis = pruef_ergebnis(ok=True, kontext=Path("C:/tmp/verfahren.gpkg"))
|
||||
|
||||
self.assertEqual(ergebnis.dateipfad, Path("C:/tmp/verfahren.gpkg"))
|
||||
|
||||
def test_liefert_none_wenn_kontext_kein_path_ist(self):
|
||||
"""Prüft, dass dateipfad None liefert, wenn der Kontext kein Path-Objekt ist (z. B. String oder Layer)."""
|
||||
self.assertIsNone(pruef_ergebnis(ok=True, kontext="C:/tmp/verfahren.gpkg").dateipfad)
|
||||
self.assertIsNone(pruef_ergebnis(ok=True, kontext=None).dateipfad)
|
||||
self.assertIsNone(pruef_ergebnis(ok=True, kontext=object()).dateipfad)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg (Issue #65)."""
|
||||
"""Tests für sn_basis/functions/qgiscore_wrapper.py::polygon_zu_linie/layer_exists_in_gpkg/create_buffer_layer (Issue #65/#67)."""
|
||||
|
||||
import sqlite3
|
||||
import tempfile
|
||||
@@ -164,5 +164,25 @@ class LayerExistsInGpkgTests(unittest.TestCase):
|
||||
self.assertFalse(qc.layer_exists_in_gpkg(pfad, "Flurstuecke"))
|
||||
|
||||
|
||||
class CreateBufferLayerTests(unittest.TestCase):
|
||||
"""Deckt nur die Wächter ab: die eigentliche Pufferbildung importiert `qgis.core.QgsFeature`
|
||||
direkt und ist im Mock-Modus nicht sinnvoll prüfbar (siehe Docstring der Funktion)."""
|
||||
|
||||
def test_ohne_qgis_liefert_none(self):
|
||||
"""Prüft, dass create_buffer_layer ohne verfügbares QGIS None liefert."""
|
||||
with patch.object(qc, "QGIS_AVAILABLE", False):
|
||||
self.assertIsNone(qc.create_buffer_layer(source_layer=object(), distance_m=10.0))
|
||||
|
||||
def test_fehlender_source_layer_liefert_none(self):
|
||||
"""Prüft, dass ein fehlender Quelllayer (None) None liefert, auch wenn QGIS verfügbar wäre."""
|
||||
with patch.object(qc, "QGIS_AVAILABLE", True):
|
||||
self.assertIsNone(qc.create_buffer_layer(source_layer=None, distance_m=10.0))
|
||||
|
||||
def test_source_layer_ohne_getfeatures_liefert_none(self):
|
||||
"""Prüft, dass ein Quelllayer ohne getFeatures()-Methode None liefert."""
|
||||
with patch.object(qc, "QGIS_AVAILABLE", True):
|
||||
self.assertIsNone(qc.create_buffer_layer(source_layer=object(), distance_m=10.0))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project (Issue #65)."""
|
||||
"""Tests für sn_basis/functions/qgisui_wrapper.py::add_layer_to_project/find_dock_widgets/add_menu/remove_menu (Issue #65/#67)."""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
@@ -34,5 +34,74 @@ class AddLayerToProjectTests(unittest.TestCase):
|
||||
self.assertFalse(qui.add_layer_to_project(object()))
|
||||
|
||||
|
||||
class FindDockWidgetsTests(unittest.TestCase):
|
||||
def test_ohne_main_window_liefert_leere_liste(self):
|
||||
"""Prüft, dass find_dock_widgets ohne verfügbares Hauptfenster eine leere Liste liefert."""
|
||||
with patch.object(qui, "get_main_window", return_value=None):
|
||||
self.assertEqual(qui.find_dock_widgets(), [])
|
||||
|
||||
def test_liefert_dock_widgets_des_main_window(self):
|
||||
"""Prüft, dass find_dock_widgets die vom Hauptfenster gefundenen Dock-Widgets durchreicht."""
|
||||
fake_docks = [object(), object()]
|
||||
fake_window = MagicMock()
|
||||
fake_window.findChildren.return_value = fake_docks
|
||||
with patch.object(qui, "get_main_window", return_value=fake_window):
|
||||
self.assertEqual(qui.find_dock_widgets(), fake_docks)
|
||||
|
||||
def test_fehler_beim_suchen_liefert_leere_liste(self):
|
||||
"""Prüft, dass ein Fehler beim Suchen der Dock-Widgets abgefangen wird und eine leere Liste liefert."""
|
||||
fake_window = MagicMock()
|
||||
fake_window.findChildren.side_effect = RuntimeError("kaputt")
|
||||
with patch.object(qui, "get_main_window", return_value=fake_window):
|
||||
self.assertEqual(qui.find_dock_widgets(), [])
|
||||
|
||||
|
||||
class _FakeMenu:
|
||||
def __init__(self) -> None:
|
||||
self.menuAction = lambda: "MENU_ACTION"
|
||||
|
||||
|
||||
class AddRemoveMenuTests(unittest.TestCase):
|
||||
def test_add_menu_ohne_main_window_tut_nichts(self):
|
||||
"""Prüft, dass add_menu ohne Hauptfenster keine Ausnahme wirft."""
|
||||
fake_iface = MagicMock()
|
||||
fake_iface.mainWindow.return_value = None
|
||||
with patch.object(qui, "iface", fake_iface):
|
||||
qui.add_menu(_FakeMenu()) # darf keine Ausnahme werfen
|
||||
|
||||
def test_add_menu_mit_echtem_qt_menu_fuegt_es_der_menubar_hinzu(self):
|
||||
"""Prüft, dass ein echtes Qt-Menü (mit menuAction) der Menüleiste hinzugefügt wird."""
|
||||
menu = _FakeMenu()
|
||||
fake_iface = MagicMock()
|
||||
with patch.object(qui, "iface", fake_iface):
|
||||
qui.add_menu(menu)
|
||||
|
||||
fake_iface.mainWindow.return_value.menuBar.return_value.addMenu.assert_called_once_with(menu)
|
||||
|
||||
def test_add_menu_ohne_menuaction_wird_ignoriert(self):
|
||||
"""Prüft, dass ein Objekt ohne menuAction nicht an die Menüleiste übergeben wird."""
|
||||
fake_iface = MagicMock()
|
||||
with patch.object(qui, "iface", fake_iface):
|
||||
qui.add_menu(object())
|
||||
|
||||
fake_iface.mainWindow.return_value.menuBar.return_value.addMenu.assert_not_called()
|
||||
|
||||
def test_remove_menu_entfernt_ueber_menuaction(self):
|
||||
"""Prüft, dass remove_menu das Menü über dessen menuAction() aus der Menüleiste entfernt."""
|
||||
menu = _FakeMenu()
|
||||
fake_iface = MagicMock()
|
||||
with patch.object(qui, "iface", fake_iface):
|
||||
qui.remove_menu(menu)
|
||||
|
||||
fake_iface.mainWindow.return_value.menuBar.return_value.removeAction.assert_called_once_with("MENU_ACTION")
|
||||
|
||||
def test_remove_menu_ohne_main_window_tut_nichts(self):
|
||||
"""Prüft, dass remove_menu ohne Hauptfenster keine Ausnahme wirft."""
|
||||
fake_iface = MagicMock()
|
||||
fake_iface.mainWindow.return_value = None
|
||||
with patch.object(qui, "iface", fake_iface):
|
||||
qui.remove_menu(_FakeMenu()) # darf keine Ausnahme werfen
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data (Issue #65)."""
|
||||
"""Tests für sn_basis/ui/tabs/settings_tab.py::save_data/load_data/_build_input (Issue #65/#67)."""
|
||||
|
||||
import unittest
|
||||
|
||||
from sn_basis.functions.landkreise import LANDKREISE
|
||||
from sn_basis.functions.qt_wrapper import QComboBox, QLineEdit
|
||||
from sn_basis.ui.tabs.settings_tab import SettingsTab
|
||||
|
||||
@@ -113,5 +114,25 @@ class LoadDataTests(unittest.TestCase):
|
||||
self.assertEqual(combo.currentText(), "")
|
||||
|
||||
|
||||
class BuildInputTests(unittest.TestCase):
|
||||
def test_landkreis_feld_liefert_combobox_mit_allen_landkreisen(self):
|
||||
"""Prüft, dass _build_input für das Landkreis-Feld eine ComboBox mit Platzhalter und allen Landkreisen liefert."""
|
||||
tab = _neuer_tab(_FakeLogic())
|
||||
|
||||
widget = tab._build_input("landkreis_user")
|
||||
|
||||
self.assertIsInstance(widget, QComboBox)
|
||||
self.assertEqual(widget.count(), 1 + len(LANDKREISE))
|
||||
|
||||
def test_anderes_feld_liefert_lineedit(self):
|
||||
"""Prüft, dass _build_input für ein normales Feld ein leeres Textfeld liefert."""
|
||||
tab = _neuer_tab(_FakeLogic())
|
||||
|
||||
widget = tab._build_input("amt")
|
||||
|
||||
self.assertIsInstance(widget, QLineEdit)
|
||||
self.assertEqual(widget.text(), "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -113,6 +113,7 @@ class PrintTab(QWidget):
|
||||
self.logic.set_formfaktor(self._endlosrolle_cb.isChecked())
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setSpacing(4)
|
||||
main_layout.setContentsMargins(4, 4, 4, 4)
|
||||
|
||||
Reference in New Issue
Block a user