浏览代码

Setup a python test framework

skullY 5 年之前
父节点
当前提交
5b7a5b2a76

+ 7 - 1
bin/qmk

@@ -94,4 +94,10 @@ else:
         exit(1)
         exit(1)
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
-    milc.cli()
+    return_code = milc.cli()
+    if return_code is False:
+        exit(1)
+    elif return_code is not True and isinstance(return_code, int) and return_code < 256:
+        exit(return_code)
+    else:
+        exit(0)

+ 23 - 0
keyboards/handwired/onekey/pytest/config.h

@@ -0,0 +1,23 @@
+/* Copyright 2019
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "config_common.h"
+
+#define MATRIX_COL_PINS { A3 }
+#define MATRIX_ROW_PINS { A2 }
+#define UNUSED_PINS

+ 3 - 0
keyboards/handwired/onekey/pytest/readme.md

@@ -0,0 +1,3 @@
+# PyTest onekey
+
+This is used by the python test framework. It's probably not useful otherwise.

+ 2 - 0
keyboards/handwired/onekey/pytest/rules.mk

@@ -0,0 +1,2 @@
+# MCU name
+MCU = STM32F303

+ 1 - 0
keyboards/handwired/onekey/pytest/templates/keymap.c

@@ -0,0 +1 @@
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {__KEYMAP_GOES_HERE__};

+ 1 - 2
lib/python/milc.py

@@ -534,8 +534,7 @@ class MILC(object):
         if not self._inside_context_manager:
         if not self._inside_context_manager:
             # If they didn't use the context manager use it ourselves
             # If they didn't use the context manager use it ourselves
             with self:
             with self:
-                self.__call__()
-                return
+                return self.__call__()
 
 
         if not self._entrypoint:
         if not self._entrypoint:
             raise RuntimeError('No entrypoint provided!')
             raise RuntimeError('No entrypoint provided!')

+ 22 - 8
lib/python/qmk/cli/doctor.py

@@ -2,9 +2,11 @@
 
 
 Check up for QMK environment.
 Check up for QMK environment.
 """
 """
-import shutil
-import platform
 import os
 import os
+import platform
+import shutil
+import subprocess
+from glob import glob
 
 
 from milc import cli
 from milc import cli
 
 
@@ -16,32 +18,44 @@ def main(cli):
     This is currently very simple, it just checks that all the expected binaries are on your system.
     This is currently very simple, it just checks that all the expected binaries are on your system.
 
 
     TODO(unclaimed):
     TODO(unclaimed):
-        * [ ] Run the binaries to make sure they work
         * [ ] Compile a trivial program with each compiler
         * [ ] Compile a trivial program with each compiler
         * [ ] Check for udev entries on linux
         * [ ] Check for udev entries on linux
     """
     """
 
 
     binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc']
     binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc']
+    binaries += glob('bin/qmk-*')
 
 
-    cli.log.info('QMK Doctor is Checking your environment')
+    cli.log.info('QMK Doctor is checking your environment')
 
 
     ok = True
     ok = True
     for binary in binaries:
     for binary in binaries:
         res = shutil.which(binary)
         res = shutil.which(binary)
         if res is None:
         if res is None:
-            cli.log.error('{fg_red}QMK can\'t find ' + binary + ' in your path')
+            cli.log.error("{fg_red}QMK can't find %s in your path", binary)
             ok = False
             ok = False
+        else:
+            try:
+                subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5, check=True)
+            except subprocess.CalledProcessError:
+                cli.log.error("{fg_red}Can't run `%s --version`", binary)
+                ok = False
 
 
     OS = platform.system()
     OS = platform.system()
     if OS == "Darwin":
     if OS == "Darwin":
         cli.log.info("Detected {fg_cyan}macOS")
         cli.log.info("Detected {fg_cyan}macOS")
     elif OS == "Linux":
     elif OS == "Linux":
         cli.log.info("Detected {fg_cyan}linux")
         cli.log.info("Detected {fg_cyan}linux")
-        test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
-        if os.system(test) == 0:
-            cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
+        if shutil.which('systemctl'):
+            test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
+            if os.system(test) == 0:
+                cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
+        else:
+            cli.log.warn("Can't find systemctl to check for ModemManager.")
     else:
     else:
         cli.log.info("Assuming {fg_cyan}Windows")
         cli.log.info("Assuming {fg_cyan}Windows")
 
 
     if ok:
     if ok:
         cli.log.info('{fg_green}QMK is ready to go')
         cli.log.info('{fg_green}QMK is ready to go')
+    else:
+        cli.log.info('{fg_yellow}Problems detected, please fix these problems before proceeding.')
+        # FIXME(skullydazed): Link to a document about troubleshooting, or discord or something

+ 18 - 0
lib/python/qmk/cli/nose2.py

@@ -0,0 +1,18 @@
+"""QMK Python Unit Tests
+
+QMK script to run unit and integration tests against our python code.
+"""
+from milc import cli
+
+
+@cli.entrypoint('QMK Python Unit Tests')
+def main(cli):
+    """Use nose2 to run unittests
+    """
+    try:
+        import nose2
+    except ImportError:
+        cli.log.error('Could not import nose2! Please install it with {fg_cyan}pip3 install nose2')
+        return False
+
+    nose2.discover()

+ 0 - 0
lib/python/qmk/tests/__init__.py


+ 8 - 0
lib/python/qmk/tests/attrdict.py

@@ -0,0 +1,8 @@
+class AttrDict(dict):
+    """A dictionary that can be accessed by attributes.
+
+    This should only be used to mock objects for unit testing. Please do not use this outside of qmk.tests.
+    """
+    def __init__(self, *args, **kwargs):
+        super(AttrDict, self).__init__(*args, **kwargs)
+        self.__dict__ = self

+ 6 - 0
lib/python/qmk/tests/onekey_export.json

@@ -0,0 +1,6 @@
+{
+    "keyboard":"handwired/onekey/pytest",
+    "keymap":"pytest_unittest",
+    "layout":"LAYOUT",
+    "layers":[["KC_A"]]
+}

+ 7 - 0
lib/python/qmk/tests/test_qmk_errors.py

@@ -0,0 +1,7 @@
+from qmk.errors import NoSuchKeyboardError
+
+def test_NoSuchKeyboardError():
+    try:
+        raise(NoSuchKeyboardError("test message"))
+    except NoSuchKeyboardError as e:
+        assert e.message == 'test message'

+ 18 - 0
lib/python/qmk/tests/test_qmk_keymap.py

@@ -0,0 +1,18 @@
+import qmk.keymap
+
+def test_template_onekey_proton_c():
+    templ = qmk.keymap.template('handwired/onekey/proton_c')
+    assert templ == qmk.keymap.DEFAULT_KEYMAP_C
+
+
+def test_template_onekey_pytest():
+    templ = qmk.keymap.template('handwired/onekey/pytest')
+    assert templ == 'const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {__KEYMAP_GOES_HERE__};\n'
+
+
+def test_generate_onekey_pytest():
+    templ = qmk.keymap.generate('handwired/onekey/pytest', 'LAYOUT', [['KC_A']])
+    assert templ == 'const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {	[0] = LAYOUT(KC_A)};\n'
+
+
+# FIXME(skullydazed): Add a test for qmk.keymap.write that mocks up an FD.

+ 12 - 0
lib/python/qmk/tests/test_qmk_path.py

@@ -0,0 +1,12 @@
+import os
+
+import qmk.path
+
+def test_keymap_onekey_pytest():
+    path = qmk.path.keymap('handwired/onekey/pytest')
+    assert path == 'keyboards/handwired/onekey/keymaps'
+
+
+def test_normpath():
+    path = qmk.path.normpath('lib/python')
+    assert path == os.environ['ORIG_CWD'] + '/lib/python'

+ 2 - 0
nose2.cfg

@@ -0,0 +1,2 @@
+[unittest]
+start-dir = lib/python/qmk/tests