aboutsummaryrefslogtreecommitdiff
path: root/lib/python
diff options
context:
space:
mode:
authorErovia <Erovia@users.noreply.github.com>2020-11-16 21:09:32 +0000
committerGitHub <noreply@github.com>2020-11-16 21:09:32 +0000
commitb337ba798e23876870f8daf415bc929c0b5382fa (patch)
tree60ad63e1a8b8a33583f67a3617ad10c7c117c3c9 /lib/python
parent94e94ffb5bbe61b5da4aad205016923746010b23 (diff)
downloadqmk_firmware-b337ba798e23876870f8daf415bc929c0b5382fa.tar.gz
qmk_firmware-b337ba798e23876870f8daf415bc929c0b5382fa.zip
CLI: Udev related fixes and improvements (#10736)
Diffstat (limited to 'lib/python')
-rwxr-xr-xlib/python/qmk/cli/doctor.py187
-rw-r--r--lib/python/qmk/tests/test_cli_commands.py24
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
7import shutil 7import shutil
8import subprocess 8import subprocess
9from pathlib import Path 9from pathlib import Path
10from enum import Enum
10 11
11from milc import cli 12from milc import cli
12from qmk import submodules 13from qmk import submodules
@@ -14,6 +15,13 @@ from qmk.constants import QMK_FIRMWARE
14from qmk.questions import yesno 15from qmk.questions import yesno
15from qmk.commands import run 16from qmk.commands import run
16 17
18
19class CheckStatus(Enum):
20 OK = 1
21 WARNING = 2
22 ERROR = 3
23
24
17ESSENTIAL_BINARIES = { 25ESSENTIAL_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
75def check_avr_gcc_version(): 86def 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
92def check_avrdude_version(): 104def 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
101def check_dfu_util_version(): 113def 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
110def check_dfu_programmer_version(): 122def 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
119def check_binaries(): 131def check_binaries():
@@ -131,58 +143,56 @@ def check_binaries():
131def check_submodules(): 143def 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
147def check_udev_rules(): 157def 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
241def check_systemd():
242 """Check if it's a systemd system
243 """
244 return bool(shutil.which("systemctl"))
225 245
226 246
227def check_modem_manager(): 247def 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
239def is_executable(command): 261def 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
274def os_test_macos(): 292def 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
282def os_test_windows(): 300def 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
16def check_returncode(result, expected=0): 16def 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
26def test_cformat(): 26def test_cformat():
@@ -45,7 +45,7 @@ def test_flash():
45 45
46def test_flash_bootloaders(): 46def 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
51def test_config(): 51def test_config():
@@ -62,7 +62,7 @@ def test_kle2json():
62 62
63def test_doctor(): 63def 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
90def test_list_keymaps(): 90def 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
96def test_list_keymaps_long(): 96def 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
102def test_list_keymaps_kb_only(): 102def 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
108def test_list_keymaps_vendor_kb(): 108def 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
114def test_list_keymaps_vendor_kb_rev(): 114def 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
120def test_list_keymaps_no_keyboard_found(): 120def 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
126def test_json2c(): 126def 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