Add context_id to py_func and lua_func for shared persistent subprocess
- py_func and lua_func items accept a context_id parameter; items sharing the same id reuse the same subprocess for the duration of the test run - Subprocess-side tm.setgd/tm.gd use a local fallback dict for non-JSON- serializable values (py_func only); serializable values reach the main process global dict and are accessible from any test item or subprocess - Shared subprocess engines are cleaned up in process.py finally block - LuaProcessBase gains is_alive() (was missing, broke all lua_func items) - Validation tests cover serializable sharing across different context ids, non-serializable sharing within the same context_id, and cross-item access - RST documentation updated for both py_func and lua_func items Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,11 +39,14 @@ The ``lua_func`` test item is of the form:
|
||||
Beside common test items attributes, lua_func item has specific attribute, some of which being mandatory.
|
||||
|
||||
* ``file``: the script file name that contains the function to be executed.
|
||||
Only python script format is supported.
|
||||
Only Lua script format is supported.
|
||||
* ``func_name``: The function name to be executed.
|
||||
* ``param``: This is a list of parameters that are passed to the function
|
||||
in the order they are presented in the script. These parameters are not
|
||||
mandatory and are highly dependent of the function prototype.
|
||||
* ``context_id``: Optional. When set, all ``lua_func`` items sharing the same
|
||||
``context_id`` value run inside the same persistent Lua subprocess for the
|
||||
duration of the test. See :ref:`lua_func context<sec_lua_func_context>` for details.
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: ``lua_func`` test item example of usage
|
||||
@@ -56,16 +59,71 @@ Beside common test items attributes, lua_func item has specific attribute, some
|
||||
- $(my_param)
|
||||
|
||||
The result of the function (after eventual post treatment) is stored in the global
|
||||
variable named ``pfn_<func_name>``
|
||||
variable named ``lfn_<item_name>``
|
||||
(See :ref:`global variables<sec_global_variables>` for more detail
|
||||
on how to access to global variables from test items and scripts).
|
||||
|
||||
In the example above, the global variable ``$(lfn_activity)``
|
||||
would be created at the end of the item execution. It would contain the resulting
|
||||
value of the funcToBeExecuted python function.
|
||||
value of the methodName Lua function.
|
||||
|
||||
The ``lua_func`` will always result ``PASS``, except if the called function raises
|
||||
and exception or if the ``expected_result`` attribute is used.
|
||||
an exception or if the ``expected_result`` attribute is used.
|
||||
|
||||
.. _sec_lua_func_context:
|
||||
|
||||
Sharing state between ``lua_func`` calls
|
||||
------------------------------------------
|
||||
|
||||
Each ``lua_func`` item without a ``context_id`` runs in a dedicated subprocess that
|
||||
is started and stopped around the call. Module-level variables are not preserved
|
||||
between two such items.
|
||||
|
||||
Inside a ``lua_func`` script, the ``tm`` module exposes ``tm.setgd`` and ``tm.gd``
|
||||
to read and write the testium global dictionary of the test process. Values stored
|
||||
this way are accessible from any subsequent test item without requiring a shared
|
||||
subprocess.
|
||||
|
||||
.. code-block:: lua
|
||||
:caption: sharing a value via the global dictionary
|
||||
|
||||
local tm = require("tm")
|
||||
local module = {}
|
||||
|
||||
function module.produce(val)
|
||||
tm.setgd("my_shared_value", val)
|
||||
return val
|
||||
end
|
||||
|
||||
function module.consume()
|
||||
return tm.gd("my_shared_value")
|
||||
end
|
||||
|
||||
return module
|
||||
|
||||
When ``context_id`` is set, all ``lua_func`` items that share the same identifier
|
||||
reuse the same persistent subprocess. This allows Lua-side state (upvalues, module
|
||||
cache) to be retained across calls beyond what ``tm.setgd`` persists.
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: ``lua_func`` items sharing a persistent subprocess
|
||||
|
||||
- lua_func:
|
||||
name: produce value
|
||||
file: my_script.lua
|
||||
func_name: produce
|
||||
context_id: my_context
|
||||
param:
|
||||
- hello
|
||||
|
||||
- lua_func:
|
||||
name: consume value
|
||||
file: my_script.lua
|
||||
func_name: consume
|
||||
context_id: my_context
|
||||
expected_result: hello
|
||||
|
||||
The shared subprocess is automatically stopped at the end of the test run.
|
||||
|
||||
**Lua Interpreter environment setup**
|
||||
|
||||
|
||||
@@ -89,6 +89,9 @@ some of which being mandatory.
|
||||
* ``param``: This is a list of parameters that are passed to the function
|
||||
in the order they are presented in the script. These parameters are not
|
||||
mandatory and are highly dependent of the function prototype.
|
||||
* ``context_id``: Optional. When set, all ``py_func`` items sharing the same
|
||||
``context_id`` value run inside the same persistent Python subprocess for the
|
||||
duration of the test. See :ref:`py_func context<sec_py_func_context>` for details.
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: ``py_func`` test item example of usage
|
||||
@@ -111,6 +114,86 @@ value of the funcToBeExecuted python function.
|
||||
The ``py_func`` will always result ``PASS``, except if the called function raises
|
||||
and exception or if the ``expected_result`` attribute is used.
|
||||
|
||||
.. _sec_py_func_context:
|
||||
|
||||
Sharing state between ``py_func`` calls
|
||||
------------------------------------------
|
||||
|
||||
Each ``py_func`` item without a ``context_id`` runs in a dedicated subprocess that
|
||||
is started and stopped around the call. State cannot be shared between two such
|
||||
items using module-level variables.
|
||||
|
||||
Two mechanisms are available to share data across calls:
|
||||
|
||||
**Using the testium global dictionary**
|
||||
|
||||
Inside a ``py_func`` script, the ``tm`` module exposes ``tm.setgd`` and ``tm.gd``
|
||||
to read and write the testium global dictionary of the test process. Values stored
|
||||
this way are accessible from any subsequent test item (including other ``py_func``
|
||||
items) without requiring a shared subprocess.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: sharing a serializable value via the global dictionary
|
||||
|
||||
import py_func.tm as tm
|
||||
|
||||
def produce(val):
|
||||
tm.setgd("my_shared_value", val)
|
||||
return val
|
||||
|
||||
def consume():
|
||||
return tm.gd("my_shared_value", None)
|
||||
|
||||
Values stored with ``tm.setgd`` must be JSON-serializable (str, int, float, list,
|
||||
dict, bool, None). Non-serializable values (objects, connections, file handles…)
|
||||
are handled transparently by the local fallback described below.
|
||||
|
||||
**Using a shared persistent subprocess (``context_id``)**
|
||||
|
||||
When ``context_id`` is set, all ``py_func`` items that share the same identifier
|
||||
reuse the same subprocess. The subprocess is kept alive until the end of the test.
|
||||
|
||||
This is required for non-JSON-serializable objects (e.g. a socket connection, a
|
||||
device handle). Calling ``tm.setgd`` with such a value stores it inside the
|
||||
subprocess local dictionary instead of sending it to the main process. It can then
|
||||
be retrieved with ``tm.gd`` from any subsequent call that runs in the same subprocess.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: sharing a non-serializable object via ``context_id``
|
||||
|
||||
import py_func.tm as tm
|
||||
|
||||
class _Connection: # not JSON-serializable
|
||||
def __init__(self):
|
||||
self.value = "open"
|
||||
|
||||
def open_connection():
|
||||
tm.setgd("conn", _Connection()) # stored locally in the subprocess
|
||||
return "ok"
|
||||
|
||||
def use_connection():
|
||||
conn = tm.gd("conn") # retrieved from the subprocess local dict
|
||||
return conn.value
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: ``py_func`` items sharing a persistent subprocess
|
||||
|
||||
- py_func:
|
||||
name: open connection
|
||||
file: my_script.py
|
||||
func_name: open_connection
|
||||
context_id: my_context
|
||||
expected_result: ok
|
||||
|
||||
- py_func:
|
||||
name: use connection
|
||||
file: my_script.py
|
||||
func_name: use_connection
|
||||
context_id: my_context
|
||||
expected_result: open
|
||||
|
||||
The shared subprocess is automatically stopped at the end of the test run.
|
||||
|
||||
**Python Interpreter environment setup**
|
||||
|
||||
Some global variables have an impact on the ``py_func`` test item behavior:
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user