diff options
Diffstat (limited to 'lib/python')
-rwxr-xr-x | lib/python/qmk/cli/doctor.py | 187 | ||||
-rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 24 |
2 files changed, 118 insertions, 93 deletions
diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py index caa98a71c..a5eda555f 100755 --- a/lib/python/qmk/cli/doctor.py +++ b/lib/python/qmk/cli/doctor.py | |||
@@ -7,6 +7,7 @@ import re | |||
7 | import shutil | 7 | import shutil |
8 | import subprocess | 8 | import subprocess |
9 | from pathlib import Path | 9 | from pathlib import Path |
10 | from enum import Enum | ||
10 | 11 | ||
11 | from milc import cli | 12 | from milc import cli |
12 | from qmk import submodules | 13 | from qmk import submodules |
@@ -14,6 +15,13 @@ from qmk.constants import QMK_FIRMWARE | |||
14 | from qmk.questions import yesno | 15 | from qmk.questions import yesno |
15 | from qmk.commands import run | 16 | from qmk.commands import run |
16 | 17 | ||
18 | |||
19 | class CheckStatus(Enum): | ||
20 | OK = 1 | ||
21 | WARNING = 2 | ||
22 | ERROR = 3 | ||
23 | |||
24 | |||
17 | ESSENTIAL_BINARIES = { | 25 | ESSENTIAL_BINARIES = { |
18 | 'dfu-programmer': {}, | 26 | 'dfu-programmer': {}, |
19 | 'avrdude': {}, | 27 | 'avrdude': {}, |
@@ -33,9 +41,12 @@ def _udev_rule(vid, pid=None, *args): | |||
33 | """ | 41 | """ |
34 | rule = "" | 42 | rule = "" |
35 | if pid: | 43 | if pid: |
36 | rule = 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", ATTRS{idProduct}=="%s", TAG+="uaccess", RUN{builtin}+="uaccess"' % (vid, pid) | 44 | rule = 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", ATTRS{idProduct}=="%s", TAG+="uaccess"' % ( |
45 | vid, | ||
46 | pid, | ||
47 | ) | ||
37 | else: | 48 | else: |
38 | rule = 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", TAG+="uaccess", RUN{builtin}+="uaccess"' % vid | 49 | rule = 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="%s", TAG+="uaccess"' % vid |
39 | if args: | 50 | if args: |
40 | rule = ', '.join([rule, *args]) | 51 | rule = ', '.join([rule, *args]) |
41 | return rule | 52 | return rule |
@@ -69,24 +80,25 @@ def check_arm_gcc_version(): | |||
69 | version_number = ESSENTIAL_BINARIES['arm-none-eabi-gcc']['output'].strip() | 80 | version_number = ESSENTIAL_BINARIES['arm-none-eabi-gcc']['output'].strip() |
70 | cli.log.info('Found arm-none-eabi-gcc version %s', version_number) | 81 | cli.log.info('Found arm-none-eabi-gcc version %s', version_number) |
71 | 82 | ||
72 | return True # Right now all known arm versions are ok | 83 | return CheckStatus.OK # Right now all known arm versions are ok |
73 | 84 | ||
74 | 85 | ||
75 | def check_avr_gcc_version(): | 86 | def check_avr_gcc_version(): |
76 | """Returns True if the avr-gcc version is not known to cause problems. | 87 | """Returns True if the avr-gcc version is not known to cause problems. |
77 | """ | 88 | """ |
89 | rc = CheckStatus.ERROR | ||
78 | if 'output' in ESSENTIAL_BINARIES['avr-gcc']: | 90 | if 'output' in ESSENTIAL_BINARIES['avr-gcc']: |
79 | version_number = ESSENTIAL_BINARIES['avr-gcc']['output'].strip() | 91 | version_number = ESSENTIAL_BINARIES['avr-gcc']['output'].strip() |
80 | 92 | ||
93 | cli.log.info('Found avr-gcc version %s', version_number) | ||
94 | rc = CheckStatus.OK | ||
95 | |||
81 | parsed_version = parse_gcc_version(version_number) | 96 | parsed_version = parse_gcc_version(version_number) |
82 | if parsed_version['major'] > 8: | 97 | if parsed_version['major'] > 8: |
83 | cli.log.error('We do not recommend avr-gcc newer than 8. Downgrading to 8.x is recommended.') | 98 | cli.log.warning('{fg_yellow}We do not recommend avr-gcc newer than 8. Downgrading to 8.x is recommended.') |
84 | return False | 99 | rc = CheckStatus.WARNING |
85 | 100 | ||
86 | cli.log.info('Found avr-gcc version %s', version_number) | 101 | return rc |
87 | return True | ||
88 | |||
89 | return False | ||
90 | 102 | ||
91 | 103 | ||
92 | def check_avrdude_version(): | 104 | def check_avrdude_version(): |
@@ -95,7 +107,7 @@ def check_avrdude_version(): | |||
95 | version_number = last_line.split()[2][:-1] | 107 | version_number = last_line.split()[2][:-1] |
96 | cli.log.info('Found avrdude version %s', version_number) | 108 | cli.log.info('Found avrdude version %s', version_number) |
97 | 109 | ||
98 | return True | 110 | return CheckStatus.OK |
99 | 111 | ||
100 | 112 | ||
101 | def check_dfu_util_version(): | 113 | def check_dfu_util_version(): |
@@ -104,7 +116,7 @@ def check_dfu_util_version(): | |||
104 | version_number = first_line.split()[1] | 116 | version_number = first_line.split()[1] |
105 | cli.log.info('Found dfu-util version %s', version_number) | 117 | cli.log.info('Found dfu-util version %s', version_number) |
106 | 118 | ||
107 | return True | 119 | return CheckStatus.OK |
108 | 120 | ||
109 | 121 | ||
110 | def check_dfu_programmer_version(): | 122 | def check_dfu_programmer_version(): |
@@ -113,7 +125,7 @@ def check_dfu_programmer_version(): | |||
113 | version_number = first_line.split()[1] | 125 | version_number = first_line.split()[1] |
114 | cli.log.info('Found dfu-programmer version %s', version_number) | 126 | cli.log.info('Found dfu-programmer version %s', version_number) |
115 | 127 | ||
116 | return True | 128 | return CheckStatus.OK |
117 | 129 | ||
118 | 130 | ||
119 | def check_binaries(): | 131 | def check_binaries(): |
@@ -131,58 +143,56 @@ def check_binaries(): | |||
131 | def check_submodules(): | 143 | def check_submodules(): |
132 | """Iterates through all submodules to make sure they're cloned and up to date. | 144 | """Iterates through all submodules to make sure they're cloned and up to date. |
133 | """ | 145 | """ |
134 | ok = True | ||
135 | |||
136 | for submodule in submodules.status().values(): | 146 | for submodule in submodules.status().values(): |
137 | if submodule['status'] is None: | 147 | if submodule['status'] is None: |
138 | cli.log.error('Submodule %s has not yet been cloned!', submodule['name']) | 148 | cli.log.error('Submodule %s has not yet been cloned!', submodule['name']) |
139 | ok = False | 149 | return CheckStatus.ERROR |
140 | elif not submodule['status']: | 150 | elif not submodule['status']: |
141 | cli.log.error('Submodule %s is not up to date!', submodule['name']) | 151 | cli.log.warning('Submodule %s is not up to date!', submodule['name']) |
142 | ok = False | 152 | return CheckStatus.WARNING |
143 | 153 | ||
144 | return ok | 154 | return CheckStatus.OK |
145 | 155 | ||
146 | 156 | ||
147 | def check_udev_rules(): | 157 | def check_udev_rules(): |
148 | """Make sure the udev rules look good. | 158 | """Make sure the udev rules look good. |
149 | """ | 159 | """ |
150 | ok = True | 160 | rc = CheckStatus.OK |
151 | udev_dir = Path("/etc/udev/rules.d/") | 161 | udev_dir = Path("/etc/udev/rules.d/") |
152 | desired_rules = { | 162 | desired_rules = { |
153 | 'atmel-dfu': { | 163 | 'atmel-dfu': { |
154 | _udev_rule("03EB", "2FEF"), # ATmega16U2 | 164 | _udev_rule("03eb", "2fef"), # ATmega16U2 |
155 | _udev_rule("03EB", "2FF0"), # ATmega32U2 | 165 | _udev_rule("03eb", "2ff0"), # ATmega32U2 |
156 | _udev_rule("03EB", "2FF3"), # ATmega16U4 | 166 | _udev_rule("03eb", "2ff3"), # ATmega16U4 |
157 | _udev_rule("03EB", "2FF4"), # ATmega32U4 | 167 | _udev_rule("03eb", "2ff4"), # ATmega32U4 |
158 | _udev_rule("03EB", "2FF9"), # AT90USB64 | 168 | _udev_rule("03eb", "2ff9"), # AT90USB64 |
159 | _udev_rule("03EB", "2FFB") # AT90USB128 | 169 | _udev_rule("03eb", "2ffb") # AT90USB128 |
160 | }, | 170 | }, |
161 | 'kiibohd': {_udev_rule("1C11", "B007")}, | 171 | 'kiibohd': {_udev_rule("1c11", "b007")}, |
162 | 'stm32': { | 172 | 'stm32': { |
163 | _udev_rule("1EAF", "0003"), # STM32duino | 173 | _udev_rule("1eaf", "0003"), # STM32duino |
164 | _udev_rule("0483", "DF11") # STM32 DFU | 174 | _udev_rule("0483", "df11") # STM32 DFU |
165 | }, | 175 | }, |
166 | 'bootloadhid': {_udev_rule("16C0", "05DF")}, | 176 | 'bootloadhid': {_udev_rule("16c0", "05df")}, |
167 | 'usbasploader': {_udev_rule("16C0", "05DC")}, | 177 | 'usbasploader': {_udev_rule("16c0", "05dc")}, |
168 | 'massdrop': {_udev_rule("03EB", "6124", 'ENV{ID_MM_DEVICE_IGNORE}="1"')}, | 178 | 'massdrop': {_udev_rule("03eb", "6124", 'ENV{ID_MM_DEVICE_IGNORE}="1"')}, |
169 | 'caterina': { | 179 | 'caterina': { |
170 | # Spark Fun Electronics | 180 | # Spark Fun Electronics |
171 | _udev_rule("1B4F", "9203", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 3V3/8MHz | 181 | _udev_rule("1b4f", "9203", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 3V3/8MHz |
172 | _udev_rule("1B4F", "9205", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 5V/16MHz | 182 | _udev_rule("1b4f", "9205", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Pro Micro 5V/16MHz |
173 | _udev_rule("1B4F", "9207", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # LilyPad 3V3/8MHz (and some Pro Micro clones) | 183 | _udev_rule("1b4f", "9207", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # LilyPad 3V3/8MHz (and some Pro Micro clones) |
174 | # Pololu Electronics | 184 | # Pololu EleCTRONICS |
175 | _udev_rule("1FFB", "0101", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # A-Star 32U4 | 185 | _udev_rule("1ffb", "0101", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # A-Star 32U4 |
176 | # Arduino SA | 186 | # Arduino SA |
177 | _udev_rule("2341", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Leonardo | 187 | _udev_rule("2341", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Leonardo |
178 | _udev_rule("2341", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Micro | 188 | _udev_rule("2341", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Micro |
179 | # Adafruit Industries LLC | 189 | # Adafruit INDUSTRIES llC |
180 | _udev_rule("239A", "000C", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Feather 32U4 | 190 | _udev_rule("239a", "000c", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Feather 32U4 |
181 | _udev_rule("239A", "000D", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # ItsyBitsy 32U4 3V3/8MHz | 191 | _udev_rule("239a", "000d", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # ItsyBitsy 32U4 3V3/8MHz |
182 | _udev_rule("239A", "000E", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # ItsyBitsy 32U4 5V/16MHz | 192 | _udev_rule("239a", "000e", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # ItsyBitsy 32U4 5V/16MHz |
183 | # dog hunter AG | 193 | # dog hunter ag |
184 | _udev_rule("2A03", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Leonardo | 194 | _udev_rule("2a03", "0036", 'ENV{ID_MM_DEVICE_IGNORE}="1"'), # Leonardo |
185 | _udev_rule("2A03", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"') # Micro | 195 | _udev_rule("2a03", "0037", 'ENV{ID_MM_DEVICE_IGNORE}="1"') # Micro |
186 | } | 196 | } |
187 | } | 197 | } |
188 | 198 | ||
@@ -209,31 +219,43 @@ def check_udev_rules(): | |||
209 | 219 | ||
210 | # Check if the desired rules are among the currently present rules | 220 | # Check if the desired rules are among the currently present rules |
211 | for bootloader, rules in desired_rules.items(): | 221 | for bootloader, rules in desired_rules.items(): |
212 | # For caterina, check if ModemManager is running | ||
213 | if bootloader == "caterina": | ||
214 | if check_modem_manager(): | ||
215 | ok = False | ||
216 | cli.log.warn("{bg_yellow}Detected ModemManager without the necessary udev rules. Please either disable it or set the appropriate udev rules if you are using a Pro Micro.") | ||
217 | if not rules.issubset(current_rules): | 222 | if not rules.issubset(current_rules): |
218 | deprecated_rule = deprecated_rules.get(bootloader) | 223 | deprecated_rule = deprecated_rules.get(bootloader) |
219 | if deprecated_rule and deprecated_rule.issubset(current_rules): | 224 | if deprecated_rule and deprecated_rule.issubset(current_rules): |
220 | cli.log.warn("{bg_yellow}Found old, deprecated udev rules for '%s' boards. The new rules on https://docs.qmk.fm/#/faq_build?id=linux-udev-rules offer better security with the same functionality.", bootloader) | 225 | cli.log.warning("{fg_yellow}Found old, deprecated udev rules for '%s' boards. The new rules on https://docs.qmk.fm/#/faq_build?id=linux-udev-rules offer better security with the same functionality.", bootloader) |
221 | else: | 226 | else: |
222 | cli.log.warn("{bg_yellow}Missing udev rules for '%s' boards. See https://docs.qmk.fm/#/faq_build?id=linux-udev-rules for more details.", bootloader) | 227 | # For caterina, check if ModemManager is running |
228 | if bootloader == "caterina": | ||
229 | if check_modem_manager(): | ||
230 | rc = CheckStatus.WARNING | ||
231 | cli.log.warning("{fg_yellow}Detected ModemManager without the necessary udev rules. Please either disable it or set the appropriate udev rules if you are using a Pro Micro.") | ||
232 | rc = CheckStatus.WARNING | ||
233 | cli.log.warning("{fg_yellow}Missing or outdated udev rules for '%s' boards. Run 'sudo cp %s/util/udev/50-qmk.rules /etc/udev/rules.d/'.", bootloader, QMK_FIRMWARE) | ||
223 | 234 | ||
224 | return ok | 235 | else: |
236 | cli.log.warning("{fg_yellow}'%s' does not exist. Skipping udev rule checking...", udev_dir) | ||
237 | |||
238 | return rc | ||
239 | |||
240 | |||
241 | def check_systemd(): | ||
242 | """Check if it's a systemd system | ||
243 | """ | ||
244 | return bool(shutil.which("systemctl")) | ||
225 | 245 | ||
226 | 246 | ||
227 | def check_modem_manager(): | 247 | def check_modem_manager(): |
228 | """Returns True if ModemManager is running. | 248 | """Returns True if ModemManager is running. |
249 | |||
229 | """ | 250 | """ |
230 | if shutil.which("systemctl"): | 251 | if check_systemd(): |
231 | mm_check = run(["systemctl", "--quiet", "is-active", "ModemManager.service"], timeout=10) | 252 | mm_check = run(["systemctl", "--quiet", "is-active", "ModemManager.service"], timeout=10) |
232 | if mm_check.returncode == 0: | 253 | if mm_check.returncode == 0: |
233 | return True | 254 | return True |
234 | |||
235 | else: | 255 | else: |
236 | cli.log.warn("Can't find systemctl to check for ModemManager.") | 256 | """(TODO): Add check for non-systemd systems |
257 | """ | ||
258 | return False | ||
237 | 259 | ||
238 | 260 | ||
239 | def is_executable(command): | 261 | def is_executable(command): |
@@ -263,12 +285,8 @@ def os_test_linux(): | |||
263 | """Run the Linux specific tests. | 285 | """Run the Linux specific tests. |
264 | """ | 286 | """ |
265 | cli.log.info("Detected {fg_cyan}Linux.") | 287 | cli.log.info("Detected {fg_cyan}Linux.") |
266 | ok = True | ||
267 | 288 | ||
268 | if not check_udev_rules(): | 289 | return check_udev_rules() |
269 | ok = False | ||
270 | |||
271 | return ok | ||
272 | 290 | ||
273 | 291 | ||
274 | def os_test_macos(): | 292 | def os_test_macos(): |
@@ -276,7 +294,7 @@ def os_test_macos(): | |||
276 | """ | 294 | """ |
277 | cli.log.info("Detected {fg_cyan}macOS.") | 295 | cli.log.info("Detected {fg_cyan}macOS.") |
278 | 296 | ||
279 | return True | 297 | return CheckStatus.OK |
280 | 298 | ||
281 | 299 | ||
282 | def os_test_windows(): | 300 | def os_test_windows(): |
@@ -284,7 +302,7 @@ def os_test_windows(): | |||
284 | """ | 302 | """ |
285 | cli.log.info("Detected {fg_cyan}Windows.") | 303 | cli.log.info("Detected {fg_cyan}Windows.") |
286 | 304 | ||
287 | return True | 305 | return CheckStatus.OK |
288 | 306 | ||
289 | 307 | ||
290 | @cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.') | 308 | @cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.') |
@@ -299,23 +317,20 @@ def doctor(cli): | |||
299 | * [ ] Compile a trivial program with each compiler | 317 | * [ ] Compile a trivial program with each compiler |
300 | """ | 318 | """ |
301 | cli.log.info('QMK Doctor is checking your environment.') | 319 | cli.log.info('QMK Doctor is checking your environment.') |
302 | ok = True | 320 | status = CheckStatus.OK |
303 | 321 | ||
304 | # Determine our OS and run platform specific tests | 322 | # Determine our OS and run platform specific tests |
305 | platform_id = platform.platform().lower() | 323 | platform_id = platform.platform().lower() |
306 | 324 | ||
307 | if 'darwin' in platform_id or 'macos' in platform_id: | 325 | if 'darwin' in platform_id or 'macos' in platform_id: |
308 | if not os_test_macos(): | 326 | status = os_test_macos() |
309 | ok = False | ||
310 | elif 'linux' in platform_id: | 327 | elif 'linux' in platform_id: |
311 | if not os_test_linux(): | 328 | status = os_test_linux() |
312 | ok = False | ||
313 | elif 'windows' in platform_id: | 329 | elif 'windows' in platform_id: |
314 | if not os_test_windows(): | 330 | status = os_test_windows() |
315 | ok = False | ||
316 | else: | 331 | else: |
317 | cli.log.error('Unsupported OS detected: %s', platform_id) | 332 | cli.log.warning('Unsupported OS detected: %s', platform_id) |
318 | ok = False | 333 | status = CheckStatus.WARNING |
319 | 334 | ||
320 | cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE) | 335 | cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE) |
321 | 336 | ||
@@ -330,31 +345,41 @@ def doctor(cli): | |||
330 | if bin_ok: | 345 | if bin_ok: |
331 | cli.log.info('All dependencies are installed.') | 346 | cli.log.info('All dependencies are installed.') |
332 | else: | 347 | else: |
333 | ok = False | 348 | status = CheckStatus.ERROR |
334 | 349 | ||
335 | # Make sure the tools are at the correct version | 350 | # Make sure the tools are at the correct version |
351 | ver_ok = [] | ||
336 | for check in (check_arm_gcc_version, check_avr_gcc_version, check_avrdude_version, check_dfu_util_version, check_dfu_programmer_version): | 352 | for check in (check_arm_gcc_version, check_avr_gcc_version, check_avrdude_version, check_dfu_util_version, check_dfu_programmer_version): |
337 | if not check(): | 353 | ver_ok.append(check()) |
338 | ok = False | 354 | |
355 | if CheckStatus.ERROR in ver_ok: | ||
356 | status = CheckStatus.ERROR | ||
357 | elif CheckStatus.WARNING in ver_ok and status == CheckStatus.OK: | ||
358 | status = CheckStatus.WARNING | ||
339 | 359 | ||
340 | # Check out the QMK submodules | 360 | # Check out the QMK submodules |
341 | sub_ok = check_submodules() | 361 | sub_ok = check_submodules() |
342 | 362 | ||
343 | if sub_ok: | 363 | if sub_ok == CheckStatus.OK: |
344 | cli.log.info('Submodules are up to date.') | 364 | cli.log.info('Submodules are up to date.') |
345 | else: | 365 | else: |
346 | if yesno('Would you like to clone the submodules?', default=True): | 366 | if yesno('Would you like to clone the submodules?', default=True): |
347 | submodules.update() | 367 | submodules.update() |
348 | sub_ok = check_submodules() | 368 | sub_ok = check_submodules() |
349 | 369 | ||
350 | if not sub_ok: | 370 | if CheckStatus.ERROR in sub_ok: |
351 | ok = False | 371 | status = CheckStatus.ERROR |
372 | elif CheckStatus.WARNING in sub_ok and status == CheckStatus.OK: | ||
373 | status = CheckStatus.WARNING | ||
352 | 374 | ||
353 | # Report a summary of our findings to the user | 375 | # Report a summary of our findings to the user |
354 | if ok: | 376 | if status == CheckStatus.OK: |
355 | cli.log.info('{fg_green}QMK is ready to go') | 377 | cli.log.info('{fg_green}QMK is ready to go') |
378 | return 0 | ||
379 | elif status == CheckStatus.WARNING: | ||
380 | cli.log.info('{fg_yellow}QMK is ready to go, but minor problems were found') | ||
381 | return 1 | ||
356 | else: | 382 | else: |
357 | cli.log.info('{fg_yellow}Problems detected, please fix these problems before proceeding.') | 383 | cli.log.info('{fg_red}Major problems detected, please fix these problems before proceeding.') |
358 | # FIXME(skullydazed/unclaimed): Link to a document about troubleshooting, or discord or something | 384 | cli.log.info('{fg_blue}Check out the FAQ (https://docs.qmk.fm/#/faq_build) or join the QMK Discord (https://discord.gg/Uq7gcHh) for help.') |
359 | 385 | return 2 | |
360 | return ok | ||
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index df5f047da..dd0c572a7 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py | |||
@@ -13,14 +13,14 @@ def check_subcommand(command, *args): | |||
13 | return result | 13 | return result |
14 | 14 | ||
15 | 15 | ||
16 | def check_returncode(result, expected=0): | 16 | def check_returncode(result, expected=[0]): |
17 | """Print stdout if `result.returncode` does not match `expected`. | 17 | """Print stdout if `result.returncode` does not match `expected`. |
18 | """ | 18 | """ |
19 | if result.returncode != expected: | 19 | if result.returncode not in expected: |
20 | print('`%s` stdout:' % ' '.join(result.args)) | 20 | print('`%s` stdout:' % ' '.join(result.args)) |
21 | print(result.stdout) | 21 | print(result.stdout) |
22 | print('returncode:', result.returncode) | 22 | print('returncode:', result.returncode) |
23 | assert result.returncode == expected | 23 | assert result.returncode in expected |
24 | 24 | ||
25 | 25 | ||
26 | def test_cformat(): | 26 | def test_cformat(): |
@@ -45,7 +45,7 @@ def test_flash(): | |||
45 | 45 | ||
46 | def test_flash_bootloaders(): | 46 | def test_flash_bootloaders(): |
47 | result = check_subcommand('flash', '-b') | 47 | result = check_subcommand('flash', '-b') |
48 | check_returncode(result, 1) | 48 | check_returncode(result, [1]) |
49 | 49 | ||
50 | 50 | ||
51 | def test_config(): | 51 | def test_config(): |
@@ -62,7 +62,7 @@ def test_kle2json(): | |||
62 | 62 | ||
63 | def test_doctor(): | 63 | def test_doctor(): |
64 | result = check_subcommand('doctor', '-n') | 64 | result = check_subcommand('doctor', '-n') |
65 | check_returncode(result) | 65 | check_returncode(result, [0, 1]) |
66 | assert 'QMK Doctor is checking your environment.' in result.stdout | 66 | assert 'QMK Doctor is checking your environment.' in result.stdout |
67 | assert 'QMK is ready to go' in result.stdout | 67 | assert 'QMK is ready to go' in result.stdout |
68 | 68 | ||
@@ -89,43 +89,43 @@ def test_list_keyboards(): | |||
89 | 89 | ||
90 | def test_list_keymaps(): | 90 | def test_list_keymaps(): |
91 | result = check_subcommand('list-keymaps', '-kb', 'handwired/onekey/pytest') | 91 | result = check_subcommand('list-keymaps', '-kb', 'handwired/onekey/pytest') |
92 | check_returncode(result, 0) | 92 | check_returncode(result) |
93 | assert 'default' and 'test' in result.stdout | 93 | assert 'default' and 'test' in result.stdout |
94 | 94 | ||
95 | 95 | ||
96 | def test_list_keymaps_long(): | 96 | def test_list_keymaps_long(): |
97 | result = check_subcommand('list-keymaps', '--keyboard', 'handwired/onekey/pytest') | 97 | result = check_subcommand('list-keymaps', '--keyboard', 'handwired/onekey/pytest') |
98 | check_returncode(result, 0) | 98 | check_returncode(result) |
99 | assert 'default' and 'test' in result.stdout | 99 | assert 'default' and 'test' in result.stdout |
100 | 100 | ||
101 | 101 | ||
102 | def test_list_keymaps_kb_only(): | 102 | def test_list_keymaps_kb_only(): |
103 | result = check_subcommand('list-keymaps', '-kb', 'niu_mini') | 103 | result = check_subcommand('list-keymaps', '-kb', 'niu_mini') |
104 | check_returncode(result, 0) | 104 | check_returncode(result) |
105 | assert 'default' and 'via' in result.stdout | 105 | assert 'default' and 'via' in result.stdout |
106 | 106 | ||
107 | 107 | ||
108 | def test_list_keymaps_vendor_kb(): | 108 | def test_list_keymaps_vendor_kb(): |
109 | result = check_subcommand('list-keymaps', '-kb', 'ai03/lunar') | 109 | result = check_subcommand('list-keymaps', '-kb', 'ai03/lunar') |
110 | check_returncode(result, 0) | 110 | check_returncode(result) |
111 | assert 'default' and 'via' in result.stdout | 111 | assert 'default' and 'via' in result.stdout |
112 | 112 | ||
113 | 113 | ||
114 | def test_list_keymaps_vendor_kb_rev(): | 114 | def test_list_keymaps_vendor_kb_rev(): |
115 | result = check_subcommand('list-keymaps', '-kb', 'kbdfans/kbd67/mkiirgb/v2') | 115 | result = check_subcommand('list-keymaps', '-kb', 'kbdfans/kbd67/mkiirgb/v2') |
116 | check_returncode(result, 0) | 116 | check_returncode(result) |
117 | assert 'default' and 'via' in result.stdout | 117 | assert 'default' and 'via' in result.stdout |
118 | 118 | ||
119 | 119 | ||
120 | def test_list_keymaps_no_keyboard_found(): | 120 | def test_list_keymaps_no_keyboard_found(): |
121 | result = check_subcommand('list-keymaps', '-kb', 'asdfghjkl') | 121 | result = check_subcommand('list-keymaps', '-kb', 'asdfghjkl') |
122 | check_returncode(result, 1) | 122 | check_returncode(result, [1]) |
123 | assert 'does not exist' in result.stdout | 123 | assert 'does not exist' in result.stdout |
124 | 124 | ||
125 | 125 | ||
126 | def test_json2c(): | 126 | def test_json2c(): |
127 | result = check_subcommand('json2c', 'keyboards/handwired/onekey/keymaps/default_json/keymap.json') | 127 | result = check_subcommand('json2c', 'keyboards/handwired/onekey/keymaps/default_json/keymap.json') |
128 | check_returncode(result, 0) | 128 | check_returncode(result) |
129 | assert result.stdout == '#include QMK_KEYBOARD_H\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\t[0] = LAYOUT_ortho_1x1(KC_A)};\n\n' | 129 | assert result.stdout == '#include QMK_KEYBOARD_H\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\t[0] = LAYOUT_ortho_1x1(KC_A)};\n\n' |
130 | 130 | ||
131 | 131 | ||