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>
219 lines
8.4 KiB
Python
219 lines
8.4 KiB
Python
# Copyright (c) 2023 Nordic Semiconductor ASA
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from twister_harness.device.hardware_adapter import HardwareAdapter
|
|
from twister_harness.exceptions import TwisterHarnessException
|
|
from twister_harness.twister_harness_config import DeviceConfig
|
|
|
|
|
|
@pytest.fixture(name='device')
|
|
def fixture_adapter(tmp_path) -> HardwareAdapter:
|
|
build_dir = tmp_path / 'build_dir'
|
|
os.mkdir(build_dir)
|
|
device_config = DeviceConfig(
|
|
type='hardware',
|
|
build_dir=build_dir,
|
|
runner='runner',
|
|
platform='platform',
|
|
id='p_id',
|
|
base_timeout=5.0,
|
|
)
|
|
return HardwareAdapter(device_config)
|
|
|
|
|
|
@mock.patch('shutil.which', return_value=None)
|
|
def test_if_hardware_adapter_raise_exception_when_west_not_found(patched_which, device: HardwareAdapter) -> None:
|
|
with pytest.raises(TwisterHarnessException, match='west not found'):
|
|
device.generate_command()
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_1(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == ['west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'runner']
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_2(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'pyocd'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'pyocd', '--', '--board-id', 'p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_3(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'nrfjprog'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'nrfjprog', '--', '--dev-id', 'p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_4(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'openocd'
|
|
device.device_config.product = 'STM32 STLink'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'openocd',
|
|
'--', '--cmd-pre-init', 'hla_serial p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_5(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'openocd'
|
|
device.device_config.product = 'EDBG CMSIS-DAP'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'openocd',
|
|
'--', '--cmd-pre-init', 'cmsis_dap_serial p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_6(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'jlink'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'jlink',
|
|
'--tool-opt=-SelectEmuBySN p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_7(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'stm32cubeprogrammer'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'stm32cubeprogrammer',
|
|
'--tool-opt=sn=p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_8(patched_which, device: HardwareAdapter) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.runner = 'openocd'
|
|
device.device_config.product = 'STLINK-V3'
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build',
|
|
'--runner', 'openocd', '--', '--cmd-pre-init', 'hla_serial p_id'
|
|
]
|
|
|
|
|
|
@mock.patch('shutil.which', return_value='west')
|
|
def test_if_get_command_returns_proper_string_with_west_flash_extra_args(
|
|
patched_which, device: HardwareAdapter
|
|
) -> None:
|
|
device.device_config.build_dir = Path('build')
|
|
device.device_config.west_flash_extra_args = ['--board-id=foobar', '--erase']
|
|
device.device_config.runner = 'pyocd'
|
|
device.device_config.id = ''
|
|
device.generate_command()
|
|
assert isinstance(device.command, list)
|
|
assert device.command == [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', 'build', '--runner', 'pyocd',
|
|
'--', '--board-id=foobar', '--erase'
|
|
]
|
|
|
|
|
|
def test_if_hardware_adapter_raises_exception_empty_command(device: HardwareAdapter) -> None:
|
|
device.command = []
|
|
exception_msg = 'Flash command is empty, please verify if it was generated properly.'
|
|
with pytest.raises(TwisterHarnessException, match=exception_msg):
|
|
device._flash_and_run()
|
|
|
|
|
|
@mock.patch('twister_harness.device.hardware_adapter.subprocess.Popen')
|
|
def test_device_log_correct_error_handle(patched_popen, device: HardwareAdapter, tmp_path: Path) -> None:
|
|
popen_mock = mock.Mock()
|
|
popen_mock.communicate.return_value = (b'flashing error', b'')
|
|
patched_popen.return_value = popen_mock
|
|
device.device_config.build_dir = tmp_path
|
|
device.command = [
|
|
'west', 'flash', '--skip-rebuild', '--build-dir', str(tmp_path),
|
|
'--runner', 'nrfjprog', '--', '--dev-id', 'p_id'
|
|
]
|
|
with pytest.raises(expected_exception=TwisterHarnessException, match='Could not flash device p_id'):
|
|
device._flash_and_run()
|
|
assert os.path.isfile(device.device_log_path)
|
|
with open(device.device_log_path, 'r') as file:
|
|
assert 'flashing error' in file.readlines()
|
|
|
|
|
|
@mock.patch('twister_harness.device.hardware_adapter.subprocess.Popen')
|
|
@mock.patch('twister_harness.device.hardware_adapter.serial.Serial')
|
|
def test_if_hardware_adapter_uses_serial_pty(
|
|
patched_serial, patched_popen, device: HardwareAdapter, monkeypatch: pytest.MonkeyPatch
|
|
):
|
|
device.device_config.serial_pty = 'script.py'
|
|
|
|
popen_mock = mock.Mock()
|
|
popen_mock.communicate.return_value = (b'output', b'error')
|
|
patched_popen.return_value = popen_mock
|
|
|
|
monkeypatch.setattr('twister_harness.device.hardware_adapter.pty.openpty', lambda: (123, 456))
|
|
monkeypatch.setattr('twister_harness.device.hardware_adapter.os.ttyname', lambda x: f'/pty/ttytest/{x}')
|
|
|
|
serial_mock = mock.Mock()
|
|
serial_mock.port = '/pty/ttytest/456'
|
|
patched_serial.return_value = serial_mock
|
|
|
|
device._device_run.set()
|
|
device.connect()
|
|
assert device._serial_connection.port == '/pty/ttytest/456' # type: ignore[union-attr]
|
|
assert device._serial_pty_proc
|
|
patched_popen.assert_called_with(
|
|
['script.py'],
|
|
stdout=123,
|
|
stdin=123,
|
|
stderr=123
|
|
)
|
|
|
|
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')
|