Merge branch 'refactor/ui-modularization'
- Extract TestControllerService service layer over TestSetController - Split MainWindow into TestRunner and TestFileManager coordinators - Merge 21 QTestTreeItem subclasses into a single factory - Replace _test_started/_test_paused booleans with TestState enum
This commit is contained in:
171
src/testium/main_win/test_file_manager.py
Normal file
171
src/testium/main_win/test_file_manager.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
from queue import Empty
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QApplication, QFileDialog
|
||||||
|
|
||||||
|
from interpreter.process import TestProcess
|
||||||
|
from interpreter.utils.test_ctrl import TestSetController
|
||||||
|
from main_win.test_controller_service import TestControllerService
|
||||||
|
import interpreter.utils.settings as prefs
|
||||||
|
from lib.tum_except import ETUMFileError, ETUMRuntimeError
|
||||||
|
|
||||||
|
|
||||||
|
class TestFileManager:
|
||||||
|
"""Manages test file loading, process lifecycle, and recent files."""
|
||||||
|
|
||||||
|
def __init__(self, win) -> None:
|
||||||
|
self._win = win
|
||||||
|
|
||||||
|
# --- Process lifecycle ---
|
||||||
|
|
||||||
|
def clear_process(self):
|
||||||
|
w = self._win
|
||||||
|
if (
|
||||||
|
w.test_proc is not None
|
||||||
|
and w.test_proc.is_alive()
|
||||||
|
and w.test_service is not None
|
||||||
|
):
|
||||||
|
w.test_service.stop()
|
||||||
|
w.test_service.close()
|
||||||
|
w.test_proc.join()
|
||||||
|
del w.test_proc
|
||||||
|
w.test_proc = None
|
||||||
|
del w.test_service
|
||||||
|
w.test_service = None
|
||||||
|
del w.ts_controller
|
||||||
|
w.ts_controller = None
|
||||||
|
|
||||||
|
def reload(self, file_name: str):
|
||||||
|
w = self._win
|
||||||
|
w.disconnect_signals()
|
||||||
|
self.clear_process()
|
||||||
|
self.load(file_name)
|
||||||
|
w.reconnect_signals()
|
||||||
|
|
||||||
|
def load(self, file_name: str) -> bool:
|
||||||
|
"""Load a test file. Returns True on success, False otherwise."""
|
||||||
|
w = self._win
|
||||||
|
try:
|
||||||
|
if not file_name:
|
||||||
|
raise ETUMFileError("No file to load")
|
||||||
|
|
||||||
|
file_name = os.path.abspath(file_name)
|
||||||
|
initial_dir = os.path.dirname(file_name)
|
||||||
|
|
||||||
|
if not os.path.isdir(initial_dir):
|
||||||
|
raise ETUMFileError("Could not find %s directory" % initial_dir)
|
||||||
|
if not os.path.isfile(file_name):
|
||||||
|
raise ETUMFileError("Could not find %s file" % file_name)
|
||||||
|
|
||||||
|
w.testFile = None
|
||||||
|
w.ts_controller = TestSetController()
|
||||||
|
w.test_service = TestControllerService(w.ts_controller)
|
||||||
|
w.test_proc = TestProcess(
|
||||||
|
file_name,
|
||||||
|
w.status_queue,
|
||||||
|
w.ts_controller,
|
||||||
|
w.config_files,
|
||||||
|
w.defines,
|
||||||
|
self._defaults_for_process(),
|
||||||
|
)
|
||||||
|
w.test_proc.start()
|
||||||
|
while w.test_proc.is_alive():
|
||||||
|
try:
|
||||||
|
if w.test_service.loaded(timeout=1.0):
|
||||||
|
break
|
||||||
|
except Empty:
|
||||||
|
w.test_service.clear()
|
||||||
|
|
||||||
|
if not w.test_proc.is_alive():
|
||||||
|
del w.test_proc
|
||||||
|
w.test_proc = None
|
||||||
|
del w.test_service
|
||||||
|
w.test_service = None
|
||||||
|
del w.ts_controller
|
||||||
|
w.ts_controller = None
|
||||||
|
raise ETUMRuntimeError(
|
||||||
|
"Test could not be loaded (test process crashed for any reason)"
|
||||||
|
)
|
||||||
|
|
||||||
|
test_data = w.test_service.tree()
|
||||||
|
w.treeTests.clear()
|
||||||
|
w.treeTests.loadTestRecursively(w.treeTests.invisibleRootItem(), test_data)
|
||||||
|
w.treeTests.setFoldDefault()
|
||||||
|
w.treeTests.updateTreeSkipState(w.test_service)
|
||||||
|
|
||||||
|
w.checkSelect.setChecked(True)
|
||||||
|
w.testFile = file_name
|
||||||
|
test_dir = os.path.dirname(w.testFile)
|
||||||
|
|
||||||
|
sys.path.append(test_dir)
|
||||||
|
w.statusBar().showMessage("Test file loaded", 10000)
|
||||||
|
w.textLog.set_test_dir(test_dir)
|
||||||
|
self.add_file_to_recent(file_name)
|
||||||
|
w.setWindowTitle(w.mainWindowTitle + " - " + w.testFile)
|
||||||
|
w.actionStart_test.setEnabled(True)
|
||||||
|
w.actionRefresh_test.setEnabled(True)
|
||||||
|
w.show_checkboxes()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
w.statusBar().showMessage("No test file could be loaded", 10000)
|
||||||
|
w.treeTests.clear()
|
||||||
|
print(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _defaults_for_process(self) -> dict:
|
||||||
|
d = {}
|
||||||
|
pp = prefs.settings.python_bin
|
||||||
|
if pp != "":
|
||||||
|
d["python_bin"] = pp
|
||||||
|
pp = prefs.settings.lua_bin
|
||||||
|
if pp != "":
|
||||||
|
d["lua_bin"] = pp
|
||||||
|
return d
|
||||||
|
|
||||||
|
# --- Recent files ---
|
||||||
|
|
||||||
|
def add_file_to_recent(self, filename: str):
|
||||||
|
files = prefs.settings.recent_files
|
||||||
|
try:
|
||||||
|
files.remove(filename)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
files.insert(0, filename)
|
||||||
|
del files[self._win.MaxRecentFiles:]
|
||||||
|
prefs.settings.recent_files = files
|
||||||
|
for widget in QApplication.topLevelWidgets():
|
||||||
|
from main_win.testium_win import MainWindow
|
||||||
|
if isinstance(widget, MainWindow):
|
||||||
|
widget.file_manager.update_recent_file_actions()
|
||||||
|
|
||||||
|
def update_recent_file_actions(self):
|
||||||
|
w = self._win
|
||||||
|
files = prefs.settings.recent_files
|
||||||
|
numRecentFiles = min(len(files), w.MaxRecentFiles)
|
||||||
|
for i in range(numRecentFiles):
|
||||||
|
text = "&%d %s" % (i + 1, w._stripped_name(files[i]))
|
||||||
|
w.recentFileActs[i].setText(text)
|
||||||
|
w.recentFileActs[i].setData(files[i])
|
||||||
|
w.recentFileActs[i].setVisible(True)
|
||||||
|
for j in range(numRecentFiles, w.MaxRecentFiles):
|
||||||
|
w.recentFileActs[j].setVisible(False)
|
||||||
|
w.separatorAct.setVisible(numRecentFiles > 0)
|
||||||
|
|
||||||
|
def on_open_recent_file(self):
|
||||||
|
w = self._win
|
||||||
|
action = w.sender()
|
||||||
|
if action:
|
||||||
|
self.reload(action.data())
|
||||||
|
|
||||||
|
def on_open_test(self):
|
||||||
|
w = self._win
|
||||||
|
d = ""
|
||||||
|
if w.testFile is not None:
|
||||||
|
d = os.path.dirname(w.testFile)
|
||||||
|
file_name, _ = QFileDialog.getOpenFileName(
|
||||||
|
w, "Open the test file", d, "testium file (*.tum);;All Files (*)"
|
||||||
|
)
|
||||||
|
if file_name:
|
||||||
|
self.reload(file_name)
|
||||||
245
src/testium/main_win/test_runner.py
Normal file
245
src/testium/main_win/test_runner.py
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
from enum import Enum, auto
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
from PySide6 import QtGui
|
||||||
|
from PySide6.QtCore import QDateTime
|
||||||
|
from PySide6.QtGui import QIcon, QPixmap
|
||||||
|
|
||||||
|
from interpreter.utils.icons import icon_prefix
|
||||||
|
import interpreter.utils.settings as prefs
|
||||||
|
|
||||||
|
|
||||||
|
class TestState(Enum):
|
||||||
|
IDLE = auto()
|
||||||
|
RUNNING = auto()
|
||||||
|
PAUSED = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunner:
|
||||||
|
"""Manages the test execution lifecycle: start/pause/stop, timers, log file, UI adaptation."""
|
||||||
|
|
||||||
|
def __init__(self, win) -> None:
|
||||||
|
self._win = win
|
||||||
|
self.logFileHandler = None
|
||||||
|
self.state = TestState.IDLE
|
||||||
|
|
||||||
|
# --- Timer helpers ---
|
||||||
|
|
||||||
|
def start_pause_timer(self):
|
||||||
|
w = self._win
|
||||||
|
w.timerPause.setSingleShot(False)
|
||||||
|
w.timerPause.setInterval(500)
|
||||||
|
w.timerPause.start()
|
||||||
|
w.timerPause.state = False
|
||||||
|
|
||||||
|
# --- Execution control ---
|
||||||
|
|
||||||
|
def on_start_test(self):
|
||||||
|
w = self._win
|
||||||
|
|
||||||
|
if self.state != TestState.IDLE:
|
||||||
|
if self.state == TestState.RUNNING:
|
||||||
|
w.test_service.pause()
|
||||||
|
self.start_pause_timer()
|
||||||
|
self.state = TestState.PAUSED
|
||||||
|
else:
|
||||||
|
w.test_service.cont()
|
||||||
|
w.timerPause.stop()
|
||||||
|
w.timerPause.state = False
|
||||||
|
self.on_timer_pause()
|
||||||
|
self.state = TestState.RUNNING
|
||||||
|
return
|
||||||
|
|
||||||
|
w.start_time = QDateTime.currentDateTime()
|
||||||
|
|
||||||
|
# Log file setup
|
||||||
|
log_file = w.editLogFilePath.text()
|
||||||
|
if w.buttLogFileSaved.isChecked() and (log_file != ""):
|
||||||
|
try:
|
||||||
|
if not os.path.isabs(log_file):
|
||||||
|
default_path = prefs.settings.log_path
|
||||||
|
default_path = w.test_service.process_param(default_path)
|
||||||
|
log_file = os.path.join(default_path, log_file)
|
||||||
|
if not os.path.exists(os.path.dirname(log_file)):
|
||||||
|
os.makedirs(os.path.dirname(log_file))
|
||||||
|
if os.path.isfile(log_file):
|
||||||
|
i = 0
|
||||||
|
fname = log_file
|
||||||
|
while os.path.isfile(fname):
|
||||||
|
i += 1
|
||||||
|
fname = log_file + "-" + str(i) + ".saved"
|
||||||
|
os.rename(log_file, fname)
|
||||||
|
self.logFileHandler = open(log_file, "w")
|
||||||
|
w.out_log.set(self.logFileHandler)
|
||||||
|
w.logFileName = log_file
|
||||||
|
except:
|
||||||
|
self.logFileHandler = NamedTemporaryFile(mode="w", suffix=".log", delete=False)
|
||||||
|
w.out_log.set(self.logFileHandler)
|
||||||
|
w.logFileName = self.logFileHandler.name
|
||||||
|
else:
|
||||||
|
self.logFileHandler = NamedTemporaryFile(mode="w", suffix=".log", delete=False)
|
||||||
|
w.out_log.set(self.logFileHandler)
|
||||||
|
w.logFileName = self.logFileHandler.name
|
||||||
|
|
||||||
|
# Report setup and execution
|
||||||
|
rep_file = w.test_service.process_param(w.reportFileName)
|
||||||
|
w.test_service.set_report(rep_file, w.report_type, w.report_pattern)
|
||||||
|
self.adapt_interface_during_test()
|
||||||
|
w.treeTests.clearAllStatus()
|
||||||
|
try:
|
||||||
|
w.textLog.clear()
|
||||||
|
w.textLog.appendPlainText("Test is started\n")
|
||||||
|
w.timer.setSingleShot(False)
|
||||||
|
w.timer.setInterval(100)
|
||||||
|
w.timer.start()
|
||||||
|
w.test_service.set_test_outputs([w.logFileName])
|
||||||
|
w.test_service.execute()
|
||||||
|
except:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
self.restore_interface_after_test()
|
||||||
|
|
||||||
|
def on_stop_test(self):
|
||||||
|
self._win.test_service.stop()
|
||||||
|
|
||||||
|
def on_run_finished(self):
|
||||||
|
w = self._win
|
||||||
|
w.timer.setSingleShot(True)
|
||||||
|
w.timer.setInterval(1000)
|
||||||
|
txt = w.stream.read()
|
||||||
|
w.textLog.appendPlainText(txt)
|
||||||
|
self.restore_interface_after_test()
|
||||||
|
|
||||||
|
if self.logFileHandler is not None:
|
||||||
|
w.out_log.reset()
|
||||||
|
self.logFileHandler.write(txt + "\n")
|
||||||
|
self.logFileHandler.close()
|
||||||
|
self.logFileHandler = None
|
||||||
|
|
||||||
|
w.textLog.appendPlainText("Test is finished")
|
||||||
|
if w.runandclose:
|
||||||
|
w.on_actionExit_triggered()
|
||||||
|
|
||||||
|
def on_breakpoint(self):
|
||||||
|
self.state = TestState.PAUSED
|
||||||
|
self.start_pause_timer()
|
||||||
|
|
||||||
|
# --- Timer slots ---
|
||||||
|
|
||||||
|
def on_timer_event(self):
|
||||||
|
w = self._win
|
||||||
|
text_to_append = []
|
||||||
|
while not w.threads_queue.empty():
|
||||||
|
text_to_append.append(w.threads_queue.get())
|
||||||
|
if text_to_append:
|
||||||
|
for t in text_to_append:
|
||||||
|
w.textLog.appendPlainText(t)
|
||||||
|
if self.logFileHandler is not None:
|
||||||
|
self.logFileHandler.write(t + "\n")
|
||||||
|
self.logFileHandler.flush()
|
||||||
|
|
||||||
|
def on_timer_blink(self):
|
||||||
|
w = self._win
|
||||||
|
if w.buttBlink.current_color != "gray":
|
||||||
|
self.set_blink_gray()
|
||||||
|
elif w.treeTests.getGlobalSuccess():
|
||||||
|
self.set_blink_green()
|
||||||
|
else:
|
||||||
|
self.set_blink_red()
|
||||||
|
|
||||||
|
def on_timer_pause(self):
|
||||||
|
w = self._win
|
||||||
|
if self.state == TestState.PAUSED:
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
if w.timerPause.state:
|
||||||
|
icon.addPixmap(QtGui.QPixmap(icon_prefix() + "/pause2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
else:
|
||||||
|
icon.addPixmap(QtGui.QPixmap(icon_prefix() + "/pause.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
w.timerPause.state = not w.timerPause.state
|
||||||
|
w.actionStart_test.setIcon(icon)
|
||||||
|
|
||||||
|
def on_timer_count(self):
|
||||||
|
w = self._win
|
||||||
|
secfromstart = w.start_time.secsTo(QDateTime.currentDateTime())
|
||||||
|
w.label_runtime.setText(
|
||||||
|
"%02d:%02d:%02d" % (secfromstart / 3600, (secfromstart / 60) % 60, secfromstart % 60)
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Interface adaptation ---
|
||||||
|
|
||||||
|
def adapt_interface_during_test(self):
|
||||||
|
w = self._win
|
||||||
|
try:
|
||||||
|
w.disconnect_signals()
|
||||||
|
w.actionOpenTest.setDisabled(True)
|
||||||
|
w.actionExit.setDisabled(True)
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(icon_prefix() + "/pause.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
w.actionStart_test.setIcon(icon)
|
||||||
|
w.actionStart_test.setText("Pause test")
|
||||||
|
w.actionPreferences.setDisabled(True)
|
||||||
|
w.actionRefresh_test.setDisabled(True)
|
||||||
|
w.actionShow_Results.setDisabled(True)
|
||||||
|
w.actionSave_report.setDisabled(True)
|
||||||
|
w.logSettingsBox.setDisabled(True)
|
||||||
|
w.actionStop_test.setEnabled(True)
|
||||||
|
if prefs.settings.show_checkboxes:
|
||||||
|
w._checklist = w.treeTests.getCheckList()
|
||||||
|
w.treeTests.removeCheckBoxes()
|
||||||
|
w.checkSelect.setDisabled(True)
|
||||||
|
w.checkFold.setDisabled(True)
|
||||||
|
w.timerBlink.setSingleShot(False)
|
||||||
|
w.timerBlink.setInterval(1000)
|
||||||
|
w.timerBlink.start()
|
||||||
|
self.set_blink_green()
|
||||||
|
w.treeTests.clearGlobalSuccess()
|
||||||
|
finally:
|
||||||
|
self.state = TestState.RUNNING
|
||||||
|
|
||||||
|
def restore_interface_after_test(self):
|
||||||
|
w = self._win
|
||||||
|
try:
|
||||||
|
w.timerPause.stop()
|
||||||
|
w.timerBlink.stop()
|
||||||
|
w.actionOpenTest.setEnabled(True)
|
||||||
|
w.actionExit.setEnabled(True)
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(icon_prefix() + "/start.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
w.actionStart_test.setIcon(icon)
|
||||||
|
w.actionStart_test.setText("Start test")
|
||||||
|
w.actionPreferences.setEnabled(True)
|
||||||
|
w.actionRefresh_test.setEnabled(True)
|
||||||
|
w.actionStop_test.setDisabled(True)
|
||||||
|
w.actionShow_Results.setEnabled(True)
|
||||||
|
w.actionSave_report.setEnabled(True)
|
||||||
|
w.logSettingsBox.setEnabled(True)
|
||||||
|
if prefs.settings.show_checkboxes:
|
||||||
|
w.checkSelect.setEnabled(True)
|
||||||
|
w.treeTests.showCheckBoxes(w._checklist, w.test_service)
|
||||||
|
w.checkFold.setEnabled(True)
|
||||||
|
w.treeTests.setChildrenEnabled()
|
||||||
|
w.reconnect_signals()
|
||||||
|
if w.treeTests.getGlobalSuccess():
|
||||||
|
self.set_blink_green()
|
||||||
|
else:
|
||||||
|
self.set_blink_red()
|
||||||
|
finally:
|
||||||
|
self.state = TestState.IDLE
|
||||||
|
|
||||||
|
# --- Blink indicator ---
|
||||||
|
|
||||||
|
def set_blink_green(self):
|
||||||
|
w = self._win
|
||||||
|
w.buttBlink.setIcon(w.iconBlinkGreen)
|
||||||
|
w.buttBlink.current_color = "green"
|
||||||
|
|
||||||
|
def set_blink_red(self):
|
||||||
|
w = self._win
|
||||||
|
w.buttBlink.setIcon(w.iconBlinkRed)
|
||||||
|
w.buttBlink.current_color = "red"
|
||||||
|
|
||||||
|
def set_blink_gray(self):
|
||||||
|
w = self._win
|
||||||
|
w.buttBlink.setIcon(w.iconBlinkGray)
|
||||||
|
w.buttBlink.current_color = "gray"
|
||||||
@@ -12,32 +12,7 @@ from time import (time)
|
|||||||
from main_win.test_tree_items.common import (TEST_COLS, TEST_COLS_WITH_TIME)
|
from main_win.test_tree_items.common import (TEST_COLS, TEST_COLS_WITH_TIME)
|
||||||
from lib.tum_except import (ETUMFileError, ETUMSyntaxError)
|
from lib.tum_except import (ETUMFileError, ETUMSyntaxError)
|
||||||
from main_win.test_controller_service import TestControllerService
|
from main_win.test_controller_service import TestControllerService
|
||||||
from main_win.test_tree_items.test_tree_git import QTestTreeItemGit
|
from main_win.test_tree_items.test_tree_item import make_tree_item
|
||||||
|
|
||||||
# to be removed in the future and replaced by a more "sexy" mechanism
|
|
||||||
from main_win.test_tree_items.test_tree_unittest import (QTestTreeItemUnittest,
|
|
||||||
QTestTreeItemUnittestElement)
|
|
||||||
from main_win.test_tree_items.test_tree_sleep import QTestTreeItemSleep
|
|
||||||
from main_win.test_tree_items.test_tree_cycle import QTestTreeItemCycle
|
|
||||||
from main_win.test_tree_items.test_tree_group import QTestTreeItemGroup
|
|
||||||
from main_win.test_tree_items.test_tree_git import QTestTreeItemGit
|
|
||||||
from main_win.test_tree_items.test_tree_py_func import QTestTreeItemPyFunc
|
|
||||||
from main_win.test_tree_items.test_tree_lua_func import QTestTreeItemLuaFunc
|
|
||||||
from main_win.test_tree_items.test_tree_jsonrpc import QTestTreeItemJSONRPC, QTestTreeItemJSONRPCAction
|
|
||||||
from main_win.test_tree_items.test_tree_run import QTestTreeItemRun
|
|
||||||
from main_win.test_tree_items.test_tree_runtime_plot import QTestTreePlot, QTestTreePlotAction
|
|
||||||
from main_win.test_tree_items.test_tree_report import QTestTreeItemReport
|
|
||||||
from main_win.test_tree_items.test_tree_let import QTestTreeItemLet
|
|
||||||
from main_win.test_tree_items.test_tree_check import QTestTreeItemCheckValue
|
|
||||||
from main_win.test_tree_items.test_tree_unittest import QTestTreeItemUnittest, QTestTreeItemUnittestElement
|
|
||||||
from main_win.test_tree_items.test_tree_value_dialog import QTestTreeItemValueDialog
|
|
||||||
from main_win.test_tree_items.test_tree_note_dialog import QTestTreeItemNoteDialog
|
|
||||||
from main_win.test_tree_items.test_tree_image_dialog import QTestTreeItemImageDialog
|
|
||||||
from main_win.test_tree_items.test_tree_msg_dialog import QTestTreeItemMsgDialog
|
|
||||||
from main_win.test_tree_items.test_tree_question_dialog import QTestTreeItemQuestionDialog
|
|
||||||
from main_win.test_tree_items.test_tree_tested_references_dialog import QTestTreeItemTestedRefsDialog
|
|
||||||
from main_win.test_tree_items.test_tree_choices_dialog import QTestTreeItemChoicesDialog
|
|
||||||
from main_win.test_tree_items.test_tree_console import (QTestTreeItemConsole, QTestTreeItemConsoleAction)
|
|
||||||
|
|
||||||
from interpreter.test_items.test_result import (TestValue)
|
from interpreter.test_items.test_result import (TestValue)
|
||||||
import libs.testium as tm
|
import libs.testium as tm
|
||||||
@@ -47,33 +22,8 @@ from interpreter.utils.icons import icon_prefix
|
|||||||
|
|
||||||
class QTestTree(QTreeWidget):
|
class QTestTree(QTreeWidget):
|
||||||
breakpoint = Signal()
|
breakpoint = Signal()
|
||||||
DICT_TREE_ITEMS = {
|
|
||||||
cst.TYPE_UNITTEST_FILE.item_name : QTestTreeItemUnittest,
|
_KNOWN_TYPES = {e.item_name for e in cst}
|
||||||
cst.TYPE_UNITTEST_STEP.item_name : QTestTreeItemUnittestElement,
|
|
||||||
cst.TYPE_SLEEP.item_name : QTestTreeItemSleep,
|
|
||||||
cst.TYPE_CYCLE.item_name : QTestTreeItemCycle,
|
|
||||||
cst.TYPE_GRAPH.item_name : QTestTreePlot,
|
|
||||||
cst.TYPE_GRAPH_ACTION.item_name : QTestTreePlotAction,
|
|
||||||
cst.TYPE_GROUP.item_name : QTestTreeItemGroup,
|
|
||||||
cst.TYPE_GIT.item_name : QTestTreeItemGit,
|
|
||||||
cst.TYPE_PY_FUNCTION.item_name : QTestTreeItemPyFunc,
|
|
||||||
cst.TYPE_LUA_FUNCTION.item_name : QTestTreeItemLuaFunc,
|
|
||||||
cst.TYPE_LET.item_name : QTestTreeItemLet,
|
|
||||||
cst.TYPE_CHECK.item_name : QTestTreeItemCheckValue,
|
|
||||||
cst.TYPE_JSON_RPC.item_name : QTestTreeItemJSONRPC,
|
|
||||||
cst.TYPE_JSON_RPC_ACTION.item_name : QTestTreeItemJSONRPCAction,
|
|
||||||
cst.TYPE_RUN.item_name : QTestTreeItemRun,
|
|
||||||
cst.TYPE_REPORT.item_name : QTestTreeItemReport,
|
|
||||||
cst.TYPE_VALUE_DLG.item_name : QTestTreeItemValueDialog,
|
|
||||||
cst.TYPE_NOTE_DLG.item_name : QTestTreeItemNoteDialog,
|
|
||||||
cst.TYPE_IMAGE_DLG.item_name : QTestTreeItemImageDialog,
|
|
||||||
cst.TYPE_MESSAGE_DLG.item_name : QTestTreeItemMsgDialog,
|
|
||||||
cst.TYPE_QUESTION_DLG.item_name : QTestTreeItemQuestionDialog,
|
|
||||||
cst.TYPE_REFERENCE_DLG.item_name : QTestTreeItemTestedRefsDialog,
|
|
||||||
cst.TYPE_CHOICES_DLG.item_name : QTestTreeItemChoicesDialog,
|
|
||||||
cst.TYPE_CONSOLE.item_name : QTestTreeItemConsole,
|
|
||||||
cst.TYPE_CONSOLE_ACTION.item_name : QTestTreeItemConsoleAction,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@@ -297,12 +247,9 @@ class QTestTree(QTreeWidget):
|
|||||||
|
|
||||||
for test_id in test_set_item.keys():
|
for test_id in test_set_item.keys():
|
||||||
childType = test_set_item[test_id]["type"]
|
childType = test_set_item[test_id]["type"]
|
||||||
if childType in self.DICT_TREE_ITEMS.keys():
|
if childType not in self._KNOWN_TYPES:
|
||||||
tree_item = self.DICT_TREE_ITEMS[childType](tree_parent,
|
|
||||||
test_set_item[test_id],
|
|
||||||
self.cols)
|
|
||||||
else:
|
|
||||||
raise ETUMSyntaxError(f"Error in the test_set, type {childType} undefined")
|
raise ETUMSyntaxError(f"Error in the test_set, type {childType} undefined")
|
||||||
|
tree_item = make_tree_item(tree_parent, test_set_item[test_id], self.cols)
|
||||||
|
|
||||||
cb = QComboBox(self)
|
cb = QComboBox(self)
|
||||||
self.setItemWidget(tree_item, self.cols['desc']['index'], cb)
|
self.setItemWidget(tree_item, self.cols['desc']['index'], cb)
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemCheckValue(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/verif.png")
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemChoicesDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/label.png")
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemConsole(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.recursive_unfoldable = False
|
|
||||||
self.setRowIcon(icon_prefix() + "/terminal.png")
|
|
||||||
|
|
||||||
class QTestTreeItemConsoleAction(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/terminal.png")
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemCycle(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/cycle.png")
|
|
||||||
self.setExpanded(True)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemGit(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/git.png")
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemGroup(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/group.png")
|
|
||||||
self.setExpanded(True)
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemImageDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/image.png")
|
|
||||||
|
|
||||||
@@ -7,6 +7,56 @@ from PySide6.QtWidgets import (QTreeWidgetItem)
|
|||||||
from interpreter.utils.icons import icon_prefix
|
from interpreter.utils.icons import icon_prefix
|
||||||
from libs.testium import print_warn
|
from libs.testium import print_warn
|
||||||
|
|
||||||
|
# Maps item_name (from TestItemType.item_name) to visual config.
|
||||||
|
# Keys: icon (required), icon_on (optional 2nd state), expanded, unfoldable, no_breakpoint
|
||||||
|
_ITEM_CONFIG = {
|
||||||
|
"unittest file": {"icon": "folder.png", "icon_on": "folder-open.png", "expanded": True, "no_breakpoint": True},
|
||||||
|
"unittest step": {"icon": "document.png", "no_breakpoint": True},
|
||||||
|
"Console": {"icon": "terminal.png", "unfoldable": False},
|
||||||
|
"Console action": {"icon": "terminal.png"},
|
||||||
|
"Cycle": {"icon": "cycle.png", "expanded": True},
|
||||||
|
"python Function": {"icon": "python.png"},
|
||||||
|
"lua Function": {"icon": "lua.png"},
|
||||||
|
"Report": {"icon": "report.png"},
|
||||||
|
"git repository": {"icon": "git.png"},
|
||||||
|
"Runtime plot": {"icon": "plot.png"},
|
||||||
|
"Runtime plot action": {"icon": "plot.png"},
|
||||||
|
"Group": {"icon": "group.png", "expanded": True},
|
||||||
|
"Image Dialog": {"icon": "image.png"},
|
||||||
|
"Message Dialog": {"icon": "info.png"},
|
||||||
|
"Let": {"icon": "let.png"},
|
||||||
|
"Check value": {"icon": "verif.png"},
|
||||||
|
"Note Dialog": {"icon": "note.png"},
|
||||||
|
"Question Dialog": {"icon": "question.png"},
|
||||||
|
"Sleep": {"icon": "sleep.png"},
|
||||||
|
"References Dialog": {"icon": "label.png"},
|
||||||
|
"Value Dialog": {"icon": "question.png"},
|
||||||
|
"Choices Dialog": {"icon": "label.png"},
|
||||||
|
"Run tum": {"icon": "testium_logo.svg"},
|
||||||
|
"JSON-RPC": {"icon": "json.png", "unfoldable": False},
|
||||||
|
"JSON-RPC action": {"icon": "json.png"},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def make_tree_item(parent, test_set_item, cols):
|
||||||
|
"""Factory: create a QTestTreeItem configured for the given test_set_item type."""
|
||||||
|
item = QTestTreeItem(parent, test_set_item, cols)
|
||||||
|
cfg = _ITEM_CONFIG.get(test_set_item["type"], {})
|
||||||
|
if cfg.get("unfoldable") is False:
|
||||||
|
item.recursive_unfoldable = False
|
||||||
|
if cfg.get("expanded"):
|
||||||
|
item.setExpanded(True)
|
||||||
|
if cfg.get("no_breakpoint"):
|
||||||
|
item._no_breakpoint = True
|
||||||
|
icon = cfg.get("icon", "")
|
||||||
|
if icon:
|
||||||
|
icon_on = cfg.get("icon_on", "")
|
||||||
|
item.setRowIcon(
|
||||||
|
icon_prefix() + "/" + icon,
|
||||||
|
icon_prefix() + "/" + icon_on if icon_on else "",
|
||||||
|
)
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
def __iter__QTreeWidgetItem(self):
|
def __iter__QTreeWidgetItem(self):
|
||||||
for item in chain(*map(iter, self.children())):
|
for item in chain(*map(iter, self.children())):
|
||||||
@@ -51,6 +101,7 @@ class QTestTreeItem(QTreeWidgetItem):
|
|||||||
self._is_highlighted = False
|
self._is_highlighted = False
|
||||||
self._initial_brush = None
|
self._initial_brush = None
|
||||||
self._failure_list = None
|
self._failure_list = None
|
||||||
|
self._no_breakpoint = False
|
||||||
parent.addChild(self)
|
parent.addChild(self)
|
||||||
self._has_failed = False
|
self._has_failed = False
|
||||||
self._display_pause = False
|
self._display_pause = False
|
||||||
@@ -106,9 +157,7 @@ class QTestTreeItem(QTreeWidgetItem):
|
|||||||
self.setIcon(self._cols["status"]["index"], icon)
|
self.setIcon(self._cols["status"]["index"], icon)
|
||||||
|
|
||||||
def setBreakpoint(self):
|
def setBreakpoint(self):
|
||||||
if (self.__class__.__name__ == "QTestTreeItemUnittest") or (
|
if self._no_breakpoint:
|
||||||
self.__class__.__name__ == "QTestTreeItemUnittestElement"
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
self._display_pause = not self._display_pause
|
self._display_pause = not self._display_pause
|
||||||
if self._display_pause:
|
if self._display_pause:
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemJSONRPCAction(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/json.png")
|
|
||||||
|
|
||||||
class QTestTreeItemJSONRPC(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.recursive_unfoldable = False
|
|
||||||
self.setRowIcon(icon_prefix() + "/json.png")
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemLet(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/let.png")
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemLuaFunc(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/lua.png")
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemMsgDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/info.png")
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemNoteDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/note.png")
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemPyFunc(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/python.png")
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemQuestionDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/question.png")
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemReport(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/report.png")
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemRun(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/testium_logo.svg")
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreePlotAction(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/plot.png")
|
|
||||||
|
|
||||||
class QTestTreePlot(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/plot.png")
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemSleep(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/sleep.png")
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemTestedRefsDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/label.png")
|
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemUnittest(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/folder.png", icon_prefix() + "/folder-open.png")
|
|
||||||
self.setExpanded(True)
|
|
||||||
|
|
||||||
class QTestTreeItemUnittestElement(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
self.setRowIcon(icon_prefix() + "/document.png")
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
from .test_tree_item import QTestTreeItem
|
|
||||||
from interpreter.utils.icons import icon_prefix
|
|
||||||
|
|
||||||
class QTestTreeItemValueDialog(QTestTreeItem):
|
|
||||||
def __init__(self, parent, test_set_item, cols):
|
|
||||||
super().__init__(parent, test_set_item, cols)
|
|
||||||
|
|
||||||
self.setRowIcon(icon_prefix() + "/question.png")
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user