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 <piotr.golyzniak@nordicsemi.no>
This commit is contained in:
parent
577d421ba7
commit
e326015cda
8 changed files with 138 additions and 52 deletions
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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()
|
|
@ -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')
|
Loading…
Add table
Add a link
Reference in a new issue