# 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)