1# Copyright (c) 2020 Teslabs Engineering S.L.
2#
3# SPDX-License-Identifier: Apache-2.0
4
5import argparse
6import os
7from pathlib import Path
8from unittest.mock import patch, call
9
10import pytest
11
12from runners.stm32cubeprogrammer import STM32CubeProgrammerBinaryRunner
13from conftest import RC_KERNEL_HEX, RC_KERNEL_ELF, RC_KERNEL_BIN
14
15CLI_PATH = Path("STM32_Programmer_CLI")
16"""Default CLI path used in tests."""
17
18HOME_PATH = Path("/home", "test")
19"""Home path (used for Linux system CLI path)."""
20
21PROGRAMFILESX86_PATH = Path("C:", "Program Files (x86)")
22"""Program files x86 path (used for Windows system CLI path)."""
23
24ENVIRON = {
25    "PROGRAMFILES(X86)": str(PROGRAMFILESX86_PATH),
26}
27"""Environment (used for Windows system CLI path)."""
28
29LINUX_CLI_PATH = (
30    HOME_PATH
31    / "STMicroelectronics"
32    / "STM32Cube"
33    / "STM32CubeProgrammer"
34    / "bin"
35    / "STM32_Programmer_CLI"
36)
37"""Linux CLI path."""
38
39WINDOWS_CLI_PATH = (
40    PROGRAMFILESX86_PATH
41    / "STMicroelectronics"
42    / "STM32Cube"
43    / "STM32CubeProgrammer"
44    / "bin"
45    / "STM32_Programmer_CLI.exe"
46)
47"""Windows CLI path."""
48
49MACOS_CLI_PATH = (
50    Path("/Applications")
51    / "STMicroelectronics"
52    / "STM32Cube"
53    / "STM32CubeProgrammer"
54    / "STM32CubeProgrammer.app"
55    / "Contents"
56    / "MacOs"
57    / "bin"
58    / "STM32_Programmer_CLI"
59)
60"""macOS CLI path."""
61
62TEST_CASES = (
63    {
64        "port": "swd",
65        "frequency": None,
66        "reset_mode": None,
67        "download_address": None,
68        "start_address": None,
69        "conn_modifiers": None,
70        "start_modifiers": [],
71        "download_modifiers": [],
72        "cli": CLI_PATH,
73        "use_elf": False,
74        "erase": False,
75        "extload": None,
76        "tool_opt": [],
77        "system": "",
78        "cli_path": str(CLI_PATH),
79        "calls": [
80            [
81                str(CLI_PATH),
82                "--connect",
83                "port=swd",
84                "--download",
85                RC_KERNEL_HEX,
86                "--start",
87            ],
88        ],
89    },
90    {
91        "port": "swd",
92        "frequency": None,
93        "reset_mode": None,
94        "download_address": None,
95        "start_address": 0x8001000,
96        "conn_modifiers": None,
97        "start_modifiers": [],
98        "download_modifiers": [],
99        "cli": CLI_PATH,
100        "use_elf": False,
101        "erase": False,
102        "extload": None,
103        "tool_opt": [],
104        "system": "",
105        "cli_path": str(CLI_PATH),
106        "calls": [
107            [
108                str(CLI_PATH),
109                "--connect",
110                "port=swd",
111                "--download",
112                RC_KERNEL_HEX,
113                "--start",
114                "0x8001000"
115            ],
116        ],
117    },
118    {
119        "port": "swd",
120        "frequency": "4000",
121        "reset_mode": None,
122        "download_address": None,
123        "start_address": None,
124        "conn_modifiers": None,
125        "start_modifiers": [],
126        "download_modifiers": [],
127        "cli": CLI_PATH,
128        "use_elf": False,
129        "erase": False,
130        "extload": None,
131        "tool_opt": [],
132        "system": "",
133        "cli_path": str(CLI_PATH),
134        "calls": [
135            [
136                str(CLI_PATH),
137                "--connect",
138                "port=swd freq=4000",
139                "--download",
140                RC_KERNEL_HEX,
141                "--start",
142            ],
143        ],
144    },
145    {
146        "port": "swd",
147        "frequency": None,
148        "reset_mode": "hw",
149        "download_address": None,
150        "start_address": None,
151        "conn_modifiers": None,
152        "start_modifiers": [],
153        "download_modifiers": [],
154        "cli": CLI_PATH,
155        "use_elf": False,
156        "erase": False,
157        "extload": None,
158        "tool_opt": [],
159        "system": "",
160        "cli_path": str(CLI_PATH),
161        "calls": [
162            [
163                str(CLI_PATH),
164                "--connect",
165                "port=swd reset=HWrst",
166                "--download",
167                RC_KERNEL_HEX,
168                "--start",
169            ],
170        ],
171    },
172    {
173        "port": "swd",
174        "frequency": None,
175        "reset_mode": "sw",
176        "download_address": None,
177        "start_address": None,
178        "conn_modifiers": None,
179        "start_modifiers": [],
180        "download_modifiers": [],
181        "cli": CLI_PATH,
182        "use_elf": False,
183        "erase": False,
184        "extload": None,
185        "tool_opt": [],
186        "system": "",
187        "cli_path": str(CLI_PATH),
188        "calls": [
189            [
190                str(CLI_PATH),
191                "--connect",
192                "port=swd reset=SWrst",
193                "--download",
194                RC_KERNEL_HEX,
195                "--start",
196            ],
197        ],
198    },
199    {
200        "port": "swd",
201        "frequency": None,
202        "reset_mode": "core",
203        "download_address": None,
204        "start_address": None,
205        "conn_modifiers": None,
206        "start_modifiers": [],
207        "download_modifiers": [],
208        "cli": CLI_PATH,
209        "use_elf": False,
210        "erase": False,
211        "extload": None,
212        "tool_opt": [],
213        "system": "",
214        "cli_path": str(CLI_PATH),
215        "calls": [
216            [
217                str(CLI_PATH),
218                "--connect",
219                "port=swd reset=Crst",
220                "--download",
221                RC_KERNEL_HEX,
222                "--start",
223            ],
224        ],
225    },
226    {
227        "port": "swd",
228        "frequency": None,
229        "reset_mode": None,
230        "download_address": None,
231        "start_address": None,
232        "conn_modifiers": "br=115200 sn=TEST",
233        "start_modifiers": [],
234        "download_modifiers": [],
235        "cli": CLI_PATH,
236        "use_elf": False,
237        "erase": False,
238        "extload": None,
239        "tool_opt": [],
240        "system": "",
241        "cli_path": str(CLI_PATH),
242        "calls": [
243            [
244                str(CLI_PATH),
245                "--connect",
246                "port=swd br=115200 sn=TEST",
247                "--download",
248                RC_KERNEL_HEX,
249                "--start",
250            ],
251        ],
252    },
253    {
254        "port": "swd",
255        "frequency": None,
256        "reset_mode": None,
257        "download_address": None,
258        "start_address": None,
259        "conn_modifiers": None,
260        "start_modifiers": [],
261        "download_modifiers": [],
262        "cli": CLI_PATH,
263        "use_elf": True,
264        "erase": False,
265        "extload": None,
266        "tool_opt": [],
267        "system": "",
268        "cli_path": str(CLI_PATH),
269        "calls": [
270            [
271                str(CLI_PATH),
272                "--connect",
273                "port=swd",
274                "--download",
275                RC_KERNEL_ELF,
276                "--start",
277            ],
278        ],
279    },
280    {
281        "port": "swd",
282        "frequency": None,
283        "reset_mode": None,
284        "download_address": None,
285        "start_address": None,
286        "conn_modifiers": None,
287        "start_modifiers": [],
288        "download_modifiers": [],
289        "cli": CLI_PATH,
290        "use_elf": False,
291        "erase": True,
292        "extload": None,
293        "tool_opt": [],
294        "system": "",
295        "cli_path": str(CLI_PATH),
296        "calls": [
297            [str(CLI_PATH), "--connect", "port=swd", "--erase", "all",],
298            [
299                str(CLI_PATH),
300                "--connect",
301                "port=swd",
302                "--download",
303                RC_KERNEL_HEX,
304                "--start",
305            ],
306        ],
307    },
308    {
309        "port": "swd",
310        "frequency": None,
311        "reset_mode": None,
312        "download_address": None,
313        "start_address": None,
314        "conn_modifiers": None,
315        "start_modifiers": [],
316        "download_modifiers": [],
317        "cli": CLI_PATH,
318        "use_elf": False,
319        "erase": False,
320        "extload": None,
321        "tool_opt": ["--skipErase"],
322        "system": "",
323        "cli_path": str(CLI_PATH),
324        "calls": [
325            [
326                str(CLI_PATH),
327                "--connect",
328                "port=swd",
329                "--skipErase",
330                "--download",
331                RC_KERNEL_HEX,
332                "--start",
333            ],
334        ],
335    },
336    {
337        "port": "swd",
338        "frequency": None,
339        "reset_mode": None,
340        "download_address": None,
341        "start_address": None,
342        "conn_modifiers": None,
343        "start_modifiers": [],
344        "download_modifiers": [],
345        "cli": None,
346        "use_elf": False,
347        "erase": False,
348        "extload": None,
349        "tool_opt": [],
350        "system": "Linux",
351        "cli_path": str(LINUX_CLI_PATH),
352        "calls": [
353            [
354                str(LINUX_CLI_PATH),
355                "--connect",
356                "port=swd",
357                "--download",
358                RC_KERNEL_HEX,
359                "--start",
360            ],
361        ],
362    },
363    {
364        "port": "swd",
365        "frequency": None,
366        "reset_mode": None,
367        "download_address": None,
368        "start_address": None,
369        "conn_modifiers": None,
370        "start_modifiers": [],
371        "download_modifiers": [],
372        "cli": None,
373        "use_elf": False,
374        "erase": False,
375        "extload": None,
376        "tool_opt": [],
377        "system": "Darwin",
378        "cli_path": str(MACOS_CLI_PATH),
379        "calls": [
380            [
381                str(MACOS_CLI_PATH),
382                "--connect",
383                "port=swd",
384                "--download",
385                RC_KERNEL_HEX,
386                "--start",
387            ],
388        ],
389    },
390    {
391        "port": "swd",
392        "frequency": None,
393        "reset_mode": None,
394        "download_address": None,
395        "start_address": None,
396        "conn_modifiers": None,
397        "start_modifiers": [],
398        "download_modifiers": [],
399        "cli": None,
400        "use_elf": False,
401        "erase": False,
402        "extload": None,
403        "tool_opt": [],
404        "system": "Windows",
405        "cli_path": str(WINDOWS_CLI_PATH),
406        "calls": [
407            [
408                str(WINDOWS_CLI_PATH),
409                "--connect",
410                "port=swd",
411                "--download",
412                RC_KERNEL_HEX,
413                "--start",
414            ],
415        ],
416    },
417    {
418        "port": "swd",
419        "frequency": None,
420        "reset_mode": None,
421        "download_address": 0x80000000,
422        "start_address": None,
423        "conn_modifiers": None,
424        "start_modifiers": ["noack"],
425        "download_modifiers": ["0x1"],
426        "cli": CLI_PATH,
427        "use_elf": False,
428        "erase": False,
429        "extload": None,
430        "tool_opt": [],
431        "system": "",
432        "cli_path": str(CLI_PATH),
433        "calls": [
434            [
435                str(CLI_PATH),
436                "--connect",
437                "port=swd",
438                "--download",
439                RC_KERNEL_BIN,
440                "0x80000000",
441                "0x1",
442                "--start",
443                "noack",
444            ],
445        ],
446    },
447)
448"""Test cases."""
449
450os_path_isfile = os.path.isfile
451
452def os_path_isfile_patch(filename):
453    if filename == RC_KERNEL_BIN:
454        return True
455    return os_path_isfile(filename)
456
457@pytest.mark.parametrize("tc", TEST_CASES)
458@patch("runners.stm32cubeprogrammer.platform.system")
459@patch("runners.stm32cubeprogrammer.Path.home", return_value=HOME_PATH)
460@patch("runners.stm32cubeprogrammer.Path.exists", return_value=True)
461@patch.dict("runners.stm32cubeprogrammer.os.environ", ENVIRON)
462@patch("runners.core.ZephyrBinaryRunner.require")
463@patch("runners.stm32cubeprogrammer.STM32CubeProgrammerBinaryRunner.check_call")
464@patch("os.path.isfile", side_effect=os_path_isfile_patch)
465def test_stm32cubeprogrammer_init(
466    os_path_isfile_patch,
467    check_call, require, path_exists, path_home, system, tc, runner_config
468):
469    """Tests that ``STM32CubeProgrammerBinaryRunner`` class can be initialized
470    and that ``flash`` command works as expected.
471    """
472
473    system.return_value = tc["system"]
474
475    runner = STM32CubeProgrammerBinaryRunner(
476        cfg=runner_config,
477        port=tc["port"],
478        frequency=tc["frequency"],
479        reset_mode=tc["reset_mode"],
480        download_address=tc["download_address"],
481        download_modifiers=tc["download_modifiers"],
482        start_address=tc["start_address"],
483        start_modifiers=tc["start_modifiers"],
484        conn_modifiers=tc["conn_modifiers"],
485        cli=tc["cli"],
486        use_elf=tc["use_elf"],
487        erase=tc["erase"],
488        extload=tc["extload"],
489        tool_opt=tc["tool_opt"],
490    )
491
492    runner.run("flash")
493
494    require.assert_called_with(tc["cli_path"])
495    assert check_call.call_args_list == [call(x) for x in tc["calls"]]
496
497
498@pytest.mark.parametrize("tc", TEST_CASES)
499@patch("runners.stm32cubeprogrammer.platform.system")
500@patch("runners.stm32cubeprogrammer.Path.home", return_value=HOME_PATH)
501@patch("runners.stm32cubeprogrammer.Path.exists", return_value=True)
502@patch.dict("runners.stm32cubeprogrammer.os.environ", ENVIRON)
503@patch("runners.core.ZephyrBinaryRunner.require")
504@patch("runners.stm32cubeprogrammer.STM32CubeProgrammerBinaryRunner.check_call")
505@patch("os.path.isfile", side_effect=os_path_isfile_patch)
506def test_stm32cubeprogrammer_create(
507    os_path_isfile_patch,
508    check_call, require, path_exists, path_home, system, tc, runner_config
509):
510    """Tests that ``STM32CubeProgrammerBinaryRunner`` class can be created using
511    the ``create`` factory method and that ``flash`` command works as expected.
512    """
513
514    system.return_value = tc["system"]
515
516    args = ["--port", tc["port"]]
517    if tc["frequency"]:
518        args.extend(["--frequency", tc["frequency"]])
519    if tc["reset_mode"]:
520        args.extend(["--reset-mode", tc["reset_mode"]])
521    if tc["download_address"]:
522        args.extend(["--download-address", str(tc["download_address"])])
523    if tc["start_address"]:
524        args.extend(["--start-address", str(tc["start_address"])])
525    if tc["conn_modifiers"]:
526        args.extend(["--conn-modifiers", tc["conn_modifiers"]])
527    if tc["cli"]:
528        args.extend(["--cli", str(tc["cli"])])
529    if tc["use_elf"]:
530        args.extend(["--use-elf"])
531    if tc["erase"]:
532        args.append("--erase")
533    if tc["extload"]:
534        args.extend(["--extload", tc["extload"]])
535    if tc["tool_opt"]:
536        args.extend(["--tool-opt", " " + tc["tool_opt"][0]])
537    if tc["download_modifiers"]:
538        args.extend(["--download-modifiers", " " + tc["download_modifiers"][0]])
539    if tc["start_modifiers"]:
540        args.extend(["--start-modifiers", " " + tc["start_modifiers"][0]])
541
542    parser = argparse.ArgumentParser(allow_abbrev=False)
543    STM32CubeProgrammerBinaryRunner.add_parser(parser)
544    arg_namespace = parser.parse_args(args)
545
546    runner = STM32CubeProgrammerBinaryRunner.create(runner_config, arg_namespace)
547    runner.run("flash")
548
549    require.assert_called_with(tc["cli_path"])
550    assert check_call.call_args_list == [call(x) for x in tc["calls"]]
551