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
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from twister_harness.device.binary_adapter import NativeSimulatorAdapter
|
||||||
|
from twister_harness.twister_harness_config import DeviceConfig
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def resources(request: pytest.FixtureRequest) -> Path:
|
def resources() -> Path:
|
||||||
"""Return path to `data` folder"""
|
"""Return path to `resources` folder"""
|
||||||
return Path(request.module.__file__).parent.joinpath('data')
|
return Path(__file__).parent.joinpath('resources')
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -30,3 +34,24 @@ def twister_harness(zephyr_base) -> str:
|
||||||
"""Retrun path to pytest-twister-harness src directory"""
|
"""Retrun path to pytest-twister-harness src directory"""
|
||||||
pytest_twister_harness_path = str(Path(zephyr_base) / 'scripts' / 'pylib' / 'pytest-twister-harness' / 'src')
|
pytest_twister_harness_path = str(Path(zephyr_base) / 'scripts' / 'pylib' / 'pytest-twister-harness' / 'src')
|
||||||
return pytest_twister_harness_path
|
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
|
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:
|
def test_if_native_binary_adapter_get_command_returns_proper_string(device: NativeSimulatorAdapter) -> None:
|
||||||
device.generate_command()
|
device.generate_command()
|
||||||
assert isinstance(device.command, list)
|
assert isinstance(device.command, list)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
@ -198,3 +199,21 @@ def test_if_hardware_adapter_uses_serial_pty(
|
||||||
|
|
||||||
device.disconnect()
|
device.disconnect()
|
||||||
assert not device._serial_pty_proc
|
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
|
import time
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
content = """
|
from zen_of_python import zen_of_python
|
||||||
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!
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class FifoFile:
|
class FifoFile:
|
||||||
|
@ -90,7 +68,7 @@ def main():
|
||||||
logger.info('Start')
|
logger.info('Start')
|
||||||
|
|
||||||
with FifoFile(write_path, 'wb') as wf, FifoFile(read_path, 'rb'):
|
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'))
|
wf.write(f'{line}\n'.encode('utf-8'))
|
||||||
time.sleep(1) # give a moment for external programs to collect all outputs
|
time.sleep(1) # give a moment for external programs to collect all outputs
|
||||||
return 0
|
return 0
|
|
@ -10,29 +10,7 @@ import sys
|
||||||
import time
|
import time
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
s = """
|
from zen_of_python import zen_of_python
|
||||||
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!
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
|
@ -50,12 +28,12 @@ def main() -> int:
|
||||||
|
|
||||||
if args.long_sleep:
|
if args.long_sleep:
|
||||||
# prints data and wait for certain time
|
# prints data and wait for certain time
|
||||||
for line in s.split('\n'):
|
for line in zen_of_python:
|
||||||
print(line, flush=True)
|
print(line, flush=True)
|
||||||
time.sleep(args.sleep)
|
time.sleep(args.sleep)
|
||||||
else:
|
else:
|
||||||
# prints lines with delay
|
# prints lines with delay
|
||||||
for line in s.split('\n'):
|
for line in zen_of_python:
|
||||||
print(line, flush=True)
|
print(line, flush=True)
|
||||||
time.sleep(args.sleep)
|
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