added debug features to the lua and python jrpc servers.
Added doc of lua func item
This commit is contained in:
@@ -166,7 +166,7 @@ The test items returning a value are:
|
||||
|
||||
* :ref:`dialog_value test items<sec_dialog_value_test_item>`
|
||||
|
||||
* :ref:`py_func test item<sec_func_item>`
|
||||
* :ref:`py_func test item<sec_py_func_item>`
|
||||
|
||||
* :ref:`dialog_choices test item<sec_dialog_choices_test_item>`
|
||||
|
||||
|
||||
65
doc/manual/sphinx/source/test_items/lua_func_test_item.rst
Normal file
65
doc/manual/sphinx/source/test_items/lua_func_test_item.rst
Normal file
@@ -0,0 +1,65 @@
|
||||
.. _sec_lua_func_item:
|
||||
|
||||
**lua_func** test item
|
||||
============================================================
|
||||
|
||||
The ``lua_func`` test item is used to execute custom lua 5.4 scripts with the given
|
||||
input parameters.
|
||||
|
||||
The ``lua_func`` test item is of the form:
|
||||
|
||||
.. code-block:: lua
|
||||
:caption: ``lua_func`` python function example
|
||||
:name: script_file.lua
|
||||
|
||||
local module = {}
|
||||
|
||||
function module.dummy_func(param1, param2, param4, param4):
|
||||
...
|
||||
return 10
|
||||
|
||||
return module
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: corresponding ``lua_func`` tum extract
|
||||
|
||||
- lua_func:
|
||||
name: lua function test item
|
||||
file: script_file.lua
|
||||
func_name: dummy_func
|
||||
param:
|
||||
- 123
|
||||
- 0.123
|
||||
- True
|
||||
- $(global_dict_key)
|
||||
expected_result: 10
|
||||
|
||||
**Attributes**
|
||||
|
||||
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.
|
||||
* ``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.
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: ``lua_func`` test item example of usage
|
||||
|
||||
- lua_func:
|
||||
name: activity
|
||||
file: script_name.lua
|
||||
func_name: methodName
|
||||
param:
|
||||
- $(my_param)
|
||||
|
||||
The result of the function (after eventual post treatment) is stored in the global
|
||||
variable named ``pfn_<func_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.
|
||||
@@ -1,4 +1,4 @@
|
||||
.. _sec_func_item:
|
||||
.. _sec_py_func_item:
|
||||
|
||||
**py_func** test item
|
||||
============================================================
|
||||
@@ -99,10 +99,10 @@ Beside common test items attributes, py_func item has specific attribute, some o
|
||||
- $(my_param)
|
||||
|
||||
The result of the function (after eventual post treatment) is stored in the global
|
||||
variable named ``fn_<func_name>``
|
||||
variable named ``pfn_<func_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 ``$(fn_function test item)``
|
||||
In the example above, the global variable ``$(pfn_function test item)``
|
||||
would be created at the end of the item execution. It would contain the resulting
|
||||
value of the funcToBeExecuted python function.
|
||||
@@ -142,7 +142,8 @@ library API (see :ref:`helper library<sec_python_helper_library>`)
|
||||
* ``ts_end_<item_name>``: timestamp at the end of test item execution (see :ref:`sec_item_common`),
|
||||
* ``ts_duration_<item_name>``: duration of test item execution in seconds (see :ref:`sec_item_common`),
|
||||
* ``cn_<test_name>``: console test item result (see section :ref:`sec_console_test_item`),
|
||||
* ``fn_<func_name>``: py_func test item result (see section :ref:`sec_func_item`),
|
||||
* ``pfn_<func_name>``: py_func test item result (see section :ref:`sec_py_func_item`),
|
||||
* ``lfn_<func_name>``: lua_func test item result (see section :ref:`sec_lua_func_item`),
|
||||
* ``cs_<test_name>``: dialog_choices test item result (see section :ref:`sec_dialog_choices_test_item`),
|
||||
* ``loop_param``: loop iterator (available from within a loop item,
|
||||
see :ref:`sec_loop_item`),
|
||||
@@ -237,6 +238,7 @@ step list attributes.
|
||||
test_items/json-rpc_test_item.rst
|
||||
test_items/let_test_item.rst
|
||||
test_items/loop_test_item.rst
|
||||
test_items/lua_func_test_item.rst
|
||||
test_items/plot_test_item.rst
|
||||
test_items/report_test_item.rst
|
||||
test_items/run_test_item.rst
|
||||
|
||||
Binary file not shown.
@@ -8,7 +8,7 @@ from interpreter.utils.eval import evaluate
|
||||
|
||||
class TestItemCheckValue(TestItem):
|
||||
"""check item usage.
|
||||
check usage:{check: {name: check my func output, steps: ['$(fn_echo) < 5']}}
|
||||
check usage:{check: {name: check my func output, steps: ['$(pfn_echo) < 5']}}
|
||||
"""
|
||||
def __init__(self, dict_item, parent = None, status_queue=None, filename=""):
|
||||
self._name = cst.TYPE_CHECK.item_name
|
||||
|
||||
@@ -50,6 +50,7 @@ class TestItemLuaFunc(TestItem):
|
||||
if success == TestValue.SUCCESS:
|
||||
self.result.set(TestValue.SUCCESS)
|
||||
res, reported_values = ret
|
||||
print(res)
|
||||
reported_values = {**reported_values, "returned": res}
|
||||
self.result.reported = ret[1]
|
||||
|
||||
@@ -58,7 +59,7 @@ class TestItemLuaFunc(TestItem):
|
||||
tm.print_debug(textwrap.indent(pprint.pformat(res), " |"))
|
||||
|
||||
# The result of the func test item is put in global dir and result
|
||||
tm.setgd("fn_" + self._name, res)
|
||||
tm.setgd("lfn_" + self._name, res)
|
||||
self.result.value = res
|
||||
|
||||
else:
|
||||
|
||||
@@ -58,7 +58,7 @@ class TestItemPyFunc(TestItem):
|
||||
tm.print_debug(textwrap.indent(pprint.pformat(res), " |"))
|
||||
|
||||
# The result of the func test item is put in global dir and result
|
||||
tm.setgd("fn_" + self._name, res)
|
||||
tm.setgd("pfn_" + self._name, res)
|
||||
self.result.value = res
|
||||
|
||||
else:
|
||||
|
||||
@@ -38,7 +38,7 @@ Usage example (server):
|
||||
|
||||
Usage example (client):
|
||||
|
||||
clt = JsonRpcClient(port)
|
||||
clt = JsonRpcClient(host, port)
|
||||
clt.start()
|
||||
result = clt.call('method_name', {'foo': 'bar'})
|
||||
|
||||
@@ -236,8 +236,9 @@ class JsonRpcBase(threading.Thread):
|
||||
- `call()` raises `ETUMRuntimeError` if no active connection exists.
|
||||
"""
|
||||
|
||||
def __init__(self, port, req_handler: Callable[[dict], Any]=None, timeout=10, dbg_out=None):
|
||||
def __init__(self, host, port, req_handler: Callable[[dict], Any]=None, timeout=10, dbg_out=None):
|
||||
super().__init__()
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._timeout = timeout
|
||||
self._rpc = None
|
||||
@@ -306,8 +307,8 @@ class JsonRpcSrv(JsonRpcBase):
|
||||
The server will raise `ETUMRuntimeError` on accept/connect timeout.
|
||||
"""
|
||||
|
||||
def __init__(self, port, req_handler = None, timeout=10):
|
||||
super().__init__(port, req_handler, timeout)
|
||||
def __init__(self, host, port, req_handler = None, timeout=10):
|
||||
super().__init__(host, port, req_handler, timeout)
|
||||
self.name = f"JsonRpcSvr_{port}"
|
||||
|
||||
def run(self):
|
||||
@@ -318,12 +319,13 @@ class JsonRpcSrv(JsonRpcBase):
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
# Link of the socket at the configured port
|
||||
sock.bind(("localhost", self._port))
|
||||
sock.bind((self._host, self._port))
|
||||
|
||||
sock.settimeout(self._timeout)
|
||||
|
||||
# Listens incoming connections
|
||||
sock.listen(1)
|
||||
self.print_info(f"listening on {self._host}:{self._port}")
|
||||
|
||||
self.print_info("awaiting connection")
|
||||
tslice = 0.2
|
||||
@@ -331,6 +333,7 @@ class JsonRpcSrv(JsonRpcBase):
|
||||
while True:
|
||||
try:
|
||||
conn, addr = sock.accept()
|
||||
self.print_info("Client connected")
|
||||
except socket.timeout:
|
||||
if t >= 0:
|
||||
sleep(tslice)
|
||||
@@ -364,13 +367,13 @@ class JsonRpcClient(JsonRpcBase):
|
||||
|
||||
Typical usage::
|
||||
|
||||
clt = JsonRpcClient(port)
|
||||
clt = JsonRpcClient(host, port)
|
||||
clt.start()
|
||||
resp = clt.call('method', {'a': 1})
|
||||
"""
|
||||
|
||||
def __init__(self, port, req_handler = None, timeout=10):
|
||||
super().__init__(port, req_handler, timeout)
|
||||
def __init__(self, host, port, req_handler = None, timeout=10):
|
||||
super().__init__(host, port, req_handler, timeout)
|
||||
self.name = f"JsonRpcClt_{port}"
|
||||
|
||||
def run(self):
|
||||
@@ -383,7 +386,7 @@ class JsonRpcClient(JsonRpcBase):
|
||||
t = self._timeout
|
||||
while True:
|
||||
try:
|
||||
sock.connect(("localhost", self._port))
|
||||
sock.connect((self._host, self._port))
|
||||
except OSError:
|
||||
t -= tslice
|
||||
if t >= 0:
|
||||
|
||||
@@ -75,15 +75,20 @@ class LuaFuncExecEngine:
|
||||
func_proc_path = os.path.join(tm.gd("testium_path"),"lua_func")
|
||||
lua_env = tm.gd("lua_env", {})
|
||||
|
||||
params = [self._lpath, "main.lua", "--host", "127.0.0.1", "--port", f"{self._port}"]
|
||||
|
||||
if tm.debug_enabled():
|
||||
params.append("--verbose")
|
||||
|
||||
self._process = subprocess.Popen(
|
||||
[self._lpath, "main.lua", "--host", "127.0.0.1", "--port", f"{self._port}"], env=lua_env, cwd=func_proc_path
|
||||
params, env=lua_env, cwd=func_proc_path
|
||||
)
|
||||
|
||||
# Port was reserved until the sub-process is started. Now released.
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
|
||||
self._rpc = JsonRpcClient(self._port, req_handler=self._req_handler)
|
||||
self._rpc = JsonRpcClient("localhost", self._port, req_handler=self._req_handler)
|
||||
self._rpc.start()
|
||||
|
||||
def join(self):
|
||||
|
||||
@@ -78,15 +78,19 @@ class PyFuncExecEngine:
|
||||
|
||||
func_proc_path = tm.gd("testium_path")
|
||||
|
||||
params = [self._ppath, "-m", "py_func", "-p", f"{self._port}"]
|
||||
if tm.debug_enabled():
|
||||
params.append("-v")
|
||||
|
||||
self._process = subprocess.Popen(
|
||||
[self._ppath, "-m", "py_func", "-p", f"{self._port}"], cwd=func_proc_path
|
||||
params, cwd=func_proc_path
|
||||
)
|
||||
|
||||
# Port was reserved until the sub-process is started. Now released.
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
|
||||
self._rpc = JsonRpcClient(self._port, req_handler=self._req_handler)
|
||||
self._rpc = JsonRpcClient("localhost", self._port, req_handler=self._req_handler)
|
||||
self._rpc.start()
|
||||
|
||||
def join(self):
|
||||
|
||||
@@ -27,7 +27,7 @@ local function _get_func_by_path(file_path, func_name)
|
||||
|
||||
local target_func = module[func_name]
|
||||
if type(target_func) ~= "function" then
|
||||
return nil, "Function '" .. func_name .. "' not found in " .. file_path
|
||||
return nil, "Function '" .. func_name .. "' not found in '" .. file_path .. "'"
|
||||
end
|
||||
|
||||
return target_func
|
||||
@@ -52,9 +52,10 @@ function handle.func_call(params)
|
||||
|
||||
-- 3. Execute the function
|
||||
if err == nil then
|
||||
print(string.format("Function executed from '%s'", pfile))
|
||||
utils.log("func_call function found '%s', '%s'", file, fname)
|
||||
succ, ret = pcall(func, table.unpack(prms))
|
||||
utils.log("func_call returned '%s'", tostring(ret))
|
||||
utils.log("func_call returned '%s', '%s'", tostring(succ), tostring(ret))
|
||||
|
||||
if succ then
|
||||
res = ret
|
||||
|
||||
@@ -6,9 +6,9 @@ JSONRPC.__index = JSONRPC
|
||||
|
||||
function JSONRPC.new(sock)
|
||||
local self = setmetatable({}, JSONRPC)
|
||||
self.sock = sock -- Function to transmit string data to transport (TCP/Websocket)
|
||||
self.methods = {} -- Methods the server provides to the client
|
||||
self.pending = {} -- Requests sent to client waiting for response
|
||||
self.sock = sock -- Function to transmit string data to transport (TCP/Websocket)
|
||||
self.methods = {} -- Methods the server provides to the client
|
||||
self.pending = {} -- Requests sent to client waiting for response
|
||||
self.next_id = 1
|
||||
|
||||
self.sock:settimeout(0.2)
|
||||
@@ -24,7 +24,7 @@ end
|
||||
function JSONRPC:handle_message(raw_data)
|
||||
utils.log("received: '%s'", raw_data)
|
||||
local ok, msg = pcall(json.decode, raw_data)
|
||||
if not ok then return self:_send_error(nil, -32700, "Parse error") end
|
||||
if not ok then return self:_send_error(nil, "received message parse error in lua server") end
|
||||
|
||||
-- 1. Check if it's a Response (has 'result' or 'error' and 'id')
|
||||
if (msg.id ~= nil) and (msg.result ~= nil or msg.error ~= nil) then
|
||||
@@ -44,37 +44,27 @@ function JSONRPC:_handle_request(req)
|
||||
local ok, ret
|
||||
local res, err
|
||||
if not method then
|
||||
if req.id then self:_send_error(req.id, -32601, "Method not found") end
|
||||
if req.id then self:_send_error(req.id, string.format("Method '%s' not registered in lua server")) end
|
||||
return
|
||||
end
|
||||
utils.log("calling '%s'", method)
|
||||
ok, ret = pcall(method, req.params)
|
||||
utils.log("returned '%s', '%s'", tostring(ok), tostring(res))
|
||||
ok, ret, err = pcall(method, req.params)
|
||||
utils.log("Call returned ok='%s', ret='%s'", tostring(ok), tostring(ret))
|
||||
|
||||
-- Only send response if it's not a Notification (notifications have no ID)
|
||||
if req.id then
|
||||
if ok then
|
||||
res, err = ret
|
||||
res = ret
|
||||
if res == nil then
|
||||
self:_send_error(req.id, -32603, "Internal error: " .. tostring(err))
|
||||
self:_send_error(req.id, tostring(err))
|
||||
else
|
||||
self:_send({ jsonrpc = "2.0", result = {returned = res}, id = req.id })
|
||||
self:_send({ jsonrpc = "2.0", result = { returned_value = res }, id = req.id })
|
||||
end
|
||||
else
|
||||
self:_send_error(req.id, -32603, "Internal error: " .. tostring(ret))
|
||||
self:_send_error(req.id, tostring(err))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- INTERNAL: Handle responses to requests WE sent
|
||||
-- function JSONRPC:_handle_response(res)
|
||||
-- local callback = self.pending[res.id]
|
||||
-- if callback then
|
||||
-- callback(res.error, res.result)
|
||||
-- self.pending[res.id] = nil
|
||||
-- end
|
||||
-- end
|
||||
|
||||
--- Call a method on the client
|
||||
function JSONRPC:call(method, params)
|
||||
local id = self.next_id
|
||||
@@ -109,10 +99,10 @@ function JSONRPC:_send(data)
|
||||
return self.sock:send(j .. "\n")
|
||||
end
|
||||
|
||||
function JSONRPC:_send_error(id, code, message)
|
||||
function JSONRPC:_send_error(id, message)
|
||||
self:_send({
|
||||
jsonrpc = "2.0",
|
||||
error = { code = code, message = message },
|
||||
error = message,
|
||||
id = id
|
||||
})
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ local config = {
|
||||
host = "0.0.0.0",
|
||||
port = 9000,
|
||||
timeout = 60,
|
||||
verbose = true,
|
||||
verbose = false,
|
||||
}
|
||||
|
||||
local function usage()
|
||||
|
||||
@@ -22,15 +22,19 @@ def main():
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-i", "--ip", type=str, help="Ip address or hostname to listen to",
|
||||
default="localhost")
|
||||
parser.add_argument("-p", "--port", type=int, help="port to listen to",
|
||||
default="/etc/jsonrpc-echo.conf")
|
||||
default=9000)
|
||||
parser.add_argument("-v", "--verbose", action='store_true', help="port to listen to")
|
||||
args = parser.parse_args()
|
||||
|
||||
thrd_api = _init_api(args.port)
|
||||
thrd_api = _init_api(args.ip, args.port)
|
||||
outstream = TcpStdOut()
|
||||
stdio_redir.redirect(outstream)
|
||||
# debug the server
|
||||
# thrd_api.dbg_out = stdio_redir.ini_stdout
|
||||
if args.verbose:
|
||||
thrd_api.dbg_out = stdio_redir.ini_stdout
|
||||
try:
|
||||
while thrd_api.is_alive():
|
||||
thrd_api.join(1)
|
||||
|
||||
@@ -46,7 +46,7 @@ def func_exec(file: str, func_name: str, params: list, verbose: bool=True):
|
||||
reported_values = {}
|
||||
mod = func_module(file)
|
||||
if verbose:
|
||||
print("Function executed from {}".format(
|
||||
print("Function executed from '{}'".format(
|
||||
inspect.getabsfile(mod)))
|
||||
|
||||
# check of the FunctionItem descendants
|
||||
|
||||
@@ -49,7 +49,7 @@ def _make_api(name):
|
||||
for k in SUPPORTED_API:
|
||||
setattr(thismodule, k, _make_api(k))
|
||||
|
||||
def _init_api(port):
|
||||
def _init_api(host, port):
|
||||
"""Start and initialize the remote function handler.
|
||||
|
||||
Starts a ``FuncHandler`` bound to ``port``, runs it and blocks until
|
||||
@@ -63,7 +63,7 @@ def _init_api(port):
|
||||
``_func_call_thread``.
|
||||
"""
|
||||
global _func_call_thread
|
||||
_func_call_thread = FuncHandler(port)
|
||||
_func_call_thread = FuncHandler(host, port)
|
||||
_func_call_thread.start()
|
||||
_func_call_thread.wait_ready()
|
||||
return _func_call_thread
|
||||
|
||||
Reference in New Issue
Block a user