208 lines
6.9 KiB
Python
208 lines
6.9 KiB
Python
|
|
#!/usr/bin/python
|
||
|
|
|
||
|
|
# Copyright 2021-2024 NXP
|
||
|
|
#
|
||
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import multiprocessing
|
||
|
|
import time
|
||
|
|
from typing import Optional, Generator, cast
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from _pytest.config import Config, Notset
|
||
|
|
from _pytest.config.argparsing import Parser
|
||
|
|
from _pytest.fixtures import FixtureRequest
|
||
|
|
from erpc.arbitrator import TransportArbitrator
|
||
|
|
from erpc.basic_codec import BasicCodec
|
||
|
|
from erpc.client import ClientManager
|
||
|
|
from erpc.server import Service
|
||
|
|
from erpc.simple_server import SimpleServer
|
||
|
|
from erpc.transport import TCPTransport, SerialTransport
|
||
|
|
|
||
|
|
from .test_const.service.erpc_outputs import test_unit_test_common
|
||
|
|
from .test_const.service.erpc_outputs.test_unit_test_common.client import CommonClient
|
||
|
|
from .test_const.service.erpc_outputs.test_unit_test_common.server import CommonService
|
||
|
|
|
||
|
|
|
||
|
|
def pytest_addoption(parser: Parser):
|
||
|
|
parser.addoption("--client", action="store_true", default=False, help="run all combinations")
|
||
|
|
parser.addoption("--server", action="store_true", default=False, help="run all combinations")
|
||
|
|
parser.addoption("--host", action="store", default='localhost', help="run all combinations")
|
||
|
|
parser.addoption("--port", action="store", default='40', help="run all combinations")
|
||
|
|
parser.addoption("--serial", action="store", default=None, help="run all combinations")
|
||
|
|
parser.addoption("--baud", action="store", default='115200', help="run all combinations")
|
||
|
|
|
||
|
|
|
||
|
|
###############################################################################
|
||
|
|
# Classes
|
||
|
|
###############################################################################
|
||
|
|
global_server: SimpleServer | None = None
|
||
|
|
|
||
|
|
|
||
|
|
class TransportFactory:
|
||
|
|
"""
|
||
|
|
Build a concreate transport layer based on pytestconfig.
|
||
|
|
|
||
|
|
This class can be passed to new process and then build new transport layer (concreate transport layer cannot be
|
||
|
|
passed to new process because of thread lock, so we have to pass it this way).
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self, config: Config):
|
||
|
|
self.port = int(cast(int, config.getoption("port")))
|
||
|
|
self.host = config.getoption("host")
|
||
|
|
self.serial = config.getoption("serial")
|
||
|
|
self.baud = int(cast(int, config.getoption("baud")))
|
||
|
|
self.arbitrator = None
|
||
|
|
|
||
|
|
def build_transport(self, is_server: bool,
|
||
|
|
create_arbitrator: bool = False) -> TCPTransport | SerialTransport | TransportArbitrator:
|
||
|
|
if self.serial:
|
||
|
|
transport = SerialTransport(self.serial, self.baud)
|
||
|
|
else:
|
||
|
|
transport = TCPTransport(self.host, self.port, is_server)
|
||
|
|
|
||
|
|
if create_arbitrator:
|
||
|
|
transport = TransportArbitrator(transport, BasicCodec())
|
||
|
|
|
||
|
|
return transport
|
||
|
|
|
||
|
|
|
||
|
|
class CommonTestServiceHandler(test_unit_test_common.interface.ICommon):
|
||
|
|
"""
|
||
|
|
Common handler to stop and quit the server
|
||
|
|
"""
|
||
|
|
|
||
|
|
def quit(self):
|
||
|
|
global global_server
|
||
|
|
if global_server:
|
||
|
|
global_server.stop()
|
||
|
|
|
||
|
|
def getServerAllocated(self):
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
###############################################################################
|
||
|
|
# Fixtures
|
||
|
|
###############################################################################
|
||
|
|
|
||
|
|
@pytest.fixture(scope="session")
|
||
|
|
def config(request: FixtureRequest, pytestconfig: Config) -> Generator[Config, None, None]:
|
||
|
|
device_object = None
|
||
|
|
|
||
|
|
if pytestconfig.getoption("twister_harness", default=False):
|
||
|
|
device_object = request.getfixturevalue('device_object')
|
||
|
|
device_object.launch()
|
||
|
|
device_object.disconnect()
|
||
|
|
time.sleep(1)
|
||
|
|
pytestconfig.option.serial = pytestconfig.getoption("device_serial")
|
||
|
|
pytestconfig.option.baud = pytestconfig.getoption("device_serial_baud")
|
||
|
|
pytestconfig.option.client = True
|
||
|
|
pytestconfig.option.server = False
|
||
|
|
|
||
|
|
yield pytestconfig
|
||
|
|
|
||
|
|
if device_object is not None:
|
||
|
|
device_object.close()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="module")
|
||
|
|
def transport_factory(config: Config):
|
||
|
|
"""
|
||
|
|
Fixture for transport factory
|
||
|
|
:param config: pytest config
|
||
|
|
:return: new transport factory
|
||
|
|
"""
|
||
|
|
return TransportFactory(config)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="module")
|
||
|
|
def client_manager(config: Config, transport_factory: TransportFactory, server: SimpleServer) -> ClientManager:
|
||
|
|
"""
|
||
|
|
Fixture for client manager, that is used by all tests for creating client.
|
||
|
|
:param config: pytestconfig
|
||
|
|
:param transport_factory: TransportFactory to use
|
||
|
|
:param server: Ensure that server is created before client
|
||
|
|
:return: new client manager
|
||
|
|
"""
|
||
|
|
if not config.getoption("client"):
|
||
|
|
pytest.skip("No test's client, skipping.")
|
||
|
|
else:
|
||
|
|
time.sleep(0.1) # Add delay to allow server run
|
||
|
|
return ClientManager(transport_factory.build_transport(False), BasicCodec)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="module")
|
||
|
|
def common_client(client_manager: ClientManager) -> CommonClient:
|
||
|
|
"""
|
||
|
|
This client is used in all tests to stop the server
|
||
|
|
:param client_manager:
|
||
|
|
:return: new CommonClient
|
||
|
|
"""
|
||
|
|
return CommonClient(client_manager)
|
||
|
|
|
||
|
|
|
||
|
|
def run_server(transport_factory: TransportFactory, services: list[Service]) -> SimpleServer:
|
||
|
|
"""
|
||
|
|
Auxiliary function, that create SimpleServer using TransportFactory, add CommonService and services specified by
|
||
|
|
current test and run it.
|
||
|
|
|
||
|
|
Created server is assigned to global_server variable.
|
||
|
|
:param transport_factory: TransportFactory to use
|
||
|
|
:param services: list of services defined by current test
|
||
|
|
:return: new server
|
||
|
|
"""
|
||
|
|
global global_server
|
||
|
|
|
||
|
|
global_server = SimpleServer(transport_factory.build_transport(True), BasicCodec)
|
||
|
|
|
||
|
|
global_server.add_service(CommonService(CommonTestServiceHandler()))
|
||
|
|
|
||
|
|
for service in services:
|
||
|
|
global_server.add_service(service)
|
||
|
|
|
||
|
|
global_server.run()
|
||
|
|
print("Server: started")
|
||
|
|
|
||
|
|
return global_server
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="module", autouse=True)
|
||
|
|
def server(config: Config,
|
||
|
|
transport_factory: TransportFactory,
|
||
|
|
services: list[Service]) -> Generator[SimpleServer | None, None, None]:
|
||
|
|
"""
|
||
|
|
Create server if necessary. If server and client should run together, spawn new process with Server.
|
||
|
|
:param config: pytestconfig
|
||
|
|
:param transport_factory: Transport factory to use or pass to server (new process)
|
||
|
|
:param services: list of services defined by current test
|
||
|
|
:return: new server or None if executed in new process
|
||
|
|
"""
|
||
|
|
global global_server
|
||
|
|
p: Optional[multiprocessing.Process] = None
|
||
|
|
|
||
|
|
# We don't need the server
|
||
|
|
if not config.getoption("server"):
|
||
|
|
yield
|
||
|
|
return
|
||
|
|
|
||
|
|
if config.getoption("server") and config.getoption("client"):
|
||
|
|
# Create server in new process
|
||
|
|
p = multiprocessing.Process(
|
||
|
|
target=run_server,
|
||
|
|
args=(transport_factory, services,)
|
||
|
|
)
|
||
|
|
p.start()
|
||
|
|
else:
|
||
|
|
run_server(transport_factory, services)
|
||
|
|
|
||
|
|
yield global_server
|
||
|
|
|
||
|
|
if config.getoption("server") and config.getoption("client"):
|
||
|
|
assert p is not None
|
||
|
|
# Wait for server's process to stop
|
||
|
|
p.join()
|
||
|
|
|
||
|
|
print("Server: stopped")
|