diff options
Diffstat (limited to 'lib/python/qmk/tests/test_cli_commands.py')
| -rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py new file mode 100644 index 0000000000..2716459989 --- /dev/null +++ b/lib/python/qmk/tests/test_cli_commands.py | |||
| @@ -0,0 +1,408 @@ | |||
| 1 | import platform | ||
| 2 | from subprocess import DEVNULL | ||
| 3 | |||
| 4 | from milc import cli | ||
| 5 | |||
| 6 | is_windows = 'windows' in platform.platform().lower() | ||
| 7 | |||
| 8 | |||
| 9 | def check_subcommand(command, *args): | ||
| 10 | cmd = ['qmk', command, *args] | ||
| 11 | result = cli.run(cmd, stdin=DEVNULL, combined_output=True) | ||
| 12 | return result | ||
| 13 | |||
| 14 | |||
| 15 | def check_subcommand_stdin(file_to_read, command, *args): | ||
| 16 | """Pipe content of a file to a command and return output. | ||
| 17 | """ | ||
| 18 | with open(file_to_read, encoding='utf-8') as my_file: | ||
| 19 | cmd = ['qmk', command, *args] | ||
| 20 | result = cli.run(cmd, stdin=my_file, combined_output=True) | ||
| 21 | return result | ||
| 22 | |||
| 23 | |||
| 24 | def check_returncode(result, expected=[0]): | ||
| 25 | """Print stdout if `result.returncode` does not match `expected`. | ||
| 26 | """ | ||
| 27 | if result.returncode not in expected: | ||
| 28 | print('`%s` stdout:' % ' '.join(result.args)) | ||
| 29 | print(result.stdout) | ||
| 30 | print('returncode:', result.returncode) | ||
| 31 | assert result.returncode in expected | ||
| 32 | |||
| 33 | |||
| 34 | def test_format_c(): | ||
| 35 | result = check_subcommand('format-c', '-n', 'quantum/matrix.c') | ||
| 36 | check_returncode(result) | ||
| 37 | |||
| 38 | |||
| 39 | def test_format_c_all(): | ||
| 40 | result = check_subcommand('format-c', '-n', '-a') | ||
| 41 | check_returncode(result, [0, 1]) | ||
| 42 | |||
| 43 | |||
| 44 | def test_compile(): | ||
| 45 | result = check_subcommand('compile', '-kb', 'handwired/pytest/basic', '-km', 'default', '-n') | ||
| 46 | check_returncode(result) | ||
| 47 | |||
| 48 | |||
| 49 | def test_compile_json(): | ||
| 50 | result = check_subcommand('compile', '-kb', 'handwired/pytest/basic', '-km', 'default_json', '-n') | ||
| 51 | check_returncode(result) | ||
| 52 | |||
| 53 | |||
| 54 | def test_flash(): | ||
| 55 | result = check_subcommand('flash', '-kb', 'handwired/pytest/basic', '-km', 'default', '-n') | ||
| 56 | check_returncode(result) | ||
| 57 | |||
| 58 | |||
| 59 | def test_flash_bootloaders(): | ||
| 60 | result = check_subcommand('flash', '-b') | ||
| 61 | check_returncode(result, [1]) | ||
| 62 | |||
| 63 | |||
| 64 | def test_kle2json(): | ||
| 65 | result = check_subcommand('kle2json', 'lib/python/qmk/tests/kle.txt', '-f') | ||
| 66 | check_returncode(result) | ||
| 67 | assert 'Wrote out' in result.stdout | ||
| 68 | |||
| 69 | |||
| 70 | def test_doctor(): | ||
| 71 | result = check_subcommand('doctor', '-n') | ||
| 72 | check_returncode(result, [0, 1]) | ||
| 73 | assert 'QMK Doctor is checking your environment.' in result.stdout | ||
| 74 | assert 'QMK is ready to go' in result.stdout | ||
| 75 | |||
| 76 | |||
| 77 | def test_hello(): | ||
| 78 | result = check_subcommand('hello') | ||
| 79 | check_returncode(result) | ||
| 80 | assert 'Hello,' in result.stdout | ||
| 81 | |||
| 82 | |||
| 83 | def test_format_python(): | ||
| 84 | result = check_subcommand('format-python', '-n', '-a') | ||
| 85 | check_returncode(result) | ||
| 86 | assert 'Successfully formatted the python code.' in result.stdout | ||
| 87 | |||
| 88 | |||
| 89 | def test_list_keyboards(): | ||
| 90 | result = check_subcommand('list-keyboards') | ||
| 91 | check_returncode(result) | ||
| 92 | # check to see if a known keyboard is returned | ||
| 93 | # this will fail if handwired/pytest/basic is removed | ||
| 94 | assert 'handwired/pytest/basic' in result.stdout | ||
| 95 | |||
| 96 | |||
| 97 | def test_list_keymaps(): | ||
| 98 | result = check_subcommand('list-keymaps', '-kb', 'handwired/pytest/basic') | ||
| 99 | check_returncode(result) | ||
| 100 | assert 'default' in result.stdout | ||
| 101 | assert 'default_json' in result.stdout | ||
| 102 | |||
| 103 | |||
| 104 | def test_list_keymaps_long(): | ||
| 105 | result = check_subcommand('list-keymaps', '--keyboard', 'handwired/pytest/basic') | ||
| 106 | check_returncode(result) | ||
| 107 | assert 'default' in result.stdout | ||
| 108 | assert 'default_json' in result.stdout | ||
| 109 | |||
| 110 | |||
| 111 | def test_list_keymaps_community(): | ||
| 112 | result = check_subcommand('list-keymaps', '--keyboard', 'handwired/pytest/has_community') | ||
| 113 | check_returncode(result) | ||
| 114 | assert 'test' in result.stdout | ||
| 115 | |||
| 116 | |||
| 117 | def test_list_keymaps_kb_only(): | ||
| 118 | result = check_subcommand('list-keymaps', '-kb', 'contra') | ||
| 119 | check_returncode(result) | ||
| 120 | assert 'default' in result.stdout | ||
| 121 | |||
| 122 | |||
| 123 | def test_list_keymaps_vendor_kb(): | ||
| 124 | result = check_subcommand('list-keymaps', '-kb', 'ai03/lunar') | ||
| 125 | check_returncode(result) | ||
| 126 | assert 'default' in result.stdout | ||
| 127 | |||
| 128 | |||
| 129 | def test_list_keymaps_vendor_kb_rev(): | ||
| 130 | result = check_subcommand('list-keymaps', '-kb', 'kbdfans/kbd67/mkiirgb/v2') | ||
| 131 | check_returncode(result) | ||
| 132 | assert 'default' in result.stdout | ||
| 133 | |||
| 134 | |||
| 135 | def test_list_keymaps_no_keyboard_found(): | ||
| 136 | result = check_subcommand('list-keymaps', '-kb', 'asdfghjkl') | ||
| 137 | check_returncode(result, [2]) | ||
| 138 | assert 'invalid keyboard_folder value' in result.stdout | ||
| 139 | |||
| 140 | |||
| 141 | def test_json2c(): | ||
| 142 | result = check_subcommand('json2c', 'keyboards/handwired/pytest/basic/keymaps/default_json/keymap.json') | ||
| 143 | check_returncode(result) | ||
| 144 | assert result.stdout == """#include QMK_KEYBOARD_H | ||
| 145 | #if __has_include("keymap.h") | ||
| 146 | # include "keymap.h" | ||
| 147 | #endif | ||
| 148 | |||
| 149 | |||
| 150 | /* THIS FILE WAS GENERATED! | ||
| 151 | * | ||
| 152 | * This file was generated by qmk json2c. You may or may not want to | ||
| 153 | * edit it directly. | ||
| 154 | */ | ||
| 155 | |||
| 156 | const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | ||
| 157 | [0] = LAYOUT_ortho_1x1(KC_A) | ||
| 158 | }; | ||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | #ifdef OTHER_KEYMAP_C | ||
| 164 | # include OTHER_KEYMAP_C | ||
| 165 | #endif // OTHER_KEYMAP_C | ||
| 166 | |||
| 167 | |||
| 168 | """ | ||
| 169 | |||
| 170 | |||
| 171 | def test_json2c_macros(): | ||
| 172 | result = check_subcommand("json2c", 'keyboards/handwired/pytest/macro/keymaps/default/keymap.json') | ||
| 173 | check_returncode(result) | ||
| 174 | assert 'LAYOUT_ortho_1x1(QK_MACRO_0)' in result.stdout | ||
| 175 | assert 'case QK_MACRO_0:' in result.stdout | ||
| 176 | assert 'SEND_STRING("Hello, World!"SS_TAP(X_ENTER));' in result.stdout | ||
| 177 | |||
| 178 | |||
| 179 | def test_json2c_stdin(): | ||
| 180 | result = check_subcommand_stdin('keyboards/handwired/pytest/basic/keymaps/default_json/keymap.json', 'json2c', '-') | ||
| 181 | check_returncode(result) | ||
| 182 | assert result.stdout == """#include QMK_KEYBOARD_H | ||
| 183 | #if __has_include("keymap.h") | ||
| 184 | # include "keymap.h" | ||
| 185 | #endif | ||
| 186 | |||
| 187 | |||
| 188 | /* THIS FILE WAS GENERATED! | ||
| 189 | * | ||
| 190 | * This file was generated by qmk json2c. You may or may not want to | ||
| 191 | * edit it directly. | ||
| 192 | */ | ||
| 193 | |||
| 194 | const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | ||
| 195 | [0] = LAYOUT_ortho_1x1(KC_A) | ||
| 196 | }; | ||
| 197 | |||
| 198 | |||
| 199 | |||
| 200 | |||
| 201 | #ifdef OTHER_KEYMAP_C | ||
| 202 | # include OTHER_KEYMAP_C | ||
| 203 | #endif // OTHER_KEYMAP_C | ||
| 204 | |||
| 205 | |||
| 206 | """ | ||
| 207 | |||
| 208 | |||
| 209 | def test_json2c_no_json(): | ||
| 210 | result = check_subcommand('json2c', 'keyboards/handwired/pytest/basic/keymaps/default/keymap.c') | ||
| 211 | check_returncode(result, [1]) | ||
| 212 | assert 'Invalid JSON encountered' in result.stdout | ||
| 213 | |||
| 214 | |||
| 215 | def test_info(): | ||
| 216 | result = check_subcommand('info', '-kb', 'handwired/pytest/basic') | ||
| 217 | check_returncode(result) | ||
| 218 | assert 'Keyboard Name: pytest' in result.stdout | ||
| 219 | assert 'Processor: atmega32u4' in result.stdout | ||
| 220 | assert 'Layout:' not in result.stdout | ||
| 221 | assert 'k0' not in result.stdout | ||
| 222 | |||
| 223 | |||
| 224 | def test_info_keyboard_render(): | ||
| 225 | result = check_subcommand('info', '-kb', 'handwired/pytest/basic', '-l') | ||
| 226 | check_returncode(result) | ||
| 227 | assert 'Keyboard Name: pytest' in result.stdout | ||
| 228 | assert 'Processor: atmega32u4' in result.stdout | ||
| 229 | assert 'Layouts:' in result.stdout | ||
| 230 | |||
| 231 | if is_windows: | ||
| 232 | assert '| |' in result.stdout | ||
| 233 | else: | ||
| 234 | assert '│ │' in result.stdout | ||
| 235 | |||
| 236 | |||
| 237 | def test_info_keymap_render(): | ||
| 238 | result = check_subcommand('info', '-kb', 'handwired/pytest/basic', '-km', 'default_json') | ||
| 239 | check_returncode(result) | ||
| 240 | assert 'Keyboard Name: pytest' in result.stdout | ||
| 241 | assert 'Processor: atmega32u4' in result.stdout | ||
| 242 | |||
| 243 | if is_windows: | ||
| 244 | assert '|A |' in result.stdout | ||
| 245 | else: | ||
| 246 | assert '│A │' in result.stdout | ||
| 247 | |||
| 248 | |||
| 249 | def test_info_matrix_render(): | ||
| 250 | result = check_subcommand('info', '-kb', 'handwired/pytest/basic', '-m') | ||
| 251 | check_returncode(result) | ||
| 252 | assert 'Keyboard Name: pytest' in result.stdout | ||
| 253 | assert 'Processor: atmega32u4' in result.stdout | ||
| 254 | assert 'LAYOUT_ortho_1x1' in result.stdout | ||
| 255 | |||
| 256 | if is_windows: | ||
| 257 | assert '|0A|' in result.stdout | ||
| 258 | else: | ||
| 259 | assert '│0A│' in result.stdout | ||
| 260 | |||
| 261 | assert 'Matrix for "LAYOUT_ortho_1x1"' in result.stdout | ||
| 262 | |||
| 263 | |||
| 264 | def test_c2json(): | ||
| 265 | result = check_subcommand("c2json", "-kb", "handwired/pytest/basic", "-km", "default", "keyboards/handwired/pytest/basic/keymaps/default/keymap.c") | ||
| 266 | check_returncode(result) | ||
| 267 | assert result.stdout.strip() == '{"keyboard": "handwired/pytest/basic", "keymap": "default", "layout": "LAYOUT_ortho_1x1", "layers": [["KC_A"]]}' | ||
| 268 | |||
| 269 | |||
| 270 | def test_c2json_stdin(): | ||
| 271 | result = check_subcommand_stdin("keyboards/handwired/pytest/basic/keymaps/default/keymap.c", "c2json", "-kb", "handwired/pytest/basic", "-km", "default", "-") | ||
| 272 | check_returncode(result) | ||
| 273 | assert result.stdout.strip() == '{"keyboard": "handwired/pytest/basic", "keymap": "default", "layout": "LAYOUT_ortho_1x1", "layers": [["KC_A"]]}' | ||
| 274 | |||
| 275 | |||
| 276 | def test_clean(): | ||
| 277 | result = check_subcommand('clean', '-a') | ||
| 278 | check_returncode(result) | ||
| 279 | assert (result.stdout.count('done') == 2 and 'userspace' not in result.stdout) or (result.stdout.count('done') == 3 and 'userspace' in result.stdout) | ||
| 280 | |||
| 281 | |||
| 282 | def test_generate_api(): | ||
| 283 | result = check_subcommand('generate-api', '--dry-run', '--filter', 'handwired/pytest') | ||
| 284 | check_returncode(result) | ||
| 285 | |||
| 286 | |||
| 287 | def test_generate_rgb_breathe_table(): | ||
| 288 | result = check_subcommand("generate-rgb-breathe-table", "-c", "1.2", "-m", "127") | ||
| 289 | check_returncode(result) | ||
| 290 | assert 'Breathing center: 1.2' in result.stdout | ||
| 291 | assert 'Breathing max: 127' in result.stdout | ||
| 292 | |||
| 293 | |||
| 294 | def test_generate_config_h(): | ||
| 295 | result = check_subcommand('generate-config-h', '-kb', 'handwired/pytest/basic') | ||
| 296 | check_returncode(result) | ||
| 297 | assert '# define DEVICE_VER 0x0001' in result.stdout | ||
| 298 | assert '# define DIODE_DIRECTION COL2ROW' in result.stdout | ||
| 299 | assert '# define MANUFACTURER "none"' in result.stdout | ||
| 300 | assert '# define PRODUCT "pytest"' in result.stdout | ||
| 301 | assert '# define PRODUCT_ID 0x6465' in result.stdout | ||
| 302 | assert '# define VENDOR_ID 0xFEED' in result.stdout | ||
| 303 | assert '# define MATRIX_COLS 1' in result.stdout | ||
| 304 | assert '# define MATRIX_COL_PINS { F4 }' in result.stdout | ||
| 305 | assert '# define MATRIX_ROWS 1' in result.stdout | ||
| 306 | assert '# define MATRIX_ROW_PINS { F5 }' in result.stdout | ||
| 307 | |||
| 308 | |||
| 309 | def test_generate_rules_mk(): | ||
| 310 | result = check_subcommand('generate-rules-mk', '-kb', 'handwired/pytest/basic') | ||
| 311 | check_returncode(result) | ||
| 312 | assert 'BOOTLOADER ?= atmel-dfu' in result.stdout | ||
| 313 | assert 'MCU ?= atmega32u4' in result.stdout | ||
| 314 | |||
| 315 | |||
| 316 | def test_generate_version_h(): | ||
| 317 | result = check_subcommand('generate-version-h') | ||
| 318 | check_returncode(result) | ||
| 319 | assert '#define QMK_VERSION' in result.stdout | ||
| 320 | |||
| 321 | |||
| 322 | def test_format_json_keyboard(): | ||
| 323 | result = check_subcommand('format-json', '--format', 'keyboard', 'lib/python/qmk/tests/minimal_info.json') | ||
| 324 | check_returncode(result) | ||
| 325 | assert result.stdout == '{\n "keyboard_name": "tester",\n "maintainer": "qmk",\n "layouts": {\n "LAYOUT": {\n "layout": [\n {"label": "KC_A", "matrix": [0, 0], "x": 0, "y": 0}\n ]\n }\n }\n}\n' | ||
| 326 | |||
| 327 | |||
| 328 | def test_format_json_keymap(): | ||
| 329 | result = check_subcommand('format-json', '--format', 'keymap', 'lib/python/qmk/tests/minimal_keymap.json') | ||
| 330 | check_returncode(result) | ||
| 331 | assert result.stdout == '{\n "version": 1,\n "keyboard": "handwired/pytest/basic",\n "keymap": "test",\n "layout": "LAYOUT_ortho_1x1",\n "layers": [\n [\n "KC_A"\n ]\n ]\n}\n' | ||
| 332 | |||
| 333 | |||
| 334 | def test_format_json_keyboard_auto(): | ||
| 335 | result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_info.json') | ||
| 336 | check_returncode(result) | ||
| 337 | assert result.stdout == '{\n "keyboard_name": "tester",\n "maintainer": "qmk",\n "layouts": {\n "LAYOUT": {\n "layout": [\n {"label": "KC_A", "matrix": [0, 0], "x": 0, "y": 0}\n ]\n }\n }\n}\n' | ||
| 338 | |||
| 339 | |||
| 340 | def test_format_json_keymap_auto(): | ||
| 341 | result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_keymap.json') | ||
| 342 | check_returncode(result) | ||
| 343 | assert result.stdout == '{\n "keyboard": "handwired/pytest/basic",\n "keymap": "test",\n "layers": [\n ["KC_A"]\n ],\n "layout": "LAYOUT_ortho_1x1",\n "version": 1\n}\n' | ||
| 344 | |||
| 345 | |||
| 346 | def test_find_exists(): | ||
| 347 | result = check_subcommand('find', '-f', 'exists(rgb_matrix.split_count)', '-p', 'rgb_matrix.split_count') | ||
| 348 | check_returncode(result) | ||
| 349 | values = [s for s in result.stdout.splitlines() if 'rgb_matrix.split_count=' in s] | ||
| 350 | assert len(values) > 0 | ||
| 351 | for s in values: | ||
| 352 | assert '=None' not in s | ||
| 353 | assert '=[' in s | ||
| 354 | |||
| 355 | |||
| 356 | def test_find_absent(): | ||
| 357 | result = check_subcommand('find', '-f', 'absent(rgb_matrix.split_count)', '-p', 'rgb_matrix.split_count') | ||
| 358 | check_returncode(result) | ||
| 359 | values = [s for s in result.stdout.splitlines() if 'rgb_matrix.split_count=' in s] | ||
| 360 | assert len(values) > 0 | ||
| 361 | for s in values: | ||
| 362 | assert '=None' in s | ||
| 363 | assert '=[' not in s | ||
| 364 | |||
| 365 | |||
| 366 | def test_find_length(): | ||
| 367 | result = check_subcommand('find', '-f', 'length(matrix_pins.cols, 6)', '-p', 'matrix_pins.cols') | ||
| 368 | check_returncode(result) | ||
| 369 | values = [s for s in result.stdout.splitlines() if 'matrix_pins.cols=' in s] | ||
| 370 | assert len(values) > 0 | ||
| 371 | for s in values: | ||
| 372 | assert s.count(',') == 5 | ||
| 373 | |||
| 374 | |||
| 375 | def test_find_contains(): | ||
| 376 | result = check_subcommand('find', '-f', 'contains(matrix_pins.cols, B1)', '-p', 'matrix_pins.cols') | ||
| 377 | check_returncode(result) | ||
| 378 | values = [s for s in result.stdout.splitlines() if 'matrix_pins.cols=' in s] | ||
| 379 | assert len(values) > 0 | ||
| 380 | for s in values: | ||
| 381 | assert "'B1'" in s | ||
| 382 | |||
| 383 | |||
| 384 | def test_find_multiple_conditions(): | ||
| 385 | # this is intended to match at least 'crkbd/rev1' | ||
| 386 | result = check_subcommand( | ||
| 387 | 'find', '-f', 'exists(rgb_matrix.split_count)', '-f', 'contains(matrix_pins.cols, B1)', '-f', 'length(matrix_pins.cols, 6)', '-f', 'absent(eeprom.driver)', '-f', 'ws2812.pin == D3', '-p', 'rgb_matrix.split_count', '-p', 'matrix_pins.cols', '-p', | ||
| 388 | 'eeprom.driver', '-p', 'ws2812.pin' | ||
| 389 | ) | ||
| 390 | check_returncode(result) | ||
| 391 | rgb_matrix_split_count_values = [s for s in result.stdout.splitlines() if 'rgb_matrix.split_count=' in s] | ||
| 392 | assert len(rgb_matrix_split_count_values) > 0 | ||
| 393 | for s in rgb_matrix_split_count_values: | ||
| 394 | assert '=None' not in s | ||
| 395 | assert '=[' in s | ||
| 396 | matrix_pins_cols_values = [s for s in result.stdout.splitlines() if 'matrix_pins.cols=' in s] | ||
| 397 | assert len(matrix_pins_cols_values) > 0 | ||
| 398 | for s in matrix_pins_cols_values: | ||
| 399 | assert s.count(',') == 5 | ||
| 400 | assert "'B1'" in s | ||
| 401 | eeprom_driver_values = [s for s in result.stdout.splitlines() if 'eeprom.driver=' in s] | ||
| 402 | assert len(eeprom_driver_values) > 0 | ||
| 403 | for s in eeprom_driver_values: | ||
| 404 | assert '=None' in s | ||
| 405 | ws2812_pin_values = [s for s in result.stdout.splitlines() if 'ws2812.pin=' in s] | ||
| 406 | assert len(ws2812_pin_values) > 0 | ||
| 407 | for s in ws2812_pin_values: | ||
| 408 | assert '=D3' in s | ||
