""" 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" CYAN = "\033[96m" MAGENTA = "\033[95m" RESET = "\033[0m" GLOBAL_TEST_COUNTER = 0 # --------------------------------------------------------- # Farbige TestResult-Klasse # --------------------------------------------------------- class ColoredTestResult(unittest.TextTestResult): def startTest(self, test): global GLOBAL_TEST_COUNTER GLOBAL_TEST_COUNTER += 1 self.stream.write(f"{CYAN}[Test {GLOBAL_TEST_COUNTER}]{RESET}\n") super().startTest(test) def startTestClass(self, test): cls = test.__class__ file = inspect.getfile(cls) filename = os.path.basename(file) self.stream.write( f"\n{MAGENTA}{'=' * 70}\n" f"Starte Testklasse: {filename} → {cls.__name__}\n" f"{'=' * 70}{RESET}\n" ) def addError(self, test, err): super().addError(test, err) self.stream.write(f"{RED}ERROR{RESET}\n") def addFailure(self, test, err): super().addFailure(test, err) self.stream.write(f"{RED}FAILURE{RESET}\n") def addSkip(self, test, reason): super().addSkip(test, reason) self.stream.write(f"{YELLOW}SKIPPED{RESET}: {reason}\n") def addSuccess(self, test): super().addSuccess(test) self.stream.write(f"{GREEN}OK{RESET}\n") # --------------------------------------------------------- # Farbiger TestRunner # --------------------------------------------------------- class ColoredTestRunner(unittest.TextTestRunner): resultclass = ColoredTestResult def _makeResult(self): result = super()._makeResult() original_start_test = result.startTest def patched_start_test(test): 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 return result # --------------------------------------------------------- # Testlauf starten # --------------------------------------------------------- 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 = loader.discover( start_dir=os.path.dirname(__file__), pattern="test_*.py" ) runner = ColoredTestRunner(verbosity=2) result = runner.run(suite) # Exit-Code für CI / Skripte return 0 if result.wasSuccessful() else 1 if __name__ == "__main__": raise SystemExit(main())