5b7a5b2a7629fbb667d23a55836dce3c6c46a203

Author
skullY <skullydazed@gmail.com>
Committer
skullydazed <skullydazed@users.noreply.github.com>
Date

Message

Setup a python test framework

Diff

  1diff --git a/bin/qmk b/bin/qmk
  2index 3c00a675eb16b4ab652009073470c9f80c0e7773..1aa16e17dcccf512390d75880f7d9a07e093b45a 100755
  3--- a/bin/qmk
  4+++ b/bin/qmk
  5@@ -94,4 +94,10 @@ else:
  6         exit(1)
  7 
  8 if __name__ == '__main__':
  9-    milc.cli()
 10+    return_code = milc.cli()
 11+    if return_code is False:
 12+        exit(1)
 13+    elif return_code is not True and isinstance(return_code, int) and return_code < 256:
 14+        exit(return_code)
 15+    else:
 16+        exit(0)
 17diff --git a/keyboards/handwired/onekey/pytest/config.h b/keyboards/handwired/onekey/pytest/config.h
 18new file mode 100644
 19index 0000000000000000000000000000000000000000..f6bedcfe64812b0330624c00049d7abcf67c8925
 20--- /dev/null
 21+++ b/keyboards/handwired/onekey/pytest/config.h
 22@@ -0,0 +1,23 @@
 23+/* Copyright 2019
 24+ *
 25+ * This program is free software: you can redistribute it and/or modify
 26+ * it under the terms of the GNU General Public License as published by
 27+ * the Free Software Foundation, either version 2 of the License, or
 28+ * (at your option) any later version.
 29+ *
 30+ * This program is distributed in the hope that it will be useful,
 31+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 32+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 33+ * GNU General Public License for more details.
 34+ *
 35+ * You should have received a copy of the GNU General Public License
 36+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 37+ */
 38+
 39+#pragma once
 40+
 41+#include "config_common.h"
 42+
 43+#define MATRIX_COL_PINS { A3 }
 44+#define MATRIX_ROW_PINS { A2 }
 45+#define UNUSED_PINS
 46diff --git a/keyboards/handwired/onekey/pytest/readme.md b/keyboards/handwired/onekey/pytest/readme.md
 47new file mode 100644
 48index 0000000000000000000000000000000000000000..dbbd991f1c3778a3078f1ed6831e7273d8e5838f
 49--- /dev/null
 50+++ b/keyboards/handwired/onekey/pytest/readme.md
 51@@ -0,0 +1,3 @@
 52+# PyTest onekey
 53+
 54+This is used by the python test framework. It's probably not useful otherwise.
 55diff --git a/keyboards/handwired/onekey/pytest/rules.mk b/keyboards/handwired/onekey/pytest/rules.mk
 56new file mode 100644
 57index 0000000000000000000000000000000000000000..b17a3d0316b57f873d2c36a5916e34ce2cb10f83
 58--- /dev/null
 59+++ b/keyboards/handwired/onekey/pytest/rules.mk
 60@@ -0,0 +1,2 @@
 61+# MCU name
 62+MCU = STM32F303
 63diff --git a/keyboards/handwired/onekey/pytest/templates/keymap.c b/keyboards/handwired/onekey/pytest/templates/keymap.c
 64new file mode 100644
 65index 0000000000000000000000000000000000000000..d355210c47f72b941219b50897a58d417a9984b3
 66--- /dev/null
 67+++ b/keyboards/handwired/onekey/pytest/templates/keymap.c
 68@@ -0,0 +1 @@
 69+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {__KEYMAP_GOES_HERE__};
 70diff --git a/lib/python/milc.py b/lib/python/milc.py
 71index 6e82edf8b1935b79bc6a79f996c9c32db2de9268..c62c1b166c4215fcf5e85fc80c36e74ad2384fa1 100644
 72--- a/lib/python/milc.py
 73+++ b/lib/python/milc.py
 74@@ -534,8 +534,7 @@ class MILC(object):
 75         if not self._inside_context_manager:
 76             # If they didn't use the context manager use it ourselves
 77             with self:
 78-                self.__call__()
 79-                return
 80+                return self.__call__()
 81 
 82         if not self._entrypoint:
 83             raise RuntimeError('No entrypoint provided!')
 84diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py
 85index 9ce765a4b517d759832649719c86da0da62664f3..c5a144363c95a22865ad7e8ab3d6169971867736 100755
 86--- a/lib/python/qmk/cli/doctor.py
 87+++ b/lib/python/qmk/cli/doctor.py
 88@@ -2,9 +2,11 @@
 89 
 90 Check up for QMK environment.
 91 """
 92-import shutil
 93-import platform
 94 import os
 95+import platform
 96+import shutil
 97+import subprocess
 98+from glob import glob
 99 
100 from milc import cli
101 
102@@ -16,32 +18,44 @@ def main(cli):
103     This is currently very simple, it just checks that all the expected binaries are on your system.
104 
105     TODO(unclaimed):
106-        * [ ] Run the binaries to make sure they work
107         * [ ] Compile a trivial program with each compiler
108         * [ ] Check for udev entries on linux
109     """
110 
111     binaries = ['dfu-programmer', 'avrdude', 'dfu-util', 'avr-gcc', 'arm-none-eabi-gcc']
112+    binaries += glob('bin/qmk-*')
113 
114-    cli.log.info('QMK Doctor is Checking your environment')
115+    cli.log.info('QMK Doctor is checking your environment')
116 
117     ok = True
118     for binary in binaries:
119         res = shutil.which(binary)
120         if res is None:
121-            cli.log.error('{fg_red}QMK can\'t find ' + binary + ' in your path')
122+            cli.log.error("{fg_red}QMK can't find %s in your path", binary)
123             ok = False
124+        else:
125+            try:
126+                subprocess.run([binary, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5, check=True)
127+            except subprocess.CalledProcessError:
128+                cli.log.error("{fg_red}Can't run `%s --version`", binary)
129+                ok = False
130 
131     OS = platform.system()
132     if OS == "Darwin":
133         cli.log.info("Detected {fg_cyan}macOS")
134     elif OS == "Linux":
135         cli.log.info("Detected {fg_cyan}linux")
136-        test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
137-        if os.system(test) == 0:
138-            cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
139+        if shutil.which('systemctl'):
140+            test = 'systemctl list-unit-files | grep enabled | grep -i ModemManager'
141+            if os.system(test) == 0:
142+                cli.log.warn("{bg_yellow}Detected modem manager. Please disable it if you are using Pro Micros")
143+        else:
144+            cli.log.warn("Can't find systemctl to check for ModemManager.")
145     else:
146         cli.log.info("Assuming {fg_cyan}Windows")
147 
148     if ok:
149         cli.log.info('{fg_green}QMK is ready to go')
150+    else:
151+        cli.log.info('{fg_yellow}Problems detected, please fix these problems before proceeding.')
152+        # FIXME(skullydazed): Link to a document about troubleshooting, or discord or something
153diff --git a/lib/python/qmk/cli/nose2.py b/lib/python/qmk/cli/nose2.py
154new file mode 100644
155index 0000000000000000000000000000000000000000..c6c9c67b30a540b17652d1af621526a223b94e3c
156--- /dev/null
157+++ b/lib/python/qmk/cli/nose2.py
158@@ -0,0 +1,18 @@
159+"""QMK Python Unit Tests
160+
161+QMK script to run unit and integration tests against our python code.
162+"""
163+from milc import cli
164+
165+
166+@cli.entrypoint('QMK Python Unit Tests')
167+def main(cli):
168+    """Use nose2 to run unittests
169+    """
170+    try:
171+        import nose2
172+    except ImportError:
173+        cli.log.error('Could not import nose2! Please install it with {fg_cyan}pip3 install nose2')
174+        return False
175+
176+    nose2.discover()
177diff --git a/lib/python/qmk/tests/__init__.py b/lib/python/qmk/tests/__init__.py
178new file mode 100644
179index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
180--- /dev/null
181+++ b/lib/python/qmk/tests/__init__.py
182diff --git a/lib/python/qmk/tests/attrdict.py b/lib/python/qmk/tests/attrdict.py
183new file mode 100644
184index 0000000000000000000000000000000000000000..a2584b923337be0f9455e70ab0cf9d731abb6f17
185--- /dev/null
186+++ b/lib/python/qmk/tests/attrdict.py
187@@ -0,0 +1,8 @@
188+class AttrDict(dict):
189+    """A dictionary that can be accessed by attributes.
190+
191+    This should only be used to mock objects for unit testing. Please do not use this outside of qmk.tests.
192+    """
193+    def __init__(self, *args, **kwargs):
194+        super(AttrDict, self).__init__(*args, **kwargs)
195+        self.__dict__ = self
196diff --git a/lib/python/qmk/tests/onekey_export.json b/lib/python/qmk/tests/onekey_export.json
197new file mode 100644
198index 0000000000000000000000000000000000000000..95f0a980fe6338498842b5885cb99504f5b18a7a
199--- /dev/null
200+++ b/lib/python/qmk/tests/onekey_export.json
201@@ -0,0 +1,6 @@
202+{
203+    "keyboard":"handwired/onekey/pytest",
204+    "keymap":"pytest_unittest",
205+    "layout":"LAYOUT",
206+    "layers":[["KC_A"]]
207+}
208diff --git a/lib/python/qmk/tests/test_qmk_errors.py b/lib/python/qmk/tests/test_qmk_errors.py
209new file mode 100644
210index 0000000000000000000000000000000000000000..3f6b567137c14b4879a2bfd107aba9c3ea40580d
211--- /dev/null
212+++ b/lib/python/qmk/tests/test_qmk_errors.py
213@@ -0,0 +1,7 @@
214+from qmk.errors import NoSuchKeyboardError
215+
216+def test_NoSuchKeyboardError():
217+    try:
218+        raise(NoSuchKeyboardError("test message"))
219+    except NoSuchKeyboardError as e:
220+        assert e.message == 'test message'
221diff --git a/lib/python/qmk/tests/test_qmk_keymap.py b/lib/python/qmk/tests/test_qmk_keymap.py
222new file mode 100644
223index 0000000000000000000000000000000000000000..6a565ee900c1d3b5ddf147c19b7ed514ad40d400
224--- /dev/null
225+++ b/lib/python/qmk/tests/test_qmk_keymap.py
226@@ -0,0 +1,18 @@
227+import qmk.keymap
228+
229+def test_template_onekey_proton_c():
230+    templ = qmk.keymap.template('handwired/onekey/proton_c')
231+    assert templ == qmk.keymap.DEFAULT_KEYMAP_C
232+
233+
234+def test_template_onekey_pytest():
235+    templ = qmk.keymap.template('handwired/onekey/pytest')
236+    assert templ == 'const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {__KEYMAP_GOES_HERE__};\n'
237+
238+
239+def test_generate_onekey_pytest():
240+    templ = qmk.keymap.generate('handwired/onekey/pytest', 'LAYOUT', [['KC_A']])
241+    assert templ == 'const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {	[0] = LAYOUT(KC_A)};\n'
242+
243+
244+# FIXME(skullydazed): Add a test for qmk.keymap.write that mocks up an FD.
245diff --git a/lib/python/qmk/tests/test_qmk_path.py b/lib/python/qmk/tests/test_qmk_path.py
246new file mode 100644
247index 0000000000000000000000000000000000000000..23816be7ede28cf9b4b6efdf700f096a5d4548d9
248--- /dev/null
249+++ b/lib/python/qmk/tests/test_qmk_path.py
250@@ -0,0 +1,12 @@
251+import os
252+
253+import qmk.path
254+
255+def test_keymap_onekey_pytest():
256+    path = qmk.path.keymap('handwired/onekey/pytest')
257+    assert path == 'keyboards/handwired/onekey/keymaps'
258+
259+
260+def test_normpath():
261+    path = qmk.path.normpath('lib/python')
262+    assert path == os.environ['ORIG_CWD'] + '/lib/python'
263diff --git a/nose2.cfg b/nose2.cfg
264new file mode 100644
265index 0000000000000000000000000000000000000000..136354553bd63fab740f7b121c21e8f3852967bc
266--- /dev/null
267+++ b/nose2.cfg
268@@ -0,0 +1,2 @@
269+[unittest]
270+start-dir = lib/python/qmk/tests