forked from AG_QGIS/Plugin_SN_Basis
Auf Wrapper umgestellt, Prüfarchitektur QT6-kompatibel gemacht (Nicht lauffähig)
This commit is contained in:
@@ -1,11 +1,49 @@
|
||||
#run_tests.py
|
||||
import sys
|
||||
import os
|
||||
"""
|
||||
sn_basis/test/run_tests.py
|
||||
|
||||
Zentraler Test-Runner für sn_basis.
|
||||
Wrapper-konform, QGIS-unabhängig, CI- und IDE-fähig.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import datetime
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
|
||||
# Minimaler Bootstrap, um sn_basis importierbar zu machen
|
||||
TEST_DIR = os.path.dirname(__file__)
|
||||
PLUGIN_ROOT = os.path.abspath(os.path.join(TEST_DIR, "..", ".."))
|
||||
|
||||
if PLUGIN_ROOT not in sys.path:
|
||||
sys.path.insert(0, PLUGIN_ROOT)
|
||||
|
||||
|
||||
from sn_basis.functions import syswrapper
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Bootstrap: Plugin-Root in sys.path eintragen
|
||||
# ---------------------------------------------------------
|
||||
|
||||
def bootstrap():
|
||||
"""
|
||||
Simuliert das QGIS-Plugin-Startverhalten:
|
||||
stellt sicher, dass sn_basis importierbar ist.
|
||||
"""
|
||||
plugin_root = syswrapper.get_plugin_root()
|
||||
syswrapper.add_to_sys_path(plugin_root)
|
||||
|
||||
|
||||
bootstrap()
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Farben
|
||||
# ---------------------------------------------------------
|
||||
|
||||
RED = "\033[91m"
|
||||
YELLOW = "\033[93m"
|
||||
GREEN = "\033[92m"
|
||||
@@ -13,36 +51,30 @@ CYAN = "\033[96m"
|
||||
MAGENTA = "\033[95m"
|
||||
RESET = "\033[0m"
|
||||
|
||||
# Globaler Testzähler
|
||||
GLOBAL_TEST_COUNTER = 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Eigene TestResult-Klasse (färbt Fehler/Skipped/OK)
|
||||
# Farbige TestResult-Klasse
|
||||
# ---------------------------------------------------------
|
||||
|
||||
class ColoredTestResult(unittest.TextTestResult):
|
||||
|
||||
def startTest(self, test):
|
||||
"""Vor jedem Test eine Nummer ausgeben."""
|
||||
global GLOBAL_TEST_COUNTER
|
||||
GLOBAL_TEST_COUNTER += 1
|
||||
self.stream.write(f"{CYAN}[Test {GLOBAL_TEST_COUNTER}]{RESET}\n")
|
||||
super().startTest(test)
|
||||
|
||||
def startTestRun(self):
|
||||
"""Wird einmal zu Beginn des gesamten Testlaufs ausgeführt."""
|
||||
super().startTestRun()
|
||||
|
||||
def startTestClass(self, test):
|
||||
"""Wird aufgerufen, wenn eine neue Testklasse beginnt."""
|
||||
cls = test.__class__
|
||||
file = inspect.getfile(cls)
|
||||
filename = os.path.basename(file)
|
||||
|
||||
self.stream.write(
|
||||
f"\n{MAGENTA}{'='*70}\n"
|
||||
f"\n{MAGENTA}{'=' * 70}\n"
|
||||
f"Starte Testklasse: {filename} → {cls.__name__}\n"
|
||||
f"{'='*70}{RESET}\n"
|
||||
f"{'=' * 70}{RESET}\n"
|
||||
)
|
||||
|
||||
def addError(self, test, err):
|
||||
@@ -57,31 +89,27 @@ class ColoredTestResult(unittest.TextTestResult):
|
||||
super().addSkip(test, reason)
|
||||
self.stream.write(f"{YELLOW}SKIPPED{RESET}: {reason}\n")
|
||||
|
||||
# unittest ruft diese Methode nicht automatisch auf → wir patchen es unten
|
||||
def addSuccess(self, test):
|
||||
super().addSuccess(test)
|
||||
self.stream.write(f"{GREEN}OK{RESET}\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Eigener TestRunner, der unser ColoredTestResult nutzt
|
||||
# Farbiger TestRunner
|
||||
# ---------------------------------------------------------
|
||||
|
||||
class ColoredTestRunner(unittest.TextTestRunner):
|
||||
resultclass = ColoredTestResult
|
||||
|
||||
def _makeResult(self):
|
||||
result = super()._makeResult()
|
||||
|
||||
# Patch: unittest ruft startTestClass nicht automatisch auf
|
||||
original_start_test = result.startTest
|
||||
|
||||
def patched_start_test(test):
|
||||
# Wenn neue Klasse → Kopf ausgeben
|
||||
if not hasattr(result, "_last_test_class") or \
|
||||
result._last_test_class != test.__class__:
|
||||
result.startTestClass(test)
|
||||
result._last_test_class = test.__class__
|
||||
|
||||
original_start_test(test)
|
||||
|
||||
result.startTest = patched_start_test
|
||||
@@ -89,37 +117,30 @@ class ColoredTestRunner(unittest.TextTestRunner):
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Testlauf starten
|
||||
# Testlauf starten
|
||||
# ---------------------------------------------------------
|
||||
print("\n" + "="*70)
|
||||
print(f"{CYAN}Testlauf gestartet am: {datetime.datetime.now():%Y-%m-%d %H:%M:%S}{RESET}")
|
||||
print("="*70 + "\n")
|
||||
|
||||
# Projekt-Root dem Suchpfad hinzufügen
|
||||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
if project_root not in sys.path:
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
|
||||
def main():
|
||||
print("\n" + "=" * 70)
|
||||
print(
|
||||
f"{CYAN}Testlauf gestartet am: "
|
||||
f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S}{RESET}"
|
||||
)
|
||||
print("=" * 70 + "\n")
|
||||
|
||||
loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
test_modules = [
|
||||
"test_dateipruefer",
|
||||
"test_stilpruefer",
|
||||
"test_linkpruefer",
|
||||
"test_qt_compat",
|
||||
"test_pruefmanager",
|
||||
]
|
||||
|
||||
for mod_name in test_modules:
|
||||
mod = __import__(mod_name)
|
||||
suite.addTests(loader.loadTestsFromModule(mod))
|
||||
suite = loader.discover(
|
||||
start_dir=os.path.dirname(__file__),
|
||||
pattern="test_*.py"
|
||||
)
|
||||
|
||||
runner = ColoredTestRunner(verbosity=2)
|
||||
runner.run(suite)
|
||||
result = runner.run(suite)
|
||||
|
||||
# Exit-Code für CI / Skripte
|
||||
return 0 if result.wasSuccessful() else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
raise SystemExit(main())
|
||||
|
||||
2
test/test_bootstrap.py
Normal file
2
test/test_bootstrap.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from sn_basis.functions import syswrapper
|
||||
syswrapper.add_to_sys_path(syswrapper.get_plugin_root())
|
||||
@@ -1,89 +1,102 @@
|
||||
#test_dateipruefer.py
|
||||
# sn_basis/test/test_dateipruefer.py
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import tempfile
|
||||
import sys
|
||||
# Plugin-Root ermitteln (ein Verzeichnis über "test")
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path.insert(0, ROOT)
|
||||
from modules.Dateipruefer import (
|
||||
Dateipruefer,
|
||||
LeererPfadModus,
|
||||
DateiEntscheidung,
|
||||
DateipruefErgebnis
|
||||
)
|
||||
from unittest.mock import patch
|
||||
|
||||
from sn_basis.modules.Dateipruefer import Dateipruefer
|
||||
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||
|
||||
|
||||
|
||||
|
||||
class TestDateipruefer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.pruefer = Dateipruefer()
|
||||
self.plugin_pfad = tempfile.gettempdir()
|
||||
self.standardname = "test_standard.gpkg"
|
||||
|
||||
def test_verbotener_leerer_pfad(self):
|
||||
result = self.pruefer.pruefe(
|
||||
# -----------------------------------------------------
|
||||
# 1. Leere Eingabe erlaubt
|
||||
# -----------------------------------------------------
|
||||
def test_leereingabe_erlaubt(self):
|
||||
pruefer = Dateipruefer(
|
||||
pfad="",
|
||||
leer_modus=LeererPfadModus.VERBOTEN
|
||||
leereingabe_erlaubt=True
|
||||
)
|
||||
self.assertFalse(result.erfolgreich)
|
||||
self.assertIn("Kein Pfad angegeben.", result.fehler)
|
||||
|
||||
def test_standardpfad_wird_verwendet(self):
|
||||
result = self.pruefer.pruefe(
|
||||
result = pruefer.pruefe()
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "leereingabe_erlaubt")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 2. Leere Eingabe nicht erlaubt
|
||||
# -----------------------------------------------------
|
||||
def test_leereingabe_nicht_erlaubt(self):
|
||||
pruefer = Dateipruefer(
|
||||
pfad="",
|
||||
leer_modus=LeererPfadModus.NUTZE_STANDARD,
|
||||
standardname=self.standardname,
|
||||
plugin_pfad=self.plugin_pfad
|
||||
leereingabe_erlaubt=False
|
||||
)
|
||||
self.assertTrue(result.erfolgreich)
|
||||
expected_path = os.path.join(self.plugin_pfad, self.standardname)
|
||||
self.assertEqual(result.pfad, expected_path)
|
||||
|
||||
def test_temporärer_layer_wird_erkannt(self):
|
||||
result = self.pruefer.pruefe(
|
||||
result = pruefer.pruefe()
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "leereingabe_nicht_erlaubt")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 3. Standarddatei vorschlagen
|
||||
# -----------------------------------------------------
|
||||
def test_standarddatei_vorschlagen(self):
|
||||
pruefer = Dateipruefer(
|
||||
pfad="",
|
||||
leer_modus=LeererPfadModus.TEMPORAER_ERLAUBT
|
||||
standarddatei="/tmp/std.txt"
|
||||
)
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertIsNone(result.pfad)
|
||||
self.assertFalse(result.temporär)
|
||||
|
||||
def test_existierende_datei_ohne_entscheidung(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
||||
tmp_path = tmp_file.name
|
||||
try:
|
||||
result = self.pruefer.pruefe(
|
||||
pfad=tmp_path,
|
||||
leer_modus=LeererPfadModus.VERBOTEN
|
||||
)
|
||||
self.assertTrue(result.erfolgreich) # neu: jetzt True, nicht False
|
||||
self.assertIn("Datei existiert bereits – Entscheidung ausstehend.", result.fehler)
|
||||
self.assertIsNone(result.entscheidung)
|
||||
finally:
|
||||
os.remove(tmp_path)
|
||||
result = pruefer.pruefe()
|
||||
|
||||
def test_existierende_datei_mit_entscheidung_ersetzen(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
||||
tmp_path = tmp_file.name
|
||||
try:
|
||||
result = self.pruefer.pruefe(
|
||||
pfad=tmp_path,
|
||||
leer_modus=LeererPfadModus.VERBOTEN,
|
||||
vorhandene_datei_entscheidung=DateiEntscheidung.ERSETZEN
|
||||
)
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertEqual(result.entscheidung, DateiEntscheidung.ERSETZEN)
|
||||
finally:
|
||||
os.remove(tmp_path)
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "standarddatei_vorschlagen")
|
||||
self.assertEqual(result.pfad, "/tmp/std.txt")
|
||||
|
||||
def test_datei_nicht_existiert(self):
|
||||
fake_path = os.path.join(self.plugin_pfad, "nicht_existierend.gpkg")
|
||||
result = self.pruefer.pruefe(
|
||||
pfad=fake_path,
|
||||
leer_modus=LeererPfadModus.VERBOTEN
|
||||
# -----------------------------------------------------
|
||||
# 4. Temporäre Datei erlaubt
|
||||
# -----------------------------------------------------
|
||||
def test_temporaer_erlaubt(self):
|
||||
pruefer = Dateipruefer(
|
||||
pfad="",
|
||||
temporaer_erlaubt=True
|
||||
)
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertEqual(result.pfad, fake_path)
|
||||
|
||||
result = pruefer.pruefe()
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "temporaer_erlaubt")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 5. Datei existiert nicht
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists", return_value=False)
|
||||
def test_datei_nicht_gefunden(self, mock_exists):
|
||||
pruefer = Dateipruefer(
|
||||
pfad="/tmp/nichtvorhanden.txt"
|
||||
)
|
||||
|
||||
result = pruefer.pruefe()
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "datei_nicht_gefunden")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 6. Datei existiert
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists", return_value=True)
|
||||
@patch("sn_basis.functions.syswrapper.is_file", return_value=True)
|
||||
def test_datei_ok(self, mock_isfile, mock_exists):
|
||||
pruefer = Dateipruefer(
|
||||
pfad="/tmp/test.txt"
|
||||
)
|
||||
|
||||
result = pruefer.pruefe()
|
||||
|
||||
self.assertTrue(result.ok)
|
||||
self.assertEqual(result.aktion, "ok")
|
||||
self.assertEqual(result.pfad, "/tmp/test.txt")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
170
test/test_layerpruefer.py
Normal file
170
test/test_layerpruefer.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# sn_basis/test/test_layerpruefer.py
|
||||
|
||||
import unittest
|
||||
|
||||
from sn_basis.modules.layerpruefer import Layerpruefer
|
||||
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Mock-Layer für Wrapper-Tests
|
||||
# ---------------------------------------------------------
|
||||
class MockLayer:
|
||||
def __init__(
|
||||
self,
|
||||
exists=True,
|
||||
visible=True,
|
||||
layer_type="vector",
|
||||
geometry_type="Polygon",
|
||||
feature_count=10,
|
||||
crs="EPSG:25833",
|
||||
fields=None,
|
||||
source="/tmp/test.shp",
|
||||
editable=True,
|
||||
):
|
||||
self.exists = exists
|
||||
self.visible = visible
|
||||
self.layer_type = layer_type
|
||||
self.geometry_type = geometry_type
|
||||
self.feature_count = feature_count
|
||||
self.crs = crs
|
||||
self.fields = fields or []
|
||||
self.source = source
|
||||
self.editable = editable
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Wrapper-Mocks (monkeypatching)
|
||||
# ---------------------------------------------------------
|
||||
def mock_layer_exists(layer):
|
||||
return layer is not None and layer.exists
|
||||
|
||||
|
||||
def mock_is_layer_visible(layer):
|
||||
return layer.visible
|
||||
|
||||
|
||||
def mock_get_layer_type(layer):
|
||||
return layer.layer_type
|
||||
|
||||
|
||||
def mock_get_layer_geometry_type(layer):
|
||||
return layer.geometry_type
|
||||
|
||||
|
||||
def mock_get_layer_feature_count(layer):
|
||||
return layer.feature_count
|
||||
|
||||
|
||||
def mock_get_layer_crs(layer):
|
||||
return layer.crs
|
||||
|
||||
|
||||
def mock_get_layer_fields(layer):
|
||||
return layer.fields
|
||||
|
||||
|
||||
def mock_get_layer_source(layer):
|
||||
return layer.source
|
||||
|
||||
|
||||
def mock_is_layer_editable(layer):
|
||||
return layer.editable
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Testklasse
|
||||
# ---------------------------------------------------------
|
||||
class TestLayerpruefer(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Monkeypatching der Wrapper-Funktionen
|
||||
import sn_basis.functions.qgisqt_wrapper as wrapper
|
||||
|
||||
wrapper.layer_exists = mock_layer_exists
|
||||
wrapper.is_layer_visible = mock_is_layer_visible
|
||||
wrapper.get_layer_type = mock_get_layer_type
|
||||
wrapper.get_layer_geometry_type = mock_get_layer_geometry_type
|
||||
wrapper.get_layer_feature_count = mock_get_layer_feature_count
|
||||
wrapper.get_layer_crs = mock_get_layer_crs
|
||||
wrapper.get_layer_fields = mock_get_layer_fields
|
||||
wrapper.get_layer_source = mock_get_layer_source
|
||||
wrapper.is_layer_editable = mock_is_layer_editable
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Tests
|
||||
# -----------------------------------------------------
|
||||
|
||||
def test_layer_exists(self):
|
||||
layer = MockLayer(exists=False)
|
||||
pruefer = Layerpruefer(layer)
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "layer_nicht_gefunden")
|
||||
|
||||
def test_layer_unsichtbar(self):
|
||||
layer = MockLayer(visible=False)
|
||||
pruefer = Layerpruefer(layer, muss_sichtbar_sein=True)
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "layer_unsichtbar")
|
||||
|
||||
def test_falscher_layertyp(self):
|
||||
layer = MockLayer(layer_type="raster")
|
||||
pruefer = Layerpruefer(layer, erwarteter_layertyp="vector")
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "falscher_layertyp")
|
||||
|
||||
def test_falscher_geotyp(self):
|
||||
layer = MockLayer(geometry_type="Point")
|
||||
pruefer = Layerpruefer(layer, erwarteter_geotyp="Polygon")
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "falscher_geotyp")
|
||||
|
||||
def test_layer_leer(self):
|
||||
layer = MockLayer(feature_count=0)
|
||||
pruefer = Layerpruefer(layer)
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "layer_leer")
|
||||
|
||||
def test_falsches_crs(self):
|
||||
layer = MockLayer(crs="EPSG:4326")
|
||||
pruefer = Layerpruefer(layer, erwartetes_crs="EPSG:25833")
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "falsches_crs")
|
||||
|
||||
def test_felder_fehlen(self):
|
||||
layer = MockLayer(fields=["id"])
|
||||
pruefer = Layerpruefer(layer, erforderliche_felder=["id", "name"])
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "felder_fehlen")
|
||||
|
||||
def test_datenquelle_unerwartet(self):
|
||||
layer = MockLayer(source="/tmp/test.shp")
|
||||
pruefer = Layerpruefer(layer, erlaubte_datenquellen=["/tmp/allowed.shp"])
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "datenquelle_unerwartet")
|
||||
|
||||
def test_layer_nicht_editierbar(self):
|
||||
layer = MockLayer(editable=False)
|
||||
pruefer = Layerpruefer(layer, muss_editierbar_sein=True)
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertFalse(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "layer_nicht_editierbar")
|
||||
|
||||
def test_layer_ok(self):
|
||||
layer = MockLayer()
|
||||
pruefer = Layerpruefer(layer)
|
||||
ergebnis = pruefer.pruefe()
|
||||
self.assertTrue(ergebnis.ok)
|
||||
self.assertEqual(ergebnis.aktion, "ok")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,77 +1,107 @@
|
||||
#test_linkpruefer.py
|
||||
# sn_basis/test/test_linkpruefer.py
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
# QGIS-Module mocken, damit der Import funktioniert
|
||||
with patch.dict("sys.modules", {
|
||||
"qgis": MagicMock(),
|
||||
"qgis.PyQt": MagicMock(),
|
||||
"qgis.PyQt.QtCore": MagicMock(),
|
||||
"qgis.PyQt.QtNetwork": MagicMock(),
|
||||
"qgis.core": MagicMock(),
|
||||
}):
|
||||
from modules.linkpruefer import Linkpruefer
|
||||
from sn_basis.modules.linkpruefer import Linkpruefer
|
||||
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Mock-Ergebnisse für network_head()
|
||||
# ---------------------------------------------------------
|
||||
|
||||
class MockResponseOK:
|
||||
ok = True
|
||||
status = 200
|
||||
error = None
|
||||
|
||||
|
||||
class MockResponseNotFound:
|
||||
ok = False
|
||||
status = 404
|
||||
error = "Not Found"
|
||||
|
||||
|
||||
class MockResponseConnectionError:
|
||||
ok = False
|
||||
status = None
|
||||
error = "Connection refused"
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Testklasse
|
||||
# ---------------------------------------------------------
|
||||
|
||||
class TestLinkpruefer(unittest.TestCase):
|
||||
|
||||
@patch("modules.linkpruefer.QNetworkReply")
|
||||
@patch("modules.linkpruefer.QNetworkRequest")
|
||||
@patch("modules.linkpruefer.QUrl")
|
||||
@patch("modules.linkpruefer.QEventLoop")
|
||||
@patch("modules.linkpruefer.QgsNetworkAccessManager")
|
||||
def test_remote_link_ok(
|
||||
self, mock_manager, mock_loop, mock_url, mock_request, mock_reply
|
||||
):
|
||||
# Setup: simulate successful HEAD request
|
||||
reply_instance = MagicMock()
|
||||
reply_instance.error.return_value = mock_reply.NetworkError.NoError
|
||||
reply_instance.attribute.return_value = 200
|
||||
|
||||
mock_manager.return_value.head.return_value = reply_instance
|
||||
# -----------------------------------------------------
|
||||
# 1. Remote-Link erreichbar
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.network_head")
|
||||
def test_remote_link_ok(self, mock_head):
|
||||
mock_head.return_value = MockResponseOK()
|
||||
|
||||
lp = Linkpruefer("http://example.com", "REST")
|
||||
result = lp.pruefe_link()
|
||||
result = lp.pruefe()
|
||||
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertEqual(result.daten["quelle"], "remote")
|
||||
self.assertTrue(result.ok)
|
||||
self.assertEqual(result.aktion, "ok")
|
||||
|
||||
@patch("modules.linkpruefer.QNetworkReply")
|
||||
@patch("modules.linkpruefer.QNetworkRequest")
|
||||
@patch("modules.linkpruefer.QUrl")
|
||||
@patch("modules.linkpruefer.QEventLoop")
|
||||
@patch("modules.linkpruefer.QgsNetworkAccessManager")
|
||||
def test_remote_link_error(
|
||||
self, mock_manager, mock_loop, mock_url, mock_request, mock_reply
|
||||
):
|
||||
# Fake-Reply erzeugen
|
||||
reply_instance = MagicMock()
|
||||
reply_instance.error.return_value = mock_reply.NetworkError.ConnectionRefusedError
|
||||
reply_instance.errorString.return_value = "Connection refused"
|
||||
|
||||
# WICHTIG: finished-Signal simulieren
|
||||
reply_instance.finished = MagicMock()
|
||||
reply_instance.finished.connect = MagicMock()
|
||||
|
||||
# Wenn loop.exec() aufgerufen wird, rufen wir loop.quit() sofort auf
|
||||
mock_loop.return_value.exec.side_effect = lambda: mock_loop.return_value.quit()
|
||||
|
||||
# Manager gibt unser Fake-Reply zurück
|
||||
mock_manager.return_value.head.return_value = reply_instance
|
||||
# -----------------------------------------------------
|
||||
# 2. Remote-Link nicht erreichbar
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.network_head")
|
||||
def test_remote_link_error(self, mock_head):
|
||||
mock_head.return_value = MockResponseConnectionError()
|
||||
|
||||
lp = Linkpruefer("http://example.com", "REST")
|
||||
result = lp.pruefe_link()
|
||||
result = lp.pruefe()
|
||||
|
||||
self.assertFalse(result.erfolgreich)
|
||||
self.assertIn("Verbindungsfehler", result.fehler[0])
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "url_nicht_erreichbar")
|
||||
self.assertIn("Connection refused", result.meldung)
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 3. Remote-Link 404
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.network_head")
|
||||
def test_remote_link_404(self, mock_head):
|
||||
mock_head.return_value = MockResponseNotFound()
|
||||
|
||||
lp = Linkpruefer("http://example.com/missing", "REST")
|
||||
result = lp.pruefe()
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "url_nicht_erreichbar")
|
||||
self.assertIn("404", result.meldung)
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 4. Lokaler Pfad existiert nicht
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists")
|
||||
def test_local_link_not_found(self, mock_exists):
|
||||
mock_exists.return_value = False
|
||||
|
||||
lp = Linkpruefer("/path/to/missing/file.shp", "OGR")
|
||||
result = lp.pruefe()
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "pfad_nicht_gefunden")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 5. Lokaler Pfad existiert, aber ungewöhnlich
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists")
|
||||
def test_local_link_warning(self, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
|
||||
def test_local_link_warning(self):
|
||||
lp = Linkpruefer("/path/to/file_without_extension", "OGR")
|
||||
result = lp.pruefe_link()
|
||||
result = lp.pruefe()
|
||||
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertIn("ungewöhnlich", result.warnungen[0])
|
||||
self.assertTrue(result.ok)
|
||||
self.assertEqual(result.aktion, "ok")
|
||||
self.assertIn("ungewöhnlich", result.meldung)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,86 +1,133 @@
|
||||
#test_pruefmanager.py
|
||||
# sn_basis/test/test_pruefmanager.py
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import patch, MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
# Plugin-Root ermitteln (ein Verzeichnis über "test")
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
from modules.Pruefmanager import PruefManager
|
||||
from modules.Dateipruefer import DateiEntscheidung
|
||||
import modules.qt_compat as qt_compat
|
||||
from sn_basis.modules.Pruefmanager import Pruefmanager
|
||||
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||
|
||||
|
||||
# Skip-Decorator für Mock-Modus
|
||||
def skip_if_mock(reason):
|
||||
return unittest.skipIf(
|
||||
qt_compat.QT_VERSION == 0,
|
||||
f"{reason} — MOCK-Modus erkannt. "
|
||||
"Bitte diesen Test in einer echten QGIS-Umgebung ausführen."
|
||||
)
|
||||
|
||||
|
||||
class TestPruefManager(unittest.TestCase):
|
||||
class TestPruefmanager(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.manager = PruefManager(plugin_pfad="/tmp")
|
||||
self.manager = Pruefmanager()
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Tests für frage_datei_ersetzen_oder_anhaengen
|
||||
# ---------------------------------------------------------
|
||||
# -----------------------------------------------------
|
||||
# 1. OK-Ergebnis → keine Interaktion
|
||||
# -----------------------------------------------------
|
||||
def test_ok(self):
|
||||
ergebnis = pruef_ergebnis(True, "Alles gut", "ok", None)
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=qt_compat.YES)
|
||||
def test_frage_datei_ersetzen(self, mock_exec):
|
||||
entscheidung = self.manager.frage_datei_ersetzen_oder_anhaengen("dummy.gpkg")
|
||||
self.assertEqual(entscheidung, DateiEntscheidung.ERSETZEN)
|
||||
self.assertTrue(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "ok")
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=qt_compat.NO)
|
||||
def test_frage_datei_anhaengen(self, mock_exec):
|
||||
entscheidung = self.manager.frage_datei_ersetzen_oder_anhaengen("dummy.gpkg")
|
||||
self.assertEqual(entscheidung, DateiEntscheidung.ANHAENGEN)
|
||||
# -----------------------------------------------------
|
||||
# 2. Leere Eingabe erlaubt → Nutzer sagt JA
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=True)
|
||||
def test_leereingabe_erlaubt_ja(self, mock_ask):
|
||||
ergebnis = pruef_ergebnis(False, "Leer?", "leereingabe_erlaubt", None)
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=qt_compat.CANCEL)
|
||||
def test_frage_datei_abbrechen(self, mock_exec):
|
||||
entscheidung = self.manager.frage_datei_ersetzen_oder_anhaengen("dummy.gpkg")
|
||||
self.assertEqual(entscheidung, DateiEntscheidung.ABBRECHEN)
|
||||
self.assertTrue(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "ok")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Fehlerfall: exec_dialog liefert etwas Unerwartetes
|
||||
# ---------------------------------------------------------
|
||||
# -----------------------------------------------------
|
||||
# 3. Leere Eingabe erlaubt → Nutzer sagt NEIN
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=False)
|
||||
def test_leereingabe_erlaubt_nein(self, mock_ask):
|
||||
ergebnis = pruef_ergebnis(False, "Leer?", "leereingabe_erlaubt", None)
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=999)
|
||||
def test_frage_datei_unbekannte_antwort(self, mock_exec):
|
||||
entscheidung = self.manager.frage_datei_ersetzen_oder_anhaengen("dummy.gpkg")
|
||||
self.assertEqual(entscheidung, DateiEntscheidung.ABBRECHEN)
|
||||
self.assertFalse(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "leereingabe_erlaubt")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Tests für frage_temporär_verwenden
|
||||
# ---------------------------------------------------------
|
||||
# -----------------------------------------------------
|
||||
# 4. Standarddatei vorschlagen → Nutzer sagt JA
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=True)
|
||||
def test_standarddatei_vorschlagen_ja(self, mock_ask):
|
||||
ergebnis = pruef_ergebnis(False, "Standarddatei verwenden?", "standarddatei_vorschlagen", "/tmp/std.txt")
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=qt_compat.YES)
|
||||
def test_frage_temporär_verwenden_ja(self, mock_exec):
|
||||
self.assertTrue(self.manager.frage_temporär_verwenden())
|
||||
self.assertTrue(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "ok")
|
||||
self.assertEqual(entscheidung.pfad, "/tmp/std.txt")
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=qt_compat.NO)
|
||||
def test_frage_temporär_verwenden_nein(self, mock_exec):
|
||||
self.assertFalse(self.manager.frage_temporär_verwenden())
|
||||
# -----------------------------------------------------
|
||||
# 5. Standarddatei vorschlagen → Nutzer sagt NEIN
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=False)
|
||||
def test_standarddatei_vorschlagen_nein(self, mock_ask):
|
||||
ergebnis = pruef_ergebnis(False, "Standarddatei verwenden?", "standarddatei_vorschlagen", "/tmp/std.txt")
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Fehlerfall: exec_dialog liefert etwas Unerwartetes
|
||||
# ---------------------------------------------------------
|
||||
self.assertFalse(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "standarddatei_vorschlagen")
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
@patch("modules.qt_compat.exec_dialog", return_value=None)
|
||||
def test_frage_temporär_verwenden_unbekannt(self, mock_exec):
|
||||
self.assertFalse(self.manager.frage_temporär_verwenden())
|
||||
# -----------------------------------------------------
|
||||
# 6. Temporäre Datei erzeugen → Nutzer sagt JA
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=True)
|
||||
def test_temporaer_erlaubt_ja(self, mock_ask):
|
||||
ergebnis = pruef_ergebnis(False, "Temporär?", "temporaer_erlaubt", None)
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
self.assertTrue(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "temporaer_erzeugen")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 7. Temporäre Datei erzeugen → Nutzer sagt NEIN
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=False)
|
||||
def test_temporaer_erlaubt_nein(self, mock_ask):
|
||||
ergebnis = pruef_ergebnis(False, "Temporär?", "temporaer_erlaubt", None)
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
self.assertFalse(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "temporaer_erlaubt")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 8. Layer unsichtbar → Nutzer sagt JA
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=True)
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.set_layer_visible")
|
||||
def test_layer_unsichtbar_ja(self, mock_set, mock_ask):
|
||||
fake_layer = object()
|
||||
ergebnis = pruef_ergebnis(False, "Layer unsichtbar", "layer_unsichtbar", fake_layer)
|
||||
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
mock_set.assert_called_once_with(fake_layer, True)
|
||||
self.assertTrue(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "ok")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 9. Layer unsichtbar → Nutzer sagt NEIN
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.ask_yes_no", return_value=False)
|
||||
def test_layer_unsichtbar_nein(self, mock_ask):
|
||||
fake_layer = object()
|
||||
ergebnis = pruef_ergebnis(False, "Layer unsichtbar", "layer_unsichtbar", fake_layer)
|
||||
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
self.assertFalse(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "layer_unsichtbar")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 10. Fehlerhafte Aktion → Fallback
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.warning")
|
||||
def test_unbekannte_aktion(self, mock_warn):
|
||||
ergebnis = pruef_ergebnis(False, "???", "unbekannt", None)
|
||||
entscheidung = self.manager.verarbeite(ergebnis)
|
||||
|
||||
mock_warn.assert_called_once()
|
||||
self.assertFalse(entscheidung.ok)
|
||||
self.assertEqual(entscheidung.aktion, "unbekannt")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
#test_qt_compat.py
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
import modules.qt_compat as qt_compat
|
||||
# Plugin-Root ermitteln (ein Verzeichnis über "test")
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
def skip_if_mock(reason):
|
||||
"""Decorator: überspringt Test, wenn qt_compat im Mock-Modus läuft."""
|
||||
return unittest.skipIf(
|
||||
qt_compat.QT_VERSION == 0,
|
||||
f"{reason} — MOCK-Modus erkannt."
|
||||
f"Bitte diesen Test in einer echten QGIS-Umgebung ausführen."
|
||||
)
|
||||
|
||||
|
||||
class TestQtCompat(unittest.TestCase):
|
||||
|
||||
def test_exports_exist(self):
|
||||
"""Prüft, ob alle erwarteten Symbole exportiert werden."""
|
||||
expected = {
|
||||
"QMessageBox",
|
||||
"QFileDialog",
|
||||
"QEventLoop",
|
||||
"QUrl",
|
||||
"QNetworkRequest",
|
||||
"QNetworkReply",
|
||||
"YES",
|
||||
"NO",
|
||||
"CANCEL",
|
||||
"ICON_QUESTION",
|
||||
"exec_dialog",
|
||||
"QT_VERSION",
|
||||
}
|
||||
|
||||
for symbol in expected:
|
||||
self.assertTrue(
|
||||
hasattr(qt_compat, symbol),
|
||||
f"qt_compat sollte '{symbol}' exportieren"
|
||||
)
|
||||
|
||||
@skip_if_mock("QT_VERSION kann im Mock-Modus nicht 5 oder 6 sein")
|
||||
def test_qt_version_flag(self):
|
||||
"""QT_VERSION muss 5 oder 6 sein."""
|
||||
self.assertIn(qt_compat.QT_VERSION, (5, 6))
|
||||
|
||||
@skip_if_mock("Qt-Enums können im Mock-Modus nicht OR-kombiniert werden")
|
||||
def test_enums_are_valid(self):
|
||||
"""Prüft, ob die Enums gültige QMessageBox-Werte sind."""
|
||||
|
||||
msg = qt_compat.QMessageBox()
|
||||
try:
|
||||
msg.setStandardButtons(
|
||||
qt_compat.YES |
|
||||
qt_compat.NO |
|
||||
qt_compat.CANCEL
|
||||
)
|
||||
except Exception as e:
|
||||
self.fail(f"Qt-Enums sollten OR-kombinierbar sein, Fehler: {e}")
|
||||
|
||||
self.assertTrue(True)
|
||||
|
||||
@skip_if_mock("exec_dialog benötigt echtes Qt-Verhalten")
|
||||
def test_exec_dialog_calls_correct_method(self):
|
||||
"""Prüft, ob exec_dialog() die richtige Methode aufruft."""
|
||||
|
||||
mock_msg = MagicMock()
|
||||
|
||||
if qt_compat.QT_VERSION == 6:
|
||||
qt_compat.exec_dialog(mock_msg)
|
||||
mock_msg.exec.assert_called_once()
|
||||
|
||||
elif qt_compat.QT_VERSION == 5:
|
||||
qt_compat.exec_dialog(mock_msg)
|
||||
mock_msg.exec_.assert_called_once()
|
||||
|
||||
else:
|
||||
self.fail("QT_VERSION hat einen unerwarteten Wert.")
|
||||
|
||||
@skip_if_mock("Qt-Klassen können im Mock-Modus nicht real instanziiert werden")
|
||||
def test_qt_classes_importable(self):
|
||||
"""Prüft, ob die wichtigsten Qt-Klassen instanziierbar sind."""
|
||||
|
||||
loop = qt_compat.QEventLoop()
|
||||
self.assertIsNotNone(loop)
|
||||
|
||||
url = qt_compat.QUrl("http://example.com")
|
||||
self.assertTrue(url.isValid())
|
||||
|
||||
req = qt_compat.QNetworkRequest(url)
|
||||
self.assertIsNotNone(req)
|
||||
|
||||
self.assertTrue(hasattr(qt_compat.QNetworkReply, "NetworkError"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
60
test/test_settings_logic.py
Normal file
60
test/test_settings_logic.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# sn_basis/test/test_settings_logic.py
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from sn_basis.functions.settings_logic import SettingsLogic
|
||||
|
||||
|
||||
class TestSettingsLogic(unittest.TestCase):
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Test: load() liest alle Variablen über get_variable()
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.get_variable")
|
||||
def test_load(self, mock_get):
|
||||
# Mock-Rückgabe für jede Variable
|
||||
mock_get.side_effect = lambda key, scope="project": f"wert_{key}"
|
||||
|
||||
logic = SettingsLogic()
|
||||
daten = logic.load()
|
||||
|
||||
# Alle Variablen müssen enthalten sein
|
||||
for key in SettingsLogic.VARIABLEN:
|
||||
self.assertIn(key, daten)
|
||||
self.assertEqual(daten[key], f"wert_{key}")
|
||||
|
||||
# get_variable muss für jede Variable genau einmal aufgerufen werden
|
||||
self.assertEqual(mock_get.call_count, len(SettingsLogic.VARIABLEN))
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Test: save() ruft set_variable() nur für bekannte Keys auf
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.qgisqt_wrapper.set_variable")
|
||||
def test_save(self, mock_set):
|
||||
logic = SettingsLogic()
|
||||
|
||||
# Eingabedaten enthalten gültige und ungültige Keys
|
||||
daten = {
|
||||
"amt": "A1",
|
||||
"behoerde": "B1",
|
||||
"unbekannt": "IGNORIEREN",
|
||||
"gemeinden": "G1",
|
||||
}
|
||||
|
||||
logic.save(daten)
|
||||
|
||||
# set_variable muss nur für gültige Keys aufgerufen werden
|
||||
expected_calls = 3 # amt, behoerde, gemeinden
|
||||
self.assertEqual(mock_set.call_count, expected_calls)
|
||||
|
||||
# Prüfen, ob die richtigen Keys gespeichert wurden
|
||||
saved_keys = [call.args[0] for call in mock_set.call_args_list]
|
||||
self.assertIn("amt", saved_keys)
|
||||
self.assertIn("behoerde", saved_keys)
|
||||
self.assertIn("gemeinden", saved_keys)
|
||||
self.assertNotIn("unbekannt", saved_keys)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,50 +1,79 @@
|
||||
#test_stilpruefer.py
|
||||
# sn_basis/test/test_stilpruefer.py
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
from sn_basis.modules.stilpruefer import Stilpruefer
|
||||
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||
|
||||
|
||||
# Plugin-Root ermitteln (ein Verzeichnis über "test")
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path.insert(0, ROOT)
|
||||
from modules.stilpruefer import Stilpruefer
|
||||
from modules.pruef_ergebnis import PruefErgebnis
|
||||
class TestStilpruefer(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.pruefer = Stilpruefer()
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 1. Keine Datei angegeben
|
||||
# -----------------------------------------------------
|
||||
def test_keine_datei_angegeben(self):
|
||||
result = self.pruefer.pruefe("")
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertIn("Kein Stil angegeben.", result.warnungen)
|
||||
self.assertIsNone(result.daten["stil"])
|
||||
|
||||
def test_datei_existiert_mit_qml(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".qml", delete=False) as tmp_file:
|
||||
tmp_path = tmp_file.name
|
||||
self.assertTrue(result.ok)
|
||||
self.assertEqual(result.aktion, "ok")
|
||||
self.assertIn("Kein Stil angegeben", result.meldung)
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 2. Datei existiert und ist .qml
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists", return_value=True)
|
||||
@patch("sn_basis.functions.syswrapper.is_file", return_value=True)
|
||||
def test_datei_existiert_mit_qml(self, mock_isfile, mock_exists):
|
||||
with tempfile.NamedTemporaryFile(suffix=".qml", delete=False) as tmp:
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
result = self.pruefer.pruefe(tmp_path)
|
||||
self.assertTrue(result.erfolgreich)
|
||||
self.assertEqual(result.daten["stil"], tmp_path)
|
||||
self.assertEqual(result.fehler, [])
|
||||
|
||||
self.assertTrue(result.ok)
|
||||
self.assertEqual(result.aktion, "ok")
|
||||
self.assertEqual(result.pfad, tmp_path)
|
||||
|
||||
finally:
|
||||
os.remove(tmp_path)
|
||||
|
||||
def test_datei_existiert_falsche_endung(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp_file:
|
||||
tmp_path = tmp_file.name
|
||||
# -----------------------------------------------------
|
||||
# 3. Datei existiert, aber falsche Endung
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists", return_value=True)
|
||||
@patch("sn_basis.functions.syswrapper.is_file", return_value=True)
|
||||
def test_datei_existiert_falsche_endung(self, mock_isfile, mock_exists):
|
||||
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp:
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
result = self.pruefer.pruefe(tmp_path)
|
||||
self.assertFalse(result.erfolgreich)
|
||||
self.assertIn("Ungültige Dateiendung", result.fehler[0])
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "falsche_endung")
|
||||
self.assertIn(".qml", result.meldung)
|
||||
|
||||
finally:
|
||||
os.remove(tmp_path)
|
||||
|
||||
def test_datei_existiert_nicht(self):
|
||||
fake_path = os.path.join(tempfile.gettempdir(), "nichtvorhanden.qml")
|
||||
# -----------------------------------------------------
|
||||
# 4. Datei existiert nicht
|
||||
# -----------------------------------------------------
|
||||
@patch("sn_basis.functions.syswrapper.file_exists", return_value=False)
|
||||
def test_datei_existiert_nicht(self, mock_exists):
|
||||
fake_path = "/tmp/nichtvorhanden.qml"
|
||||
|
||||
result = self.pruefer.pruefe(fake_path)
|
||||
self.assertFalse(result.erfolgreich)
|
||||
self.assertIn("Stildatei nicht gefunden", result.fehler[0])
|
||||
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(result.aktion, "datei_nicht_gefunden")
|
||||
self.assertIn("nicht gefunden", result.meldung)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
164
test/test_wrapper.py
Normal file
164
test/test_wrapper.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# sn_basis/test/test_wrapper.py
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# Wrapper importieren
|
||||
import sn_basis.functions.syswrapper as syswrapper
|
||||
import sn_basis.functions.qgisqt_wrapper as qgisqt
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Mock-Layer für qgisqt_wrapper
|
||||
# ---------------------------------------------------------
|
||||
class MockLayer:
|
||||
def __init__(
|
||||
self,
|
||||
exists=True,
|
||||
visible=True,
|
||||
layer_type="vector",
|
||||
geometry_type="Polygon",
|
||||
feature_count=10,
|
||||
crs="EPSG:25833",
|
||||
fields=None,
|
||||
source="/tmp/test.shp",
|
||||
editable=True,
|
||||
):
|
||||
self.exists = exists
|
||||
self.visible = visible
|
||||
self.layer_type = layer_type
|
||||
self.geometry_type = geometry_type
|
||||
self.feature_count = feature_count
|
||||
self.crs = crs
|
||||
self.fields = fields or []
|
||||
self.source = source
|
||||
self.editable = editable
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Monkeypatching für qgisqt_wrapper
|
||||
# ---------------------------------------------------------
|
||||
def mock_layer_exists(layer):
|
||||
return layer is not None and layer.exists
|
||||
|
||||
|
||||
def mock_is_layer_visible(layer):
|
||||
return layer.visible
|
||||
|
||||
|
||||
def mock_get_layer_type(layer):
|
||||
return layer.layer_type
|
||||
|
||||
|
||||
def mock_get_layer_geometry_type(layer):
|
||||
return layer.geometry_type
|
||||
|
||||
|
||||
def mock_get_layer_feature_count(layer):
|
||||
return layer.feature_count
|
||||
|
||||
|
||||
def mock_get_layer_crs(layer):
|
||||
return layer.crs
|
||||
|
||||
|
||||
def mock_get_layer_fields(layer):
|
||||
return layer.fields
|
||||
|
||||
|
||||
def mock_get_layer_source(layer):
|
||||
return layer.source
|
||||
|
||||
|
||||
def mock_is_layer_editable(layer):
|
||||
return layer.editable
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Testklasse
|
||||
# ---------------------------------------------------------
|
||||
class TestWrapper(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# qgisqt_wrapper monkeypatchen
|
||||
qgisqt.layer_exists = mock_layer_exists
|
||||
qgisqt.is_layer_visible = mock_is_layer_visible
|
||||
qgisqt.get_layer_type = mock_get_layer_type
|
||||
qgisqt.get_layer_geometry_type = mock_get_layer_geometry_type
|
||||
qgisqt.get_layer_feature_count = mock_get_layer_feature_count
|
||||
qgisqt.get_layer_crs = mock_get_layer_crs
|
||||
qgisqt.get_layer_fields = mock_get_layer_fields
|
||||
qgisqt.get_layer_source = mock_get_layer_source
|
||||
qgisqt.is_layer_editable = mock_is_layer_editable
|
||||
|
||||
# -----------------------------------------------------
|
||||
# syswrapper Tests
|
||||
# -----------------------------------------------------
|
||||
|
||||
def test_syswrapper_file_exists(self):
|
||||
with tempfile.NamedTemporaryFile(delete=True) as tmp:
|
||||
self.assertTrue(syswrapper.file_exists(tmp.name))
|
||||
self.assertFalse(syswrapper.file_exists("/path/does/not/exist"))
|
||||
|
||||
def test_syswrapper_is_file(self):
|
||||
with tempfile.NamedTemporaryFile(delete=True) as tmp:
|
||||
self.assertTrue(syswrapper.is_file(tmp.name))
|
||||
self.assertFalse(syswrapper.is_file("/path/does/not/exist"))
|
||||
|
||||
def test_syswrapper_join_path(self):
|
||||
result = syswrapper.join_path("/tmp", "test.txt")
|
||||
self.assertEqual(result, "/tmp/test.txt")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# qgisqt_wrapper Tests (Mock-Modus)
|
||||
# -----------------------------------------------------
|
||||
|
||||
def test_qgisqt_layer_exists(self):
|
||||
layer = MockLayer(exists=True)
|
||||
self.assertTrue(qgisqt.layer_exists(layer))
|
||||
|
||||
layer = MockLayer(exists=False)
|
||||
self.assertFalse(qgisqt.layer_exists(layer))
|
||||
|
||||
def test_qgisqt_layer_visible(self):
|
||||
layer = MockLayer(visible=True)
|
||||
self.assertTrue(qgisqt.is_layer_visible(layer))
|
||||
|
||||
layer = MockLayer(visible=False)
|
||||
self.assertFalse(qgisqt.is_layer_visible(layer))
|
||||
|
||||
def test_qgisqt_layer_type(self):
|
||||
layer = MockLayer(layer_type="vector")
|
||||
self.assertEqual(qgisqt.get_layer_type(layer), "vector")
|
||||
|
||||
def test_qgisqt_geometry_type(self):
|
||||
layer = MockLayer(geometry_type="Polygon")
|
||||
self.assertEqual(qgisqt.get_layer_geometry_type(layer), "Polygon")
|
||||
|
||||
def test_qgisqt_feature_count(self):
|
||||
layer = MockLayer(feature_count=12)
|
||||
self.assertEqual(qgisqt.get_layer_feature_count(layer), 12)
|
||||
|
||||
def test_qgisqt_crs(self):
|
||||
layer = MockLayer(crs="EPSG:4326")
|
||||
self.assertEqual(qgisqt.get_layer_crs(layer), "EPSG:4326")
|
||||
|
||||
def test_qgisqt_fields(self):
|
||||
layer = MockLayer(fields=["id", "name"])
|
||||
self.assertEqual(qgisqt.get_layer_fields(layer), ["id", "name"])
|
||||
|
||||
def test_qgisqt_source(self):
|
||||
layer = MockLayer(source="/tmp/test.shp")
|
||||
self.assertEqual(qgisqt.get_layer_source(layer), "/tmp/test.shp")
|
||||
|
||||
def test_qgisqt_editable(self):
|
||||
layer = MockLayer(editable=True)
|
||||
self.assertTrue(qgisqt.is_layer_editable(layer))
|
||||
|
||||
layer = MockLayer(editable=False)
|
||||
self.assertFalse(qgisqt.is_layer_editable(layer))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user