Restructure: consolidate everything inside testium/ package
Move src/lib/ → src/testium/runtime/ (internal plumbing)
Move src/testium/libs/ → src/testium/api/ (public SDK for test scripts)
Move src/py_func/ → src/testium/py_func/ (Python subprocess)
Move src/lua_func/ → src/testium/lua_func/ (Lua subprocess data)
The package now ships as a single coherent unit instead of four sibling
top-level packages (testium, lib, py_func, lua_func) — pip install
gives a clean site-packages/testium/ with no namespace pollution; .lua
files travel with the wheel via package_data; the wheel installs
cleanly and `testium -b` runs end-to-end including py_func subprocesses
and entry-point exporter plugins.
Naming:
- runtime/ (internal, no API guarantees) clearer than lib/
- api/ (public SDK consumed as `import api.testium as tm`) clearer than libs/
Imports updated en masse: from lib. → from runtime. and from libs. →
from api., plus the importlib.import_module("libs.*") strings in
test_item_console.py and test_item_runtime_plot.py. Test/example
scripts (helper_lib.py, parallel.py, post_execution.py) and the
fake_exporter test suite migrated too.
paths.py: subproc_path() now returns testium_path() — both point at
the testium package directory since the subprocesses live inside.
pyproject.toml: removed exclude=["lua_func", "py_func"] (no longer
needed), added package-data for testium.lua_func/*.lua, removed the
license classifier (PEP 639 conflict with license expression).
Subprocess isolation contract: py_func/ and lua_func/ may only import
runtime/ and their own modules — never interpreter/, main_win/, api/,
or testium/. Enforced by test/validation/items/isolation/ which runs a
py_func that statically scans subprocess source files for forbidden
imports. The contract holds today; the test prevents future drift.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
25
CLAUDE.md
25
CLAUDE.md
@@ -109,9 +109,28 @@ All dialog items (`dialog_image`, `dialog_question`, `dialog_references`, `dialo
|
||||
| `src/testium/interpreter/test_items/test_item_parallel.py` | `parallel` and `parallel_branch` items |
|
||||
| `src/testium/interpreter/utils/globdict.py` | Global variable dict |
|
||||
| `src/testium/interpreter/utils/termlog.py` | Terminal color output |
|
||||
| `src/lib/stdout_redirect.py` | `StdioRedirect` singleton (`stdio_redir`) |
|
||||
| `src/lib/string_queue.py` | Thread-safe string buffer used for stdout redirection |
|
||||
| `src/testium/libs/testium.py` | Public API for test scripts (`tm.*`) |
|
||||
| `src/testium/runtime/stdout_redirect.py` | `StdioRedirect` singleton (`stdio_redir`) |
|
||||
| `src/testium/runtime/string_queue.py` | Thread-safe string buffer used for stdout redirection |
|
||||
| `src/testium/api/testium.py` | Public API for test scripts (`tm.*`) |
|
||||
| `src/testium/py_func/` | Python subprocess for `py_func` items (sandboxed: imports only `runtime/` and `py_func/`) |
|
||||
| `src/testium/lua_func/` | Lua subprocess scripts for `lua_func` items |
|
||||
|
||||
## Package layout
|
||||
|
||||
The whole project is a single Python package under `src/testium/`:
|
||||
|
||||
```
|
||||
src/testium/
|
||||
├── __init__.py / __main__.py
|
||||
├── runtime/ internal plumbing (jrpc, stdout_redirect, string_queue, tum_except, api)
|
||||
├── api/ public SDK exposed to test scripts (`import api.testium as tm`)
|
||||
├── interpreter/ test execution engine (NOT visible to py_func/lua_func)
|
||||
├── main_win/ GUI (NOT visible to py_func/lua_func)
|
||||
├── py_func/ subprocess code for python_func items
|
||||
└── lua_func/ subprocess scripts for lua_func items (data files)
|
||||
```
|
||||
|
||||
`subproc_path()` and `testium_path()` both return the package directory. The py_func subprocess is launched with cwd=that directory and `python3 py_func`. The contract that `py_func/` and `lua_func/` only depend on `runtime/` (no `interpreter`, `main_win`, `api`, `testium`) is enforced by `test/validation/items/isolation/`.
|
||||
|
||||
## GUI icons (main_win)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
def post_exec():
|
||||
print('Success !!!!')
|
||||
|
||||
@@ -13,7 +13,6 @@ license-files = ["../LICENSE"]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Programming Language :: Python",
|
||||
"License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)",
|
||||
]
|
||||
dependencies = [
|
||||
"setuptools",
|
||||
@@ -36,7 +35,9 @@ testium = "testium:main"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where=["."]
|
||||
exclude=["lua_func", "py_func"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"testium.lua_func" = ["*.lua"]
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {file = ["VERSION"]}
|
||||
|
||||
@@ -245,7 +245,7 @@ A {classname}.close() is missing somewhere in your code !'.format(classname=type
|
||||
if not sys.platform.startswith('win'):
|
||||
# import SshConsole if pexpect is installed
|
||||
try:
|
||||
from libs.console_ssh import SshConsole
|
||||
from api.console_ssh import SshConsole
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -8,7 +8,7 @@ import os
|
||||
import pexpect
|
||||
from pexpect import ExceptionPexpect, TIMEOUT, EOF, spawn
|
||||
|
||||
from libs.console import Console
|
||||
from api.console import Console
|
||||
|
||||
# Exception classes used by this module.
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
import socket
|
||||
import traceback
|
||||
|
||||
from libs.console import *
|
||||
from api.console import *
|
||||
|
||||
class RawTCPConsole(Console):
|
||||
TYPE = 'rawtcp'
|
||||
@@ -16,9 +16,9 @@ import numpy as np
|
||||
import matplotlib.dates as mdates
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from interpreter.utils.py_func_exec import PyFuncExecEngine
|
||||
from interpreter.utils.api_srv import api_request
|
||||
from interpreter.utils.eval import post_evaluate
|
||||
@@ -10,7 +10,7 @@ import os
|
||||
|
||||
ourPath = os.path.dirname(__file__)
|
||||
sys.path.append(ourPath)
|
||||
from libs.console import (Console, BytesStore, TIMEOUT_NULL)
|
||||
from api.console import (Console, BytesStore, TIMEOUT_NULL)
|
||||
|
||||
class TermConsole(Console):
|
||||
TYPE = 'term'
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
import textwrap
|
||||
from time import monotonic
|
||||
import interpreter.utils.globdict as globdict
|
||||
from lib.tum_except import (ETUMSyntaxError)
|
||||
from runtime.tum_except import (ETUMSyntaxError)
|
||||
|
||||
###############################################################################
|
||||
# Console helper functions
|
||||
@@ -14,7 +14,7 @@ def add_console(console):
|
||||
''' Function which adds a ``Console`` class instance to *testium*
|
||||
|
||||
:param console: The ``Console`` instance.
|
||||
:type console: ``libs.console.Console`` or child class instance
|
||||
:type console: ``api.console.Console`` or child class instance
|
||||
:return: No returned value
|
||||
|
||||
'''
|
||||
@@ -48,7 +48,7 @@ def console(name):
|
||||
:param name: The name of the ``Console`` instance.
|
||||
:type name: str
|
||||
:return: The ``Console`` or child class object
|
||||
:rtype: ``libs.console.Console`` or child class instance
|
||||
:rtype: ``api.console.Console`` or child class instance
|
||||
"""
|
||||
cons = None
|
||||
for c in globdict.gd('console_instances', []):
|
||||
@@ -65,7 +65,7 @@ def add_plot(plot: object) -> None:
|
||||
''' Function which adds a ``RuntimePlot`` class instance to *testium*
|
||||
|
||||
:param plot: The ``RuntimePlot`` instance.
|
||||
:type plot: ``libs.runtime_plot.RuntimePlot`` or child class instance
|
||||
:type plot: ``api.runtime_plot.RuntimePlot`` or child class instance
|
||||
:return: No returned value
|
||||
|
||||
'''
|
||||
@@ -99,7 +99,7 @@ def plot(name: str) -> object:
|
||||
:param name: The name of the ``RuntimePlot`` instance.
|
||||
:type name: str
|
||||
:return: The ``RuntimePlot`` or child class object
|
||||
:rtype: ``libs.runtime_plot.RuntimePlot`` or child class instance
|
||||
:rtype: ``api.runtime_plot.RuntimePlot`` or child class instance
|
||||
"""
|
||||
plot = None
|
||||
for g in globdict.gd('plot_instances', []):
|
||||
@@ -9,8 +9,8 @@ from multiprocessing import Queue
|
||||
|
||||
from interpreter.process import TestProcess
|
||||
from interpreter.utils.test_ctrl import TestSetController
|
||||
from lib.tum_except import ETUMFileError, ETUMRuntimeError
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
from runtime.tum_except import ETUMFileError, ETUMRuntimeError
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
|
||||
|
||||
class Batch:
|
||||
|
||||
@@ -6,9 +6,9 @@ from threading import Thread
|
||||
from time import sleep
|
||||
import copy
|
||||
|
||||
from lib.string_queue import StringQueue
|
||||
from lib.tum_except import print_exception, ETUMRuntimeError, ETUMSyntaxError
|
||||
import libs.testium as tm
|
||||
from runtime.string_queue import StringQueue
|
||||
from runtime.tum_except import print_exception, ETUMRuntimeError, ETUMSyntaxError
|
||||
import api.testium as tm
|
||||
import interpreter.utils.globdict as globdict
|
||||
from interpreter.utils.params import expanse
|
||||
from interpreter.utils.test_ctrl import TestSetController
|
||||
@@ -26,7 +26,7 @@ from interpreter.utils.test_init import (
|
||||
from interpreter.utils.constants import TestItemType as cst_type
|
||||
from interpreter.test_set import TestSet
|
||||
from interpreter.utils.include import TUMLoader, TUMLoaderNoIncludes, TUMLoaderRawIncludes
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
from interpreter.utils.template import template_to_test
|
||||
from interpreter.utils.yaml_load import yaml_load
|
||||
from interpreter.utils.py_eval import eval_process_init
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
from interpreter.test_items.test_item import TestItem, test_run, test_data
|
||||
from interpreter.test_items.test_result import TestResult, TestValue
|
||||
from interpreter.test_items.item_actions.action import TestItemAction
|
||||
|
||||
@@ -3,11 +3,11 @@ from time import sleep
|
||||
import yaml
|
||||
from copy import deepcopy
|
||||
from interpreter.test_items.test_result import TestResult, TestValue
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.params import TestItemParams
|
||||
from interpreter.utils.constants import TestItemType as cst_type
|
||||
from interpreter.utils.eval import eval_to_boolean, evaluate, post_evaluate
|
||||
from lib.tum_except import ETUMSyntaxError, item_load_context
|
||||
from runtime.tum_except import ETUMSyntaxError, item_load_context
|
||||
|
||||
LOG_TEST_STOP = '<----- step "{}" finished'
|
||||
LOG_TEST_START = '-----> step "{}" started'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
from lib.tum_except import ETUMSyntaxError, item_load_context
|
||||
import libs.testium as tm
|
||||
from runtime.tum_except import ETUMSyntaxError, item_load_context
|
||||
import api.testium as tm
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from interpreter.utils.eval import evaluate
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
class TestItemChoicesDialog(TestItemDialogBase):
|
||||
|
||||
@@ -3,9 +3,9 @@ import os
|
||||
import importlib
|
||||
import traceback
|
||||
|
||||
import libs.testium as tm
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
import api.testium as tm
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.item_actions import TestItemActions
|
||||
from interpreter.test_items.item_actions.action import TestItemAction
|
||||
@@ -345,17 +345,17 @@ class TestItemConsole(TestItemActions):
|
||||
self.actions_token = {}
|
||||
|
||||
global console
|
||||
console = importlib.import_module("libs.console")
|
||||
console = importlib.import_module("api.console")
|
||||
|
||||
if not sys.platform.startswith("win"):
|
||||
global console_ssh
|
||||
console_ssh = importlib.import_module("libs.console_ssh")
|
||||
console_ssh = importlib.import_module("api.console_ssh")
|
||||
|
||||
global termconsole
|
||||
termconsole = importlib.import_module("libs.termconsole")
|
||||
termconsole = importlib.import_module("api.termconsole")
|
||||
|
||||
global raw_tcp_console
|
||||
raw_tcp_console = importlib.import_module("libs.raw_tcp_console")
|
||||
raw_tcp_console = importlib.import_module("api.raw_tcp_console")
|
||||
|
||||
self.actions_token["console_name"] = self._prms.getParam(
|
||||
"console_name", required=True
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import traceback
|
||||
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError
|
||||
from interpreter.utils.py_func_exec import PyFuncExecEngine
|
||||
from interpreter.utils.api_srv import api_request
|
||||
from interpreter.test_items.test_item import TestItem, test_run
|
||||
from interpreter.test_items.test_result import TestResult, TestValue
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.params import TestItemParams
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import multiprocessing
|
||||
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.test_items.test_item import TestItem
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import (TestValue)
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from lib.tum_except import ETUMParamError, ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMParamError, ETUMSyntaxError
|
||||
import interpreter.utils.version as git
|
||||
|
||||
class TestItemGit(TestItem):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import (TestResult, TestValue)
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
import libs.testium as tm
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
import api.testium as tm
|
||||
|
||||
class TestItemGroup(TestItem):
|
||||
def __init__(self, dict_cycle, parent = None, status_queue=None, filename=""):
|
||||
|
||||
@@ -4,8 +4,8 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
class TestItemImageDialog(TestItemDialogBase):
|
||||
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
import traceback
|
||||
from random import randint
|
||||
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
from interpreter.test_items.test_item import TestItem, test_run
|
||||
from interpreter.test_items.test_result import TestResult, TestValue
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import socket
|
||||
import re
|
||||
import struct
|
||||
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
import libs.testium as tm
|
||||
from libs.console import Console
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
import api.testium as tm
|
||||
from api.console import Console
|
||||
|
||||
|
||||
def is_ip_address(address):
|
||||
|
||||
@@ -5,8 +5,8 @@ import time
|
||||
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import (TestResult, TestValue)
|
||||
from lib.tum_except import ETUMSyntaxError, item_load_context
|
||||
import libs.testium as tm
|
||||
from runtime.tum_except import ETUMSyntaxError, item_load_context
|
||||
import api.testium as tm
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
|
||||
class TestItemLet(TestItem):
|
||||
|
||||
@@ -4,10 +4,10 @@ import traceback
|
||||
import pprint
|
||||
import textwrap
|
||||
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
from interpreter.test_items.test_item import TestItem, test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.lua_func_exec import LuaFuncExecEngine
|
||||
from interpreter.utils.api_srv import api_request
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
|
||||
@@ -5,7 +5,7 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
|
||||
|
||||
class TestItemMsgDialog(TestItemDialogBase):
|
||||
|
||||
@@ -2,8 +2,8 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
class TestItemNoteDialog(TestItemDialogBase):
|
||||
|
||||
@@ -6,9 +6,9 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestResult, TestValue
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from interpreter.utils.eval import eval_to_boolean
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from lib.string_queue import StringQueue
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
from runtime.string_queue import StringQueue
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
|
||||
|
||||
class TestItemParallelBranch(TestItemContainer):
|
||||
|
||||
@@ -4,10 +4,10 @@ import time
|
||||
import pprint
|
||||
import textwrap
|
||||
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
from interpreter.test_items.test_item import TestItem, test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.py_func_exec import PyFuncExecEngine
|
||||
from interpreter.utils.api_srv import api_request
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
|
||||
@@ -2,7 +2,7 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
|
||||
|
||||
class TestItemQuestionDialog(TestItemDialogBase):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import (TestValue)
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from interpreter.test_report.test_report import Export
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import traceback
|
||||
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import (TestValue)
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
|
||||
|
||||
def nowInBetween(start, end):
|
||||
|
||||
@@ -3,8 +3,8 @@ import importlib
|
||||
import traceback
|
||||
from functools import wraps
|
||||
|
||||
import libs.testium as tm
|
||||
from lib.tum_except import ETUMSyntaxError, item_load_context
|
||||
import api.testium as tm
|
||||
from runtime.tum_except import ETUMSyntaxError, item_load_context
|
||||
from interpreter.test_items.test_item import TestItem, test_run
|
||||
from interpreter.test_items.test_result import TestResult, TestValue
|
||||
from interpreter.test_items.item_actions import TestItemActions
|
||||
@@ -235,4 +235,4 @@ class TestItemPlot(TestItemActions):
|
||||
self.actions_token = self._prms.getParam("plot_name", required=True)
|
||||
|
||||
global runtime_plot
|
||||
runtime_plot = importlib.import_module("libs.runtime_plot")
|
||||
runtime_plot = importlib.import_module("api.runtime_plot")
|
||||
|
||||
@@ -3,11 +3,11 @@ from time import sleep
|
||||
from datetime import timedelta
|
||||
from multiprocessing import Process, Pipe
|
||||
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.test_items.test_item import (TestItem, test_run)
|
||||
from interpreter.test_items.test_result import (TestValue)
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context
|
||||
|
||||
class TestItemSleep(TestItem):
|
||||
"""sleep item usage.
|
||||
|
||||
@@ -2,8 +2,8 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
class TestItemTestedRefsDialog(TestItemDialogBase):
|
||||
|
||||
@@ -4,14 +4,14 @@ from unittest import (TestCase, TestSuite, TextTestRunner,
|
||||
TextTestResult)
|
||||
from unittest.loader import defaultTestLoader
|
||||
|
||||
import libs.testium as tm
|
||||
from lib.tum_except import (ETUMFileError)
|
||||
import api.testium as tm
|
||||
from runtime.tum_except import (ETUMFileError)
|
||||
from interpreter.utils.modules import load_source
|
||||
from interpreter.test_items.test_item import (TestItem, test_run, LOG_TEST_STOP, LOG_TEST_START)
|
||||
from interpreter.test_items.test_result import (TestResult, TestValue)
|
||||
from interpreter.test_items.test_item import test_data
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
|
||||
class UnittestResult(TextTestResult):
|
||||
"""Test result adapted for unittest test"""
|
||||
|
||||
@@ -2,8 +2,8 @@ from interpreter.test_items.test_item import test_run
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
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
|
||||
from runtime.tum_except import item_load_context
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
class TestItemValueDialog(TestItemDialogBase):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from lib.tum_except import (ETUMRuntimeError)
|
||||
from runtime.tum_except import (ETUMRuntimeError)
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import interpreter.test_report.test_report as tr
|
||||
from interpreter.utils.paths import prepare_file_to_save
|
||||
import interpreter.utils.constants as cst
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
class ReportExport:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from junit_xml import (TestSuite, TestCase)
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.test_items.test_result import (TestValue)
|
||||
import interpreter.test_report.report_export as rpe
|
||||
import interpreter.test_report.test_report as tr
|
||||
|
||||
@@ -4,8 +4,8 @@ from functools import wraps
|
||||
import sqlite3
|
||||
from time import (time, sleep)
|
||||
import traceback
|
||||
from lib.tum_except import (ETUMRuntimeError, ETUMSyntaxError)
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
from runtime.tum_except import (ETUMRuntimeError, ETUMSyntaxError)
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
from interpreter.utils.params import (expanse)
|
||||
from interpreter.utils.paths import prepare_file_to_save
|
||||
import interpreter.utils.constants as cst
|
||||
|
||||
@@ -2,13 +2,13 @@ import os
|
||||
import datetime
|
||||
from queue import Queue
|
||||
from interpreter.utils.params import expanse
|
||||
import libs.testium as tm
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
import api.testium as tm
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
import interpreter.utils.settings as prefs
|
||||
from interpreter.test_report.test_report import TestReport
|
||||
from interpreter.utils.py_func_exec import PyFuncExecEngine
|
||||
from interpreter.utils.api_srv import api_request
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from interpreter.utils.constants import TestItemType as cst_type
|
||||
import interpreter.utils.constants as cst
|
||||
from interpreter.utils.constants import TEST_TYPE_LIST
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from lib.api import SUPPORTED_API
|
||||
from runtime.api import SUPPORTED_API
|
||||
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
# Fill the api_dict with the function of tm
|
||||
api_dict = {k: getattr(tm, k) for k in SUPPORTED_API if hasattr(tm, k)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.py_eval import eval_exec
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError
|
||||
|
||||
|
||||
def evaluate(val, **replacement_dict):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import yaml
|
||||
import os.path
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
from interpreter.utils.params import expanse
|
||||
from lib.tum_except import ETUMFileError
|
||||
from runtime.tum_except import ETUMFileError
|
||||
from interpreter.utils.template import template_to_test
|
||||
from copy import copy
|
||||
from interpreter.utils.globdict import global_dict
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
from interpreter.utils.lua_process import LuaProcessBase
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import shutil
|
||||
import subprocess
|
||||
import socket
|
||||
|
||||
import libs.testium as tm
|
||||
from lib.jrpc import JsonRpcClient
|
||||
import api.testium as tm
|
||||
from runtime.jrpc import JsonRpcClient
|
||||
from interpreter.utils.paths import subproc_path
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from interpreter.utils.paths import sys_app_path_lin, sys_app_path_win
|
||||
|
||||
def _lua_version(path: str):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import interpreter.utils.globdict as globdict
|
||||
from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMSyntaxError, ETUMRuntimeError
|
||||
|
||||
glob_eval_func = None
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
import testium
|
||||
from interpreter.utils.params import expanse
|
||||
import subprocess
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
def testium_path():
|
||||
@@ -18,12 +18,9 @@ def testium_path():
|
||||
return str(Path(tp).parent.resolve())
|
||||
|
||||
def subproc_path():
|
||||
if getattr(sys, 'frozen', False):
|
||||
# Exécuté depuis le .exe
|
||||
return sys._MEIPASS
|
||||
|
||||
tp = inspect.getfile(inspect.getmodule(testium))
|
||||
return str(Path(tp).parent.parent.resolve())
|
||||
# py_func and lua_func now live inside the testium package; their cwd
|
||||
# is the testium package root, same as testium_path().
|
||||
return testium_path()
|
||||
|
||||
def prepare_file_to_save(file_name, file_ext=""):
|
||||
iname = file_name
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from interpreter.utils.py_process import PyProcessBase
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
import libs.testium as tm
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
eval_process = None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
from interpreter.utils.py_process import PyProcessBase
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from interpreter.test_items.test_result import TestValue
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import shutil
|
||||
import sys
|
||||
import subprocess
|
||||
import socket
|
||||
from lib.jrpc import JsonRpcClient
|
||||
import libs.testium as tm
|
||||
from runtime.jrpc import JsonRpcClient
|
||||
import api.testium as tm
|
||||
from interpreter.utils.paths import sys_app_path_lin, sys_app_path_win
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from interpreter.utils.paths import testium_path, subproc_path
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import os
|
||||
import configparser
|
||||
import json
|
||||
import platform
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
|
||||
SettingsCompany = 'Testium'
|
||||
SettingsApplication = 'testium'
|
||||
|
||||
@@ -4,7 +4,7 @@ from jinja2 import Template
|
||||
from jinja2.exceptions import TemplateSyntaxError, TemplateError, UndefinedError
|
||||
from tempfile import TemporaryFile
|
||||
from interpreter.utils.yaml_load import print_yaml
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
|
||||
|
||||
def template_to_test(filename: str, params: list):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from multiprocessing import Queue
|
||||
from queue import Empty
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
|
||||
|
||||
class TestSetController:
|
||||
|
||||
@@ -7,13 +7,13 @@ import yaml
|
||||
import copy
|
||||
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
import interpreter.utils.globdict as globdict
|
||||
import interpreter.utils.settings as prefs
|
||||
from interpreter.utils.paths import testium_path
|
||||
from interpreter.utils.yaml_load import yaml_load
|
||||
from interpreter.utils import clear_recursively
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
from interpreter.utils.params import expanse, eval_func_init
|
||||
from interpreter.utils.eval import evaluate
|
||||
from interpreter.utils.version import (
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
from importlib import import_module
|
||||
|
||||
import interpreter.utils.settings as prefs
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
_cached_versions = {}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from yaml.parser import ParserError
|
||||
from yaml import load, Loader
|
||||
from yaml.scanner import ScannerError
|
||||
from libs.testium import print_debug
|
||||
from lib.tum_except import ETUMSyntaxError
|
||||
from api.testium import print_debug
|
||||
from runtime.tum_except import ETUMSyntaxError
|
||||
import io
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ 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
|
||||
from runtime.tum_except import ETUMFileError, ETUMRuntimeError
|
||||
|
||||
|
||||
class TestFileManager:
|
||||
|
||||
@@ -10,12 +10,12 @@ from PySide6.QtGui import (QFont, QFontInfo)
|
||||
from time import (time)
|
||||
|
||||
from main_win.test_tree_items.common import (TEST_COLS, TEST_COLS_WITH_TIME)
|
||||
from lib.tum_except import (ETUMFileError, ETUMSyntaxError)
|
||||
from runtime.tum_except import (ETUMFileError, ETUMSyntaxError)
|
||||
from main_win.test_controller_service import TestControllerService
|
||||
from main_win.test_tree_items.test_tree_item import make_tree_item
|
||||
|
||||
from interpreter.test_items.test_result import (TestValue)
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
import interpreter.utils.settings as prefs
|
||||
from interpreter.utils.constants import TestItemType as cst
|
||||
from interpreter.utils.icons import icon_prefix
|
||||
|
||||
@@ -5,7 +5,7 @@ from PySide6.QtGui import (QIcon, QPixmap, QBrush, QColor)
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (QTreeWidgetItem)
|
||||
from interpreter.utils.icons import icon_prefix
|
||||
from libs.testium import print_warn
|
||||
from api.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
|
||||
|
||||
@@ -30,7 +30,7 @@ from main_win.f1_win.d_f1_win import DialogF1
|
||||
from main_win.test_tree import QTestTree
|
||||
|
||||
from main_win.test_run.thread_output import ThreadTestOutput
|
||||
from lib.string_queue import StringQueue
|
||||
from runtime.string_queue import StringQueue
|
||||
from interpreter.process import TestProcess
|
||||
from interpreter.utils.test_ctrl import TestSetController
|
||||
from interpreter.utils.icons import icon_prefix
|
||||
@@ -38,14 +38,14 @@ from interpreter.utils.icons import icon_prefix
|
||||
from main_win.test_run.outlog import OutLog
|
||||
from main_win.test_run.test_run import ThreadTestStatus
|
||||
import interpreter.utils.settings as prefs
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
import libs.testium as tm
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
import api.testium as tm
|
||||
from interpreter.utils.test_init import (
|
||||
env_init,
|
||||
locate_report_file,
|
||||
)
|
||||
from interpreter.utils.version import get_testium_version
|
||||
from lib.tum_except import ETUMFileError, ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMFileError, ETUMRuntimeError
|
||||
from main_win.test_controller_service import TestControllerService
|
||||
from main_win.test_runner import TestRunner, TestState
|
||||
from main_win.test_file_manager import TestFileManager
|
||||
|
||||
@@ -6,7 +6,7 @@ from PySide6.QtGui import QCursor, QDesktopServices, QFont
|
||||
|
||||
from main_win.text_log_highlighter import TextLogHighlighter
|
||||
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
class QTextLog(QPlainTextEdit):
|
||||
def __init__(self, parent):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
import multiprocessing
|
||||
from py_func.tm import _init_api, _remote_print
|
||||
from lib.stdout_redirect import stdio_redir
|
||||
from runtime.stdout_redirect import stdio_redir
|
||||
|
||||
|
||||
class TcpStdOut:
|
||||
@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
import importlib
|
||||
import traceback
|
||||
|
||||
from lib.tum_except import ETUMRuntimeError, ETUMSyntaxError
|
||||
from runtime.tum_except import ETUMRuntimeError, ETUMSyntaxError
|
||||
from py_func import tm
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import math
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from lib.jrpc import JsonRpcSrv
|
||||
from lib.tum_except import ETUMRuntimeError, print_exception
|
||||
from runtime.jrpc import JsonRpcSrv
|
||||
from runtime.tum_except import ETUMRuntimeError, print_exception
|
||||
import py_func.tm as tm
|
||||
from py_func.func_call import func_exec
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import json
|
||||
import sys
|
||||
from py_func.handle import FuncHandler
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from lib.api import SUPPORTED_API
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
from runtime.api import SUPPORTED_API
|
||||
|
||||
thismodule = sys.modules[__name__]
|
||||
_func_call_thread = None
|
||||
0
src/testium/runtime/__init__.py
Normal file
0
src/testium/runtime/__init__.py
Normal file
@@ -6,11 +6,11 @@ import itertools
|
||||
from time import sleep
|
||||
from typing import Callable, Any
|
||||
try:
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
except:
|
||||
import py_func.tm as tm
|
||||
|
||||
from lib.tum_except import ETUMRuntimeError
|
||||
from runtime.tum_except import ETUMRuntimeError
|
||||
|
||||
"""Lightweight JSON-RPC 2.0 helpers over TCP sockets.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
import threading
|
||||
from threading import (Thread, Event)
|
||||
from lib.string_queue import StringQueue
|
||||
from runtime.string_queue import StringQueue
|
||||
from time import (sleep)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
def RetreiveData(console_name):
|
||||
print("--------------- retrieving data ---------------")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
def RetreiveData(console_name):
|
||||
print("--------------- retrieving data ---------------")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import libs.testium as libtm
|
||||
import api.testium as libtm
|
||||
|
||||
|
||||
def check_os(expected_os):
|
||||
|
||||
65
test/validation/items/isolation/check_isolation.py
Normal file
65
test/validation/items/isolation/check_isolation.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Static check that py_func/ and lua_func/ subprocess code does not depend
|
||||
on testium internals. The contract is:
|
||||
|
||||
py_func/*.py may import: py_func.*, runtime.*, plus stdlib/3rd-party
|
||||
lua_func/*.lua may require: lua_func/<own files>, plus lua stdlib
|
||||
|
||||
Forbidden top-level modules: interpreter, main_win, api, testium.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
|
||||
FORBIDDEN_PY = {"interpreter", "main_win", "api", "testium"}
|
||||
FORBIDDEN_LUA = {"interpreter", "main_win", "api", "testium"}
|
||||
|
||||
|
||||
def _collect_py_imports(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
tree = ast.parse(f.read(), filename=path)
|
||||
out = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
for n in node.names:
|
||||
out.add(n.name.split(".")[0])
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
if node.level == 0 and node.module:
|
||||
out.add(node.module.split(".")[0])
|
||||
return out
|
||||
|
||||
|
||||
def _collect_lua_requires(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
return {m.split(".")[0] for m in re.findall(r'require\s*\(?\s*["\']([^"\']+)["\']', text)}
|
||||
|
||||
|
||||
def check_isolation(testium_dir):
|
||||
failures = []
|
||||
|
||||
py_dir = os.path.join(testium_dir, "py_func")
|
||||
for root, _, files in os.walk(py_dir):
|
||||
for f in files:
|
||||
if not f.endswith(".py"):
|
||||
continue
|
||||
p = os.path.join(root, f)
|
||||
leaks = _collect_py_imports(p) & FORBIDDEN_PY
|
||||
if leaks:
|
||||
failures.append(f"py_func/{os.path.relpath(p, py_dir)} leaks: {sorted(leaks)}")
|
||||
|
||||
lua_dir = os.path.join(testium_dir, "lua_func")
|
||||
for root, _, files in os.walk(lua_dir):
|
||||
for f in files:
|
||||
if not f.endswith(".lua"):
|
||||
continue
|
||||
p = os.path.join(root, f)
|
||||
leaks = _collect_lua_requires(p) & FORBIDDEN_LUA
|
||||
if leaks:
|
||||
failures.append(f"lua_func/{os.path.relpath(p, lua_dir)} leaks: {sorted(leaks)}")
|
||||
|
||||
if failures:
|
||||
for line in failures:
|
||||
print(f" - {line}")
|
||||
return False
|
||||
return True
|
||||
1
test/validation/items/isolation/param.yaml
Normal file
1
test/validation/items/isolation/param.yaml
Normal file
@@ -0,0 +1 @@
|
||||
no_param: Null
|
||||
8
test/validation/items/isolation/test.tum
Normal file
8
test/validation/items/isolation/test.tum
Normal file
@@ -0,0 +1,8 @@
|
||||
- py_func:
|
||||
name: py_func/lua_func do not depend on testium internals
|
||||
file: $(test_path)$(psep)check_isolation.py
|
||||
func_name: check_isolation
|
||||
key: $(test)_PASS
|
||||
param:
|
||||
- $(testium_path)
|
||||
expected_result: True
|
||||
@@ -1,5 +1,5 @@
|
||||
import time
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
def sleep_func(duration):
|
||||
|
||||
Reference in New Issue
Block a user