From 2b7678c39e1e4c835b6a25647b7959f398dc1c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 19 Apr 2026 20:01:56 +0200 Subject: [PATCH] Fix dialog subprocesses: centralize Qt env setup and extract base class - Add dialog_env.py service: forces QT_QPA_PLATFORM=xcb on Linux so Qt doesn't crash under Wayland in spawned subprocesses - Use QMessageBox instance (instead of static methods) for msg/question dialogs so WindowStaysOnTopHint can be set, making them visible - Add TestItemDialogBase with _run_dialog/_run_dialog_with_result/_cleanup_process, removing duplicated subprocess launch/poll/terminate logic from all 7 dialog items - Reduce terminate() join timeout from 2s to 0.2s across all dialog items Co-Authored-By: Claude Sonnet 4.6 --- .../dialog_choices_files/choices_dialog.py | 4 +- .../interpreter/test_items/dialog_env.py | 15 +++ .../dialog_image_files/dialog_image.py | 5 +- .../test_items/dialog_msg_files/msg_dialog.py | 73 ++++++----- .../dialog_note_files/test_dialog.py | 4 +- .../dialog_question_files/question_dialog.py | 71 +++++----- .../dialog_sleep_files/dialog_sleep.py | 4 +- .../dialog_value_files/test_dialog.py | 4 +- .../test_items/test_item_choices_dialog.py | 80 ++++++------ .../test_items/test_item_dialog_base.py | 43 ++++++ .../test_items/test_item_image_dialog.py | 106 ++++++--------- .../test_items/test_item_msg_dialog.py | 81 +++++------- .../test_items/test_item_note_dialog.py | 95 ++++++-------- .../test_items/test_item_question_dialog.py | 93 +++++-------- .../test_items/test_item_tested_references.py | 123 ++++++++---------- .../test_items/test_item_value_dialog.py | 105 ++++++--------- .../tested_refs_dialog.py | 5 +- 17 files changed, 429 insertions(+), 482 deletions(-) create mode 100644 src/testium/interpreter/test_items/dialog_env.py create mode 100644 src/testium/interpreter/test_items/test_item_dialog_base.py diff --git a/src/testium/interpreter/test_items/dialog_choices_files/choices_dialog.py b/src/testium/interpreter/test_items/dialog_choices_files/choices_dialog.py index cac9e35..2b1e676 100644 --- a/src/testium/interpreter/test_items/dialog_choices_files/choices_dialog.py +++ b/src/testium/interpreter/test_items/dialog_choices_files/choices_dialog.py @@ -185,7 +185,9 @@ def main(args, conn=None): SettingsApplication = "testium_choices_dlg_" + args[0] SettingsLastChoices = "last_choice" success = True - app = QApplication() + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) d = ChoicesDialog() d.setFixedSize(800, 600) d.setWindowFlags(Qt.WindowStaysOnTopHint) diff --git a/src/testium/interpreter/test_items/dialog_env.py b/src/testium/interpreter/test_items/dialog_env.py new file mode 100644 index 0000000..35d690e --- /dev/null +++ b/src/testium/interpreter/test_items/dialog_env.py @@ -0,0 +1,15 @@ +"""Qt platform environment setup for dialog subprocesses. + +Call setup() at the start of every dialog subprocess main() function +to ensure the correct Qt platform plugin is selected. +""" +import sys +import os + + +def setup(): + """Configure the Qt environment for dialog subprocess usage.""" + if sys.platform.startswith('linux'): + # On Linux/Wayland, force X11 (via XWayland) to avoid crashes + # when Qt is initialized inside a multiprocessing subprocess. + os.environ['QT_QPA_PLATFORM'] = 'xcb' diff --git a/src/testium/interpreter/test_items/dialog_image_files/dialog_image.py b/src/testium/interpreter/test_items/dialog_image_files/dialog_image.py index 508ca58..2b928d5 100644 --- a/src/testium/interpreter/test_items/dialog_image_files/dialog_image.py +++ b/src/testium/interpreter/test_items/dialog_image_files/dialog_image.py @@ -1,5 +1,4 @@ import sys -import os from PySide6.QtCore import (Qt) from PySide6.QtWidgets import (QApplication, QDialog) @@ -18,7 +17,9 @@ class TestDialogWindow(QDialog, dialog_image_win.Ui_Dialog): def main(args, conn): success = True - app = QApplication(args) + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) d = TestDialogWindow() d.setFixedSize(700,600) d.setWindowFlags(Qt.WindowStaysOnTopHint) diff --git a/src/testium/interpreter/test_items/dialog_msg_files/msg_dialog.py b/src/testium/interpreter/test_items/dialog_msg_files/msg_dialog.py index 0bcfc02..7f0176d 100644 --- a/src/testium/interpreter/test_items/dialog_msg_files/msg_dialog.py +++ b/src/testium/interpreter/test_items/dialog_msg_files/msg_dialog.py @@ -1,36 +1,37 @@ -import sys -import os - -from PySide6.QtWidgets import (QApplication, QDialog) -from PySide6.QtCore import (Qt) -from PySide6.QtWidgets import QMessageBox -from multiprocessing import freeze_support - -def main(args): - app = QApplication(sys.argv) - reply = QMessageBox.information(None, args[0], args[1], QMessageBox.Ok) - - if hasattr(sys, "frozen"): - #all standard streams are replaced by dummy one to avoid cx_freeze flushing bug. - class dummyStream: - ''' dummyStream behaves like a stream but does nothing. ''' - def __init__(self): pass - def write(self,data): pass - def read(self,data): pass - def flush(self): pass - def close(self): pass - - # and now redirect all default streams to this dummyStream: - sys.stdout = dummyStream() - sys.stderr = dummyStream() - sys.stdin = dummyStream() - sys.__stdout__ = dummyStream() - sys.__stderr__ = dummyStream() - sys.__stdin__ = dummyStream() - - -if __name__ == '__main__': - main(sys.argv[1:]) - - - +import sys +from multiprocessing import freeze_support + +from PySide6.QtWidgets import (QApplication, QMessageBox) +from PySide6.QtCore import Qt + + +def main(args): + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) + msg = QMessageBox() + msg.setWindowFlags(Qt.WindowStaysOnTopHint) + msg.setWindowTitle(args[0]) + msg.setText(args[1]) + msg.setIcon(QMessageBox.Information) + msg.setStandardButtons(QMessageBox.Ok) + msg.exec() + + if hasattr(sys, "frozen"): + class dummyStream: + def __init__(self): pass + def write(self, data): pass + def read(self, data): pass + def flush(self): pass + def close(self): pass + + sys.stdout = dummyStream() + sys.stderr = dummyStream() + sys.stdin = dummyStream() + sys.__stdout__ = dummyStream() + sys.__stderr__ = dummyStream() + sys.__stdin__ = dummyStream() + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/src/testium/interpreter/test_items/dialog_note_files/test_dialog.py b/src/testium/interpreter/test_items/dialog_note_files/test_dialog.py index 019d608..881c98f 100644 --- a/src/testium/interpreter/test_items/dialog_note_files/test_dialog.py +++ b/src/testium/interpreter/test_items/dialog_note_files/test_dialog.py @@ -14,7 +14,9 @@ class TestDialogWindow(QDialog, dialog_note_win.Ui_Dialog): def main(args, conn=None): success = True - app = QApplication(args) + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) d = TestDialogWindow() d.setFixedSize(387,224) d.setWindowFlags(Qt.WindowStaysOnTopHint) diff --git a/src/testium/interpreter/test_items/dialog_question_files/question_dialog.py b/src/testium/interpreter/test_items/dialog_question_files/question_dialog.py index 106f7c6..824354e 100644 --- a/src/testium/interpreter/test_items/dialog_question_files/question_dialog.py +++ b/src/testium/interpreter/test_items/dialog_question_files/question_dialog.py @@ -1,32 +1,39 @@ -import sys -import os - -from PySide6.QtWidgets import (QApplication, QDialog) -from PySide6.QtCore import (Qt) -from PySide6.QtWidgets import QMessageBox -from multiprocessing import freeze_support - -def main(args, conn): - app = QApplication(sys.argv) - reply = QMessageBox.question(None, args[0], args[1], QMessageBox.Yes|QMessageBox.No) - - conn.send(reply) - conn.close() - - if hasattr(sys, "frozen"): - #all standard streams are replaced by dummy one to avoid cx_freeze flushing bug. - class dummyStream: - ''' dummyStream behaves like a stream but does nothing. ''' - def __init__(self): pass - def write(self,data): pass - def read(self,data): pass - def flush(self): pass - def close(self): pass - - # and now redirect all default streams to this dummyStream: - sys.stdout = dummyStream() - sys.stderr = dummyStream() - sys.stdin = dummyStream() - sys.__stdout__ = dummyStream() - sys.__stderr__ = dummyStream() - sys.__stdin__ = dummyStream() +import sys +from multiprocessing import freeze_support + +from PySide6.QtWidgets import (QApplication, QMessageBox) +from PySide6.QtCore import Qt + + +def main(args, conn): + try: + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) + msg = QMessageBox() + msg.setWindowFlags(Qt.WindowStaysOnTopHint) + msg.setWindowTitle(args[0]) + msg.setText(args[1]) + msg.setIcon(QMessageBox.Question) + msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + reply = msg.exec() + conn.send(reply) + except Exception as e: + print(f"dialog_question error: {e}", file=sys.stderr) + finally: + conn.close() + + if hasattr(sys, "frozen"): + class dummyStream: + def __init__(self): pass + def write(self, data): pass + def read(self, data): pass + def flush(self): pass + def close(self): pass + + sys.stdout = dummyStream() + sys.stderr = dummyStream() + sys.stdin = dummyStream() + sys.__stdout__ = dummyStream() + sys.__stderr__ = dummyStream() + sys.__stdin__ = dummyStream() diff --git a/src/testium/interpreter/test_items/dialog_sleep_files/dialog_sleep.py b/src/testium/interpreter/test_items/dialog_sleep_files/dialog_sleep.py index b5552bc..e1f1770 100644 --- a/src/testium/interpreter/test_items/dialog_sleep_files/dialog_sleep.py +++ b/src/testium/interpreter/test_items/dialog_sleep_files/dialog_sleep.py @@ -39,7 +39,9 @@ class DialogSleepWindow(QDialog, dialog_sleep_win.Ui_SleepDialogWindow): def main(args, conn=None): success = True - app = QApplication(sys.argv) + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) d = DialogSleepWindow() d.setFixedSize(379,129) d.setWindowFlags(Qt.WindowStaysOnTopHint) diff --git a/src/testium/interpreter/test_items/dialog_value_files/test_dialog.py b/src/testium/interpreter/test_items/dialog_value_files/test_dialog.py index b30bc28..f8fcdf7 100644 --- a/src/testium/interpreter/test_items/dialog_value_files/test_dialog.py +++ b/src/testium/interpreter/test_items/dialog_value_files/test_dialog.py @@ -15,7 +15,9 @@ class TestDialogWindow(QDialog, dialog_value_win.Ui_Dialog): def main(args, conn=None): success = True - app = QApplication(args) + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) d = TestDialogWindow() d.setFixedSize(387,224) d.setWindowFlags(Qt.WindowStaysOnTopHint) diff --git a/src/testium/interpreter/test_items/test_item_choices_dialog.py b/src/testium/interpreter/test_items/test_item_choices_dialog.py index ad1fa4b..30d208c 100644 --- a/src/testium/interpreter/test_items/test_item_choices_dialog.py +++ b/src/testium/interpreter/test_items/test_item_choices_dialog.py @@ -1,43 +1,37 @@ -from multiprocessing import Process, Pipe - -from interpreter.test_items.test_item import TestItem, test_run -from interpreter.test_items.test_result import TestResult, TestValue -from interpreter.test_items.dialog_choices_files import choices_dialog -import libs.testium as tm -from lib.tum_except import ETUMSyntaxError, item_load_context -from interpreter.utils.constants import TestItemType as cst - - -class TestItemChoicesDialog(TestItem): - def __init__(self, dict_item, parent=None, status_queue=None, filename=""): - self._name = cst.TYPE_CHOICES_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_CHOICES_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam("question", required=True) - self._choices = self._prms.getParam("choices", required=True) - self._default_icon = self._prms.getParam("icon", required=False, default=None) - - @test_run - def execute(self): - q = self._prms.expanse(self._question) - choices = self._prms.expanse(self._choices) - icon = self._prms.expanse(self._default_icon) - parent_conn, child_conn = Pipe() - p = Process( - target=choices_dialog.main, args=([self.name(), q, choices, icon], child_conn) - ) - p.start() - val, succ = parent_conn.recv() - p.join() - - self.result.value = val - - if succ: - # The result of the test item is put into the global dict - tm.setgd("cs_" + self._name, val) - self.result.set(TestValue.SUCCESS, str(val)) - else: - tm.delgd("cs_" + self._name) - self.result.set(TestValue.FAILURE, str(val)) +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.dialog_choices_files import choices_dialog +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context +import libs.testium as tm + + +class TestItemChoicesDialog(TestItemDialogBase): + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_CHOICES_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_CHOICES_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam("question", required=True) + self._choices = self._prms.getParam("choices", required=True) + self._default_icon = self._prms.getParam("icon", required=False, default=None) + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + choices = self._prms.expanse(self._choices) + icon = self._prms.expanse(self._default_icon) + result = self._run_dialog_with_result(choices_dialog.main, [self.name(), q, choices, icon]) + if result is None: + self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result") + return + val, succ = result + self.result.value = val + if succ: + tm.setgd("cs_" + self._name, val) + self.result.set(TestValue.SUCCESS, str(val)) + else: + tm.delgd("cs_" + self._name) + self.result.set(TestValue.FAILURE, str(val)) diff --git a/src/testium/interpreter/test_items/test_item_dialog_base.py b/src/testium/interpreter/test_items/test_item_dialog_base.py new file mode 100644 index 0000000..ce11cf2 --- /dev/null +++ b/src/testium/interpreter/test_items/test_item_dialog_base.py @@ -0,0 +1,43 @@ +from multiprocessing import Process, Pipe + +from interpreter.test_items.test_item import TestItem + + +class TestItemDialogBase(TestItem): + """Base class for test items that launch a Qt dialog in a subprocess.""" + + def _cleanup_process(self, p): + if p.is_alive(): + p.terminate() + p.join(timeout=0.2) + if p.is_alive(): + p.kill() + p.join() + + def _run_dialog(self, target, args): + """Launch target(args) in a subprocess with no return value. + + Returns the subprocess exit code. + """ + p = Process(target=target, args=(args,)) + p.start() + while p.is_alive() and not self._is_stopped: + p.join(timeout=0.5) + self._cleanup_process(p) + return p.exitcode + + def _run_dialog_with_result(self, target, args): + """Launch target(args, child_conn) in a subprocess and return what it sends. + + Returns the received value, or None if stopped or if the subprocess crashed. + """ + parent_conn, child_conn = Pipe() + p = Process(target=target, args=(args, child_conn)) + p.start() + result = None + while p.is_alive() and not self._is_stopped: + if parent_conn.poll(0.5): + result = parent_conn.recv() + break + self._cleanup_process(p) + return result diff --git a/src/testium/interpreter/test_items/test_item_image_dialog.py b/src/testium/interpreter/test_items/test_item_image_dialog.py index da32805..f244687 100644 --- a/src/testium/interpreter/test_items/test_item_image_dialog.py +++ b/src/testium/interpreter/test_items/test_item_image_dialog.py @@ -1,66 +1,40 @@ -import os -import sys -from multiprocessing import Process, Pipe - -from interpreter.test_items.test_item import TestItem, test_run -from interpreter.test_items.test_result import TestResult, TestValue -from interpreter.test_items.dialog_image_files import dialog_image -import libs.testium as tm -from interpreter.utils.constants import TestItemType as cst -from lib.tum_except import ETUMSyntaxError, item_load_context - - -class TestItemImageDialog(TestItem): - """dialog_image item usage. - dialog_image name: Nice image, question: could you press the red button, filename: img.jpg - """ - - def __init__(self, dict_item, parent=None, status_queue=None, filename=""): - self._name = cst.TYPE_IMAGE_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_IMAGE_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam("question", required=True) - self._filename = self._prms.getParam("filename", required=True) - - @test_run - def execute(self): - ourpath = __file__ - test_file = os.path.join( - os.path.dirname(ourpath), "dialog_image_files", "dialog_image.py" - ) - - q = self._prms.expanse(self._question) - image_path = self._prms.expanse(self._filename) - print("Image Displayed:\n" + q + "\n" + image_path) - if not os.path.isfile(image_path): - image_path = os.path.normpath( - os.path.join(tm.gd("test_directory"), image_path) - ) - - parent_conn, child_conn = Pipe() - p = Process( - target=dialog_image.main, args=([self.name(), q, image_path], child_conn) - ) - p.start() - succ = parent_conn.recv() - p.join() - if succ: - self.result.set(TestValue.SUCCESS) - else: - self.result.set(TestValue.FAILURE) - - -def mypath(): - if hasattr(sys, "frozen"): - return os.path.dirname(sys.executable) - return os.path.dirname(__file__) - - -from multiprocessing import Process - -if __name__ == "__main__": - p = Process(target=test_dialog.main, args=(["bob", "bab"],)) - p.start() - p.join() +import os + +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.dialog_image_files import dialog_image +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context +import libs.testium as tm + + +class TestItemImageDialog(TestItemDialogBase): + """dialog_image item usage. + dialog_image name: Nice image, question: could you press the red button, filename: img.jpg + """ + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_IMAGE_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_IMAGE_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam("question", required=True) + self._filename = self._prms.getParam("filename", required=True) + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + image_path = self._prms.expanse(self._filename) + print("Image Displayed:\n" + q + "\n" + image_path) + if not os.path.isfile(image_path): + image_path = os.path.normpath( + os.path.join(tm.gd("test_directory"), image_path) + ) + succ = self._run_dialog_with_result(dialog_image.main, [self.name(), q, image_path]) + if succ is None: + self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result") + elif succ: + self.result.set(TestValue.SUCCESS) + else: + self.result.set(TestValue.FAILURE) diff --git a/src/testium/interpreter/test_items/test_item_msg_dialog.py b/src/testium/interpreter/test_items/test_item_msg_dialog.py index cd41265..280a5fb 100644 --- a/src/testium/interpreter/test_items/test_item_msg_dialog.py +++ b/src/testium/interpreter/test_items/test_item_msg_dialog.py @@ -1,49 +1,32 @@ -import os -import sys -from multiprocessing import Process, Pipe - -from interpreter.test_items.test_item import (TestItem, test_run) -from interpreter.test_items.test_result import (TestValue) -from interpreter.test_items.dialog_msg_files import msg_dialog -from interpreter.utils.constants import TestItemType as cst -from lib.tum_except import ETUMSyntaxError, item_load_context - -class TestItemMsgDialog(TestItem): - """dialog_message item usage. - dialog_message name: Nice message, question: Open the door and press OK - """ - def __init__(self, dict_item, parent = None, status_queue=None, filename=""): - self._name = cst.TYPE_MESSAGE_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_MESSAGE_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam('question', required=True) - - @test_run - def execute(self): - ourpath = __file__ - test_file = os.path.join(os.path.dirname(ourpath), - 'dialog_msg_files', - 'msg_dialog.py') - - q = self._prms.expanse(self._question) - print("Message Displayed:\n" + q) - parent_conn, child_conn = Pipe() - p=Process(target=msg_dialog.main, - args=([self.name(), q],)) - p.start() - p.join() - self.result.set(TestValue.SUCCESS) - -def mypath(): - if hasattr(sys, "frozen"): - return os.path.dirname(sys.executable) - return os.path.dirname(__file__) - -from multiprocessing import Process - -if __name__=='__main__': - p=Process(target=msg_dialog.main, args=(['bob', 'bab'],)) - p.start() - p.join() +import os +import sys + +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.dialog_msg_files import msg_dialog +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context + + +class TestItemMsgDialog(TestItemDialogBase): + """dialog_message item usage. + dialog_message name: Nice message, question: Open the door and press OK + """ + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_MESSAGE_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_MESSAGE_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + print("Message Displayed:\n" + q) + exitcode = self._run_dialog(msg_dialog.main, [self.name(), q]) + if exitcode == 0: + self.result.set(TestValue.SUCCESS) + else: + self.result.set(TestValue.FAILURE, f"Dialog subprocess exited with code {exitcode}") diff --git a/src/testium/interpreter/test_items/test_item_note_dialog.py b/src/testium/interpreter/test_items/test_item_note_dialog.py index 4259dfd..b890074 100644 --- a/src/testium/interpreter/test_items/test_item_note_dialog.py +++ b/src/testium/interpreter/test_items/test_item_note_dialog.py @@ -1,57 +1,38 @@ -import os -import sys -from multiprocessing import Process, Pipe - -from interpreter.test_items.test_item import (TestItem, test_run) -from interpreter.test_items.test_result import (TestResult, TestValue) -from interpreter.test_items.dialog_note_files import test_dialog -from lib.tum_except import ETUMSyntaxError, item_load_context -import libs.testium as tm -from interpreter.utils.constants import TestItemType as cst - -class TestItemNoteDialog(TestItem): - def __init__(self, dict_item, parent = None, status_queue=None, filename=""): - self._name = cst.TYPE_NOTE_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_NOTE_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam('question', required=True) - - @test_run - def execute(self): - ourpath = __file__ - test_file = os.path.join(os.path.dirname(ourpath), - 'dialog_note_files', - 'test_dialog.py') - - q = self._prms.expanse(self._question) - print("Question:\n" + q) - parent_conn, child_conn = Pipe() - p=Process(target=test_dialog.main, args=([self.name(), q],child_conn)) - p.start() - val, succ = parent_conn.recv() - p.join() - tm.setgd(self.name(), val) - print("\n" + ("-" * 80) + "\n") - print("- Test note\n") - print("-" * 80 + "\n") - print(val) - print("-" * 80 + "\n") - self.result.reported = {'note': val} - if succ: - self.result.set(TestValue.SUCCESS, val) - else: - self.result.set(TestValue.FAILURE, val) - -def mypath(): - if hasattr(sys, "frozen"): - return os.path.dirname(sys.executable) - return os.path.dirname(__file__) - -from multiprocessing import Process - -if __name__=='__main__': - p=Process(target=test_dialog.main, args=(['bob', 'bab'],)) - p.start() - p.join() +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.dialog_note_files import test_dialog +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context +import libs.testium as tm + + +class TestItemNoteDialog(TestItemDialogBase): + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_NOTE_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_NOTE_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + print("Question:\n" + q) + result = self._run_dialog_with_result(test_dialog.main, [self.name(), q]) + if result is None: + self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result") + return + val, succ = result + tm.setgd(self.name(), val) + print("\n" + ("-" * 80) + "\n") + print("- Test note\n") + print("-" * 80 + "\n") + print(val) + print("-" * 80 + "\n") + self.result.reported = {'note': val} + if succ: + self.result.set(TestValue.SUCCESS, val) + else: + self.result.set(TestValue.FAILURE, val) diff --git a/src/testium/interpreter/test_items/test_item_question_dialog.py b/src/testium/interpreter/test_items/test_item_question_dialog.py index 8e7bd5a..ed1159f 100644 --- a/src/testium/interpreter/test_items/test_item_question_dialog.py +++ b/src/testium/interpreter/test_items/test_item_question_dialog.py @@ -1,57 +1,36 @@ -import os -import sys -from multiprocessing import Process, Pipe - -from PySide6.QtWidgets import QMessageBox - -from interpreter.test_items.test_item import (TestItem, test_run) -from interpreter.test_items.test_result import (TestResult, TestValue) -from interpreter.test_items.dialog_question_files import question_dialog -from lib.tum_except import ETUMSyntaxError, item_load_context -from interpreter.utils.constants import TestItemType as cst - -class TestItemQuestionDialog(TestItem): - """dialog_question item usage. - dialog_question name: Nice question, question: "If OK, press OK, If not, press cancel" - """ - def __init__(self, dict_item, parent = None, status_queue=None, filename=""): - self._name = cst.TYPE_QUESTION_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_QUESTION_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam('question', required=True) - - @test_run - def execute(self): - ourpath = __file__ - test_file = os.path.join(os.path.dirname(ourpath), - 'dialog_question_files', - 'question_dialog.py') - - q = self._prms.expanse(self._question) - print('Question asked:\n' + q + '\n') - parent_conn, child_conn = Pipe() - p=Process(target=question_dialog.main, - args=([self.name(), q],child_conn)) - p.start() - succ = parent_conn.recv() - p.join() - if succ == QMessageBox.Yes: - self.result.set(TestValue.SUCCESS) - print('Answer: YES\n') - else: - self.result.set(TestValue.FAILURE) - print('Answer: NO\n') - -def mypath(): - if hasattr(sys, "frozen"): - return os.path.dirname(sys.executable) - return os.path.dirname(__file__) - -from multiprocessing import Process - -if __name__=='__main__': - p=Process(target=test_dialog.main, args=(['bob', 'bab'],)) - p.start() - p.join() +from PySide6.QtWidgets import QMessageBox + +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.dialog_question_files import question_dialog +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context + + +class TestItemQuestionDialog(TestItemDialogBase): + """dialog_question item usage. + dialog_question name: Nice question, question: "If OK, press OK, If not, press cancel" + """ + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_QUESTION_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_QUESTION_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + print('Question asked:\n' + q + '\n') + succ = self._run_dialog_with_result(question_dialog.main, [self.name(), q]) + if succ is None: + self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result") + return + if succ == QMessageBox.Yes: + self.result.set(TestValue.SUCCESS) + print('Answer: YES\n') + else: + self.result.set(TestValue.FAILURE) + print('Answer: NO\n') diff --git a/src/testium/interpreter/test_items/test_item_tested_references.py b/src/testium/interpreter/test_items/test_item_tested_references.py index 9345206..9c14b22 100644 --- a/src/testium/interpreter/test_items/test_item_tested_references.py +++ b/src/testium/interpreter/test_items/test_item_tested_references.py @@ -1,72 +1,51 @@ -import os -import sys -from multiprocessing import Process, Pipe - -from interpreter.test_items.test_item import (TestItem, test_run) -from interpreter.test_items.test_result import (TestResult, TestValue) -from interpreter.test_items.tested_references_files import tested_refs_dialog -import libs.testium as tm -from lib.tum_except import ETUMSyntaxError, item_load_context -from interpreter.utils.constants import TestItemType as cst - -class TestItemTestedRefsDialog(TestItem): - def __init__(self, dict_item, parent=None, status_queue=None, filename=""): - self._name = cst.TYPE_REFERENCE_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_REFERENCE_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam('question', required=True) - self._init_values = self._prms.getParamAll('reference', required=False, processed=True) - - @test_run - def execute(self): - ourpath=__file__ - test_file=os.path.join(os.path.dirname(ourpath), - 'tested_references_files', - 'tested_refs_dialog.py') - - q=self._prms.expanse(self._question) - parent_conn, child_conn=Pipe() - init_values=','.join(self._init_values) - p=Process(target=tested_refs_dialog.main, - args=([self.name(), q, init_values], - child_conn)) - p.start() - val, succ=parent_conn.recv() - p.join() - - titems=[] - if len(val) > 0: - i = 0 - for sitem in val.split(','): - titem={} - telems=sitem.split('/') - titem['reference']=telems[0] - titem['revision']=telems[1] - titem['serial']=telems[2] - print("Identification:\n" + str(titem)) - titems.append(titem) - self.result.reported = {'reference_{}'.format(i): titem} - i = i + 1 - self.result.value = titems - tm.setgd('tested_items', titems) - if len(val) > 0: - if succ: - self.result.set(TestValue.SUCCESS, val) - else: - self.result.set(TestValue.FAILURE, val) - else: - self.result.set(TestValue.FAILURE, 'The dialog did not return any value') - -def mypath(): - if hasattr(sys, "frozen"): - return os.path.dirname(sys.executable) - return os.path.dirname(__file__) - -from multiprocessing import Process - -if __name__ == '__main__': - p=Process(target=test_dialog.main, args=(['bob', 'bab'],)) - p.start() - p.join() +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.tested_references_files import tested_refs_dialog +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context +import libs.testium as tm + + +class TestItemTestedRefsDialog(TestItemDialogBase): + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_REFERENCE_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_REFERENCE_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) + self._init_values = self._prms.getParamAll('reference', required=False, processed=True) + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + init_values = ','.join(self._init_values) + result = self._run_dialog_with_result(tested_refs_dialog.main, [self.name(), q, init_values]) + if result is None: + self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result") + return + val, succ = result + + titems = [] + if len(val) > 0: + i = 0 + for sitem in val.split(','): + titem = {} + telems = sitem.split('/') + titem['reference'] = telems[0] + titem['revision'] = telems[1] + titem['serial'] = telems[2] + print("Identification:\n" + str(titem)) + titems.append(titem) + self.result.reported = {'reference_{}'.format(i): titem} + i += 1 + self.result.value = titems + tm.setgd('tested_items', titems) + if len(val) > 0: + if succ: + self.result.set(TestValue.SUCCESS, val) + else: + self.result.set(TestValue.FAILURE, val) + else: + self.result.set(TestValue.FAILURE, 'The dialog did not return any value') diff --git a/src/testium/interpreter/test_items/test_item_value_dialog.py b/src/testium/interpreter/test_items/test_item_value_dialog.py index 027fc3f..c589598 100644 --- a/src/testium/interpreter/test_items/test_item_value_dialog.py +++ b/src/testium/interpreter/test_items/test_item_value_dialog.py @@ -1,62 +1,43 @@ -import os -import sys -from multiprocessing import Process, Pipe - -from interpreter.test_items.test_item import (TestItem, test_run) -from interpreter.test_items.test_result import (TestResult, TestValue) -from interpreter.test_items.dialog_value_files import test_dialog -import libs.testium as tm -from lib.tum_except import ETUMSyntaxError, item_load_context -from interpreter.utils.constants import TestItemType as cst - -class TestItemValueDialog(TestItem): - """dialog_value item usage. - dialog_value name: Enter value, question: "Which value did you measure?" - """ - def __init__(self, dict_item, parent = None, status_queue=None, filename=""): - self._name = cst.TYPE_VALUE_DLG.item_name - super().__init__(dict_item, parent, status_queue, filename=filename) - self._type = cst.TYPE_VALUE_DLG - self.is_container = False - with item_load_context(self.cmd(), self.name(), self.seqFilename()): - self._question = self._prms.getParam('question', required=True) - self._default = self._prms.getParam('default', '') - - @test_run - def execute(self): - ourpath = __file__ - test_file = os.path.join(os.path.dirname(ourpath), - 'dialog_value_files', - 'test_dialog.py') - - q = self._prms.expanse(self._question) - d = self._prms.expanse(self._default) - print("Question:\n" + q) - parent_conn, child_conn = Pipe() - p=Process(target=test_dialog.main, args=([self.name(), q, d],child_conn)) - p.start() - val, succ = parent_conn.recv() - p.join() - tm.setgd(self.name(), val) - print("Answer: " + val) - if len(val) > 0: - self.result.reported = {'question': q, 'answer': val} - self.result.value = val - if succ: - self.result.set(TestValue.SUCCESS, val) - else: - self.result.set(TestValue.FAILURE, val) - else: - self.result.set(TestValue.FAILURE, 'The dialog did not return any value') - -def mypath(): - if hasattr(sys, "frozen"): - return os.path.dirname(sys.executable) - return os.path.dirname(__file__) - -from multiprocessing import Process - -if __name__=='__main__': - p=Process(target=test_dialog.main, args=(['bob', 'bab'],)) - p.start() - p.join() +from interpreter.test_items.test_item import test_run +from interpreter.test_items.test_result import TestValue +from interpreter.test_items.dialog_value_files import test_dialog +from interpreter.test_items.test_item_dialog_base import TestItemDialogBase +from interpreter.utils.constants import TestItemType as cst +from lib.tum_except import item_load_context +import libs.testium as tm + + +class TestItemValueDialog(TestItemDialogBase): + """dialog_value item usage. + dialog_value name: Enter value, question: "Which value did you measure?" + """ + def __init__(self, dict_item, parent=None, status_queue=None, filename=""): + self._name = cst.TYPE_VALUE_DLG.item_name + super().__init__(dict_item, parent, status_queue, filename=filename) + self._type = cst.TYPE_VALUE_DLG + self.is_container = False + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) + self._default = self._prms.getParam('default', '') + + @test_run + def execute(self): + q = self._prms.expanse(self._question) + d = self._prms.expanse(self._default) + print("Question:\n" + q) + result = self._run_dialog_with_result(test_dialog.main, [self.name(), q, d]) + if result is None: + self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result") + return + val, succ = result + tm.setgd(self.name(), val) + print("Answer: " + val) + if len(val) > 0: + self.result.reported = {'question': q, 'answer': val} + self.result.value = val + if succ: + self.result.set(TestValue.SUCCESS, val) + else: + self.result.set(TestValue.FAILURE, val) + else: + self.result.set(TestValue.FAILURE, 'The dialog did not return any value') diff --git a/src/testium/interpreter/test_items/tested_references_files/tested_refs_dialog.py b/src/testium/interpreter/test_items/tested_references_files/tested_refs_dialog.py index 6e4868f..5789356 100644 --- a/src/testium/interpreter/test_items/tested_references_files/tested_refs_dialog.py +++ b/src/testium/interpreter/test_items/tested_references_files/tested_refs_dialog.py @@ -1,5 +1,4 @@ import sys -import os from multiprocessing import freeze_support from PySide6.QtWidgets import (QApplication, QDialog, QTableWidgetItem) @@ -20,7 +19,9 @@ def main(args, conn=None): SettingsApplication = 'testium_ref_item' SettingsLastReference = 'lastReference' success = True - app = QApplication(args) + from interpreter.test_items import dialog_env + dialog_env.setup() + app = QApplication(['testium']) d = TestedRefsWindow() d.setFixedSize(481,386) d.setWindowFlags(Qt.WindowStaysOnTopHint)