twister: pytest: Parametrize scope of the dut fixture
Added pytest_dut_scope keyword under harness_config section. New keyword is used to determine the scope of dut and shell fixtures in pytest-twister-harness plugin. Signed-off-by: Grzegorz Chwierut <grzegorz.chwierut@nordicsemi.no>
This commit is contained in:
parent
e57e7f28a9
commit
fffe0b9fad
7 changed files with 57 additions and 7 deletions
|
@ -69,6 +69,9 @@ DUT (initialize logging, flash device, connect serial etc).
|
||||||
This fixture yields a device prepared according to the requested type
|
This fixture yields a device prepared according to the requested type
|
||||||
(native posix, qemu, hardware, etc.). All types of devices share the same API.
|
(native posix, qemu, hardware, etc.). All types of devices share the same API.
|
||||||
This allows for writing tests which are device-type-agnostic.
|
This allows for writing tests which are device-type-agnostic.
|
||||||
|
Scope of this fixture is determined by the ``pytest_dut_scope``
|
||||||
|
keyword placed under ``harness_config`` section.
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -81,8 +84,10 @@ shell
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Provide an object with methods used to interact with shell application.
|
Provide an object with methods used to interact with shell application.
|
||||||
It calls `wait_for_promt` method, to not start scenario until DUT is ready.
|
It calls ``wait_for_promt`` method, to not start scenario until DUT is ready.
|
||||||
Note that it uses `dut` fixture, so `dut` can be skipped when `shell` is used.
|
Note that it uses ``dut`` fixture, so ``dut`` can be skipped when ``shell`` is used.
|
||||||
|
Scope of this fixture is determined by the ``pytest_dut_scope``
|
||||||
|
keyword placed under ``harness_config`` section.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
@ -514,6 +514,11 @@ harness_config: <harness configuration options>
|
||||||
pytest_args: <list of arguments> (default empty)
|
pytest_args: <list of arguments> (default empty)
|
||||||
Specify a list of additional arguments to pass to ``pytest``.
|
Specify a list of additional arguments to pass to ``pytest``.
|
||||||
|
|
||||||
|
pytest_dut_scope: <function|class|module|package|session> (default function)
|
||||||
|
The scope for which ``dut`` and ``shell`` pytest fixtures are shared.
|
||||||
|
If the scope is set to ``function``, DUT is launched for every test case
|
||||||
|
in python script. For ``session`` scope, DUT is launched only once.
|
||||||
|
|
||||||
robot_test_path: <robot file path> (default empty)
|
robot_test_path: <robot file path> (default empty)
|
||||||
Specify a path to a file containing a Robot Framework test suite to be run.
|
Specify a path to a file containing a Robot Framework test suite to be run.
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,16 @@ def device_object(twister_harness_config: TwisterHarnessConfig) -> Generator[Dev
|
||||||
device_object.close()
|
device_object.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
def determine_scope(fixture_name, config):
|
||||||
|
if dut_scope := config.getoption("--dut-scope", None):
|
||||||
|
return dut_scope
|
||||||
|
return 'function'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope=determine_scope)
|
||||||
def dut(request: pytest.FixtureRequest, device_object: DeviceAdapter) -> Generator[DeviceAdapter, None, None]:
|
def dut(request: pytest.FixtureRequest, device_object: DeviceAdapter) -> Generator[DeviceAdapter, None, None]:
|
||||||
"""Return launched device - with run application."""
|
"""Return launched device - with run application."""
|
||||||
test_name = request.node.name
|
device_object.initialize_log_files(request.node.name)
|
||||||
device_object.initialize_log_files(test_name)
|
|
||||||
try:
|
try:
|
||||||
device_object.launch()
|
device_object.launch()
|
||||||
yield device_object
|
yield device_object
|
||||||
|
@ -48,7 +53,7 @@ def dut(request: pytest.FixtureRequest, device_object: DeviceAdapter) -> Generat
|
||||||
device_object.close()
|
device_object.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope=determine_scope)
|
||||||
def shell(dut: DeviceAdapter) -> Shell:
|
def shell(dut: DeviceAdapter) -> Shell:
|
||||||
"""Return ready to use shell interface"""
|
"""Return ready to use shell interface"""
|
||||||
shell = Shell(dut, timeout=20.0)
|
shell = Shell(dut, timeout=20.0)
|
||||||
|
|
|
@ -100,6 +100,11 @@ def pytest_addoption(parser: pytest.Parser):
|
||||||
metavar='PATH',
|
metavar='PATH',
|
||||||
help='Script executed after closing serial connection.'
|
help='Script executed after closing serial connection.'
|
||||||
)
|
)
|
||||||
|
twister_harness_group.addoption(
|
||||||
|
'--dut-scope',
|
||||||
|
choices=('function', 'class', 'module', 'package', 'session'),
|
||||||
|
help='The scope for which `dut` and `shell` fixtures are shared.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pytest_configure(config: pytest.Config):
|
def pytest_configure(config: pytest.Config):
|
||||||
|
|
|
@ -268,6 +268,7 @@ class Pytest(Harness):
|
||||||
config = self.instance.testsuite.harness_config
|
config = self.instance.testsuite.harness_config
|
||||||
pytest_root = config.get('pytest_root', ['pytest']) if config else ['pytest']
|
pytest_root = config.get('pytest_root', ['pytest']) if config else ['pytest']
|
||||||
pytest_args = config.get('pytest_args', []) if config else []
|
pytest_args = config.get('pytest_args', []) if config else []
|
||||||
|
pytest_dut_scope = config.get('pytest_dut_scope', None) if config else None
|
||||||
command = [
|
command = [
|
||||||
'pytest',
|
'pytest',
|
||||||
'--twister-harness',
|
'--twister-harness',
|
||||||
|
@ -281,6 +282,8 @@ class Pytest(Harness):
|
||||||
command.extend([os.path.normpath(os.path.join(
|
command.extend([os.path.normpath(os.path.join(
|
||||||
self.source_dir, os.path.expanduser(os.path.expandvars(src)))) for src in pytest_root])
|
self.source_dir, os.path.expanduser(os.path.expandvars(src)))) for src in pytest_root])
|
||||||
command.extend(pytest_args)
|
command.extend(pytest_args)
|
||||||
|
if pytest_dut_scope:
|
||||||
|
command.append(f'--dut-scope={pytest_dut_scope}')
|
||||||
|
|
||||||
handler: Handler = self.instance.handler
|
handler: Handler = self.instance.handler
|
||||||
|
|
||||||
|
@ -427,7 +430,7 @@ class Pytest(Harness):
|
||||||
self.instance.execution_time = float(elem_ts.get('time'))
|
self.instance.execution_time = float(elem_ts.get('time'))
|
||||||
|
|
||||||
for elem_tc in elem_ts.findall('testcase'):
|
for elem_tc in elem_ts.findall('testcase'):
|
||||||
tc = self.instance.get_case_or_create(f"{self.id}.{elem_tc.get('name')}")
|
tc = self.instance.add_testcase(f"{self.id}.{elem_tc.get('name')}")
|
||||||
tc.duration = float(elem_tc.get('time'))
|
tc.duration = float(elem_tc.get('time'))
|
||||||
elem = elem_tc.find('*')
|
elem = elem_tc.find('*')
|
||||||
if elem is None:
|
if elem is None:
|
||||||
|
|
|
@ -104,6 +104,10 @@ mapping:
|
||||||
required: false
|
required: false
|
||||||
sequence:
|
sequence:
|
||||||
- type: str
|
- type: str
|
||||||
|
"pytest_dut_scope":
|
||||||
|
type: str
|
||||||
|
enum: ["function", "class", "module", "package", "session"]
|
||||||
|
required: false
|
||||||
"regex":
|
"regex":
|
||||||
type: seq
|
type: seq
|
||||||
required: false
|
required: false
|
||||||
|
@ -304,6 +308,10 @@ mapping:
|
||||||
required: false
|
required: false
|
||||||
sequence:
|
sequence:
|
||||||
- type: str
|
- type: str
|
||||||
|
"pytest_dut_scope":
|
||||||
|
type: str
|
||||||
|
enum: ["function", "class", "module", "package", "session"]
|
||||||
|
required: false
|
||||||
"regex":
|
"regex":
|
||||||
type: seq
|
type: seq
|
||||||
required: false
|
required: false
|
||||||
|
|
|
@ -48,6 +48,25 @@ def test_pytest_command(testinstance: TestInstance, device_type):
|
||||||
assert c in command
|
assert c in command
|
||||||
|
|
||||||
|
|
||||||
|
def test_pytest_command_dut_scope(testinstance: TestInstance):
|
||||||
|
pytest_harness = Pytest()
|
||||||
|
dut_scope = 'session'
|
||||||
|
testinstance.testsuite.harness_config['pytest_dut_scope'] = dut_scope
|
||||||
|
pytest_harness.configure(testinstance)
|
||||||
|
command = pytest_harness.generate_command()
|
||||||
|
assert f'--dut-scope={dut_scope}' in command
|
||||||
|
|
||||||
|
|
||||||
|
def test_pytest_command_extra_args(testinstance: TestInstance):
|
||||||
|
pytest_harness = Pytest()
|
||||||
|
pytest_args = ['-k test1', '-m mark1']
|
||||||
|
testinstance.testsuite.harness_config['pytest_args'] = pytest_args
|
||||||
|
pytest_harness.configure(testinstance)
|
||||||
|
command = pytest_harness.generate_command()
|
||||||
|
for c in pytest_args:
|
||||||
|
assert c in command
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('pytest_root', 'expected'),
|
('pytest_root', 'expected'),
|
||||||
[
|
[
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue