1# Copyright (c) 2018 Foundries.io
2# Copyright (c) 2019 Nordic Semiconductor ASA.
3#
4# SPDX-License-Identifier: Apache-2.0
5
6import argparse
7from unittest.mock import patch, call
8import os
9
10import pytest
11
12from runners import blackmagicprobe
13from runners.blackmagicprobe import BlackMagicProbeRunner
14from conftest import RC_KERNEL_ELF, RC_GDB
15import serial.tools.list_ports
16
17TEST_GDB_SERIAL = 'test-gdb-serial'
18
19# Expected subprocesses to be run for each command. Using the
20# runner_config fixture (and always specifying gdb-serial) means we
21# don't get 100% coverage, but it's a starting out point.
22EXPECTED_COMMANDS = {
23    'attach':
24    ([RC_GDB,
25      '-ex', "set confirm off",
26      '-ex', "target extended-remote {}".format(TEST_GDB_SERIAL),
27      '-ex', "monitor swdp_scan",
28      '-ex', "attach 1",
29      '-ex', "file {}".format(RC_KERNEL_ELF)],),
30    'debug':
31    ([RC_GDB,
32      '-ex', "set confirm off",
33      '-ex', "target extended-remote {}".format(TEST_GDB_SERIAL),
34      '-ex', "monitor swdp_scan",
35      '-ex', "attach 1",
36      '-ex', "file {}".format(RC_KERNEL_ELF),
37      '-ex', "load {}".format(RC_KERNEL_ELF)],),
38    'flash':
39    ([RC_GDB,
40      '-ex', "set confirm off",
41      '-ex', "target extended-remote {}".format(TEST_GDB_SERIAL),
42      '-ex', "monitor swdp_scan",
43      '-ex', "attach 1",
44      '-ex', "load {}".format(RC_KERNEL_ELF),
45      '-ex', "kill",
46      '-ex', "quit",
47      '-silent'],),
48}
49
50EXPECTED_CONNECT_SRST_COMMAND = {
51        'attach': 'monitor connect_rst disable',
52        'debug': 'monitor connect_rst enable',
53        'flash': 'monitor connect_rst enable',
54}
55
56def require_patch(program):
57    assert program == RC_GDB
58
59@pytest.mark.parametrize('command', EXPECTED_COMMANDS)
60@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
61@patch('runners.core.ZephyrBinaryRunner.check_call')
62def test_blackmagicprobe_init(cc, req, command, runner_config):
63    '''Test commands using a runner created by constructor.'''
64    runner = BlackMagicProbeRunner(runner_config, TEST_GDB_SERIAL)
65    runner.run(command)
66    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS[command]]
67
68@pytest.mark.parametrize('command', EXPECTED_COMMANDS)
69@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
70@patch('runners.core.ZephyrBinaryRunner.check_call')
71def test_blackmagicprobe_create(cc, req, command, runner_config):
72    '''Test commands using a runner created from command line parameters.'''
73    args = ['--gdb-serial', TEST_GDB_SERIAL]
74    parser = argparse.ArgumentParser(allow_abbrev=False)
75    BlackMagicProbeRunner.add_parser(parser)
76    arg_namespace = parser.parse_args(args)
77    runner = BlackMagicProbeRunner.create(runner_config, arg_namespace)
78    runner.run(command)
79    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS[command]]
80
81@pytest.mark.parametrize('command', EXPECTED_CONNECT_SRST_COMMAND)
82@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
83@patch('runners.core.ZephyrBinaryRunner.check_call')
84def test_blackmagicprobe_connect_rst(cc, req, command, runner_config):
85    '''Test that commands list the correct connect_rst value when enabled.'''
86    args = ['--gdb-serial', TEST_GDB_SERIAL, '--connect-rst']
87    parser = argparse.ArgumentParser(allow_abbrev=False)
88    BlackMagicProbeRunner.add_parser(parser)
89    arg_namespace = parser.parse_args(args)
90    runner = BlackMagicProbeRunner.create(runner_config, arg_namespace)
91    runner.run(command)
92    expected = EXPECTED_CONNECT_SRST_COMMAND[command]
93    assert expected in cc.call_args_list[0][0][0]
94
95@pytest.mark.parametrize('arg, env, expected', [
96    # Argument has priority
97    ('/dev/XXX', None, '/dev/XXX'),
98    ('/dev/XXX', '/dev/YYYY', '/dev/XXX'),
99
100    # Then BMP_GDB_SERIAL env variable
101    (None, '/dev/XXX', '/dev/XXX'),
102    ])
103def test_blackmagicprobe_gdb_serial_generic(arg, env, expected):
104    if env:
105        os.environ['BMP_GDB_SERIAL'] = env
106    else:
107        if 'BMP_GDB_SERIAL' in os.environ:
108            os.environ.pop('BMP_GDB_SERIAL')
109
110    ret = blackmagicprobe.blackmagicprobe_gdb_serial(arg)
111    assert expected == ret
112
113@pytest.mark.parametrize('known_path, comports, globs, expected', [
114    (True, False, ['/dev/ttyACM0', '/dev/ttyACM1'],
115     blackmagicprobe.DEFAULT_LINUX_BMP_PATH),
116    (False, True, [], '/dev/ttyACM3'),
117    (False, False,  ['/dev/ttyACM0', '/dev/ttyACM1'], '/dev/ttyACM0'),
118    (False, False, ['/dev/ttyACM1', '/dev/ttyACM0'], '/dev/ttyACM0'),
119    ])
120@patch('serial.tools.list_ports.comports')
121@patch('os.path.exists')
122@patch('glob.glob')
123def test_blackmagicprobe_gdb_serial_linux(gg, ope, stlpc, known_path, comports,
124                                          globs, expected):
125    gg.return_value = globs
126    ope.return_value = known_path
127    if comports:
128        fake_comport1 = serial.tools.list_ports_common.ListPortInfo(
129                '/dev/ttyACM1')
130        fake_comport1.interface = 'something'
131        fake_comport2 = serial.tools.list_ports_common.ListPortInfo(
132                '/dev/ttyACM2')
133        fake_comport2.interface = None
134        fake_comport3 = serial.tools.list_ports_common.ListPortInfo(
135                '/dev/ttyACM3')
136        fake_comport3.interface = blackmagicprobe.BMP_GDB_INTERFACE
137        stlpc.return_value = [fake_comport1, fake_comport2, fake_comport3]
138    else:
139        stlpc.return_value = []
140
141    ret = blackmagicprobe.blackmagicprobe_gdb_serial_linux()
142    assert expected == ret
143
144@pytest.mark.parametrize('comports, globs, expected', [
145    (True, [], '/dev/cu.usbmodem3'),
146    (False, ['/dev/cu.usbmodemAABBCC0', '/dev/cu.usbmodemAABBCC1'],
147     '/dev/cu.usbmodemAABBCC0'),
148    (False, ['/dev/cu.usbmodemAABBCC1', '/dev/cu.usbmodemAABBCC0'],
149     '/dev/cu.usbmodemAABBCC0'),
150    ])
151@patch('serial.tools.list_ports.comports')
152@patch('glob.glob')
153def test_blackmagicprobe_gdb_serial_darwin(gg, stlpc, comports, globs, expected):
154    gg.return_value = globs
155    if comports:
156        fake_comport1 = serial.tools.list_ports_common.ListPortInfo(
157                '/dev/cu.usbmodem1')
158        fake_comport1.description = 'unrelated'
159        fake_comport2 = serial.tools.list_ports_common.ListPortInfo(
160                '/dev/cu.usbmodem2')
161        fake_comport2.description = None
162        fake_comport3 = serial.tools.list_ports_common.ListPortInfo(
163                '/dev/cu.usbmodem3')
164        fake_comport3.description = f'{blackmagicprobe.BMP_GDB_PRODUCT} v1234'
165        fake_comport4 = serial.tools.list_ports_common.ListPortInfo(
166                '/dev/cu.usbmodem4')
167        fake_comport4.description = f'{blackmagicprobe.BMP_GDB_PRODUCT} v1234'
168        stlpc.return_value = [fake_comport1, fake_comport2,
169                              fake_comport4, fake_comport3]
170    else:
171        stlpc.return_value = []
172
173    ret = blackmagicprobe.blackmagicprobe_gdb_serial_darwin()
174    assert expected == ret
175
176@pytest.mark.parametrize('comports, expected', [
177    (True, 'COM4'),
178    (False, 'COM1'),
179    ])
180@patch('serial.tools.list_ports.comports')
181def test_blackmagicprobe_gdb_serial_win32(stlpc, comports, expected):
182    if comports:
183        fake_comport1 = serial.tools.list_ports_common.ListPortInfo('COM2')
184        fake_comport1.vid = 123
185        fake_comport1.pid = 456
186        fake_comport2 = serial.tools.list_ports_common.ListPortInfo('COM3')
187        fake_comport2.vid = None
188        fake_comport2.pid = None
189        fake_comport3 = serial.tools.list_ports_common.ListPortInfo('COM4')
190        fake_comport3.vid = blackmagicprobe.BMP_GDB_VID
191        fake_comport3.pid = blackmagicprobe.BMP_GDB_PID
192        fake_comport4 = serial.tools.list_ports_common.ListPortInfo('COM5')
193        fake_comport4.vid = blackmagicprobe.BMP_GDB_VID
194        fake_comport4.pid = blackmagicprobe.BMP_GDB_PID
195        stlpc.return_value = [fake_comport1, fake_comport2,
196                              fake_comport4, fake_comport3]
197    else:
198        stlpc.return_value = []
199
200    ret = blackmagicprobe.blackmagicprobe_gdb_serial_win32()
201    assert expected == ret
202