diff --git a/scripts/pylib/pytest-twister-harness/tests/conftest.py b/scripts/pylib/pytest-twister-harness/tests/conftest.py index ed6b71287be..c221d4d2eba 100755 --- a/scripts/pylib/pytest-twister-harness/tests/conftest.py +++ b/scripts/pylib/pytest-twister-harness/tests/conftest.py @@ -6,14 +6,18 @@ from __future__ import annotations 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,24 @@ 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_path(resources: Path) -> str: + return str(resources / 'shell_simulator.py') + + +@pytest.fixture +def shell_simulator_adapter( + tmp_path: Path, shell_simulator_path: str +) -> Generator[NativeSimulatorAdapter, None, None]: + build_dir = tmp_path / 'build_dir' + os.mkdir(build_dir) + device = NativeSimulatorAdapter(DeviceConfig(build_dir=build_dir, type='native', base_timeout=5.0)) + 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 ab8b4d97d80..6103e2dac01 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 @@ -183,6 +183,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') + 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/device/hardware_adapter_test.py b/scripts/pylib/pytest-twister-harness/tests/device/hardware_adapter_test.py index 24e120fc8ef..5655c4f3f72 100644 --- a/scripts/pylib/pytest-twister-harness/tests/device/hardware_adapter_test.py +++ b/scripts/pylib/pytest-twister-harness/tests/device/hardware_adapter_test.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 import os +import time from pathlib import Path from unittest import mock @@ -198,3 +199,21 @@ def test_if_hardware_adapter_uses_serial_pty( device.disconnect() assert not device._serial_pty_proc + + +def test_if_hardware_adapter_properly_send_data_to_subprocess( + device: HardwareAdapter, shell_simulator_path: str +) -> None: + """ + Run shell_simulator.py under serial_pty, send "zen" command and verify + output. Flashing command is mocked by "dummy" echo command. + """ + device.command = ['echo', 'TEST'] # only to mock flashing command + device.device_config.serial_pty = f'python3 {shell_simulator_path}' + device.launch() + time.sleep(0.1) + device.write(b'zen\n') + time.sleep(1) + lines = device.readlines_until(regex='Namespaces are one honking great idea') + assert 'The Zen of Python, by Tim Peters' in lines + device.write(b'quit\n') 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 00000000000..1bab9380674 --- /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, timeout=5.0) + assert shell.wait_for_prompt() + lines = shell.exec_command('zen') + 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 b1d7f38e00b..da84716a1de 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 threading 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 e009ff6b58b..15076518223 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 sys 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 00000000000..e0a1eb17896 --- /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 00000000000..7416e6ed2dc --- /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')