Text mode upgrade (to be fixed).

This commit is contained in:
2026-04-26 09:20:39 +02:00
parent 5a065128be
commit fa7f8cef7c
18 changed files with 279 additions and 102 deletions

View File

@@ -4,6 +4,7 @@ SUPPORTED_API = [
"setgd",
"delgd",
"add_plot_values",
"last_plot_value"
"last_plot_value",
"text_mode",
]

View File

@@ -102,7 +102,7 @@ def main():
if (lf != '') or (rf != '') or (tf != '') or (pn != []):
print('"-l", "-p", "-t", "-n" options are not supported in this mode.')
t = Terminal(os.getcwd(), cf, defines, args.no_color)
t = Terminal(os.getcwd(), cf, defines, args.no_color, text_mode=True)
loop = 1
while loop:
@@ -124,7 +124,7 @@ def main():
print('"-l" option is not supported in this mode.')
from interpreter.batch import Batch
b = Batch(tf, cf, defines, rf, args.report_type, pn, args.no_color)
b = Batch(tf, cf, defines, rf, args.report_type, pn, args.no_color, text_mode=True)
else:
from main_win.testium_win import MainWin

View File

@@ -22,6 +22,7 @@ class Batch:
report_type,
report_pattern,
no_color,
text_mode=False,
):
try:
try:
@@ -59,6 +60,7 @@ class Batch:
self.tst_ctrl,
config_files,
defines,
text_mode=text_mode,
)
tst_proc.start()
@@ -82,6 +84,8 @@ class Batch:
# No id -> finished
break
except Empty:
if not tst_proc.is_alive():
break
continue
# Close the process and wait for termination
@@ -95,4 +99,7 @@ class Batch:
stdio_redir.restore()
def sigint_handler(self, signal_received, frame):
self.tst_ctrl.control("stop")
try:
self.tst_ctrl.control("stop", timeout=5)
except Exception:
pass

View File

@@ -1,4 +1,5 @@
import os
import signal
from multiprocessing import Process, Queue, Pipe
from queue import Empty
from threading import Thread
@@ -41,6 +42,7 @@ class TestProcess(Process):
config_files,
defines,
gui_defaults={},
text_mode=False,
) -> None:
super().__init__()
self.__fname = file_name
@@ -49,6 +51,7 @@ class TestProcess(Process):
self.__cfgf = config_files
self.__defs = defines
self.__gui_defaults = gui_defaults # default values coming from GUI prefs
self.__text_mode = text_mode
self.__exec = False
self.__loaded = False
self.__closed = False
@@ -194,6 +197,7 @@ class TestProcess(Process):
def run(self):
signal.signal(signal.SIGINT, signal.SIG_IGN)
try:
try:
# Thread for stdout redirection
@@ -224,6 +228,10 @@ Is the python exec path correct ?"""
# Load the test file
test_dict, param_files = self._load_test(init_param_files, glob_variables)
if self.__text_mode:
tm.setgd("_text_mode", True)
tm.setgd("_interactive", False)
# Backup the global dict in case of restart of the test
gdict = backup_gd()
@@ -421,7 +429,7 @@ Is the python exec path correct ?"""
try:
# read the pipe data
data = cconn.recv()
print(data, end="")
print(data, end="", flush=True)
except EOFError:
# exit the loop is the pipe is closed
break

View File

@@ -56,7 +56,7 @@ class Terminal(Cmd):
cst.TYPE_CYCLE
]
def __init__(self, working_dir, config_files, defines, no_color):
def __init__(self, working_dir, config_files, defines, no_color, text_mode=False):
super().__init__()
self.working_dir = working_dir
self.config_files = config_files
@@ -69,6 +69,8 @@ class Terminal(Cmd):
# Define the builtin variables
set_standard_gd_keys("Unnamed", self.working_dir, '', config_files)
update_global([], defines)
if text_mode:
tm.setgd("_text_mode", True)
# creation of the functions
for tst in self.SUPPORTED_TESTS:

