Harald Albrecht
5 years ago
31 changed files with 493 additions and 266 deletions
@ -1,5 +1,37 @@ |
|||
std = { |
|||
-- Configuration for "luacheck"ing Multibow |
|||
|
|||
--[[ |
|||
Copyright 2019 Harald Albrecht |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
]]-- |
|||
|
|||
stds.multibow = { |
|||
read_globals = { |
|||
"insl", "require" |
|||
-- 3rd party stuff that doesn't exactly play by the new Lua module rules... |
|||
"keybow", |
|||
|
|||
-- our unit testing setup |
|||
"insl", "inslit" |
|||
} |
|||
} |
|||
|
|||
std = "max+multibow" |
|||
|
|||
exclude_files = { "spec/mock/keybow.lua" } |
|||
|
@ -0,0 +1,12 @@ |
|||
#!/bin/bash |
|||
hascmd() { |
|||
command -v "$1" >/dev/null |
|||
} |
|||
if ! hascmd busted || ! hascmd luacheck ; then |
|||
echo "missing busted TDD library and luacheck Lua static source code checker; trying to install..." |
|||
bash ./setup-tests.sh |
|||
fi |
|||
echo "testing..." |
|||
busted |
|||
echo "linting..." |
|||
luacheck -q ./sdcard ./spec |
@ -1,16 +0,0 @@ |
|||
local busted=require "busted" |
|||
|
|||
require "keybow" |
|||
busted.stub(keybow, "auto_lights") |
|||
busted.stub(keybow, "clear_lights") |
|||
busted.stub(keybow, "load_pattern") |
|||
busted.stub(keybow, "set_pixel") |
|||
-- keybow.set_pixel = function(led, r, g, b) print("LED #"..led..", "..r..", "..g..", "..b) end |
|||
busted.stub(keybow, "set_key") |
|||
busted.stub(keybow, "set_modifier") |
|||
busted.stub(keybow, "tap_key") |
|||
|
|||
busted.stub(keybow, "sleep") -- FIXME |
|||
busted.stub(keybow, "usleep") -- FIXME |
|||
|
|||
return keybow |
@ -0,0 +1,158 @@ |
|||
-- Multibow internal "module" implementing keymap-related management and |
|||
-- handling. |
|||
|
|||
--[[ |
|||
Copyright 2019 Harald Albrecht |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
]]-- |
|||
|
|||
-- luacheck: globals mb |
|||
|
|||
-- Internal variables for housekeeping... |
|||
|
|||
-- The registered keymaps, indexed by their names (.name field). |
|||
mb.keymaps = {} |
|||
-- The ordered sequence of primary keymap, in the sequence they were |
|||
-- registered. |
|||
mb.primary_keymaps = {} |
|||
-- The currently activated keymap. |
|||
mb.current_keymap = nil |
|||
-- A temporary keymap while grabbing. |
|||
mb.grab_keymap = nil |
|||
|
|||
-- Registers a keymap (by name), so it can be easily activated later by its name. |
|||
-- Multiple keymaps can be registered. Keymaps can be either "primary" by |
|||
-- default, or permanent or secondary keymaps. |
|||
-- |
|||
-- A primary keymap is any keymap without either a "permanent" or "secondary" |
|||
-- table element. Users can cycle through primary keymaps using the "shift" |
|||
-- permanent keyboard layout. |
|||
-- |
|||
-- permanent keymaps (marked by table element "permanent=true") are always |
|||
-- active, thus they don't need to be activated. |
|||
-- |
|||
-- Secondary keymaps (marked by table element "secondary=true") are intended |
|||
-- as SHIFT/modifier layers. As such the get ignored by cycling, but instead |
|||
-- need to be activated explicitly. The "shift" permanent keyboard layout |
|||
-- automates this. |
|||
-- |
|||
-- If this is the first keymap getting registered, then it will also made |
|||
-- activated. |
|||
function mb.register_keymap(keymap) |
|||
local name = keymap.name |
|||
-- register |
|||
mb.keymaps[name] = keymap |
|||
-- ensure that first registered keymap also automatically gets activated |
|||
-- (albeit the LEDs will only update later). Also maintain the (ordered) |
|||
-- sequence of registered primary keymaps. |
|||
if not (keymap.permanent or keymap.secondary) then |
|||
mb.current_keymap = mb.current_keymap or keymap |
|||
table.insert(mb.primary_keymaps, keymap) |
|||
end |
|||
end |
|||
|
|||
-- Returns the list of currently registered keymaps; this list is a table, |
|||
-- with its registered keymaps at indices 1, 2, ... |
|||
function mb.registered_keymaps() |
|||
local keymaps = {} |
|||
for _, keymap in pairs(mb.keymaps) do |
|||
table.insert(keymaps, keymap) |
|||
end |
|||
return keymaps |
|||
end |
|||
|
|||
-- Returns the list of currently registered *primary* keymaps, in the same order |
|||
-- as they were registered. First primary is at index 1, second at 2, ... |
|||
function mb.registered_primary_keymaps() |
|||
return mb.primary_keymaps |
|||
end |
|||
|
|||
-- Cycles through the available (primary) keymaps, ignoring secondary and |
|||
-- permanent keymaps. This is convenient for assigning primary keymap switching |
|||
-- using a key on the Keybow device itself. |
|||
function mb.cycle_primary_keymaps() |
|||
local km = mb.current_keymap |
|||
if km == nil then return end |
|||
-- If this is a secondary keymap, locate its corresponding primary keymap. |
|||
if km.secondary then |
|||
if not km.shift_to then |
|||
-- No SHIFT's shift_to cyclic chain available, so rely on the naming |
|||
-- schema instead and try to locate the primary keymap with the first |
|||
-- match instead. This assumes that the name of the secondary keymaps |
|||
-- have some suffix and thus are longer than the name of their |
|||
-- corresponding primary keymap. |
|||
for _, pkm in ipairs(mb.primary_keymaps) do |
|||
if string.sub(km.name, 1, #pkm.name) == pkm.name then |
|||
km = pkm |
|||
break |
|||
end |
|||
end |
|||
-- Checks if locating the primary keymap failed and then bails out |
|||
-- immediately. |
|||
if km.secondary then return end |
|||
else |
|||
-- Follows the cyclic chain of SHIFT's shift_to keymaps, until we get |
|||
-- to the primary keymap in the cycle, or until we have completed one |
|||
-- cycle. |
|||
repeat |
|||
km = km.shift_to |
|||
if not km or km == mb.current_keymap then |
|||
return |
|||
end |
|||
until not(km.secondary) |
|||
end |
|||
end |
|||
-- Move on to the next primary keymap, rolling over at the end of our list. |
|||
for idx, pkm in ipairs(mb.primary_keymaps) do |
|||
if pkm == km then |
|||
idx = idx + 1 |
|||
if idx > #mb.primary_keymaps then idx = 1 end |
|||
mb.activate_keymap(mb.primary_keymaps[idx].name) |
|||
end |
|||
end |
|||
end |
|||
|
|||
-- Activates a specific keymap by name. Please note that it isn't necessary |
|||
-- to "activate" permanent keymaps at all (and thus this deed cannot be done). |
|||
function mb.activate_keymap(name) |
|||
name = type(name) == "table" and name.name or name |
|||
local keymap = mb.keymaps[name] |
|||
if keymap and not keymap.permanent then |
|||
mb.current_keymap = keymap |
|||
mb.activate_leds() |
|||
end |
|||
end |
|||
|
|||
-- Sets a "grabbing" keymap that takes (temporarily) grabs all keys. While a |
|||
-- grab keymap is in place, key presses and releases will only be routed to |
|||
-- the grab keymap, but never to the permanent keymaps, nor the previously |
|||
-- "active" primary keymap. |
|||
function mb.grab(name) |
|||
name = type(name) == "table" and name.name or name |
|||
mb.grab_keymap = mb.keymaps[name] |
|||
mb.activate_leds() |
|||
end |
|||
|
|||
-- Removes a "grabbing" keymap, thus reactivating the permanent keymaps, as |
|||
-- well as the previously active primary keymap. |
|||
function mb.ungrab() |
|||
mb.grab_keymap = nil |
|||
mb.activate_leds() |
|||
end |
@ -0,0 +1,54 @@ |
|||
-- Mocks some parts of the Keybow Lua module during unit tests, so we can run |
|||
-- the tests outside the Keybow firmware on a standard (full-blown) Lua host |
|||
-- system. |
|||
|
|||
--[[ |
|||
Copyright 2019 Harald Albrecht |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
]]-- |
|||
|
|||
local busted=require("busted") |
|||
local sock=require("socket") |
|||
|
|||
require "keybow" |
|||
-- luacheck: globals keybow.no_delay |
|||
keybow.no_delay = keybow.no_delay or true |
|||
|
|||
busted.stub(keybow, "auto_lights") |
|||
busted.stub(keybow, "clear_lights") |
|||
busted.stub(keybow, "load_pattern") |
|||
busted.stub(keybow, "set_pixel") |
|||
busted.stub(keybow, "set_key") |
|||
busted.stub(keybow, "set_modifier") |
|||
busted.stub(keybow, "tap_key") |
|||
|
|||
-- luacheck: globals keybow.sleep |
|||
function keybow.sleep(ms) |
|||
if not keybow.no_delay then |
|||
sock.sleep(ms / 1000) |
|||
end |
|||
end |
|||
|
|||
-- luacheck: globals keybow.usleep |
|||
function keybow.usleep(us) |
|||
keybow.sleep(us / 1000) |
|||
end |
|||
|
|||
return keybow -- adhere to Lua's (new) module rules |
@ -0,0 +1,50 @@ |
|||
--[[ |
|||
Copyright 2019 Harald Albrecht |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
]]-- |
|||
|
|||
-- luacheck: globals keybow.no_delay |
|||
require("mocked-keybow") |
|||
|
|||
describe("Mocked Keybow API", function() |
|||
|
|||
local sock=require("socket") |
|||
|
|||
local sleep = function(time, factor, sf, on) |
|||
local old = keybow.no_delay |
|||
keybow.no_delay = not on |
|||
local start = sock.gettime() |
|||
sf(time) |
|||
local delay = (sock.gettime() - start) * factor |
|||
keybow.no_delay = old |
|||
return delay |
|||
end |
|||
|
|||
it("delays ms or not", function() |
|||
assert.is_true(sleep(10, 1000, keybow.sleep, true) >= 10) |
|||
assert.is_true(sleep(10, 1000, keybow.sleep, false) < 10) |
|||
end) |
|||
|
|||
it("delays us or not", function() |
|||
assert.is_true(sleep(10, 1000*1000, keybow.usleep, true) >= 10) |
|||
assert.is_true(sleep(10, 1000*1000, keybow.usleep, false) < 10) |
|||
end) |
|||
|
|||
end) |
Loading…
Reference in new issue