def on_start_tests(self) -> None:
        """
        start a test suite
        :return: Nothing
        """
        debug_log = QGISLogHandler(GeologicalDataProcessing.__name__)
        debug_log.qgis_iface = self.iface
        debug_log.save_to_file = True

        stream = StringIO()
        loader = unittest.TestLoader()
        runner = unittest.TextTestRunner(stream=stream)
        suite = unittest.TestSuite()

        suite.addTests(loader.loadTestsFromTestCase(TestExceptionHandlingClass))

        test_cases = loader.getTestCaseNames(TestPointImportClass)
        for name in test_cases:
            suite.addTest(TestPointImportClass(name, iface=self.iface, dockwidget=self.dockwidget))

        # result = runner.run(unittest.makeSuite(TestExceptionHandlingClass))
        result = runner.run(suite)

        level = LogLevel.INFO
        if len(result.errors) > 0 or len(result.failures) > 0:
            level = LogLevel.CRITICAL

        debug_log.push_message("logfile", debug_log.logfile)
        debug_log.push_message("Test runs", str(result.testsRun))
        debug_log.push_message("Test errors", str(len(result.errors)), level=level)
        debug_log.push_message("Failures", str(len(result.failures)), level=level)

        stream.seek(0)
        debug_log.push_message("Test output", '\n' + str(stream.read()), level=level)
    def check_required_modules():
        """
        Check all module requirements
        :return: True is all modules with required versions were found, else false
        """
        logger = QGISLogHandler("ModuleService")

        # don't display logging to QGIS
        safed_iface = logger.qgis_iface
        logger.qgis_iface = None

        logger.info("Checking required modules")

        python = ModuleService.get_python()

        cmd = [python, "-m", "pip", "list", "--format", "json", "--user"]

        try:
            logger.info("Checking package versions")
            logger.info("CMD: {}".format(" ".join(cmd)))

            result = run(cmd, stdout=PIPE, stderr=STDOUT, check=True)
            logger.info("run pip info successful")
            packages = json.loads(result.stdout.decode())

        except CalledProcessError as e:
            # restore QGIS logging
            logger.qgis_iface = safed_iface
            logger.error("pip info request failed!")
            logger.error("RETURN-CODE: {} - CMD: {}".format(e.returncode, e.cmd))
            logger.error("OUTPUT: {}".format(e.output))
            return False

        ModuleService.modules = list()
        for module in module_list:
            module_found = False
            for package in packages:
                if package["name"] != module:
                    continue
                module_found = True

                logger.debug("found module {} [{}]".format(package["name"], package["version"]))

                v1 = version.parse(package["version"])
                v2 = version.parse(module_list[module])
                if v1 < v2:
                    logger.warn("Module version [{}] differs from required version [{}]".format(v1, v2))
                    ModuleService.modules.append("{}=={}".format(module, module_list[module]))

            if not module_found:
                logger.debug("Module not found, adding {}=={} to install list".format(module, module_list[module]))
                ModuleService.modules.append("{}=={}".format(module, module_list[module]))

        if len(ModuleService.modules) > 0:
            # restore QGIS logging
            logger.qgis_iface = safed_iface
            logger.info("Missing packages found: {}".format(ModuleService.modules))
            return False

        logger.info("All packages up to date")

        # restore QGIS logging
        logger.qgis_iface = safed_iface
        return True
    def run(self) -> None:
        """Run method that loads and starts the plugin"""

        if not self.pluginIsActive:
            self.pluginIsActive = True

            try:
                # initialize logger
                logger = QGISLogHandler()
                logger.qgis_iface = self.iface
                logger.save_to_file = True

                if packages_found == "NO_PACKAGES" or not ModuleService.check_required_modules():
                    logger.info("installing or updating packages")
                    if not ModuleService.install_packages():
                        logger.error("package installation failed, please restart QGIS to try again.")
                    else:
                        logger.info("package installation successful, please restart QGIS")
                        msg = QMessageBox()
                        msg.setIcon(QMessageBox.Information)
                        msg.setText("packages installation successful")
                        msg.setInformativeText("Please restart QGIS to use the GeologicalDataProcessing extension")

                        msg.setWindowTitle("package update")
                        msg.exec_()
                        return

                    return
                else:
                    logger.debug("all required packages up2date")

                # dockwidget may not exist if:
                #    first run of plugin
                #    removed on close (see self.onClosePlugin method)
                if self.dockwidget is None:
                    # Create the dockwidget (after translation) and keep reference
                    self.dockwidget = GeologicalDataProcessingDockWidget()

                if self.settings_dialog is None:
                    self.settings_dialog = SettingsDialog(parent=self.dockwidget)
                    self.settings_dialog.setModal(True)
                    self.dockwidget.settings_button.clicked.connect(self.settings_dialog.exec)

                # connect to provide cleanup on closing of dockwidget
                self.dockwidget.closingPlugin.connect(self.onClosePlugin)

                # show the dockwidget
                # TODO: fix to allow choice of dock location
                self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget)
                self.dockwidget.show()

                from GeologicalDataProcessing.controller.database_controller import DatabaseController
                from GeologicalDataProcessing.services.import_service import ImportService
                from GeologicalDataProcessing.views.import_views import LineImportView, PointImportView, \
                    WellImportView, PropertyImportView, WellLogImportView

                ImportService.get_instance(self.dockwidget)

                # initialize the gui and connect signals and slots
                self.dockwidget.import_type.currentChanged.connect(self.on_import_type_changed_event)

                # start tests button
                # -> only visible and active when the debug flag is True
                if config.debug:
                    self.dockwidget.start_tests_button.clicked.connect(self.on_start_tests)
                else:
                    self.dockwidget.start_tests_button.setVisible(False)
                    self.dockwidget.start_tests_separator.setVisible(False)

                self.dockwidget.progress_bar_layout.setVisible(False)

                self.__views["import_points"] = PointImportView(self.dockwidget)
                self.__views["import_lines"] = LineImportView(self.dockwidget)
                self.__views["import_wells"] = WellImportView(self.dockwidget)
                self.__views["import_properties"] = PropertyImportView(self.dockwidget)
                self.__views["import_well_logs"] = WellLogImportView(self.dockwidget)

                self.__db_controller = DatabaseController(self.settings_dialog)

                if config.debug:
                    self.dockwidget.import_file.setText(
                        "/Users/stephan/Library/Application Support/QGIS/QGIS3/profiles/" +
                        "default/python/plugins/GeologicalDataProcessing/tests/test_data/point_data.txt")

            except Exception as e:
                ExceptionHandler(e).log()