1# Copyright (c) 2018 Foundries.io
2# Copyright (c) 2020 Nordic Semiconductor ASA
3#
4# SPDX-License-Identifier: Apache-2.0
5
6import argparse
7import functools
8import io
9import os
10import shlex
11import typing
12from unittest.mock import patch, call
13
14import pytest
15
16from runners.nrfjprog import NrfJprogBinaryRunner
17from runners.nrfutil import NrfUtilBinaryRunner
18from conftest import RC_KERNEL_HEX
19
20
21#
22# Test values
23#
24
25TEST_DEF_SNR = 'test-default-serial-number'  # for mocking user input
26TEST_OVR_SNR = 'test-override-serial-number'
27
28TEST_TOOL_OPT = '--ip 192.168.1.10'
29TEST_TOOL_OPT_L = shlex.split(TEST_TOOL_OPT)
30
31CLASS_MAP = {'nrfjprog': NrfJprogBinaryRunner, 'nrfutil': NrfUtilBinaryRunner}
32
33#
34# A dictionary mapping test cases to expected results.
35#
36# The keys are TC objects.
37#
38# The values are usually tool commands we expect to be executed for
39# each test case. Verification is done by mocking the check_call()
40# ZephyrBinaryRunner method which is used to run the commands.
41#
42# Values can also be callables which take a tmpdir and return the
43# expected commands. This is needed for nRF53 testing.
44#
45
46class TC(typing.NamedTuple):    # 'TestCase'
47    # NRF51, NRF52, etc.
48    family: str
49
50    # 'APP', 'NET', 'APP+NET', or None.
51    coprocessor: typing.Optional[str]
52
53    # Run a recover command first if True
54    recover: bool
55
56    # Use --reset instead of --pinreset if True
57    softreset: bool
58
59    # Use --pinreset instead of --reset if True
60    pinreset: bool
61
62    # --snr TEST_OVR_SNR if True, --snr TEST_DEF_SNR if False
63    snr: bool
64
65    # --chiperase if True,
66    # --sectorerase if False (or --sectoranduicrerase on nRF52)
67    erase: bool
68
69    # --tool-opt TEST_TOOL_OPT if True
70    tool_opt: bool
71
72EXPECTED_MAP = {'nrfjprog': 0, 'nrfutil': 1}
73EXPECTED_RESULTS = {
74
75    # -------------------------------------------------------------------------
76    # NRF51
77    #
78    #  family   CP    recov  soft   pin    snr    erase  tool_opt
79    TC('nrf51', None, False, False, False, False, False, False):
80    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF51',
81      '--snr', TEST_DEF_SNR],
82     ['nrfjprog', '--pinreset', '-f', 'NRF51', '--snr', TEST_DEF_SNR]),
83     (TEST_DEF_SNR, None)),
84
85    TC('nrf51', None, False, False, False, False, True, False):
86    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF51',
87      '--snr', TEST_DEF_SNR],
88     ['nrfjprog', '--pinreset', '-f', 'NRF51', '--snr', TEST_DEF_SNR]),
89     (TEST_DEF_SNR, None)),
90
91    TC('nrf51', None, False, False, True, True, False, False):
92    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF51',
93      '--snr', TEST_OVR_SNR],
94     ['nrfjprog', '--pinreset', '-f', 'NRF51', '--snr', TEST_OVR_SNR]),
95     (TEST_OVR_SNR, None)),
96
97    TC('nrf51', None, False, True, False, False, False, False):
98    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF51',
99      '--snr', TEST_DEF_SNR],
100     ['nrfjprog', '--reset', '-f', 'NRF51', '--snr', TEST_DEF_SNR]),
101     (TEST_DEF_SNR, None)),
102
103    TC('nrf51', None, True, False, True, False, False, False):
104    ((['nrfjprog', '--recover', '-f', 'NRF51', '--snr', TEST_DEF_SNR],
105     ['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF51',
106      '--snr', TEST_DEF_SNR],
107     ['nrfjprog', '--pinreset', '-f', 'NRF51', '--snr', TEST_DEF_SNR]),
108     (TEST_DEF_SNR, None)),
109
110    TC('nrf51', None, True, True, False, True, True, False):
111    ((['nrfjprog', '--recover', '-f', 'NRF51', '--snr', TEST_OVR_SNR],
112     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF51',
113      '--snr', TEST_OVR_SNR],
114     ['nrfjprog', '--reset', '-f', 'NRF51', '--snr', TEST_OVR_SNR]),
115     (TEST_OVR_SNR, None)),
116
117    TC('nrf51', None, True, True, False, True, True, True):
118    ((['nrfjprog', '--recover', '-f', 'NRF51', '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L,
119     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF51',
120      '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L,
121     ['nrfjprog', '--reset', '-f', 'NRF51', '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L),
122     (TEST_OVR_SNR, None)),
123
124    # -------------------------------------------------------------------------
125    # NRF52
126    #
127    #  family   CP    recov  soft   pin    snr    erase  tool_opt
128    TC('nrf52', None, False, False, False, False, False, False):
129    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectoranduicrerase',
130      '--verify', '-f', 'NRF52', '--snr', TEST_DEF_SNR],
131     ['nrfjprog', '--reset', '-f', 'NRF52', '--snr', TEST_DEF_SNR]),
132     (TEST_DEF_SNR, None)),
133
134    TC('nrf52', None, False, False, True, False, True, False):
135    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF52',
136      '--snr', TEST_DEF_SNR],
137     ['nrfjprog', '--pinresetenable', '-f', 'NRF52', '--snr', TEST_DEF_SNR],
138     ['nrfjprog', '--pinreset', '-f', 'NRF52', '--snr', TEST_DEF_SNR]),
139     (TEST_DEF_SNR, None)),
140
141    TC('nrf52', None, False, False, False, True, False, False):
142    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectoranduicrerase',
143      '--verify', '-f', 'NRF52', '--snr', TEST_OVR_SNR],
144     ['nrfjprog', '--reset', '-f', 'NRF52', '--snr', TEST_OVR_SNR]),
145     (TEST_OVR_SNR, None)),
146
147    TC('nrf52', None, False, True, False, False, False, False):
148    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectoranduicrerase',
149      '--verify', '-f', 'NRF52', '--snr', TEST_DEF_SNR],
150     ['nrfjprog', '--reset', '-f', 'NRF52', '--snr', TEST_DEF_SNR]),
151     (TEST_DEF_SNR, None)),
152
153    TC('nrf52', None, True, False, True, False, False, False):
154    ((['nrfjprog', '--recover', '-f', 'NRF52', '--snr', TEST_DEF_SNR],
155     ['nrfjprog', '--program', RC_KERNEL_HEX, '--sectoranduicrerase',
156      '--verify', '-f', 'NRF52', '--snr', TEST_DEF_SNR],
157     ['nrfjprog', '--pinresetenable', '-f', 'NRF52', '--snr', TEST_DEF_SNR],
158     ['nrfjprog', '--pinreset', '-f', 'NRF52', '--snr', TEST_DEF_SNR]),
159     (TEST_DEF_SNR, None)),
160
161    TC('nrf52', None, True, True, False, True, True, False):
162    ((['nrfjprog', '--recover', '-f', 'NRF52', '--snr', TEST_OVR_SNR],
163     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF52',
164      '--snr', TEST_OVR_SNR],
165     ['nrfjprog', '--reset', '-f', 'NRF52', '--snr', TEST_OVR_SNR]),
166     (TEST_OVR_SNR, None)),
167
168    TC('nrf52', None, True, True, False, True, True, True):
169    ((['nrfjprog', '--recover', '-f', 'NRF52', '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L,
170     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF52',
171      '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L,
172     ['nrfjprog', '--reset', '-f', 'NRF52', '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L),
173     (TEST_OVR_SNR, None)),
174
175    # -------------------------------------------------------------------------
176    # NRF53 APP
177    #
178    #  family   CP     recov  soft   pin    snr    erase  tool_opt
179    TC('nrf53', 'APP', False, False, False, False, False, False):
180    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
181      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', TEST_DEF_SNR],
182     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
183     (TEST_DEF_SNR, None)),
184
185    TC('nrf53', 'APP', False, False, True, False, True, False):
186    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase',
187      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', TEST_DEF_SNR],
188     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
189     (TEST_DEF_SNR, None)),
190
191    TC('nrf53', 'APP', False, False, False, True, False, False):
192    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
193      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', TEST_OVR_SNR],
194     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_OVR_SNR]),
195     (TEST_OVR_SNR, None)),
196
197    TC('nrf53', 'APP', False, True, False, False, False, False):
198    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
199      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', TEST_DEF_SNR],
200     ['nrfjprog', '--reset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
201     (TEST_DEF_SNR, None)),
202
203    TC('nrf53', 'APP', True, False, True, False, False, False):
204    ((['nrfjprog', '--recover', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK',
205      '--snr', TEST_DEF_SNR],
206     ['nrfjprog', '--recover', '-f', 'NRF53', '--snr', TEST_DEF_SNR],
207     ['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
208      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', TEST_DEF_SNR],
209     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
210     (TEST_DEF_SNR, None)),
211
212    TC('nrf53', 'APP', True, True, False, True, True, False):
213    ((['nrfjprog', '--recover', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK',
214      '--snr', TEST_OVR_SNR],
215     ['nrfjprog', '--recover', '-f', 'NRF53', '--snr', TEST_OVR_SNR],
216     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase',
217      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', TEST_OVR_SNR],
218     ['nrfjprog', '--reset', '-f', 'NRF53', '--snr', TEST_OVR_SNR]),
219     (TEST_OVR_SNR, None)),
220
221    # -------------------------------------------------------------------------
222    # NRF53 NET
223    #
224    #  family   CP     recov  soft   pin    snr    erase  tool_opt
225    TC('nrf53', 'NET', False, False, False, False, False, False):
226    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
227      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', TEST_DEF_SNR],
228     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
229     (TEST_DEF_SNR, None)),
230
231    TC('nrf53', 'NET', False, False, True, False, True, False):
232    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase',
233      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', TEST_DEF_SNR],
234     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
235     (TEST_DEF_SNR, None)),
236
237    TC('nrf53', 'NET', False, False, False, True, False, False):
238    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
239      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', TEST_OVR_SNR],
240     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_OVR_SNR]),
241     (TEST_OVR_SNR, None)),
242
243    TC('nrf53', 'NET', False, True, False, False, False, False):
244    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
245      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', TEST_DEF_SNR],
246     ['nrfjprog', '--reset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
247     (TEST_DEF_SNR, None)),
248
249    TC('nrf53', 'NET', True, False, True, False, False, False):
250    ((['nrfjprog', '--recover', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK',
251      '--snr', TEST_DEF_SNR],
252     ['nrfjprog', '--recover', '-f', 'NRF53', '--snr', TEST_DEF_SNR],
253     ['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase',
254      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', TEST_DEF_SNR],
255     ['nrfjprog', '--pinreset', '-f', 'NRF53', '--snr', TEST_DEF_SNR]),
256     (TEST_DEF_SNR, None)),
257
258    TC('nrf53', 'NET', True, True, False, True, True, False):
259    ((['nrfjprog', '--recover', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK',
260      '--snr', TEST_OVR_SNR],
261     ['nrfjprog', '--recover', '-f', 'NRF53', '--snr', TEST_OVR_SNR],
262     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase',
263      '--verify', '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', TEST_OVR_SNR],
264     ['nrfjprog', '--reset', '-f', 'NRF53', '--snr', TEST_OVR_SNR]),
265     (TEST_OVR_SNR, None)),
266
267    # -------------------------------------------------------------------------
268    # NRF91
269    #
270    #  family   CP    recov  soft   pin    snr    erase  tool_opt
271    TC('nrf91', None, False, False, False, False, False, False):
272    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF91',
273      '--snr', TEST_DEF_SNR],
274     ['nrfjprog', '--pinreset', '-f', 'NRF91', '--snr', TEST_DEF_SNR]),
275     (TEST_DEF_SNR, None)),
276
277    TC('nrf91', None, False, False, True, False, True, False):
278    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF91',
279      '--snr', TEST_DEF_SNR],
280     ['nrfjprog', '--pinreset', '-f', 'NRF91', '--snr', TEST_DEF_SNR]),
281     (TEST_DEF_SNR, None)),
282
283    TC('nrf91', None, False, False, False, True, False, False):
284    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF91',
285      '--snr', TEST_OVR_SNR],
286     ['nrfjprog', '--pinreset', '-f', 'NRF91', '--snr', TEST_OVR_SNR]),
287     (TEST_OVR_SNR, None)),
288
289    TC('nrf91', None, False, True, False, False, False, False):
290    ((['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF91',
291      '--snr', TEST_DEF_SNR],
292     ['nrfjprog', '--reset', '-f', 'NRF91', '--snr', TEST_DEF_SNR]),
293     (TEST_DEF_SNR, None)),
294
295    TC('nrf91', None, True, False, True, False, False, False):
296    ((['nrfjprog', '--recover', '-f', 'NRF91', '--snr', TEST_DEF_SNR],
297     ['nrfjprog', '--program', RC_KERNEL_HEX, '--sectorerase', '--verify', '-f', 'NRF91',
298      '--snr', TEST_DEF_SNR],
299     ['nrfjprog', '--pinreset', '-f', 'NRF91', '--snr', TEST_DEF_SNR]),
300     (TEST_DEF_SNR, None)),
301
302    TC('nrf91', None, True, True, False, True, True, False):
303    ((['nrfjprog', '--recover', '-f', 'NRF91', '--snr', TEST_OVR_SNR],
304     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF91',
305      '--snr', TEST_OVR_SNR],
306     ['nrfjprog', '--reset', '-f', 'NRF91', '--snr', TEST_OVR_SNR]),
307     (TEST_OVR_SNR, None)),
308
309    TC('nrf91', None, True, True, False, True, True, True):
310    ((['nrfjprog', '--recover', '-f', 'NRF91', '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L,
311     ['nrfjprog', '--program', RC_KERNEL_HEX, '--chiperase', '--verify', '-f', 'NRF91',
312      '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L,
313     ['nrfjprog', '--reset', '-f', 'NRF91', '--snr', TEST_OVR_SNR] + TEST_TOOL_OPT_L),
314     (TEST_OVR_SNR, None)),
315}
316
317#
318# Monkey-patches
319#
320
321def get_board_snr_patch(glob):
322    return TEST_DEF_SNR
323
324def require_patch(cur_tool, program):
325    assert cur_tool == program
326
327os_path_isfile = os.path.isfile
328
329def os_path_isfile_patch(filename):
330    if filename == RC_KERNEL_HEX:
331        return True
332    return os_path_isfile(filename)
333
334#
335# Test functions.
336#
337# These are white box tests that rely on the above monkey-patches.
338#
339
340def id_fn(test_case):
341    if test_case.coprocessor is None:
342        cp = ''
343    else:
344        cp = f'-{test_case.coprocessor}'
345    s = 'soft_reset' if test_case.softreset else 'no_soft_reset'
346    p = 'pin_reset' if test_case.pinreset else 'no_pin_reset'
347    sn = 'default_snr' if test_case.snr else 'override_snr'
348    e = 'chip_erase' if test_case.erase else 'sector[anduicr]_erase'
349    r = 'recover' if test_case.recover else 'no_recover'
350    t = 'tool_opt' if test_case.tool_opt else 'no_tool_opt'
351
352    return f'{test_case.family[:5]}{cp}-{s}-{p}-{sn}-{e}-{r}-{t}'
353
354def fix_up_runner_config(test_case, runner_config, tmpdir):
355    # Helper that adjusts the common runner_config fixture for our
356    # nRF-specific tests.
357
358    to_replace = {}
359
360    # Provide a skeletal zephyr/.config file to use as the runner's
361    # BuildConfiguration.
362    zephyr = tmpdir / 'zephyr'
363    zephyr.mkdir()
364    dotconfig = os.fspath(zephyr / '.config')
365    with open(dotconfig, 'w') as f:
366        f.write(f'''
367CONFIG_SOC_SERIES_{test_case.family.upper()}X=y
368''')
369        if test_case.family == 'nrf53':
370            f.write(f'''
371CONFIG_SOC_NRF5340_CPU{test_case.coprocessor}=y
372''')
373
374    to_replace['build_dir'] = tmpdir
375
376    return runner_config._replace(**to_replace)
377
378def check_expected(tool, test_case, check_fn, get_snr, tmpdir, runner_config):
379
380    expected = EXPECTED_RESULTS[test_case][EXPECTED_MAP[tool]]
381    if tool == 'nrfutil':
382        # Skip the preparation calls for now
383        assert len(check_fn.call_args_list) >= 1
384        xi = len(check_fn.call_args_list) - 1
385        assert len(check_fn.call_args_list[xi].args) == 1
386        # Extract filename
387        nrfutil_args = check_fn.call_args_list[xi].args[0]
388        tmpfile = nrfutil_args[nrfutil_args.index('--batch-path') + 1]
389        cmds = (['nrfutil', '--json', 'device', 'x-execute-batch', '--batch-path',
390                 tmpfile, '--serial-number', expected[0]],)
391        call_args = [call(nrfutil_args)]
392    else:
393        cmds = expected
394        call_args = check_fn.call_args_list
395
396    if callable(cmds):
397        assert (call_args ==
398                [call(x) for x in cmds(tmpdir, runner_config.hex_file)])
399    else:
400        assert call_args == [call(x) for x in cmds]
401
402    if not test_case.snr:
403        get_snr.assert_called_once_with('*')
404    else:
405        get_snr.assert_not_called()
406
407@pytest.mark.parametrize('tool', ["nrfjprog","nrfutil"])
408@pytest.mark.parametrize('test_case', EXPECTED_RESULTS.keys(), ids=id_fn)
409@patch('runners.core.ZephyrBinaryRunner.require')
410@patch('runners.nrfjprog.NrfBinaryRunner.get_board_snr',
411       side_effect=get_board_snr_patch)
412@patch('runners.nrfutil.subprocess.Popen')
413@patch('runners.nrfjprog.NrfBinaryRunner.check_call')
414def test_init(check_call, popen, get_snr, require, tool, test_case,
415              runner_config, tmpdir):
416    popen.return_value.__enter__.return_value.stdout = io.BytesIO(b'')
417
418    require.side_effect = functools.partial(require_patch, tool)
419    runner_config = fix_up_runner_config(test_case, runner_config, tmpdir)
420    snr = TEST_OVR_SNR if test_case.snr else None
421    tool_opt = TEST_TOOL_OPT_L if test_case.tool_opt else []
422    cls = CLASS_MAP[tool]
423    runner = cls(runner_config,
424                 test_case.family,
425                 test_case.softreset,
426                 test_case.pinreset,
427                 snr,
428                 erase=test_case.erase,
429                 tool_opt=tool_opt,
430                 recover=test_case.recover)
431
432    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
433        runner.run('flash')
434    assert require.called
435
436    CHECK_FN_MAP = {'nrfjprog': check_call, 'nrfutil': popen}
437    check_expected(tool, test_case, CHECK_FN_MAP[tool], get_snr, tmpdir,
438                   runner_config)
439
440@pytest.mark.parametrize('tool', ["nrfjprog","nrfutil"])
441@pytest.mark.parametrize('test_case', EXPECTED_RESULTS.keys(), ids=id_fn)
442@patch('runners.core.ZephyrBinaryRunner.require')
443@patch('runners.nrfjprog.NrfBinaryRunner.get_board_snr',
444       side_effect=get_board_snr_patch)
445@patch('runners.nrfutil.subprocess.Popen')
446@patch('runners.nrfjprog.NrfBinaryRunner.check_call')
447def test_create(check_call, popen, get_snr, require, tool, test_case,
448                runner_config, tmpdir):
449    popen.return_value.__enter__.return_value.stdout = io.BytesIO(b'')
450
451    require.side_effect = functools.partial(require_patch, tool)
452    runner_config = fix_up_runner_config(test_case, runner_config, tmpdir)
453
454    args = []
455    if test_case.softreset:
456        args.append('--softreset')
457    if test_case.pinreset:
458        args.append('--pinreset')
459    if test_case.snr:
460        args.extend(['--dev-id', TEST_OVR_SNR])
461    if test_case.erase:
462        args.append('--erase')
463    if test_case.recover:
464        args.append('--recover')
465    if test_case.tool_opt:
466        args.extend(['--tool-opt', TEST_TOOL_OPT])
467
468    parser = argparse.ArgumentParser(allow_abbrev=False)
469    cls = CLASS_MAP[tool]
470    cls.add_parser(parser)
471    arg_namespace = parser.parse_args(args)
472    runner = cls.create(runner_config, arg_namespace)
473    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
474        runner.run('flash')
475
476    assert require.called
477
478    CHECK_FN_MAP = {'nrfjprog': check_call, 'nrfutil': popen}
479    check_expected(tool, test_case, CHECK_FN_MAP[tool], get_snr, tmpdir,
480                   runner_config)
481