1# Copyright (c) 2020 Teslabs Engineering S.L.
2#
3# SPDX-License-Identifier: Apache-2.0
4
5import argparse
6from pathlib import Path
7from unittest.mock import patch, call
8
9import pytest
10
11from runners.stm32cubeprogrammer import STM32CubeProgrammerBinaryRunner
12from conftest import RC_KERNEL_HEX, RC_KERNEL_ELF
13
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        "start_address": None,
68        "conn_modifiers": None,
69        "cli": CLI_PATH,
70        "use_elf": False,
71        "erase": False,
72        "extload": None,
73        "tool_opt": [],
74        "system": "",
75        "cli_path": str(CLI_PATH),
76        "calls": [
77            [
78                str(CLI_PATH),
79                "--connect",
80                "port=swd",
81                "--download",
82                RC_KERNEL_HEX,
83                "--start",
84            ],
85        ],
86    },
87    {
88        "port": "swd",
89        "frequency": None,
90        "reset_mode": None,
91        "start_address": 0x8001000,
92        "conn_modifiers": None,
93        "cli": CLI_PATH,
94        "use_elf": False,
95        "erase": False,
96        "extload": None,
97        "tool_opt": [],
98        "system": "",
99        "cli_path": str(CLI_PATH),
100        "calls": [
101            [
102                str(CLI_PATH),
103                "--connect",
104                "port=swd",
105                "--download",
106                RC_KERNEL_HEX,
107                "--start",
108                "0x8001000"
109            ],
110        ],
111    },
112    {
113        "port": "swd",
114        "frequency": "4000",
115        "reset_mode": None,
116        "start_address": None,
117        "conn_modifiers": None,
118        "cli": CLI_PATH,
119        "use_elf": False,
120        "erase": False,
121        "extload": None,
122        "tool_opt": [],
123        "system": "",
124        "cli_path": str(CLI_PATH),
125        "calls": [
126            [
127                str(CLI_PATH),
128                "--connect",
129                "port=swd freq=4000",
130                "--download",
131                RC_KERNEL_HEX,
132                "--start",
133            ],
134        ],
135    },
136    {
137        "port": "swd",
138        "frequency": None,
139        "reset_mode": "hw",
140        "start_address": None,
141        "conn_modifiers": None,
142        "cli": CLI_PATH,
143        "use_elf": False,
144        "erase": False,
145        "extload": None,
146        "tool_opt": [],
147        "system": "",
148        "cli_path": str(CLI_PATH),
149        "calls": [
150            [
151                str(CLI_PATH),
152                "--connect",
153                "port=swd reset=HWrst",
154                "--download",
155                RC_KERNEL_HEX,
156                "--start",
157            ],
158        ],
159    },
160    {
161        "port": "swd",
162        "frequency": None,
163        "reset_mode": "sw",
164        "start_address": None,
165        "conn_modifiers": None,
166        "cli": CLI_PATH,
167        "use_elf": False,
168        "erase": False,
169        "extload": None,
170        "tool_opt": [],
171        "system": "",
172        "cli_path": str(CLI_PATH),
173        "calls": [
174            [
175                str(CLI_PATH),
176                "--connect",
177                "port=swd reset=SWrst",
178                "--download",
179                RC_KERNEL_HEX,
180                "--start",
181            ],
182        ],
183    },
184    {
185        "port": "swd",
186        "frequency": None,
187        "reset_mode": "core",
188        "start_address": None,
189        "conn_modifiers": None,
190        "cli": CLI_PATH,
191        "use_elf": False,
192        "erase": False,
193        "extload": None,
194        "tool_opt": [],
195        "system": "",
196        "cli_path": str(CLI_PATH),
197        "calls": [
198            [
199                str(CLI_PATH),
200                "--connect",
201                "port=swd reset=Crst",
202                "--download",
203                RC_KERNEL_HEX,
204                "--start",
205            ],
206        ],
207    },
208    {
209        "port": "swd",
210        "frequency": None,
211        "reset_mode": None,
212        "start_address": None,
213        "conn_modifiers": "br=115200 sn=TEST",
214        "cli": CLI_PATH,
215        "use_elf": False,
216        "erase": False,
217        "extload": None,
218        "tool_opt": [],
219        "system": "",
220        "cli_path": str(CLI_PATH),
221        "calls": [
222            [
223                str(CLI_PATH),
224                "--connect",
225                "port=swd br=115200 sn=TEST",
226                "--download",
227                RC_KERNEL_HEX,
228                "--start",
229            ],
230        ],
231    },
232    {
233        "port": "swd",
234        "frequency": None,
235        "reset_mode": None,
236        "start_address": None,
237        "conn_modifiers": None,
238        "cli": CLI_PATH,
239        "use_elf": True,
240        "erase": False,
241        "extload": None,
242        "tool_opt": [],
243        "system": "",
244        "cli_path": str(CLI_PATH),
245        "calls": [
246            [
247                str(CLI_PATH),
248                "--connect",
249                "port=swd",
250                "--download",
251                RC_KERNEL_ELF,
252                "--start",
253            ],
254        ],
255    },
256    {
257        "port": "swd",
258        "frequency": None,
259        "reset_mode": None,
260        "start_address": None,
261        "conn_modifiers": None,
262        "cli": CLI_PATH,
263        "use_elf": False,
264        "erase": True,
265        "extload": None,
266        "tool_opt": [],
267        "system": "",
268        "cli_path": str(CLI_PATH),
269        "calls": [
270            [str(CLI_PATH), "--connect", "port=swd", "--erase", "all",],
271            [
272                str(CLI_PATH),
273                "--connect",
274                "port=swd",
275                "--download",
276                RC_KERNEL_HEX,
277                "--start",
278            ],
279        ],
280    },
281    {
282        "port": "swd",
283        "frequency": None,
284        "reset_mode": None,
285        "start_address": None,
286        "conn_modifiers": None,
287        "cli": CLI_PATH,
288        "use_elf": False,
289        "erase": False,
290        "extload": None,
291        "tool_opt": ["--skipErase"],
292        "system": "",
293        "cli_path": str(CLI_PATH),
294        "calls": [
295            [
296                str(CLI_PATH),
297                "--connect",
298                "port=swd",
299                "--skipErase",
300                "--download",
301                RC_KERNEL_HEX,
302                "--start",
303            ],
304        ],
305    },
306    {
307        "port": "swd",
308        "frequency": None,
309        "reset_mode": None,
310        "start_address": None,
311        "conn_modifiers": None,
312        "cli": None,
313        "use_elf": False,
314        "erase": False,
315        "extload": None,
316        "tool_opt": [],
317        "system": "Linux",
318        "cli_path": str(LINUX_CLI_PATH),
319        "calls": [
320            [
321                str(LINUX_CLI_PATH),
322                "--connect",
323                "port=swd",
324                "--download",
325                RC_KERNEL_HEX,
326                "--start",
327            ],
328        ],
329    },
330    {
331        "port": "swd",
332        "frequency": None,
333        "reset_mode": None,
334        "start_address": None,
335        "conn_modifiers": None,
336        "cli": None,
337        "use_elf": False,
338        "erase": False,
339        "extload": None,
340        "tool_opt": [],
341        "system": "Darwin",
342        "cli_path": str(MACOS_CLI_PATH),
343        "calls": [
344            [
345                str(MACOS_CLI_PATH),
346                "--connect",
347                "port=swd",
348                "--download",
349                RC_KERNEL_HEX,
350                "--start",
351            ],
352        ],
353    },
354    {
355        "port": "swd",
356        "frequency": None,
357        "reset_mode": None,
358        "start_address": None,
359        "conn_modifiers": None,
360        "cli": None,
361        "use_elf": False,
362        "erase": False,
363        "extload": None,
364        "tool_opt": [],
365        "system": "Windows",
366        "cli_path": str(WINDOWS_CLI_PATH),
367        "calls": [
368            [
369                str(WINDOWS_CLI_PATH),
370                "--connect",
371                "port=swd",
372                "--download",
373                RC_KERNEL_HEX,
374                "--start",
375            ],
376        ],
377    },
378)
379"""Test cases."""
380
381
382@pytest.mark.parametrize("tc", TEST_CASES)
383@patch("runners.stm32cubeprogrammer.platform.system")
384@patch("runners.stm32cubeprogrammer.Path.home", return_value=HOME_PATH)
385@patch("runners.stm32cubeprogrammer.Path.exists", return_value=True)
386@patch.dict("runners.stm32cubeprogrammer.os.environ", ENVIRON)
387@patch("runners.core.ZephyrBinaryRunner.require")
388@patch("runners.stm32cubeprogrammer.STM32CubeProgrammerBinaryRunner.check_call")
389def test_stm32cubeprogrammer_init(
390    check_call, require, path_exists, path_home, system, tc, runner_config
391):
392    """Tests that ``STM32CubeProgrammerBinaryRunner`` class can be initialized
393    and that ``flash`` command works as expected.
394    """
395
396    system.return_value = tc["system"]
397
398    runner = STM32CubeProgrammerBinaryRunner(
399        cfg=runner_config,
400        port=tc["port"],
401        frequency=tc["frequency"],
402        reset_mode=tc["reset_mode"],
403        start_address=tc["start_address"],
404        conn_modifiers=tc["conn_modifiers"],
405        cli=tc["cli"],
406        use_elf=tc["use_elf"],
407        erase=tc["erase"],
408        extload=tc["extload"],
409        tool_opt=tc["tool_opt"],
410    )
411
412    runner.run("flash")
413
414    require.assert_called_with(tc["cli_path"])
415    assert check_call.call_args_list == [call(x) for x in tc["calls"]]
416
417
418@pytest.mark.parametrize("tc", TEST_CASES)
419@patch("runners.stm32cubeprogrammer.platform.system")
420@patch("runners.stm32cubeprogrammer.Path.home", return_value=HOME_PATH)
421@patch("runners.stm32cubeprogrammer.Path.exists", return_value=True)
422@patch.dict("runners.stm32cubeprogrammer.os.environ", ENVIRON)
423@patch("runners.core.ZephyrBinaryRunner.require")
424@patch("runners.stm32cubeprogrammer.STM32CubeProgrammerBinaryRunner.check_call")
425def test_stm32cubeprogrammer_create(
426    check_call, require, path_exists, path_home, system, tc, runner_config
427):
428    """Tests that ``STM32CubeProgrammerBinaryRunner`` class can be created using
429    the ``create`` factory method and that ``flash`` command works as expected.
430    """
431
432    system.return_value = tc["system"]
433
434    args = ["--port", tc["port"]]
435    if tc["frequency"]:
436        args.extend(["--frequency", tc["frequency"]])
437    if tc["reset_mode"]:
438        args.extend(["--reset-mode", tc["reset_mode"]])
439    if tc["start_address"]:
440        args.extend(["--start-address", str(tc["start_address"])])
441    if tc["conn_modifiers"]:
442        args.extend(["--conn-modifiers", tc["conn_modifiers"]])
443    if tc["cli"]:
444        args.extend(["--cli", str(tc["cli"])])
445    if tc["use_elf"]:
446        args.extend(["--use-elf"])
447    if tc["erase"]:
448        args.append("--erase")
449    if tc["extload"]:
450        args.extend(["--extload", tc["extload"]])
451    if tc["tool_opt"]:
452        args.extend(["--tool-opt", " " + tc["tool_opt"][0]])
453
454    parser = argparse.ArgumentParser(allow_abbrev=False)
455    STM32CubeProgrammerBinaryRunner.add_parser(parser)
456    arg_namespace = parser.parse_args(args)
457
458    runner = STM32CubeProgrammerBinaryRunner.create(runner_config, arg_namespace)
459    runner.run("flash")
460
461    require.assert_called_with(tc["cli_path"])
462    assert check_call.call_args_list == [call(x) for x in tc["calls"]]
463