From 3713dc7f239ec96d2ee563b107c8144c67fbaa8d Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sat, 17 Oct 2020 05:28:49 -0400 Subject: [PATCH] Add testing support for keyboard class --- src/main/python/keyboard.py | 25 ++++--- src/main/python/test/__init__.py | 0 src/main/python/test/test_keyboard.py | 102 ++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 src/main/python/test/__init__.py create mode 100644 src/main/python/test/test_keyboard.py diff --git a/src/main/python/keyboard.py b/src/main/python/keyboard.py index 4b63b4b..5b5165e 100644 --- a/src/main/python/keyboard.py +++ b/src/main/python/keyboard.py @@ -3,6 +3,7 @@ import struct import json import lzma +from collections import OrderedDict from kle_serial import Serial as KleSerial from util import MSG_LEN, hid_send @@ -20,10 +21,12 @@ CMD_VIAL_GET_DEFINITION = 0x02 class Keyboard: """ Low-level communication with a vial-enabled keyboard """ - def __init__(self, dev): + def __init__(self, dev, usb_send=hid_send): self.dev = dev + self.usb_send = usb_send - self.rowcol = set() + # n.b. using OrderedDict here to make order of layout requests consistent for tests + self.rowcol = OrderedDict() self.layout = dict() self.layers = 0 self.keys = [] @@ -31,30 +34,30 @@ class Keyboard: def reload(self): """ Load information about the keyboard: number of layers, physical key layout """ - self.rowcol = set() + self.rowcol = OrderedDict() self.layout = dict() - self.reload_layers() self.reload_layout() + self.reload_layers() self.reload_keymap() def reload_layers(self): """ Get how many layers the keyboard has """ - self.layers = hid_send(self.dev, struct.pack("B", CMD_VIA_GET_LAYER_COUNT))[1] + self.layers = self.usb_send(self.dev, struct.pack("B", CMD_VIA_GET_LAYER_COUNT))[1] def reload_layout(self): """ Requests layout data from the current device """ # get the size - data = hid_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_SIZE)) + data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_SIZE)) sz = struct.unpack(" 0: - data = hid_send(self.dev, struct.pack("H", data[4:6])[0] self.layout[(layer, row, col)] = keycode def set_key(self, layer, row, col, code): - hid_send(self.dev, struct.pack(">BBBBH", CMD_VIA_SET_KEYCODE, layer, row, col, code)) + self.usb_send(self.dev, struct.pack(">BBBBH", CMD_VIA_SET_KEYCODE, layer, row, col, code)) self.layout[(layer, row, col)] = code diff --git a/src/main/python/test/__init__.py b/src/main/python/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/main/python/test/test_keyboard.py b/src/main/python/test/test_keyboard.py new file mode 100644 index 0000000..9709995 --- /dev/null +++ b/src/main/python/test/test_keyboard.py @@ -0,0 +1,102 @@ +import unittest +import lzma +import struct + + +from keyboard import Keyboard + + +LAYOUT_2x2 = """ +{"name":"test","vendorId":"0x0000","productId":"0x1111","lighting":"none","matrix":{"rows":2,"cols":2},"layouts":{"keymap":[["0,0","0,1"],["1,0","1,1"]]}} +""" + + +def chunks(data, sz): + for i in range(0, len(data), sz): + yield data[i:i+sz] + + +class SimulatedDevice: + + def __init__(self): + # sequence of keyboard communications, pairs of (request, response) + self.expect_data = [] + # current index in communications + self.expect_idx = 0 + + def expect(self, inp, out): + if isinstance(inp, str): + inp = bytes.fromhex(inp) + if isinstance(out, str): + out = bytes.fromhex(out) + self.expect_data.append((inp, out)) + + def expect_layout(self, layout): + compressed = lzma.compress(layout.encode("utf-8")) + self.expect("FE01", struct.pack("BBBBH", 4, l, r, c, col)) + + @staticmethod + def sim_send(dev, data): + if dev.expect_idx >= len(dev.expect_data): + raise Exception("Trying to communicate more times ({}) than expected ({}); got data={}".format( + dev.expect_idx + 1, + len(dev.expect_data), + data.hex() + )) + inp, out = dev.expect_data[dev.expect_idx] + if data != inp: + raise Exception("Got unexpected data at index {}: expected={} got={}".format( + dev.expect_idx, + data.hex(), + inp.hex() + )) + dev.expect_idx += 1 + return out + + def finish(self): + if self.expect_idx != len(self.expect_data): + raise Exception("Didn't communicate all the way, remaining data = {}".format( + self.expect_data[self.expect_idx:] + )) + + +class TestKeyboard(unittest.TestCase): + + @staticmethod + def prepare_keyboard(layout, keymap): + dev = SimulatedDevice() + dev.expect_layout(layout) + dev.expect_layers(len(keymap)) + dev.expect_keymap(keymap) + + kb = Keyboard(dev, dev.sim_send) + kb.reload() + + return kb, dev + + def test_keyboard_layout(self): + kb, dev = self.prepare_keyboard(LAYOUT_2x2, [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) + self.assertEqual(kb.layers, 2) + self.assertEqual(kb.layout[(0, 0, 0)], 1) + self.assertEqual(kb.layout[(0, 0, 1)], 2) + self.assertEqual(kb.layout[(0, 1, 0)], 3) + self.assertEqual(kb.layout[(0, 1, 1)], 4) + self.assertEqual(kb.layout[(1, 0, 0)], 5) + self.assertEqual(kb.layout[(1, 0, 1)], 6) + self.assertEqual(kb.layout[(1, 1, 0)], 7) + self.assertEqual(kb.layout[(1, 1, 1)], 8) + dev.finish()