The --tool-opt runner option is the recommended practice for allowing runners to take additional arguments that are passed on to the underlying tool. It exists because we don't want to add one runner option for every single tool option that users might want to tweak -- that would be a nightmare. Enough runners are using this option that it's time to promote it to a common runner capability with consistent behavior, the same way we did for the --dev-id option in the past. This removes boilerplate from individual runner files and ensures consistent argument handling for this option when it is supported. Since --tool-opt is a bit long to type, and we've had some complaints about that, take this as an opportunity to standardize on -O as a short option equivalent for it. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
247 lines
8.7 KiB
Python
247 lines
8.7 KiB
Python
# Copyright (c) 2018 Foundries.io
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
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_OPT = 'test-opt'
|
|
|
|
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_OPT],
|
|
}
|
|
|
|
TEST_DEF_KWARGS = {}
|
|
|
|
TEST_ALL_PARAMS = (['--target', TEST_TARGET,
|
|
'--daparg', TEST_DAPARG,
|
|
'--pyocd', TEST_PYOCD] +
|
|
['--flash-opt={}'.format(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),
|
|
'--tool-opt', TEST_TOOL_OPT])
|
|
|
|
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_OPT] +
|
|
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_OPT]
|
|
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_OPT]
|
|
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()
|
|
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)
|