Skip to content

Commit

Permalink
scripts: pytest: add Shell helper unit tests
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
gopiotr committed Aug 7, 2023
1 parent 50f68b7 commit 0e24e1b
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 52 deletions.
27 changes: 24 additions & 3 deletions scripts/pylib/pytest-twister-harness/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions scripts/pylib/pytest-twister-harness/tests/helpers/shell_test.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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')

0 comments on commit 0e24e1b

Please sign in to comment.