zephyr/scripts/west_commands/tests/test_pyocd.py

248 lines
8.6 KiB
Python

# Copyright (c) 2018 Foundries.io
#
# SPDX-License-Identifier: Apache-2.0
import argparse
import itertools
from unittest.mock import patch
import pytest
from runners.pyocd import PyOcdBinaryRunner
from conftest import RC_BUILD_DIR, RC_GDB, RC_KERNEL_HEX, RC_KERNEL_ELF
#
# Test values to provide as constructor arguments and command line
# parameters, to verify they're respected.
#
TEST_PYOCD = 'test-pyocd'
TEST_ADDR = 0xadd
TEST_DEV_ID = 'test-dev-id'
TEST_FREQUENCY = 'test-frequency'
TEST_DAPARG = 'test-daparg'
TEST_TARGET = 'test-target'
TEST_FLASH_OPTS = ['--test-flash', 'args']
TEST_GDB_PORT = 1
TEST_TELNET_PORT = 2
TEST_TOOL_OPTS = ['test-opt-1', 'test-opt-2']
TEST_ALL_KWARGS = {
'pyocd': TEST_PYOCD,
'flash_addr': TEST_ADDR,
'flash_opts': TEST_FLASH_OPTS,
'gdb_port': TEST_GDB_PORT,
'telnet_port': TEST_TELNET_PORT,
'tui': False,
'dev_id': TEST_DEV_ID,
'frequency': TEST_FREQUENCY,
'daparg': TEST_DAPARG,
'tool_opt': TEST_TOOL_OPTS
}
TEST_DEF_KWARGS = {}
TEST_ALL_PARAMS = list(itertools.chain(
['--target', TEST_TARGET,
'--daparg', TEST_DAPARG,
'--pyocd', TEST_PYOCD],
[f'--flash-opt={o}' for o in TEST_FLASH_OPTS],
['--gdb-port', str(TEST_GDB_PORT),
'--telnet-port', str(TEST_TELNET_PORT),
'--dev-id', TEST_DEV_ID,
'--frequency', str(TEST_FREQUENCY)],
[f'--tool-opt={o}' for o in TEST_TOOL_OPTS]))
TEST_DEF_PARAMS = ['--target', TEST_TARGET]
#
# Expected results.
#
# These record expected argument lists for system calls made by the
# pyocd runner using its check_call() and run_server_and_client()
# methods.
#
# They are shared between tests that create runners directly and
# tests that construct runners from parsed command-line arguments, to
# ensure that results are consistent.
#
FLASH_ALL_EXPECTED_CALL = ([TEST_PYOCD,
'flash',
'-e', 'sector',
'-a', hex(TEST_ADDR), '-da', TEST_DAPARG,
'-t', TEST_TARGET, '-u', TEST_DEV_ID,
'-f', TEST_FREQUENCY] +
TEST_TOOL_OPTS +
TEST_FLASH_OPTS +
[RC_KERNEL_HEX])
FLASH_DEF_EXPECTED_CALL = ['pyocd', 'flash', '-e', 'sector',
'-t', TEST_TARGET, RC_KERNEL_HEX]
DEBUG_ALL_EXPECTED_SERVER = [TEST_PYOCD,
'gdbserver',
'-da', TEST_DAPARG,
'-p', str(TEST_GDB_PORT),
'-T', str(TEST_TELNET_PORT),
'-t', TEST_TARGET,
'-u', TEST_DEV_ID,
'-f', TEST_FREQUENCY] + TEST_TOOL_OPTS
DEBUG_ALL_EXPECTED_CLIENT = [RC_GDB, RC_KERNEL_ELF,
'-ex', 'target remote :{}'.format(TEST_GDB_PORT),
'-ex', 'monitor halt',
'-ex', 'monitor reset',
'-ex', 'load']
DEBUG_DEF_EXPECTED_SERVER = ['pyocd',
'gdbserver',
'-p', '3333',
'-T', '4444',
'-t', TEST_TARGET]
DEBUG_DEF_EXPECTED_CLIENT = [RC_GDB, RC_KERNEL_ELF,
'-ex', 'target remote :3333',
'-ex', 'monitor halt',
'-ex', 'monitor reset',
'-ex', 'load']
DEBUGSERVER_ALL_EXPECTED_CALL = [TEST_PYOCD,
'gdbserver',
'-da', TEST_DAPARG,
'-p', str(TEST_GDB_PORT),
'-T', str(TEST_TELNET_PORT),
'-t', TEST_TARGET,
'-u', TEST_DEV_ID,
'-f', TEST_FREQUENCY] + TEST_TOOL_OPTS
DEBUGSERVER_DEF_EXPECTED_CALL = ['pyocd',
'gdbserver',
'-p', '3333',
'-T', '4444',
'-t', TEST_TARGET]
#
# Fixtures
#
@pytest.fixture
def pyocd(runner_config, tmpdir):
'''PyOcdBinaryRunner from constructor kwargs or command line parameters'''
# This factory takes either a dict of kwargs to pass to the
# constructor, or a list of command-line arguments to parse and
# use with the create() method.
def _factory(args):
# Ensure kernel binaries exist (as empty files, so commands
# which use them must be patched out).
tmpdir.ensure(RC_KERNEL_HEX)
tmpdir.ensure(RC_KERNEL_ELF)
tmpdir.chdir()
if isinstance(args, dict):
return PyOcdBinaryRunner(runner_config, TEST_TARGET, **args)
elif isinstance(args, list):
parser = argparse.ArgumentParser(allow_abbrev=False)
PyOcdBinaryRunner.add_parser(parser)
arg_namespace = parser.parse_args(args)
return PyOcdBinaryRunner.create(runner_config, arg_namespace)
return _factory
#
# Helpers
#
def require_patch(program):
assert program in ['pyocd', TEST_PYOCD, RC_GDB]
#
# Test cases for runners created by constructor.
#
@pytest.mark.parametrize('pyocd_args,expected', [
(TEST_ALL_KWARGS, FLASH_ALL_EXPECTED_CALL),
(TEST_DEF_KWARGS, FLASH_DEF_EXPECTED_CALL)
])
@patch('runners.pyocd.PyOcdBinaryRunner.check_call')
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
def test_flash(require, cc, pyocd_args, expected, pyocd):
pyocd(pyocd_args).run('flash')
assert require.called
cc.assert_called_once_with(expected)
@pytest.mark.parametrize('pyocd_args,expectedv', [
(TEST_ALL_KWARGS, (DEBUG_ALL_EXPECTED_SERVER, DEBUG_ALL_EXPECTED_CLIENT)),
(TEST_DEF_KWARGS, (DEBUG_DEF_EXPECTED_SERVER, DEBUG_DEF_EXPECTED_CLIENT))
])
@patch('runners.pyocd.PyOcdBinaryRunner.run_server_and_client')
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
def test_debug(require, rsc, pyocd_args, expectedv, pyocd):
pyocd(pyocd_args).run('debug')
assert require.called
rsc.assert_called_once_with(*expectedv)
@pytest.mark.parametrize('pyocd_args,expected', [
(TEST_ALL_KWARGS, DEBUGSERVER_ALL_EXPECTED_CALL),
(TEST_DEF_KWARGS, DEBUGSERVER_DEF_EXPECTED_CALL)
])
@patch('runners.pyocd.PyOcdBinaryRunner.check_call')
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
def test_debugserver(require, cc, pyocd_args, expected, pyocd):
pyocd(pyocd_args).run('debugserver')
assert require.called
cc.assert_called_once_with(expected)
#
# Test cases for runners created via command line arguments.
#
# (Unlike the constructor tests, these require additional patching to mock and
# verify runners.core.BuildConfiguration usage.)
#
@pytest.mark.parametrize('pyocd_args,flash_addr,expected', [
(TEST_ALL_PARAMS, TEST_ADDR, FLASH_ALL_EXPECTED_CALL),
(TEST_DEF_PARAMS, 0x0, FLASH_DEF_EXPECTED_CALL)
])
@patch('runners.pyocd.BuildConfiguration')
@patch('runners.pyocd.PyOcdBinaryRunner.check_call')
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
def test_flash_args(require, cc, bc, pyocd_args, flash_addr, expected, pyocd):
with patch.object(PyOcdBinaryRunner, 'get_flash_address',
return_value=flash_addr):
pyocd(pyocd_args).run('flash')
assert require.called
bc.assert_called_once_with(RC_BUILD_DIR)
cc.assert_called_once_with(expected)
@pytest.mark.parametrize('pyocd_args, expectedv', [
(TEST_ALL_PARAMS, (DEBUG_ALL_EXPECTED_SERVER, DEBUG_ALL_EXPECTED_CLIENT)),
(TEST_DEF_PARAMS, (DEBUG_DEF_EXPECTED_SERVER, DEBUG_DEF_EXPECTED_CLIENT)),
])
@patch('runners.pyocd.BuildConfiguration')
@patch('runners.pyocd.PyOcdBinaryRunner.run_server_and_client')
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
def test_debug_args(require, rsc, bc, pyocd_args, expectedv, pyocd):
pyocd(pyocd_args).run('debug')
assert require.called
bc.assert_called_once_with(RC_BUILD_DIR)
rsc.assert_called_once_with(*expectedv)
@pytest.mark.parametrize('pyocd_args, expected', [
(TEST_ALL_PARAMS, DEBUGSERVER_ALL_EXPECTED_CALL),
(TEST_DEF_PARAMS, DEBUGSERVER_DEF_EXPECTED_CALL),
])
@patch('runners.pyocd.BuildConfiguration')
@patch('runners.pyocd.PyOcdBinaryRunner.check_call')
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
def test_debugserver_args(require, cc, bc, pyocd_args, expected, pyocd):
pyocd(pyocd_args).run('debugserver')
assert require.called
bc.assert_called_once_with(RC_BUILD_DIR)
cc.assert_called_once_with(expected)