1# Copyright (c) 2018 Foundries.io 2# 3# SPDX-License-Identifier: Apache-2.0 4 5import argparse 6import itertools 7from unittest.mock import patch 8 9import pytest 10 11from runners.pyocd import PyOcdBinaryRunner 12from conftest import RC_BUILD_DIR, RC_GDB, RC_KERNEL_HEX, RC_KERNEL_ELF 13 14 15# 16# Test values to provide as constructor arguments and command line 17# parameters, to verify they're respected. 18# 19 20TEST_PYOCD = 'test-pyocd' 21TEST_ADDR = 0xadd 22TEST_DEV_ID = 'test-dev-id' 23TEST_FREQUENCY = 'test-frequency' 24TEST_DAPARG = 'test-daparg' 25TEST_TARGET = 'test-target' 26TEST_FLASH_OPTS = ['--test-flash', 'args'] 27TEST_GDB_PORT = 1 28TEST_TELNET_PORT = 2 29TEST_TOOL_OPTS = ['test-opt-1', 'test-opt-2'] 30 31TEST_ALL_KWARGS = { 32 'pyocd': TEST_PYOCD, 33 'flash_addr': TEST_ADDR, 34 'flash_opts': TEST_FLASH_OPTS, 35 'gdb_port': TEST_GDB_PORT, 36 'telnet_port': TEST_TELNET_PORT, 37 'tui': False, 38 'dev_id': TEST_DEV_ID, 39 'frequency': TEST_FREQUENCY, 40 'daparg': TEST_DAPARG, 41 'tool_opt': TEST_TOOL_OPTS 42} 43 44TEST_DEF_KWARGS = {} 45 46TEST_ALL_PARAMS = list(itertools.chain( 47 ['--target', TEST_TARGET, 48 '--daparg', TEST_DAPARG, 49 '--pyocd', TEST_PYOCD], 50 [f'--flash-opt={o}' for o in TEST_FLASH_OPTS], 51 ['--gdb-port', str(TEST_GDB_PORT), 52 '--telnet-port', str(TEST_TELNET_PORT), 53 '--dev-id', TEST_DEV_ID, 54 '--frequency', str(TEST_FREQUENCY)], 55 [f'--tool-opt={o}' for o in TEST_TOOL_OPTS])) 56 57TEST_DEF_PARAMS = ['--target', TEST_TARGET] 58 59# 60# Expected results. 61# 62# These record expected argument lists for system calls made by the 63# pyocd runner using its check_call() and run_server_and_client() 64# methods. 65# 66# They are shared between tests that create runners directly and 67# tests that construct runners from parsed command-line arguments, to 68# ensure that results are consistent. 69# 70 71FLASH_ALL_EXPECTED_CALL = ([TEST_PYOCD, 72 'flash', 73 '-e', 'sector', 74 '-a', hex(TEST_ADDR), '-da', TEST_DAPARG, 75 '-t', TEST_TARGET, '-u', TEST_DEV_ID, 76 '-f', TEST_FREQUENCY] + 77 TEST_TOOL_OPTS + 78 TEST_FLASH_OPTS + 79 [RC_KERNEL_HEX]) 80FLASH_DEF_EXPECTED_CALL = ['pyocd', 'flash', '-e', 'sector', 81 '-t', TEST_TARGET, RC_KERNEL_HEX] 82 83 84DEBUG_ALL_EXPECTED_SERVER = [TEST_PYOCD, 85 'gdbserver', 86 '-da', TEST_DAPARG, 87 '-p', str(TEST_GDB_PORT), 88 '-T', str(TEST_TELNET_PORT), 89 '-t', TEST_TARGET, 90 '-u', TEST_DEV_ID, 91 '-f', TEST_FREQUENCY] + TEST_TOOL_OPTS 92DEBUG_ALL_EXPECTED_CLIENT = [RC_GDB, RC_KERNEL_ELF, 93 '-ex', 'target remote :{}'.format(TEST_GDB_PORT), 94 '-ex', 'monitor halt', 95 '-ex', 'monitor reset', 96 '-ex', 'load'] 97DEBUG_DEF_EXPECTED_SERVER = ['pyocd', 98 'gdbserver', 99 '-p', '3333', 100 '-T', '4444', 101 '-t', TEST_TARGET] 102DEBUG_DEF_EXPECTED_CLIENT = [RC_GDB, RC_KERNEL_ELF, 103 '-ex', 'target remote :3333', 104 '-ex', 'monitor halt', 105 '-ex', 'monitor reset', 106 '-ex', 'load'] 107 108 109DEBUGSERVER_ALL_EXPECTED_CALL = [TEST_PYOCD, 110 'gdbserver', 111 '-da', TEST_DAPARG, 112 '-p', str(TEST_GDB_PORT), 113 '-T', str(TEST_TELNET_PORT), 114 '-t', TEST_TARGET, 115 '-u', TEST_DEV_ID, 116 '-f', TEST_FREQUENCY] + TEST_TOOL_OPTS 117DEBUGSERVER_DEF_EXPECTED_CALL = ['pyocd', 118 'gdbserver', 119 '-p', '3333', 120 '-T', '4444', 121 '-t', TEST_TARGET] 122 123 124# 125# Fixtures 126# 127 128@pytest.fixture 129def pyocd(runner_config, tmpdir): 130 '''PyOcdBinaryRunner from constructor kwargs or command line parameters''' 131 # This factory takes either a dict of kwargs to pass to the 132 # constructor, or a list of command-line arguments to parse and 133 # use with the create() method. 134 def _factory(args): 135 # Ensure kernel binaries exist (as empty files, so commands 136 # which use them must be patched out). 137 tmpdir.ensure(RC_KERNEL_HEX) 138 tmpdir.ensure(RC_KERNEL_ELF) 139 tmpdir.chdir() 140 141 if isinstance(args, dict): 142 return PyOcdBinaryRunner(runner_config, TEST_TARGET, **args) 143 elif isinstance(args, list): 144 parser = argparse.ArgumentParser(allow_abbrev=False) 145 PyOcdBinaryRunner.add_parser(parser) 146 arg_namespace = parser.parse_args(args) 147 return PyOcdBinaryRunner.create(runner_config, arg_namespace) 148 return _factory 149 150 151# 152# Helpers 153# 154 155def require_patch(program): 156 assert program in ['pyocd', TEST_PYOCD, RC_GDB] 157 158 159# 160# Test cases for runners created by constructor. 161# 162 163@pytest.mark.parametrize('pyocd_args,expected', [ 164 (TEST_ALL_KWARGS, FLASH_ALL_EXPECTED_CALL), 165 (TEST_DEF_KWARGS, FLASH_DEF_EXPECTED_CALL) 166]) 167@patch('runners.pyocd.PyOcdBinaryRunner.check_call') 168@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch) 169def test_flash(require, cc, pyocd_args, expected, pyocd): 170 pyocd(pyocd_args).run('flash') 171 assert require.called 172 cc.assert_called_once_with(expected) 173 174 175@pytest.mark.parametrize('pyocd_args,expectedv', [ 176 (TEST_ALL_KWARGS, (DEBUG_ALL_EXPECTED_SERVER, DEBUG_ALL_EXPECTED_CLIENT)), 177 (TEST_DEF_KWARGS, (DEBUG_DEF_EXPECTED_SERVER, DEBUG_DEF_EXPECTED_CLIENT)) 178]) 179@patch('runners.pyocd.PyOcdBinaryRunner.run_server_and_client') 180@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch) 181def test_debug(require, rsc, pyocd_args, expectedv, pyocd): 182 pyocd(pyocd_args).run('debug') 183 assert require.called 184 rsc.assert_called_once_with(*expectedv) 185 186 187@pytest.mark.parametrize('pyocd_args,expected', [ 188 (TEST_ALL_KWARGS, DEBUGSERVER_ALL_EXPECTED_CALL), 189 (TEST_DEF_KWARGS, DEBUGSERVER_DEF_EXPECTED_CALL) 190]) 191@patch('runners.pyocd.PyOcdBinaryRunner.check_call') 192@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch) 193def test_debugserver(require, cc, pyocd_args, expected, pyocd): 194 pyocd(pyocd_args).run('debugserver') 195 assert require.called 196 cc.assert_called_once_with(expected) 197 198 199# 200# Test cases for runners created via command line arguments. 201# 202# (Unlike the constructor tests, these require additional patching to mock and 203# verify runners.core.BuildConfiguration usage.) 204# 205 206@pytest.mark.parametrize('pyocd_args,flash_addr,expected', [ 207 (TEST_ALL_PARAMS, TEST_ADDR, FLASH_ALL_EXPECTED_CALL), 208 (TEST_DEF_PARAMS, 0x0, FLASH_DEF_EXPECTED_CALL) 209]) 210@patch('runners.pyocd.BuildConfiguration') 211@patch('runners.pyocd.PyOcdBinaryRunner.check_call') 212@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch) 213def test_flash_args(require, cc, bc, pyocd_args, flash_addr, expected, pyocd): 214 with patch.object(PyOcdBinaryRunner, 'get_flash_address', 215 return_value=flash_addr): 216 pyocd(pyocd_args).run('flash') 217 assert require.called 218 bc.assert_called_once_with(RC_BUILD_DIR) 219 cc.assert_called_once_with(expected) 220 221 222@pytest.mark.parametrize('pyocd_args, expectedv', [ 223 (TEST_ALL_PARAMS, (DEBUG_ALL_EXPECTED_SERVER, DEBUG_ALL_EXPECTED_CLIENT)), 224 (TEST_DEF_PARAMS, (DEBUG_DEF_EXPECTED_SERVER, DEBUG_DEF_EXPECTED_CLIENT)), 225]) 226@patch('runners.pyocd.BuildConfiguration') 227@patch('runners.pyocd.PyOcdBinaryRunner.run_server_and_client') 228@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch) 229def test_debug_args(require, rsc, bc, pyocd_args, expectedv, pyocd): 230 pyocd(pyocd_args).run('debug') 231 assert require.called 232 bc.assert_called_once_with(RC_BUILD_DIR) 233 rsc.assert_called_once_with(*expectedv) 234 235 236@pytest.mark.parametrize('pyocd_args, expected', [ 237 (TEST_ALL_PARAMS, DEBUGSERVER_ALL_EXPECTED_CALL), 238 (TEST_DEF_PARAMS, DEBUGSERVER_DEF_EXPECTED_CALL), 239]) 240@patch('runners.pyocd.BuildConfiguration') 241@patch('runners.pyocd.PyOcdBinaryRunner.check_call') 242@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch) 243def test_debugserver_args(require, cc, bc, pyocd_args, expected, pyocd): 244 pyocd(pyocd_args).run('debugserver') 245 assert require.called 246 bc.assert_called_once_with(RC_BUILD_DIR) 247 cc.assert_called_once_with(expected) 248