Skip to content

Commit

Permalink
[nrf fromtree] twister: pytest: Parametrize scope of the dut fixture
Browse files Browse the repository at this point in the history
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 <[email protected]>
(cherry picked from commit fffe0b9)
  • Loading branch information
gchwier authored and PerMac committed Nov 8, 2023
1 parent 4a05f79 commit 8186e5c
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 7 deletions.
9 changes: 7 additions & 2 deletions doc/develop/test/pytest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ DUT (initialize logging, flash device, connect serial etc).
This fixture yields a device prepared according to the requested type
(native posix, qemu, hardware, etc.). All types of devices share the same API.
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
Expand All @@ -81,8 +84,10 @@ shell
-----

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.
Note that it uses `dut` fixture, so `dut` can be skipped when `shell` is used.
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.
Scope of this fixture is determined by the ``pytest_dut_scope``
keyword placed under ``harness_config`` section.

.. code-block:: python
Expand Down
5 changes: 5 additions & 0 deletions doc/develop/test/twister.rst
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@ harness_config: <harness configuration options>
pytest_args: <list of arguments> (default empty)
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)
Specify a path to a file containing a Robot Framework test suite to be run.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,24 @@ def device_object(twister_harness_config: TwisterHarnessConfig) -> Generator[Dev
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]:
"""Return launched device - with run application."""
test_name = request.node.name
device_object.initialize_log_files(test_name)
device_object.initialize_log_files(request.node.name)
try:
device_object.launch()
yield device_object
finally: # to make sure we close all running processes execution
device_object.close()


@pytest.fixture(scope='function')
@pytest.fixture(scope=determine_scope)
def shell(dut: DeviceAdapter) -> Shell:
"""Return ready to use shell interface"""
shell = Shell(dut, timeout=20.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ def pytest_addoption(parser: pytest.Parser):
metavar='PATH',
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):
Expand Down
5 changes: 4 additions & 1 deletion scripts/pylib/twister/twisterlib/harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ def generate_command(self):
config = self.instance.testsuite.harness_config
pytest_root = config.get('pytest_root', ['pytest']) if config else ['pytest']
pytest_args = config.get('pytest_args', []) if config else []
pytest_dut_scope = config.get('pytest_dut_scope', None) if config else None
command = [
'pytest',
'--twister-harness',
Expand All @@ -260,6 +261,8 @@ def generate_command(self):
command.extend([os.path.normpath(os.path.join(
self.source_dir, os.path.expanduser(os.path.expandvars(src)))) for src in pytest_root])
command.extend(pytest_args)
if pytest_dut_scope:
command.append(f'--dut-scope={pytest_dut_scope}')

handler: Handler = self.instance.handler

Expand Down Expand Up @@ -406,7 +409,7 @@ def _parse_report_file(self, report):
self.instance.execution_time = float(elem_ts.get('time'))

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'))
elem = elem_tc.find('*')
if elem is None:
Expand Down
8 changes: 8 additions & 0 deletions scripts/schemas/twister/testsuite-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ mapping:
required: false
sequence:
- type: str
"pytest_dut_scope":
type: str
enum: ["function", "class", "module", "package", "session"]
required: false
"regex":
type: seq
required: false
Expand Down Expand Up @@ -304,6 +308,10 @@ mapping:
required: false
sequence:
- type: str
"pytest_dut_scope":
type: str
enum: ["function", "class", "module", "package", "session"]
required: false
"regex":
type: seq
required: false
Expand Down
19 changes: 19 additions & 0 deletions scripts/tests/twister/pytest_integration/test_harness_pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ def test_pytest_command(testinstance: TestInstance, device_type):
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_root', 'expected'),
[
Expand Down

0 comments on commit 8186e5c

Please sign in to comment.