View File

@@ -1,7 +1,6 @@
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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
import libs.testium as tm
@@ -19,11 +18,54 @@ class TestItemChoicesDialog(TestItemDialogBase):
self._default_icon = self._prms.getParam("icon", required=False, default=None)
self._auto_result = self._prms.getParam("auto_result", required=False, default=None)
def _print_choices(self, choices, indent=0):
if not isinstance(choices, list):
return
for choice in choices:
name = choice.get("name", "")
desc = choice.get("description", "")
line = " " * indent + f"- {name}"
if desc:
line += f": {desc}"
print(line)
sub = choice.get("choices", None)
if sub:
self._print_choices(sub, indent + 1)
def _all_checked(self, choices):
result = []
if not isinstance(choices, list):
return result
for choice in choices:
item = {"name": choice.get("name", ""), "checked": True}
sub = choice.get("choices", None)
if sub is not None:
item["choices"] = self._all_checked(sub)
result.append(item)
return result
@test_run
def execute(self):
q = self._prms.expanse(self._question)
choices = self._prms.expanse(self._choices)
icon = self._prms.expanse(self._default_icon)
if _is_text_mode():
print(f"Choices: {q}")
self._print_choices(choices)
if _is_interactive():
ans = input("Accept all? (y/n) [default: y]: ").strip().lower()
else:
ans = ''
if ans in ('n', 'no'):
tm.delgd("cs_" + self._name)
self.result.set(TestValue.FAILURE, "Cancelled")
else:
val = self._all_checked(choices)
self.result.value = val
tm.setgd("cs_" + self._name, val)
self.result.set(TestValue.SUCCESS, str(val))
return
from interpreter.test_items.dialog_choices_files import choices_dialog
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
args = [self.name(), q, choices, icon] + ([ar] if ar is not None else [])
result = self._run_dialog_with_result(choices_dialog.main, args)

View File

@@ -1,7 +1,16 @@
import multiprocessing
import libs.testium as tm
from interpreter.test_items.test_item import TestItem
def _is_text_mode():
return tm.text_mode()
def _is_interactive():
return bool(tm.gd("_interactive", True))
_spawn_ctx = multiprocessing.get_context('spawn')

View File

@@ -2,8 +2,7 @@ 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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
import libs.testium as tm
@@ -32,6 +31,17 @@ class TestItemImageDialog(TestItemDialogBase):
image_path = os.path.normpath(
os.path.join(tm.gd("test_directory"), image_path)
)
if _is_text_mode():
if _is_interactive():
ans = input("Accept? (y/n) [default: y]: ").strip().lower()
else:
ans = ''
if ans in ('n', 'no'):
self.result.set(TestValue.FAILURE)
else:
self.result.set(TestValue.SUCCESS)
return
from interpreter.test_items.dialog_image_files import dialog_image
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
args = [self.name(), q, image_path] + ([ar] if ar is not None else [])
succ = self._run_dialog_with_result(dialog_image.main, args)

View File

@@ -3,8 +3,7 @@ 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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
@@ -26,6 +25,12 @@ class TestItemMsgDialog(TestItemDialogBase):
def execute(self):
q = self._prms.expanse(self._question)
print("Message Displayed:\n" + q)
if _is_text_mode():
if _is_interactive():
input("Press Enter to continue...")
self.result.set(TestValue.SUCCESS)
return
from interpreter.test_items.dialog_msg_files import msg_dialog
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
args = [self.name(), q] + ([ar] if ar is not None else [])
exitcode = self._run_dialog(msg_dialog.main, args)

View File

