From 7383820abacd90603de7e1e66aa242b5170b01ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sat, 18 Apr 2026 15:23:53 +0200 Subject: [PATCH] Reload test without restarting testium (closes #18) Replace os.execv restart in actionRefresh with file_manager.reload(), leveraging the subprocess architecture so py_func modules are freshly imported on each reload. Add a modal progress dialog with step labels during loading. Fix checkbox reappearing on breakpoint with show_checkboxes OFF. Co-Authored-By: Claude Sonnet 4.6 --- src/testium/main_win/test_file_manager.py | 34 +++++++++++++++++++++-- src/testium/main_win/testium_win.py | 18 ++---------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/testium/main_win/test_file_manager.py b/src/testium/main_win/test_file_manager.py index 4fd290d..95158a2 100644 --- a/src/testium/main_win/test_file_manager.py +++ b/src/testium/main_win/test_file_manager.py @@ -3,7 +3,8 @@ import sys import traceback from queue import Empty -from PySide6.QtWidgets import QApplication, QFileDialog +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QApplication, QFileDialog, QProgressDialog from interpreter.process import TestProcess from interpreter.utils.test_ctrl import TestSetController @@ -44,9 +45,25 @@ class TestFileManager: self.load(file_name) w.reconnect_signals() + def _make_progress(self, w): + progress = QProgressDialog("Starting test process…", None, 0, 0, w) + progress.setWindowTitle("Loading") + progress.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) + progress.setWindowModality(Qt.WindowModal) + progress.setMinimumDuration(0) + progress.setMinimumWidth(320) + progress._force_close = False + progress.closeEvent = lambda e: e.accept() if progress._force_close else e.ignore() + return progress + + def _close_progress(self, progress): + progress._force_close = True + progress.close() + def load(self, file_name: str) -> bool: """Load a test file. Returns True on success, False otherwise.""" w = self._win + progress = None try: if not file_name: raise ETUMFileError("No file to load") @@ -59,6 +76,10 @@ class TestFileManager: if not os.path.isfile(file_name): raise ETUMFileError("Could not find %s file" % file_name) + progress = self._make_progress(w) + progress.show() + QApplication.processEvents() + w.testFile = None w.ts_controller = TestSetController() w.test_service = TestControllerService(w.ts_controller) @@ -71,12 +92,14 @@ class TestFileManager: self._defaults_for_process(), ) w.test_proc.start() + progress.setLabelText("Loading test file…") while w.test_proc.is_alive(): try: - if w.test_service.loaded(timeout=1.0): + if w.test_service.loaded(timeout=0.05): break except Empty: w.test_service.clear() + QApplication.processEvents() if not w.test_proc.is_alive(): del w.test_proc @@ -89,9 +112,14 @@ class TestFileManager: "Test could not be loaded (test process crashed for any reason)" ) + progress.setLabelText("Building test tree…") + QApplication.processEvents() test_data = w.test_service.tree() w.treeTests.clear() + QApplication.processEvents() w.treeTests.loadTestRecursively(w.treeTests.invisibleRootItem(), test_data) + self._close_progress(progress) + progress = None w.treeTests.setFoldDefault() w.treeTests.updateTreeSkipState(w.test_service) @@ -109,6 +137,8 @@ class TestFileManager: w.show_checkboxes() return True except: + if progress is not None: + self._close_progress(progress) w.statusBar().showMessage("No test file could be loaded", 10000) w.treeTests.clear() print(traceback.format_exc()) diff --git a/src/testium/main_win/testium_win.py b/src/testium/main_win/testium_win.py index d2168b5..6631b13 100755 --- a/src/testium/main_win/testium_win.py +++ b/src/testium/main_win/testium_win.py @@ -8,7 +8,6 @@ from multiprocessing import Queue from queue import Empty from threading import Thread import shutil -import ast # Qt from PySide6 import QtGui, QtWidgets @@ -471,21 +470,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): @Slot() def on_actionRefresh_test_triggered(self): - self.on_exiting() - args = [] - if not hasattr(sys, "frozen"): - args += [sys.executable] - args += [sys.argv[0]] - if len(self.defines) > 0: - for k, v in self.defines.items(): - try: - val = ast.literal_eval(v) - except: - val = v - args += ["-d", f"{k}={val}"] - if (self.testFile is not None) and (isinstance(self.testFile, str)): - args += [self.testFile] - os.execv(sys.executable, args) + if self.testFile: + self.file_manager.reload(self.testFile) @Slot() def on_actionSave_report_triggered(self):