From 0e24e1b2f17bb3d4980cd9d4b9a062a0f7401f01 Mon Sep 17 00:00:00 2001 From: Piotr Golyzniak Date: Mon, 7 Aug 2023 11:57:54 +0200 Subject: [PATCH] scripts: pytest: add Shell helper unit tests Add unit tests dedicated for Shell helper sample class. Add shell_simulator_script.py script to simulate shell application behavior. Signed-off-by: Piotr Golyzniak --- .../pytest-twister-harness/tests/conftest.py | 27 ++++++++++++++-- .../tests/device/binary_adapter_test.py | 12 +++++++ .../tests/helpers/shell_test.py | 14 +++++++++ .../{device/data => resources}/fifo_mock.py | 26 ++-------------- .../{device/data => resources}/mock_script.py | 28 ++--------------- .../tests/resources/shell_simulator.py | 31 +++++++++++++++++++ .../tests/resources/zen_of_python.py | 29 +++++++++++++++++ 7 files changed, 115 insertions(+), 52 deletions(-) create mode 100644 scripts/pylib/pytest-twister-harness/tests/helpers/shell_test.py rename scripts/pylib/pytest-twister-harness/tests/{device/data => resources}/fifo_mock.py (68%) rename scripts/pylib/pytest-twister-harness/tests/{device/data => resources}/mock_script.py (55%) create mode 100644 scripts/pylib/pytest-twister-harness/tests/resources/shell_simulator.py create mode 100644 scripts/pylib/pytest-twister-harness/tests/resources/zen_of_python.py diff --git a/scripts/pylib/pytest-twister-harness/tests/conftest.py b/scripts/pylib/pytest-twister-harness/tests/conftest.py index ed6b71287bed8f..c6b32bfe6eb681 100755 --- a/scripts/pylib/pytest-twister-harness/tests/conftest.py +++ b/scripts/pylib/pytest-twister-harness/tests/conftest.py @@ -6,14 +6,18 @@ import os from pathlib import Path +from typing import Generator import pytest +from twister_harness.device.binary_adapter import NativeSimulatorAdapter +from twister_harness.twister_harness_config import DeviceConfig + @pytest.fixture -def resources(request: pytest.FixtureRequest) -> Path: - """Return path to `data` folder""" - return Path(request.module.__file__).parent.joinpath('data') +def resources() -> Path: + """Return path to `resources` folder""" + return Path(__file__).parent.joinpath('resources') @pytest.fixture @@ -30,3 +34,20 @@ def twister_harness(zephyr_base) -> str: """Retrun path to pytest-twister-harness src directory""" pytest_twister_harness_path = str(Path(zephyr_base) / 'scripts' / 'pylib' / 'pytest-twister-harness' / 'src') return pytest_twister_harness_path + + +@pytest.fixture +def shell_simulator_adapter( + tmp_path: Path, resources: Path +) -> Generator[NativeSimulatorAdapter, None, None]: + shell_simulator_path: str = str(resources / 'shell_simulator.py') + build_dir = tmp_path / 'build_dir' + os.mkdir(build_dir) + device = NativeSimulatorAdapter(DeviceConfig(build_dir=build_dir, type='native')) + try: + device.command = ['python3', shell_simulator_path] + device.launch() + yield device + finally: + device.write(b'quit\n') + device.close() diff --git a/scripts/pylib/pytest-twister-harness/tests/device/binary_adapter_test.py b/scripts/pylib/pytest-twister-harness/tests/device/binary_adapter_test.py index 33de2db5620b37..41c2e8ee1c524e 100755 --- a/scripts/pylib/pytest-twister-harness/tests/device/binary_adapter_test.py +++ b/scripts/pylib/pytest-twister-harness/tests/device/binary_adapter_test.py @@ -181,6 +181,18 @@ def test_if_binary_adapter_is_able_to_read_leftovers_after_disconnect_or_close( assert len(device.readlines()) > 0 +def test_if_binary_adapter_properly_send_data_to_subprocess( + shell_simulator_adapter: NativeSimulatorAdapter +) -> None: + """Run shell_simulator.py program, send "zen" command and verify output.""" + device = shell_simulator_adapter + time.sleep(0.1) + device.write(b'zen\n') + time.sleep(1) + lines = device.readlines_until(regex='Namespaces are one honking great idea', timeout=5) + assert 'The Zen of Python, by Tim Peters' in lines + + def test_if_native_binary_adapter_get_command_returns_proper_string(device: NativeSimulatorAdapter) -> None: device.generate_command() assert isinstance(device.command, list) diff --git a/scripts/pylib/pytest-twister-harness/tests/helpers/shell_test.py b/scripts/pylib/pytest-twister-harness/tests/helpers/shell_test.py new file mode 100644 index 00000000000000..c4fe93b005eb0f --- /dev/null +++ b/scripts/pylib/pytest-twister-harness/tests/helpers/shell_test.py @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +from twister_harness.device.binary_adapter import NativeSimulatorAdapter +from twister_harness.helpers.shell import Shell + + +def test_if_shell_helper_properly_send_command(shell_simulator_adapter: NativeSimulatorAdapter) -> None: + """Run shell_simulator.py program, send "zen" command via shell helper and verify output.""" + shell = Shell(shell_simulator_adapter) + assert shell.wait_for_prompt(timeout=5.0) + lines = shell.exec_command('zen', timeout=5.0) + assert 'The Zen of Python, by Tim Peters' in lines diff --git a/scripts/pylib/pytest-twister-harness/tests/device/data/fifo_mock.py b/scripts/pylib/pytest-twister-harness/tests/resources/fifo_mock.py similarity index 68% rename from scripts/pylib/pytest-twister-harness/tests/device/data/fifo_mock.py rename to scripts/pylib/pytest-twister-harness/tests/resources/fifo_mock.py index b1d7f38e00b5b7..da84716a1deec0 100755 --- a/scripts/pylib/pytest-twister-harness/tests/device/data/fifo_mock.py +++ b/scripts/pylib/pytest-twister-harness/tests/resources/fifo_mock.py @@ -9,29 +9,7 @@ import time from argparse import ArgumentParser -content = """ -The Zen of Python, by Tim Peters - -Beautiful is better than ugly. -Explicit is better than implicit. -Simple is better than complex. -Complex is better than complicated. -Flat is better than nested. -Sparse is better than dense. -Readability counts. -Special cases aren't special enough to break the rules. -Although practicality beats purity. -Errors should never pass silently. -Unless explicitly silenced. -In the face of ambiguity, refuse the temptation to guess. -There should be one-- and preferably only one --obvious way to do it. -Although that way may not be obvious at first unless you're Dutch. -Now is better than never. -Although never is often better than *right* now. -If the implementation is hard to explain, it's a bad idea. -If the implementation is easy to explain, it may be a good idea. -Namespaces are one honking great idea -- let's do more of those! -""" +from zen_of_python import zen_of_python class FifoFile: @@ -90,7 +68,7 @@ def main(): logger.info('Start') with FifoFile(write_path, 'wb') as wf, FifoFile(read_path, 'rb'): - for line in content.split('\n'): + for line in zen_of_python: wf.write(f'{line}\n'.encode('utf-8')) time.sleep(1) # give a moment for external programs to collect all outputs return 0 diff --git a/scripts/pylib/pytest-twister-harness/tests/device/data/mock_script.py b/scripts/pylib/pytest-twister-harness/tests/resources/mock_script.py similarity index 55% rename from scripts/pylib/pytest-twister-harness/tests/device/data/mock_script.py rename to scripts/pylib/pytest-twister-harness/tests/resources/mock_script.py index e009ff6b58b1e0..150765182238ce 100755 --- a/scripts/pylib/pytest-twister-harness/tests/device/data/mock_script.py +++ b/scripts/pylib/pytest-twister-harness/tests/resources/mock_script.py @@ -10,29 +10,7 @@ import time from argparse import ArgumentParser -s = """ -The Zen of Python, by Tim Peters - -Beautiful is better than ugly. -Explicit is better than implicit. -Simple is better than complex. -Complex is better than complicated. -Flat is better than nested. -Sparse is better than dense. -Readability counts. -Special cases aren't special enough to break the rules. -Although practicality beats purity. -Errors should never pass silently. -Unless explicitly silenced. -In the face of ambiguity, refuse the temptation to guess. -There should be one-- and preferably only one --obvious way to do it. -Although that way may not be obvious at first unless you're Dutch. -Now is better than never. -Although never is often better than *right* now. -If the implementation is hard to explain, it's a bad idea. -If the implementation is easy to explain, it may be a good idea. -Namespaces are one honking great idea -- let's do more of those! -""" +from zen_of_python import zen_of_python def main() -> int: @@ -50,12 +28,12 @@ def main() -> int: if args.long_sleep: # prints data and wait for certain time - for line in s.split('\n'): + for line in zen_of_python: print(line, flush=True) time.sleep(args.sleep) else: # prints lines with delay - for line in s.split('\n'): + for line in zen_of_python: print(line, flush=True) time.sleep(args.sleep) diff --git a/scripts/pylib/pytest-twister-harness/tests/resources/shell_simulator.py b/scripts/pylib/pytest-twister-harness/tests/resources/shell_simulator.py new file mode 100644 index 00000000000000..e0a1eb1789652c --- /dev/null +++ b/scripts/pylib/pytest-twister-harness/tests/resources/shell_simulator.py @@ -0,0 +1,31 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Simple shell simulator. +""" +import sys + +from zen_of_python import zen_of_python + +PROMPT = 'uart:~$ ' + + +def main() -> int: + print('Start shell simulator', flush=True) + print(PROMPT, end='', flush=True) + for line in sys.stdin: + line = line.strip() + print(line, flush=True) + if line == 'quit': + break + elif line == 'zen': + for zen_line in zen_of_python: + print(zen_line, flush=True) + + print(PROMPT, end='', flush=True) + + +if __name__ == '__main__': + main() diff --git a/scripts/pylib/pytest-twister-harness/tests/resources/zen_of_python.py b/scripts/pylib/pytest-twister-harness/tests/resources/zen_of_python.py new file mode 100644 index 00000000000000..7416e6ed2dc152 --- /dev/null +++ b/scripts/pylib/pytest-twister-harness/tests/resources/zen_of_python.py @@ -0,0 +1,29 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +zen_of_python: list[str] = """ +The Zen of Python, by Tim Peters + +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you're Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those! +""".split('\n')