@@ -1,7 +1,6 @@
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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
import libs.testium as tm
@@ -22,6 +21,29 @@ class TestItemNoteDialog(TestItemDialogBase):
def execute(self):
q = self._prms.expanse(self._question)
print("Question:\n" + q)
if _is_text_mode():
lines = []
if _is_interactive():
print("Enter your note (type '.' on a new line to finish, empty line to cancel):")
while True:
line = input()
if line == '.':
break
lines.append(line)
val = '\n'.join(lines)
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 val:
self.result.set(TestValue.SUCCESS, val)
else:
self.result.set(TestValue.FAILURE, val)
return
from interpreter.test_items.dialog_note_files import test_dialog
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
av = self._prms.expanse(self._auto_value) if self._auto_value is not None else None
args = [self.name(), q] + ([ar, av] if ar is not None else [])

View File

@@ -1,9 +1,6 @@
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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
@@ -25,12 +22,26 @@ class TestItemQuestionDialog(TestItemDialogBase):
def execute(self):
q = self._prms.expanse(self._question)
print('Question asked:\n' + q + '\n')
if _is_text_mode():
if _is_interactive():
ans = input("Answer yes (y) or no (n) [default: y]: ").strip().lower()
else:
ans = ''
if ans in ('n', 'no'):
self.result.set(TestValue.FAILURE)
print('Answer: NO\n')
else:
self.result.set(TestValue.SUCCESS)
print('Answer: YES\n')
return
from interpreter.test_items.dialog_question_files import question_dialog
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
args = [self.name(), q] + ([ar] if ar is not None else [])
succ = self._run_dialog_with_result(question_dialog.main, args)
if succ is None:
self.result.set(TestValue.FAILURE, "Dialog subprocess exited without returning a result")
return
from PySide6.QtWidgets import QMessageBox
if succ == QMessageBox.Yes:
self.result.set(TestValue.SUCCESS)
print('Answer: YES\n')

View File

@@ -3,9 +3,9 @@ from time import sleep
from datetime import timedelta
from multiprocessing import Process, Pipe
import libs.testium as tm
from interpreter.test_items.test_item import (TestItem, test_run)
from interpreter.test_items.test_result import (TestValue)
from interpreter.test_items.dialog_sleep_files import dialog_sleep
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
@@ -43,6 +43,20 @@ class TestItemSleep(TestItem):
#test core function
if has_dialog:
if tm.text_mode():
import time as _time
print(f"Sleep {timeout}s (press Ctrl+C to abort)...")
end_time = _time.time() + float(timeout)
while _time.time() < end_time and not self._is_stopped:
sleep(0.2)
if self._is_stopped:
print("Aborted")
self.result.set(TestValue.FAILURE, 'Sleep aborted')
else:
self.result.set(TestValue.SUCCESS, f'Sleep {timeout} sec')
return
from interpreter.test_items.dialog_sleep_files import dialog_sleep
parent_conn, child_conn = Pipe()
p=Process(target=dialog_sleep.main, args=([self.name(), timeout],child_conn))
p.start()

View File

@@ -1,7 +1,6 @@
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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
import libs.testium as tm
@@ -22,6 +21,23 @@ class TestItemTestedRefsDialog(TestItemDialogBase):
def execute(self):
q = self._prms.expanse(self._question)
init_values = ','.join(self._init_values)
if _is_text_mode():
print(f"References: {q}")
rows = init_values.split(',') if init_values else ['']
result_rows = []
for i, row in enumerate(rows):
parts = (row.split('/') + ['', '', ''])[:3]
if _is_interactive():
ref = input(f"Row {i+1} - Reference [{parts[0]}]: ").strip() or parts[0]
rev = input(f"Row {i+1} - Revision [{parts[1]}]: ").strip() or parts[1]
serial = input(f"Row {i+1} - Serial [{parts[2]}]: ").strip() or parts[2]
else:
ref, rev, serial = parts[0], parts[1], parts[2]
result_rows.append(f"{ref}/{rev}/{serial}")
val = ','.join(result_rows)
result = [val, True]
else:
from interpreter.test_items.tested_references_files import tested_refs_dialog
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
args = [self.name(), q, init_values] + ([ar] if ar is not None else [])
result = self._run_dialog_with_result(tested_refs_dialog.main, args)

View File

@@ -1,7 +1,6 @@
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.test_items.test_item_dialog_base import TestItemDialogBase, _is_text_mode, _is_interactive
from interpreter.utils.constants import TestItemType as cst
from lib.tum_except import item_load_context
import libs.testium as tm
@@ -27,6 +26,23 @@ class TestItemValueDialog(TestItemDialogBase):
q = self._prms.expanse(self._question)
d = self._prms.expanse(self._default)
print("Question:\n" + q)
if _is_text_mode():
if _is_interactive():
prompt = f"Enter value [{d}]: " if d else "Enter value: "
ans = input(prompt).strip()
else:
ans = ''
val = ans if ans else d
tm.setgd(self.name(), val)
print("Answer: " + str(val))
if val:
self.result.reported = {'question': q, 'answer': val}
self.result.value = val
self.result.set(TestValue.SUCCESS, val)
else:
self.result.set(TestValue.FAILURE, 'No value entered')
return
from interpreter.test_items.dialog_value_files import test_dialog
ar = self._prms.expanse(self._auto_result) if self._auto_result is not None else None
av = self._prms.expanse(self._auto_value) if self._auto_value is not None else None
args = [self.name(), q, d] + ([ar, av] if ar is not None else [])

View File

@@ -189,7 +189,10 @@ class LuaProcessBase:
if tm.debug_enabled() and tm.gd("debug_rpc", False):
params.append("--verbose")
self._process = subprocess.Popen(params, env=env, cwd=func_proc_path)
self._process = subprocess.Popen(
params, env=env, cwd=func_proc_path,
stdin=subprocess.DEVNULL, restore_signals=False,
)
self._rpc = JsonRpcClient(
"localhost", self._port, req_handler=self._req_handler

View File

@@ -158,7 +158,10 @@ class PyProcessBase:
if tm.debug_enabled() and tm.gd("debug_rpc", False):
params.append("-v")
self._process = subprocess.Popen(params, env=env, cwd=func_proc_path)
self._process = subprocess.Popen(
params, env=env, cwd=func_proc_path,
stdin=subprocess.DEVNULL, restore_signals=False,
)
self._rpc = JsonRpcClient(
"localhost", self._port, req_handler=self._req_handler

View File

@@ -209,6 +209,15 @@ def OS():
return platform.system()
def text_mode():
"""Whether testium is running in text mode (batch ``-b`` or terminal ``-m``).
:return: ``True`` if running in text mode, ``False`` otherwise.
:rtype: bool
"""
return bool(globdict.gd("_text_mode", False))
def sys_encoding():
if OS() == "Windows":
enc = "oem"

View File

@@ -1,15 +1,18 @@
- plot:
- group:
name: Plot test
condition: <| $(validation_dialogs) and not tm.text_mode() |>
steps:
- plot:
name: Open the plot
condition: $(validation_dialogs)
key: $(test)_PASS
plot_name: Mon Plot
steps:
- open:
log_path: $(validation_report_path)
- plot:
- plot:
name: Add periodic to the plot
condition: $(validation_dialogs)
key: $(test)_PASS
plot_name: Mon Plot
steps:
@@ -19,15 +22,13 @@
func_name: random_value
eval: '{"periodic": $(result)}'
- sleep:
- sleep:
name: sleep
condition: $(validation_dialogs)
dialog: true
timeout: 3
- loop:
- loop:
name: Add of other data in the plot
condition: $(validation_dialogs)
iterator: 10
steps:
@@ -52,20 +53,18 @@
param:
- Mon Plot
- plot:
- plot:
name: Export
execute_on_stop: True
condition: $(validation_dialogs)
key: $(test)_PASS
plot_name: Mon Plot
steps:
- export: $(validation_report_path)/plot_export.pdf
- export: $(validation_report_path)/plot_export.csv
- plot:
- plot:
name: Close the plot
execute_on_stop: True
condition: $(validation_dialogs)
key: $(test)_PASS
plot_name: Mon Plot
steps: