diff --git a/Makefile b/Makefile index c221cb89e..88f430619 100644 --- a/Makefile +++ b/Makefile @@ -378,11 +378,12 @@ define PARSE_ALL_KEYMAPS endef define BUILD_TEST - TEST_NAME := $1 + TEST_PATH := $1 + TEST_NAME := $$(notdir $$(TEST_PATH)) MAKE_TARGET := $2 COMMAND := $1 MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f build_test.mk $$(MAKE_TARGET) - MAKE_VARS := TEST=$$(TEST_NAME) FULL_TESTS="$$(FULL_TESTS)" + MAKE_VARS := TEST=$$(TEST_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS="$$(FULL_TESTS)" MAKE_MSG := $$(MSG_MAKE_TEST) $$(eval $$(call BUILD)) ifneq ($$(MAKE_TARGET),clean) @@ -406,7 +407,7 @@ define PARSE_TEST ifeq ($$(TEST_NAME),all) MATCHED_TESTS := $$(TEST_LIST) else - MATCHED_TESTS := $$(foreach TEST,$$(TEST_LIST),$$(if $$(findstring $$(TEST_NAME),$$(TEST)),$$(TEST),)) + MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring $$(TEST_NAME), $$(notdir $$(TEST))), $$(TEST),)) endif $$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET)))) endef diff --git a/build_full_test.mk b/build_full_test.mk index f8030cb06..4cd1ac61b 100644 --- a/build_full_test.mk +++ b/build_full_test.mk @@ -13,21 +13,24 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -#include $(TMK_PATH)/protocol.mk +$(TEST)_INC := \ + tests\test_common\common_config.h -TEST_PATH=tests/$(TEST) - -$(TEST)_SRC= \ - $(TEST_PATH)/keymap.c \ +$(TEST)_SRC := \ $(TMK_COMMON_SRC) \ $(QUANTUM_SRC) \ $(SRC) \ + tests/test_common/keymap.c \ tests/test_common/matrix.c \ tests/test_common/test_driver.cpp \ tests/test_common/keyboard_report_util.cpp \ - tests/test_common/test_fixture.cpp -$(TEST)_SRC += $(patsubst $(ROOTDIR)/%,%,$(wildcard $(TEST_PATH)/*.cpp)) + tests/test_common/test_fixture.cpp \ + tests/test_common/test_keymap_key.cpp \ + tests/test_common/test_logger.cpp \ + $(patsubst $(ROOTDIR)/%,%,$(wildcard $(TEST_PATH)/*.cpp)) -$(TEST)_DEFS=$(TMK_COMMON_DEFS) $(OPT_DEFS) -$(TEST)_CONFIG=$(TEST_PATH)/config.h -VPATH+=$(TOP_DIR)/tests/test_common +$(TEST)_DEFS := $(TMK_COMMON_DEFS) $(OPT_DEFS) + +$(TEST)_CONFIG := $(TEST_PATH)/config.h + +VPATH += $(TOP_DIR)/tests/test_common \ No newline at end of file diff --git a/build_test.mk b/build_test.mk index 36cb7936e..136a0455f 100644 --- a/build_test.mk +++ b/build_test.mk @@ -16,14 +16,14 @@ TEST_OBJ = $(BUILD_DIR)/test_obj OUTPUTS := $(TEST_OBJ)/$(TEST) $(GTEST_OUTPUT) GTEST_INC := \ - $(LIB_PATH)/googletest/googletest/include\ - $(LIB_PATH)/googletest/googlemock/include\ + $(LIB_PATH)/googletest/googletest/include \ + $(LIB_PATH)/googletest/googlemock/include -GTEST_INTERNAL_INC :=\ - $(LIB_PATH)/googletest/googletest\ +GTEST_INTERNAL_INC := \ + $(LIB_PATH)/googletest/googletest \ $(LIB_PATH)/googletest/googlemock -$(GTEST_OUTPUT)_SRC :=\ +$(GTEST_OUTPUT)_SRC := \ googletest/src/gtest-all.cc\ googlemock/src/gmock-all.cc @@ -33,9 +33,9 @@ $(GTEST_OUTPUT)_INC := $(GTEST_INC) $(GTEST_INTERNAL_INC) LDFLAGS += -lstdc++ -lpthread -shared-libgcc CREATE_MAP := no -VPATH +=\ - $(LIB_PATH)/googletest\ - $(LIB_PATH)/googlemock\ +VPATH += \ + $(LIB_PATH)/googletest \ + $(LIB_PATH)/googlemock \ $(LIB_PATH)/printf all: elf @@ -49,7 +49,8 @@ CONSOLE_ENABLE = yes endif ifneq ($(filter $(FULL_TESTS),$(TEST)),) -include tests/$(TEST)/rules.mk +include tests/test_common/build.mk +include $(TEST_PATH)/test.mk endif include common_features.mk diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index d013a6a16..cde97074d 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -558,40 +558,6 @@ enum quantum_keycodes { PROGRAMMABLE_BUTTON_31, PROGRAMMABLE_BUTTON_32, - // Dedicated macro keys for Configurator and VIA - MACRO_0, - MACRO_1, - MACRO_2, - MACRO_3, - MACRO_4, - MACRO_5, - MACRO_6, - MACRO_7, - MACRO_8, - MACRO_9, - MACRO_10, - MACRO_11, - MACRO_12, - MACRO_13, - MACRO_14, - MACRO_15, - MACRO_16, - MACRO_17, - MACRO_18, - MACRO_19, - MACRO_20, - MACRO_21, - MACRO_22, - MACRO_23, - MACRO_24, - MACRO_25, - MACRO_26, - MACRO_27, - MACRO_28, - MACRO_29, - MACRO_30, - MACRO_31, - // Start of custom keycode range for keyboards and keymaps - always leave at the end SAFE_RANGE }; diff --git a/testlist.mk b/testlist.mk index cff784dad..d2bb571be 100644 --- a/testlist.mk +++ b/testlist.mk @@ -1,5 +1,5 @@ -TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rules.mk))) -FULL_TESTS := $(TEST_LIST) +TEST_LIST = $(sort $(patsubst %/test.mk,%, $(shell find $(ROOT_DIR)tests -type f -name test.mk))) +FULL_TESTS := $(notdir $(TEST_LIST)) include $(QUANTUM_PATH)/debounce/tests/testlist.mk include $(QUANTUM_PATH)/encoder/tests/testlist.mk diff --git a/tests/auto_shift/config.h b/tests/auto_shift/config.h new file mode 100644 index 000000000..4f343b452 --- /dev/null +++ b/tests/auto_shift/config.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" \ No newline at end of file diff --git a/tests/auto_shift/test.mk b/tests/auto_shift/test.mk new file mode 100644 index 000000000..4259c606e --- /dev/null +++ b/tests/auto_shift/test.mk @@ -0,0 +1,20 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- + +AUTO_SHIFT_ENABLE = yes \ No newline at end of file diff --git a/tests/auto_shift/test_auto_shift.cpp b/tests/auto_shift/test_auto_shift.cpp new file mode 100644 index 000000000..9c5ed9a83 --- /dev/null +++ b/tests/auto_shift/test_auto_shift.cpp @@ -0,0 +1,73 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class AutoShift : public TestFixture {}; + +TEST_F(AutoShift, key_release_before_timeout) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({regular_key}); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(AutoShift, key_release_after_timeout) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({regular_key}); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + idle_for(AUTO_SHIFT_TIMEOUT); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/basic/config.h b/tests/basic/config.h index 99bd62d99..85fa9d691 100644 --- a/tests/basic/config.h +++ b/tests/basic/config.h @@ -16,5 +16,4 @@ #pragma once -#define MATRIX_ROWS 4 -#define MATRIX_COLS 10 +#include "test_common.h" \ No newline at end of file diff --git a/tests/basic/keymap.c b/tests/basic/keymap.c deleted file mode 100644 index 2b5747abb..000000000 --- a/tests/basic/keymap.c +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright 2017 Fred Sundvik - * - * 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 . - */ - -#include "quantum.h" - -// Don't rearrange keys as existing tests might rely on the order -// Col2, Row 0 has to be KC_NO, because tests rely on it - -#define COMBO1 RSFT(LCTL(KC_O)) - -const uint16_t PROGMEM - keymaps[][MATRIX_ROWS][MATRIX_COLS] = - { - [0] = - { - // 0 1 2 3 4 5 6 7 8 9 - {KC_A, KC_B, KC_NO, KC_LSFT, KC_RSFT, KC_LCTL, COMBO1, SFT_T(KC_P), M(0), KC_NO}, - {KC_EQL, KC_PLUS, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, - {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, - {KC_C, KC_D, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, - }, -}; - -const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { - if (record->event.pressed) { - switch (id) { - case 0: - return MACRO(D(LSFT), T(H), U(LSFT), T(E), T(L), T(L), T(O), T(SPACE), W(100), D(LSFT), T(W), U(LSFT), I(10), T(O), T(R), T(L), T(D), D(LSFT), T(1), U(LSFT), END); - } - } - return MACRO_NONE; -}; diff --git a/tests/basic/test.mk b/tests/basic/test.mk new file mode 100644 index 000000000..29690d1ad --- /dev/null +++ b/tests/basic/test.mk @@ -0,0 +1,18 @@ +# Copyright 2017 Fred Sundvik +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/basic/test_action_layer.cpp b/tests/basic/test_action_layer.cpp index d00a0859b..1b12d1364 100644 --- a/tests/basic/test_action_layer.cpp +++ b/tests/basic/test_action_layer.cpp @@ -14,39 +14,54 @@ * along with this program. If not, see . */ +#include "gtest/gtest.h" +#include "keyboard_report_util.hpp" #include "test_common.hpp" using testing::_; -using testing::Return; +using testing::InSequence; class ActionLayer : public TestFixture {}; -// TEST_F(ActionLayer, LayerStateDBG) { -// layer_state_set(0); -// } +TEST_F(ActionLayer, LayerStateDBG) { + TestDriver driver; -// TEST_F(ActionLayer, LayerStateSet) { -// layer_state_set(0); -// EXPECT_EQ(layer_state, 0); -// layer_state_set(0b001100); -// EXPECT_EQ(layer_state, 0b001100); -// } + layer_state_set(0); -// TEST_F(ActionLayer, LayerStateIs) { -// layer_state_set(0); -// EXPECT_EQ(layer_state_is(0), true); -// EXPECT_EQ(layer_state_is(1), true); -// layer_state_set(1); -// EXPECT_EQ(layer_state_is(0), true); -// EXPECT_EQ(layer_state_is(1), true); -// layer_state_set(2); -// EXPECT_EQ(layer_state_is(0), false); -// EXPECT_EQ(layer_state_is(1), false); -// EXPECT_EQ(layer_state_is(2), true); -// } + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerStateSet) { + TestDriver driver; + + layer_state_set(0); + EXPECT_EQ(layer_state, 0); + layer_state_set(0b001100); + EXPECT_EQ(layer_state, 0b001100); + + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerStateIs) { + TestDriver driver; + + layer_state_set(0); + EXPECT_EQ(layer_state_is(0), true); + EXPECT_EQ(layer_state_is(1), false); + layer_state_set(1); + EXPECT_EQ(layer_state_is(0), true); + EXPECT_EQ(layer_state_is(1), false); + layer_state_set(2); + EXPECT_EQ(layer_state_is(0), false); + EXPECT_EQ(layer_state_is(1), true); + EXPECT_EQ(layer_state_is(2), false); + + testing::Mock::VerifyAndClearExpectations(&driver); +} TEST_F(ActionLayer, LayerStateCmp) { - uint32_t prev_layer; + TestDriver driver; + uint32_t prev_layer; prev_layer = 0; EXPECT_EQ(layer_state_cmp(prev_layer, 0), true); @@ -60,33 +75,339 @@ TEST_F(ActionLayer, LayerStateCmp) { EXPECT_EQ(layer_state_cmp(prev_layer, 0), false); EXPECT_EQ(layer_state_cmp(prev_layer, 1), true); EXPECT_EQ(layer_state_cmp(prev_layer, 2), false); + + testing::Mock::VerifyAndClearExpectations(&driver); } -// TEST_F(ActionLayer, LayerClear) { -// layer_clear(); -// EXPECT_EQ(layer_state, 0); -// } +TEST_F(ActionLayer, LayerClear) { + TestDriver driver; -// TEST_F(ActionLayer, LayerMove) { -// layer_move(0); -// EXPECT_EQ(layer_state, 1); -// layer_move(3); -// EXPECT_EQ(layer_state, 0b1000); -// } + layer_clear(); + EXPECT_EQ(layer_state, 0); -// TEST_F(ActionLayer, LayerOn) { -// layer_clear(); -// layer_on(1); -// layer_on(3); -// layer_on(3); -// EXPECT_EQ(layer_state, 0b1010); -// } + testing::Mock::VerifyAndClearExpectations(&driver); +} -// TEST_F(ActionLayer, LayerOff) { -// layer_clear(); -// layer_on(1); -// layer_on(3); -// layer_off(3); -// layer_off(2); -// EXPECT_EQ(layer_state, 0b1000); -// } +TEST_F(ActionLayer, LayerMove) { + TestDriver driver; + + layer_move(0); + EXPECT_EQ(layer_state, 1); + layer_move(3); + EXPECT_EQ(layer_state, 0b1000); + + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerOn) { + TestDriver driver; + + layer_clear(); + layer_on(1); + layer_on(3); + layer_on(3); + EXPECT_EQ(layer_state, 0b1010); + + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerOff) { + TestDriver driver; + + layer_clear(); + layer_on(1); + layer_on(3); + layer_off(3); + layer_off(2); + EXPECT_EQ(layer_state, 0b0010); + + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, MomentaryLayerDoesNothing) { + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, MO(1)}; + + set_keymap({layer_key}); + + /* Press and release MO, nothing should happen. */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, MomentaryLayerWithKeypress) { + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, MO(1)}; + + /* These keys must have the same position in the matrix, only the layer is different. */ + KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A}; + set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}}); + + /* Press MO. */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press key on layer 1 */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_B))).Times(1); + regular_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release key on layer 1 */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + regular_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release MO */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, ToggleLayerDoesNothing) { + GTEST_SKIP() << "TODO: Toggle layer does not activate the expected layer on key press but on release."; + + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, TG(1)}; + + set_keymap({layer_key}); + + /* Press TG. Layer state should not change as it's applied on release. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release TG. */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, ToggleLayerUpAndDown) { + GTEST_SKIP() << "TODO: Toggle layer does not activate the expected layer on key press but on release."; + + TestDriver driver; + KeymapKey toggle_layer_1_on_layer_0 = KeymapKey{0, 0, 0, TG(1)}; + KeymapKey toggle_layer_0_on_layer_1 = KeymapKey{1, 1, 0, TG(0)}; + + set_keymap({toggle_layer_1_on_layer_0, toggle_layer_0_on_layer_1}); + + /* Toggle Layer 1. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + toggle_layer_1_on_layer_0.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + toggle_layer_1_on_layer_0.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Toggle Layer 0. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + toggle_layer_0_on_layer_1.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + toggle_layer_0_on_layer_1.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerTapToggleDoesNothing) { + GTEST_SKIP() << "TODO: Tap toggle layer does not activate the expected layer on key press."; + + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, TT(1)}; + + set_keymap({layer_key}); + + /* Press and release TT. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(0); + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(2); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerTapToggleWithKeypress) { + GTEST_SKIP() << "TODO: Tap toggle layer does not activate the expected layer on key press."; + + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, TT(1)}; + + /* These keys must have the same position in the matrix, only the layer is different. */ + KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A}; + set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}}); + + /* Press TT. */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(0); + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_B))).Times(1); + regular_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + regular_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerTapToggleWithToggleWithKeypress) { + GTEST_SKIP() << "TODO: Tap toggle layer does not activate the expected layer on key press."; + + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, TT(1)}; + + /* These keys must have the same position in the matrix, only the layer is different. */ + KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A}; + set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}}); + + /* Tap TT five times . */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(9); + + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + + layer_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + layer_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_B))).Times(1); + regular_key.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + regular_key.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(ActionLayer, LayerTapReleasedBeforeKeypressReleaseWithModifiers) { + GTEST_SKIP() << "TODO: Modifiers are erroneously discarded on layer changes, although a key that introduced the modifier is still held."; + TestDriver driver; + InSequence s; + + KeymapKey layer_0_key_0 = KeymapKey{0, 0, 0, LT(1, KC_T)}; + KeymapKey layer_1_key_1 = KeymapKey{1, 1, 0, RALT(KC_9)}; + + set_keymap({layer_0_key_0, layer_1_key_1}); + + /* Press layer tap and wait for tapping term to switch to layer 1 */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(0); + layer_0_key_0.press(); + idle_for(TAPPING_TERM); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press key with layer 1 mapping, result basically expected + * altough more reports are send then necessary. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_RALT))).Times(1); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_RALT, KC_9))).Times(1); + layer_1_key_1.press(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(1)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release layer tap key, no report is send because key is still held. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_0_key_0.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Unregister keycode and modifier. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_RALT))).Times(1); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + layer_1_key_1.release(); + run_one_scan_loop(); + EXPECT_TRUE(layer_state_is(0)); + testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/basic/test_keypress.cpp b/tests/basic/test_keypress.cpp index cf839f8c1..70613bbd6 100644 --- a/tests/basic/test_keypress.cpp +++ b/tests/basic/test_keypress.cpp @@ -14,11 +14,11 @@ * along with this program. If not, see . */ +#include "keycode.h" #include "test_common.hpp" using testing::_; using testing::InSequence; -using testing::Return; class KeyPress : public TestFixture {}; @@ -30,95 +30,156 @@ TEST_F(KeyPress, SendKeyboardIsNotCalledWhenNoKeyIsPressed) { TEST_F(KeyPress, CorrectKeyIsReportedWhenPressed) { TestDriver driver; - press_key(0, 0); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); - keyboard_task(); - release_key(0, 0); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); - keyboard_task(); -} + auto key = KeymapKey(0, 0, 0, KC_A); -TEST_F(KeyPress, CorrectKeysAreReportedWhenTwoKeysArePressed) { - TestDriver driver; - press_key(1, 0); - press_key(0, 3); - // Note that QMK only processes one key at a time - // See issue #1476 for more information - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_B))); - keyboard_task(); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_B, KC_C))); - keyboard_task(); - release_key(1, 0); - release_key(0, 3); - // Note that the first key released is the first one in the matrix order - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_C))); + set_keymap({key}); + + key.press(); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key.report_code))); keyboard_task(); + + key.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); keyboard_task(); } TEST_F(KeyPress, ANonMappedKeyDoesNothing) { TestDriver driver; - press_key(2, 0); + auto key = KeymapKey(0, 0, 0, KC_NO); + + set_keymap({key}); + + key.press(); EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); keyboard_task(); keyboard_task(); } +TEST_F(KeyPress, CorrectKeysAreReportedWhenTwoKeysArePressed) { + TestDriver driver; + auto key_b = KeymapKey(0, 0, 0, KC_B); + auto key_c = KeymapKey(0, 1, 1, KC_C); + + set_keymap({key_b, key_c}); + + key_b.press(); + key_c.press(); + // Note that QMK only processes one key at a time + // See issue #1476 for more information + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_b.report_code))); + keyboard_task(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_b.report_code, key_c.report_code))); + keyboard_task(); + + key_b.release(); + key_c.release(); + // Note that the first key released is the first one in the matrix order + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_c.report_code))); + keyboard_task(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + keyboard_task(); +} + TEST_F(KeyPress, LeftShiftIsReportedCorrectly) { TestDriver driver; - press_key(3, 0); - press_key(0, 0); + auto key_a = KeymapKey(0, 0, 0, KC_A); + auto key_lsft = KeymapKey(0, 3, 0, KC_LEFT_SHIFT); + + set_keymap({key_a, key_lsft}); + + key_lsft.press(); + key_a.press(); + // Unfortunately modifiers are also processed in the wrong order // See issue #1476 for more information - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_a.report_code))); keyboard_task(); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A, KC_LEFT_SHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_a.report_code, key_lsft.report_code))); keyboard_task(); - release_key(0, 0); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); + + key_a.release(); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_lsft.report_code))); keyboard_task(); - release_key(3, 0); + + key_lsft.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); keyboard_task(); } TEST_F(KeyPress, PressLeftShiftAndControl) { TestDriver driver; - press_key(3, 0); - press_key(5, 0); + auto key_lsft = KeymapKey(0, 3, 0, KC_LEFT_SHIFT); + auto key_lctrl = KeymapKey(0, 5, 0, KC_LEFT_CTRL); + + set_keymap({key_lctrl, key_lsft}); + + key_lsft.press(); + key_lctrl.press(); + // Unfortunately modifiers are also processed in the wrong order // See issue #1476 for more information - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_lsft.report_code))); keyboard_task(); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_LEFT_CTRL))); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_lsft.report_code, key_lctrl.report_code))); + keyboard_task(); + + key_lsft.release(); + key_lctrl.release(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_lctrl.report_code))); + keyboard_task(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); keyboard_task(); } TEST_F(KeyPress, LeftAndRightShiftCanBePressedAtTheSameTime) { TestDriver driver; - press_key(3, 0); - press_key(4, 0); + auto key_lsft = KeymapKey(0, 3, 0, KC_LEFT_SHIFT); + auto key_rsft = KeymapKey(0, 4, 0, KC_RIGHT_SHIFT); + + set_keymap({key_rsft, key_lsft}); + + key_lsft.press(); + key_rsft.press(); // Unfortunately modifiers are also processed in the wrong order // See issue #1476 for more information - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_lsft.report_code))); keyboard_task(); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_RIGHT_SHIFT))); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_lsft.report_code, key_rsft.report_code))); + keyboard_task(); + + key_lsft.release(); + key_rsft.release(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_rsft.report_code))); + keyboard_task(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); keyboard_task(); } TEST_F(KeyPress, RightShiftLeftControlAndCharWithTheSameKey) { TestDriver driver; - press_key(6, 0); + auto combo_key = KeymapKey(0, 0, 0, RSFT(LCTL(KC_O))); + + set_keymap({combo_key}); + // BUG: The press is split into two reports // BUG: It reports RSFT instead of LSFT // See issue #524 for more information // The underlying cause is that we use only one bit to represent the right hand // modifiers. + combo_key.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_RIGHT_SHIFT, KC_RIGHT_CTRL))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_RIGHT_SHIFT, KC_RIGHT_CTRL, KC_O))); keyboard_task(); - release_key(6, 0); + + combo_key.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_RIGHT_SHIFT, KC_RIGHT_CTRL))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); keyboard_task(); @@ -127,25 +188,29 @@ TEST_F(KeyPress, RightShiftLeftControlAndCharWithTheSameKey) { TEST_F(KeyPress, PressPlusEqualReleaseBeforePress) { TestDriver driver; InSequence s; + auto key_plus = KeymapKey(0, 1, 1, KC_PLUS); + auto key_eql = KeymapKey(0, 0, 1, KC_EQL); - press_key(1, 1); // KC_PLUS + set_keymap({key_plus, key_eql}); + + key_plus.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_EQUAL))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(1, 1); // KC_PLUS + key_plus.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - press_key(0, 1); // KC_EQUAL - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_EQUAL))); + key_eql.press(); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(key_eql.report_code))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(0, 1); // KC_EQUAL + key_eql.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); @@ -154,27 +219,31 @@ TEST_F(KeyPress, PressPlusEqualReleaseBeforePress) { TEST_F(KeyPress, PressPlusEqualDontReleaseBeforePress) { TestDriver driver; InSequence s; + auto key_plus = KeymapKey(0, 1, 1, KC_PLUS); + auto key_eql = KeymapKey(0, 0, 1, KC_EQUAL); - press_key(1, 1); // KC_PLUS + set_keymap({key_plus, key_eql}); + + key_plus.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_EQUAL))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - press_key(0, 1); // KC_EQUAL + key_eql.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_EQUAL))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(1, 1); // KC_PLUS + key_plus.release(); // BUG: Should really still return KC_EQUAL, but this is fine too // It's also called twice for some reason EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(2); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(0, 1); // KC_EQUAL + key_eql.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); @@ -183,24 +252,28 @@ TEST_F(KeyPress, PressPlusEqualDontReleaseBeforePress) { TEST_F(KeyPress, PressEqualPlusReleaseBeforePress) { TestDriver driver; InSequence s; + auto key_plus = KeymapKey(0, 1, 1, KC_PLUS); + auto key_eql = KeymapKey(0, 0, 1, KC_EQUAL); - press_key(0, 1); // KC_EQUAL + set_keymap({key_plus, key_eql}); + + key_eql.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_EQUAL))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(0, 1); // KC_EQUAL + key_eql.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - press_key(1, 1); // KC_PLUS + key_plus.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_EQUAL))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(1, 1); // KC_PLUS + key_plus.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); @@ -210,13 +283,17 @@ TEST_F(KeyPress, PressEqualPlusReleaseBeforePress) { TEST_F(KeyPress, PressEqualPlusDontReleaseBeforePress) { TestDriver driver; InSequence s; + auto key_plus = KeymapKey(0, 1, 1, KC_PLUS); + auto key_eql = KeymapKey(0, 0, 1, KC_EQL); - press_key(0, 1); // KC_EQUAL + set_keymap({key_plus, key_eql}); + + key_eql.press(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_EQUAL))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - press_key(1, 1); // KC_PLUS + key_plus.press(); // BUG: The sequence is a bit strange, but it works, the end result is that // KC_PLUS is sent EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_EQUAL))); @@ -225,16 +302,16 @@ TEST_F(KeyPress, PressEqualPlusDontReleaseBeforePress) { run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(0, 1); // KC_EQUAL + key_eql.release(); // I guess it's fine to still report shift here EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); - release_key(1, 1); // KC_PLUS + key_plus.release(); // This report is not needed EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); testing::Mock::VerifyAndClearExpectations(&driver); -} +} \ No newline at end of file diff --git a/tests/basic/test_macro.cpp b/tests/basic/test_macro.cpp index 1ac6f473f..ae2f3b32e 100644 --- a/tests/basic/test_macro.cpp +++ b/tests/basic/test_macro.cpp @@ -24,10 +24,25 @@ class Macro : public TestFixture {}; #define AT_TIME(t) WillOnce(InvokeWithoutArgs([current_time]() { EXPECT_EQ(timer_elapsed32(current_time), t); })) +extern "C" const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { + if (record->event.pressed) { + switch (id) { + case 0: + return MACRO(D(LSFT), T(H), U(LSFT), T(E), T(L), T(L), T(O), T(SPACE), W(100), D(LSFT), T(W), U(LSFT), I(10), T(O), T(R), T(L), T(D), D(LSFT), T(1), U(LSFT), END); + } + } + return MACRO_NONE; +}; + TEST_F(Macro, PlayASimpleMacro) { TestDriver driver; InSequence s; - press_key(8, 0); + auto key_macro = KeymapKey(0, 8, 0, M(0)); + + set_keymap({key_macro}); + + key_macro.press(); + uint32_t current_time = timer_read32(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(0); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT, KC_H))).AT_TIME(0); @@ -68,4 +83,6 @@ TEST_F(Macro, PlayASimpleMacro) { EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).AT_TIME(210); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).AT_TIME(220); run_one_scan_loop(); + + key_macro.release(); } diff --git a/tests/basic/test_one_shot_keys.cpp b/tests/basic/test_one_shot_keys.cpp new file mode 100644 index 000000000..98178912e --- /dev/null +++ b/tests/basic/test_one_shot_keys.cpp @@ -0,0 +1,197 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "action_util.h" +#include "keyboard_report_util.hpp" +#include "test_common.hpp" + +using testing::_; +using testing::InSequence; + +class OneShot : public TestFixture {}; +class OneShotParametrizedTestFixture : public ::testing::WithParamInterface>, public OneShot {}; + +TEST_F(OneShot, OSMWithoutAdditionalKeypressDoesNothing) { + TestDriver driver; + auto osm_key = KeymapKey(0, 0, 0, OSM(MOD_LSFT), KC_LSFT); + + set_keymap({osm_key}); + + /* Press and release OSM key*/ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + osm_key.press(); + run_one_scan_loop(); + osm_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* OSM are added when an actual report is send */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(osm_key.report_code))); + send_keyboard_report(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Make unit-test pass */ + clear_oneshot_mods(); +} + +#if defined(ONESHOT_TIMEOUT) + +TEST_P(OneShotParametrizedTestFixture, OSMExpiredDoesNothing) { + TestDriver driver; + KeymapKey osm_key = GetParam().first; + KeymapKey regular_key = GetParam().second; + + set_keymap({osm_key, regular_key}); + + /* Press and release OSM */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + osm_key.press(); + run_one_scan_loop(); + osm_key.release(); + idle_for(ONESHOT_TIMEOUT); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(regular_key.report_code))).Times(1); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +#endif + +TEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) { + TestDriver driver; + KeymapKey osm_key = GetParam().first; + KeymapKey regular_key = GetParam().second; + + set_keymap({osm_key, regular_key}); + + /* Press and release OSM */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + osm_key.press(); + run_one_scan_loop(); + osm_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(osm_key.report_code, regular_key.report_code))).Times(1); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) { + TestDriver driver; + testing::InSequence s; + + KeymapKey osm_key = GetParam().first; + KeymapKey regular_key = GetParam().second; + + set_keymap({osm_key, regular_key}); + + /* Press OSM */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + osm_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release OSM */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(regular_key.report_code, osm_key.report_code))).Times(1); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + osm_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +// clang-format off + +INSTANTIATE_TEST_CASE_P( + OneShotModifierTests, + OneShotParametrizedTestFixture, + ::testing::Values( + /* first is osm key, second is regular key. */ + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A}) + )); +// clang-format on + +TEST_F(OneShot, OSLWithAdditionalKeypress) { + TestDriver driver; + InSequence s; + KeymapKey osl_key = KeymapKey{0, 0, 0, OSL(1)}; + KeymapKey regular_key = KeymapKey{1, 1, 0, KC_A}; + + set_keymap({osl_key, regular_key}); + + /* Press OSL key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + osl_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release OSL key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(2); + osl_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(regular_key.report_code))).Times(2); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/basic/test_tapping.cpp b/tests/basic/test_tapping.cpp index 88ab97eb6..181b7ca89 100644 --- a/tests/basic/test_tapping.cpp +++ b/tests/basic/test_tapping.cpp @@ -14,8 +14,11 @@ * along with this program. If not, see . */ +#include "keyboard_report_util.hpp" +#include "keycode.h" #include "test_common.hpp" #include "action_tapping.h" +#include "test_keymap_key.hpp" using testing::_; using testing::InSequence; @@ -25,14 +28,19 @@ class Tapping : public TestFixture {}; TEST_F(Tapping, TapA_SHFT_T_KeyReportsKey) { TestDriver driver; InSequence s; + auto key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P)); + + set_keymap({key_shift_hold_p_tap}); - press_key(7, 0); // Tapping keys does nothing on press + key_shift_hold_p_tap.press(); EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); run_one_scan_loop(); - release_key(7, 0); + // First we get the key press + key_shift_hold_p_tap.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + // Then the release EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); run_one_scan_loop(); @@ -41,25 +49,38 @@ TEST_F(Tapping, TapA_SHFT_T_KeyReportsKey) { TEST_F(Tapping, HoldA_SHFT_T_KeyReportsShift) { TestDriver driver; InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 7, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + mod_tap_hold_key.press(); - press_key(7, 0); // Tapping keys does nothing on press EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); idle_for(TAPPING_TERM); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))); run_one_scan_loop(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); } TEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) { // See issue #1478 for more information TestDriver driver; InSequence s; + auto key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P)); + + set_keymap({key_shift_hold_p_tap}); - press_key(7, 0); // Tapping keys does nothing on press + key_shift_hold_p_tap.press(); EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); run_one_scan_loop(); - release_key(7, 0); + key_shift_hold_p_tap.release(); + // First we get the key press EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); // Then the release @@ -67,19 +88,20 @@ TEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) { run_one_scan_loop(); // This sends KC_P, even if it should do nothing - press_key(7, 0); + key_shift_hold_p_tap.press(); // This test should not succed if everything works correctly EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); run_one_scan_loop(); - release_key(7, 0); + + key_shift_hold_p_tap.release(); EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); idle_for(TAPPING_TERM + 1); // On the other hand, nothing is sent if we are outside the tapping term - press_key(7, 0); + key_shift_hold_p_tap.press(); EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); run_one_scan_loop(); - release_key(7, 0); + key_shift_hold_p_tap.release(); // First we get the key press EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); @@ -90,8 +112,12 @@ TEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) { // Now we are geting into strange territory, as the hold registers too early here // But the stranges part is: // If TAPPING_TERM + 1 above is changed to TAPPING_TERM or TAPPING_TERM + 2 it doesn't - press_key(7, 0); + key_shift_hold_p_tap.press(); // Shouldn't be called here really EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LEFT_SHIFT))).Times(1); idle_for(TAPPING_TERM); -} + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + key_shift_hold_p_tap.release(); + run_one_scan_loop(); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/default_mod_tap/config.h b/tests/tap_hold_configurations/default_mod_tap/config.h new file mode 100644 index 000000000..5955b8600 --- /dev/null +++ b/tests/tap_hold_configurations/default_mod_tap/config.h @@ -0,0 +1,21 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" + +#define IGNORE_MOD_TAP_INTERRUPT \ No newline at end of file diff --git a/tests/tap_hold_configurations/default_mod_tap/test.mk b/tests/tap_hold_configurations/default_mod_tap/test.mk new file mode 100644 index 000000000..cfab996e0 --- /dev/null +++ b/tests/tap_hold_configurations/default_mod_tap/test.mk @@ -0,0 +1,18 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/tap_hold_configurations/default_mod_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/default_mod_tap/test_tap_hold.cpp new file mode 100644 index 000000000..90befcdff --- /dev/null +++ b/tests/tap_hold_configurations/default_mod_tap/test_tap_hold.cpp @@ -0,0 +1,230 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class DefaultTapHold : public TestFixture {}; + +TEST_F(DefaultTapHold, tap_regular_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({mod_tap_hold_key, regular_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Idle for tapping term of mod tap hold key. */ + idle_for(TAPPING_TERM - 3); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(DefaultTapHold, tap_mod_tap_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto first_mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); + + set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key}); + + /* Press first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + first_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + first_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(DefaultTapHold, tap_regular_key_while_layer_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + auto layer_key = KeymapKey(1, 2, 0, KC_B); + + set_keymap({layer_tap_hold_key, regular_key, layer_key}); + + /* Press layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A, KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(_)); + layer_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(DefaultTapHold, tap_mod_tap_hold_key_two_times) { + GTEST_SKIP() << "TODO:Holding a modtap key results in out of bounds access to the keymap, this is a bug in QMK."; + + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod-tap-hold key again. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + mod_tap_hold_key.press(); + idle_for(TAPPING_TERM); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(DefaultTapHold, tap_mod_tap_hold_key_twice_and_hold_on_second_time) { + GTEST_SKIP() << "TODO:Holding a modtap key results in out of bounds access to the keymap, this is a bug in QMK."; + + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod-tap-hold key again. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + mod_tap_hold_key.press(); + idle_for(TAPPING_TERM); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(DefaultTapHold, tap_and_hold_mod_tap_hold_key) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + mod_tap_hold_key.press(); + idle_for(TAPPING_TERM + 1); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/ignore_mod_tap_interrupt/config.h b/tests/tap_hold_configurations/ignore_mod_tap_interrupt/config.h new file mode 100644 index 000000000..5955b8600 --- /dev/null +++ b/tests/tap_hold_configurations/ignore_mod_tap_interrupt/config.h @@ -0,0 +1,21 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" + +#define IGNORE_MOD_TAP_INTERRUPT \ No newline at end of file diff --git a/tests/tap_hold_configurations/ignore_mod_tap_interrupt/test.mk b/tests/tap_hold_configurations/ignore_mod_tap_interrupt/test.mk new file mode 100644 index 000000000..efecca2c2 --- /dev/null +++ b/tests/tap_hold_configurations/ignore_mod_tap_interrupt/test.mk @@ -0,0 +1,18 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- diff --git a/tests/tap_hold_configurations/ignore_mod_tap_interrupt/test_tap_hold.cpp b/tests/tap_hold_configurations/ignore_mod_tap_interrupt/test_tap_hold.cpp new file mode 100644 index 000000000..1702d604d --- /dev/null +++ b/tests/tap_hold_configurations/ignore_mod_tap_interrupt/test_tap_hold.cpp @@ -0,0 +1,136 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class IgnoreModTapInterrupt : public TestFixture {}; + +TEST_F(IgnoreModTapInterrupt, tap_regular_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({mod_tap_hold_key, regular_key}); + + /* Press mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A, KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(IgnoreModTapInterrupt, tap_mod_tap_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto first_mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); + + set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key}); + + /* Press first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + first_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A, KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + first_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(IgnoreModTapInterrupt, tap_regular_key_while_layer_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + auto layer_key = KeymapKey(1, 2, 0, KC_B); + + set_keymap({layer_tap_hold_key, regular_key, layer_key}); + + /* Press layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P, regular_key.report_code))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + layer_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/tap_hold_configurations/permissive_hold/config.h b/tests/tap_hold_configurations/permissive_hold/config.h new file mode 100644 index 000000000..2d5a9849e --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold/config.h @@ -0,0 +1,21 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" + +#define PERMISSIVE_HOLD \ No newline at end of file diff --git a/tests/tap_hold_configurations/permissive_hold/test.mk b/tests/tap_hold_configurations/permissive_hold/test.mk new file mode 100644 index 000000000..efecca2c2 --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold/test.mk @@ -0,0 +1,18 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- diff --git a/tests/tap_hold_configurations/permissive_hold/test_one_shot_keys.cpp b/tests/tap_hold_configurations/permissive_hold/test_one_shot_keys.cpp new file mode 100644 index 000000000..aa71ec397 --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold/test_one_shot_keys.cpp @@ -0,0 +1,76 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "action_util.h" +#include "keyboard_report_util.hpp" +#include "test_common.hpp" + +using testing::_; +using testing::InSequence; + +class OneShot : public TestFixture {}; +class OneShotParametrizedTestFixture : public ::testing::WithParamInterface>, public OneShot {}; + +TEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) { + TestDriver driver; + KeymapKey osm_key = GetParam().first; + KeymapKey regular_key = GetParam().second; + + set_keymap({osm_key, regular_key}); + + /* Press OSM */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + osm_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(osm_key.report_code))).Times(2); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(regular_key.report_code, osm_key.report_code))).Times(1); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release OSM */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + osm_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +// clang-format off + +INSTANTIATE_TEST_CASE_P( + OneShotModifierTests, + OneShotParametrizedTestFixture, + ::testing::Values( + /* first is osm key, second is regular key. */ + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}), + std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A}) + )); +// clang-format on \ No newline at end of file diff --git a/tests/tap_hold_configurations/permissive_hold/test_tap_hold.cpp b/tests/tap_hold_configurations/permissive_hold/test_tap_hold.cpp new file mode 100644 index 000000000..ab9dd1518 --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold/test_tap_hold.cpp @@ -0,0 +1,132 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; +class PermissiveHold : public TestFixture {}; + +TEST_F(PermissiveHold, tap_regular_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({mod_tap_hold_key, regular_key}); + + /* Press mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT, regular_key.report_code))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(PermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto first_mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); + + set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key}); + + /* Press first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + first_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press second mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release second mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT, second_mod_tap_hold_key.report_code))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + second_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + first_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(PermissiveHold, tap_regular_key_while_layer_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + auto layer_key = KeymapKey(1, 2, 0, KC_B); + + set_keymap({layer_tap_hold_key, regular_key, layer_key}); + + /* Press layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(layer_key.report_code))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + layer_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/config.h b/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/config.h new file mode 100644 index 000000000..a6abd50bb --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/config.h @@ -0,0 +1,22 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" + +#define IGNORE_MOD_TAP_INTERRUPT +#define PERMISSIVE_HOLD \ No newline at end of file diff --git a/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/test.mk b/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/test.mk new file mode 100644 index 000000000..efecca2c2 --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/test.mk @@ -0,0 +1,18 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- diff --git a/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/test_tap_hold.cpp b/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/test_tap_hold.cpp new file mode 100644 index 000000000..67f394653 --- /dev/null +++ b/tests/tap_hold_configurations/permissive_hold_ignore_mod_tap_interrupt/test_tap_hold.cpp @@ -0,0 +1,134 @@ + +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class PermissiveHold_IgnoreModTapInterrupt : public TestFixture {}; + +TEST_F(PermissiveHold_IgnoreModTapInterrupt, tap_regular_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({mod_tap_hold_key, regular_key}); + + /* Press mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(PermissiveHold_IgnoreModTapInterrupt, tap_mod_tap_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto first_mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); + + set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key}); + + /* Press first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + first_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + second_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + first_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(PermissiveHold_IgnoreModTapInterrupt, tap_regular_key_while_layer_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + auto layer_key = KeymapKey(1, 2, 0, KC_B); + + set_keymap({layer_tap_hold_key, regular_key, layer_key}); + + /* Press layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_B))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + layer_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/retro_tapping/config.h b/tests/tap_hold_configurations/retro_tapping/config.h new file mode 100644 index 000000000..4b38f2644 --- /dev/null +++ b/tests/tap_hold_configurations/retro_tapping/config.h @@ -0,0 +1,21 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" + +#define RETRO_TAPPING \ No newline at end of file diff --git a/tests/tap_hold_configurations/retro_tapping/test.mk b/tests/tap_hold_configurations/retro_tapping/test.mk new file mode 100644 index 000000000..efecca2c2 --- /dev/null +++ b/tests/tap_hold_configurations/retro_tapping/test.mk @@ -0,0 +1,18 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- diff --git a/tests/tap_hold_configurations/retro_tapping/test_tap_hold.cpp b/tests/tap_hold_configurations/retro_tapping/test_tap_hold.cpp new file mode 100644 index 000000000..59ae77f78 --- /dev/null +++ b/tests/tap_hold_configurations/retro_tapping/test_tap_hold.cpp @@ -0,0 +1,52 @@ + +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class RetroTapping : public TestFixture {}; + +TEST_F(RetroTapping, tap_and_hold_mod_tap_hold_key) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + idle_for(TAPPING_TERM); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + /* TODO: Why is LSHIFT send at all? */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/retro_tapping/test_tapping.cpp b/tests/tap_hold_configurations/retro_tapping/test_tapping.cpp new file mode 100644 index 000000000..cf23df831 --- /dev/null +++ b/tests/tap_hold_configurations/retro_tapping/test_tapping.cpp @@ -0,0 +1,112 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class Tapping : public TestFixture {}; + +TEST_F(Tapping, HoldA_SHFT_T_KeyReportsShift) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 7, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + idle_for(TAPPING_TERM); + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) { + TestDriver driver; + InSequence s; + auto key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P)); + + set_keymap({key_shift_hold_p_tap}); + + /* Press mod_tap_hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + key_shift_hold_p_tap.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod_tap_hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + key_shift_hold_p_tap.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod_tap_hold key again */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + key_shift_hold_p_tap.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod_tap_hold key again */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + key_shift_hold_p_tap.release(); + idle_for(TAPPING_TERM + 1); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod_tap_hold key again */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + key_shift_hold_p_tap.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod_tap_hold key again */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + key_shift_hold_p_tap.release(); + idle_for(TAPPING_TERM + 1); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod_tap_hold key again */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + key_shift_hold_p_tap.press(); + idle_for(TAPPING_TERM); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod_tap_hold key again */ + /* TODO: Why is KC_LSFT send? */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + key_shift_hold_p_tap.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/tapping_force_hold/config.h b/tests/tap_hold_configurations/tapping_force_hold/config.h new file mode 100644 index 000000000..3b4646338 --- /dev/null +++ b/tests/tap_hold_configurations/tapping_force_hold/config.h @@ -0,0 +1,21 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include "test_common.h" + +#define TAPPING_FORCE_HOLD \ No newline at end of file diff --git a/tests/tap_hold_configurations/tapping_force_hold/test.mk b/tests/tap_hold_configurations/tapping_force_hold/test.mk new file mode 100644 index 000000000..efecca2c2 --- /dev/null +++ b/tests/tap_hold_configurations/tapping_force_hold/test.mk @@ -0,0 +1,18 @@ +# Copyright 2021 Stefan Kerkmann +# +# 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 . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- diff --git a/tests/tap_hold_configurations/tapping_force_hold/test_action_layer.cpp b/tests/tap_hold_configurations/tapping_force_hold/test_action_layer.cpp new file mode 100644 index 000000000..02416eed7 --- /dev/null +++ b/tests/tap_hold_configurations/tapping_force_hold/test_action_layer.cpp @@ -0,0 +1,81 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "test_common.hpp" + +using testing::_; +using testing::InSequence; + +class ActionLayer : public TestFixture {}; + +TEST_F(ActionLayer, LayerTapToggleWithToggleWithKeypress) { + TestDriver driver; + KeymapKey layer_key = KeymapKey{0, 0, 0, TT(1)}; + + /* These keys must have the same position in the matrix, only the layer is different. */ + KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A}; + set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}}); + + /* Tap TT five times . */ + /* TODO: QMK currently sends an empty report even if nothing needs to be reported to the host! */ + /* TODO: Tapping Force Hold breaks TT */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(10); + + layer_key.press(); + run_one_scan_loop(); + layer_key.release(); + run_one_scan_loop(); + expect_layer_state(0); + + layer_key.press(); + run_one_scan_loop(); + layer_key.release(); + run_one_scan_loop(); + expect_layer_state(0); + + layer_key.press(); + run_one_scan_loop(); + layer_key.release(); + run_one_scan_loop(); + expect_layer_state(0); + + layer_key.press(); + run_one_scan_loop(); + layer_key.release(); + run_one_scan_loop(); + expect_layer_state(0); + + layer_key.press(); + run_one_scan_loop(); + layer_key.release(); + run_one_scan_loop(); + expect_layer_state(0); + + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))).Times(1); + regular_key.press(); + run_one_scan_loop(); + expect_layer_state(0); + testing::Mock::VerifyAndClearExpectations(&driver); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(1); + regular_key.release(); + run_one_scan_loop(); + expect_layer_state(0); + testing::Mock::VerifyAndClearExpectations(&driver); +} \ No newline at end of file diff --git a/tests/tap_hold_configurations/tapping_force_hold/test_tap_hold.cpp b/tests/tap_hold_configurations/tapping_force_hold/test_tap_hold.cpp new file mode 100644 index 000000000..cb6842961 --- /dev/null +++ b/tests/tap_hold_configurations/tapping_force_hold/test_tap_hold.cpp @@ -0,0 +1,215 @@ + +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class TappingForceHold : public TestFixture {}; + +TEST_F(TappingForceHold, tap_regular_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({mod_tap_hold_key, regular_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Idle for tapping term of mod tap hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + idle_for(TAPPING_TERM - 3); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(TappingForceHold, tap_mod_tap_key_while_mod_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto first_mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + auto second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); + + set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key}); + + /* Press first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + first_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release second tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + second_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release first mod-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + first_mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Idle for tapping term of first mod tap hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + idle_for(TAPPING_TERM - 3); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(TappingForceHold, tap_regular_key_while_layer_tap_key_is_held) { + TestDriver driver; + InSequence s; + auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); + auto regular_key = KeymapKey(0, 2, 0, KC_A); + auto layer_key = KeymapKey(1, 2, 0, KC_B); + + set_keymap({layer_tap_hold_key, regular_key, layer_key}); + + /* Press layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + layer_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release regular key */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + regular_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release layer-tap-hold key */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A, KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(_)); + layer_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(TappingForceHold, tap_mod_tap_hold_key_two_times) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod-tap-hold key again. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(TappingForceHold, tap_mod_tap_hold_key_twice_and_hold_on_second_time) { + TestDriver driver; + InSequence s; + auto mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + + set_keymap({mod_tap_hold_key}); + + /* Press mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_P))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Press mod-tap-hold key again. */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + mod_tap_hold_key.press(); + idle_for(TAPPING_TERM); + testing::Mock::VerifyAndClearExpectations(&driver); + + /* Release mod-tap-hold key. */ + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSHIFT))); + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())); + mod_tap_hold_key.release(); + run_one_scan_loop(); + testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/basic/rules.mk b/tests/test_common/build.mk similarity index 92% rename from tests/basic/rules.mk rename to tests/test_common/build.mk index 9fb5d4361..aeb305785 100644 --- a/tests/basic/rules.mk +++ b/tests/test_common/build.mk @@ -1,4 +1,4 @@ -# Copyright 2017 Fred Sundvik +# Copyright 2021 Stefan Kerkmann # # 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 @@ -13,4 +13,4 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -CUSTOM_MATRIX=yes +CUSTOM_MATRIX=yes \ No newline at end of file diff --git a/tests/test_common/keyboard_report_util.cpp b/tests/test_common/keyboard_report_util.cpp index f73cf239e..e148c76be 100644 --- a/tests/test_common/keyboard_report_util.cpp +++ b/tests/test_common/keyboard_report_util.cpp @@ -44,16 +44,21 @@ bool operator==(const report_keyboard_t& lhs, const report_keyboard_t& rhs) { return lhs.mods == rhs.mods && lhskeys == rhskeys; } -std::ostream& operator<<(std::ostream& stream, const report_keyboard_t& value) { - stream << "Keyboard report:" << std::endl; - stream << "Mods: " << (uint32_t)value.mods << std::endl; - stream << "Keys: "; +std::ostream& operator<<(std::ostream& stream, const report_keyboard_t& report) { + auto keys = get_keys(report); + // TODO: This should probably print friendly names for the keys - for (uint32_t k : get_keys(value)) { - stream << k << " "; + stream << "Keyboard Report: Mods (" << (uint32_t)report.mods << ") Keys ("; + + for (auto key = keys.cbegin(); key != keys.cend();) { + stream << +(*key); + key++; + if (key != keys.cend()) { + stream << ","; + } } - stream << std::endl; - return stream; + + return stream << ")" << std::endl; } KeyboardReportMatcher::KeyboardReportMatcher(const std::vector& keys) { diff --git a/tests/test_common/keymap.c b/tests/test_common/keymap.c new file mode 100644 index 000000000..fc3a56a00 --- /dev/null +++ b/tests/test_common/keymap.c @@ -0,0 +1,33 @@ +/* Copyright 2017 Fred Sundvik + * + * 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 . + */ + +#include "quantum.h" + +// clang-format off + +const uint16_t PROGMEM + keymaps[][MATRIX_ROWS][MATRIX_COLS] = + { + [0] = + { + {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, + {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, + {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, + {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO}, + }, +}; + +// clang-format on diff --git a/tests/test_common/test_common.h b/tests/test_common/test_common.h new file mode 100644 index 000000000..19ffcddce --- /dev/null +++ b/tests/test_common/test_common.h @@ -0,0 +1,4 @@ +#pragma once + +#define MATRIX_ROWS 4 +#define MATRIX_COLS 10 \ No newline at end of file diff --git a/tests/test_common/test_driver.cpp b/tests/test_common/test_driver.cpp index 84e249d83..2fa2b6a2e 100644 --- a/tests/test_common/test_driver.cpp +++ b/tests/test_common/test_driver.cpp @@ -27,7 +27,10 @@ TestDriver::~TestDriver() { m_this = nullptr; } uint8_t TestDriver::keyboard_leds(void) { return m_this->m_leds; } -void TestDriver::send_keyboard(report_keyboard_t* report) { m_this->send_keyboard_mock(*report); } +void TestDriver::send_keyboard(report_keyboard_t* report) { + test_logger.trace() << *report; + m_this->send_keyboard_mock(*report); +} void TestDriver::send_mouse(report_mouse_t* report) { m_this->send_mouse_mock(*report); } diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp index f86308df9..f9197b363 100644 --- a/tests/test_common/test_driver.hpp +++ b/tests/test_common/test_driver.hpp @@ -20,25 +20,26 @@ #include #include "host.h" #include "keyboard_report_util.hpp" - +#include "test_logger.hpp" class TestDriver { -public: + public: TestDriver(); ~TestDriver(); void set_leds(uint8_t leds) { m_leds = leds; } - MOCK_METHOD1(send_keyboard_mock, void (report_keyboard_t&)); - MOCK_METHOD1(send_mouse_mock, void (report_mouse_t&)); - MOCK_METHOD1(send_system_mock, void (uint16_t)); - MOCK_METHOD1(send_consumer_mock, void (uint16_t)); -private: - static uint8_t keyboard_leds(void); - static void send_keyboard(report_keyboard_t *report); - static void send_mouse(report_mouse_t* report); - static void send_system(uint16_t data); - static void send_consumer(uint16_t data); - host_driver_t m_driver; - uint8_t m_leds = 0; + MOCK_METHOD1(send_keyboard_mock, void(report_keyboard_t&)); + MOCK_METHOD1(send_mouse_mock, void(report_mouse_t&)); + MOCK_METHOD1(send_system_mock, void(uint16_t)); + MOCK_METHOD1(send_consumer_mock, void(uint16_t)); + + private: + static uint8_t keyboard_leds(void); + static void send_keyboard(report_keyboard_t* report); + static void send_mouse(report_mouse_t* report); + static void send_system(uint16_t data); + static void send_consumer(uint16_t data); + host_driver_t m_driver; + uint8_t m_leds = 0; static TestDriver* m_this; }; diff --git a/tests/test_common/test_fixture.cpp b/tests/test_common/test_fixture.cpp index e041df712..0601b1719 100644 --- a/tests/test_common/test_fixture.cpp +++ b/tests/test_common/test_fixture.cpp @@ -1,26 +1,48 @@ #include "test_fixture.hpp" +#include +#include +#include +#include +#include "gmock/gmock-cardinalities.h" #include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "keyboard_report_util.hpp" +#include "keycode.h" #include "test_driver.hpp" +#include "test_logger.hpp" #include "test_matrix.h" -#include "keyboard.h" -#include "action.h" -#include "action_tapping.h" +#include "test_keymap_key.hpp" extern "C" { +#include "action.h" +#include "action_tapping.h" +#include "action_util.h" +#include "action_layer.h" #include "debug.h" #include "eeconfig.h" -#include "action_layer.h" +#include "keyboard.h" +#include "keymap.h" void set_time(uint32_t t); void advance_time(uint32_t ms); } using testing::_; -using testing::AnyNumber; -using testing::Between; -using testing::Return; + +/* This is used for dynamic dispatching keymap_key_to_keycode calls to the current active test_fixture. */ +TestFixture* TestFixture::m_this = nullptr; + +/* Override weak QMK function to allow the usage of isolated per-test keymaps in unit-tests. + * The actual call is dynamicaly dispatched to the current active test fixture, which in turn has it's own keymap. */ +extern "C" uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t position) { + uint16_t keycode; + TestFixture::m_this->get_keycode(layer, position, &keycode); + return keycode; +} void TestFixture::SetUpTestCase() { + test_logger.info() << "TestFixture setup-up start." << std::endl; + // The following is enough to bootstrap the values set in main eeconfig_init_quantum(); eeconfig_update_debug(debug_config.raw); @@ -28,23 +50,99 @@ void TestFixture::SetUpTestCase() { TestDriver driver; EXPECT_CALL(driver, send_keyboard_mock(_)); keyboard_init(); + + test_logger.info() << "TestFixture setup-up end." << std::endl; } void TestFixture::TearDownTestCase() {} -TestFixture::TestFixture() {} +TestFixture::TestFixture() { m_this = this; } TestFixture::~TestFixture() { + test_logger.info() << "TestFixture clean-up start." << std::endl; TestDriver driver; - // Run for a while to make sure all keys are completely released - EXPECT_CALL(driver, send_keyboard_mock(_)).Times(AnyNumber()); - layer_clear(); + + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(2); + + /* Reset keyboard state. */ clear_all_keys(); - idle_for(TAPPING_TERM + 10); + + clear_keyboard(); + + clear_oneshot_mods(); + clear_oneshot_locked_mods(); + reset_oneshot_layer(); + + layer_clear(); + +#if defined(SWAP_HANDS_ENABLE) + clear_oneshot_swaphands(); +#endif + + idle_for(TAPPING_TERM * 10); testing::Mock::VerifyAndClearExpectations(&driver); - // Verify that the matrix really is cleared - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(0); - idle_for(TAPPING_TERM + 10); + + /* Verify that the matrix really is cleared */ + EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + idle_for(TAPPING_TERM * 10); + testing::Mock::VerifyAndClearExpectations(&driver); + + m_this = nullptr; + + test_logger.info() << "TestFixture clean-up end." << std::endl; + + print_test_log(); +} + +void TestFixture::add_key(KeymapKey key) { + if (this->find_key(key.layer, key.position)) { + FAIL() << "Key is already mapped for layer " << +key.layer << " and (column,row) (" << +key.position.col << "," << +key.position.row << ")"; + } + + this->keymap.push_back(key); +} + +void TestFixture::set_keymap(std::initializer_list keys) { + this->keymap.clear(); + for (auto& key : keys) { + add_key(key); + } +} + +const KeymapKey* TestFixture::find_key(layer_t layer, keypos_t position) const { + auto keymap_key_predicate = [&](KeymapKey candidate) { return candidate.layer == layer && candidate.position.col == position.col && candidate.position.row == position.row; }; + + auto result = std::find_if(this->keymap.begin(), this->keymap.end(), keymap_key_predicate); + + if (result != std::end(this->keymap)) { + return &(*result); + } + return nullptr; +} + +void TestFixture::get_keycode(const layer_t layer, const keypos_t position, uint16_t* result) const { + bool key_is_out_of_bounds = position.col >= MATRIX_COLS && position.row >= MATRIX_ROWS; + + if (key_is_out_of_bounds) { + /* See if this is done in hardware as well, because this is 100% out of bounds reads on all QMK keebs out there. */ + auto msg = [&]() { + std::stringstream msg; + msg << "Keycode for position (" << +position.col << "," << +position.row << ") requested! This is out of bounds." << std::endl; + return msg.str(); + }(); + + *result = KC_NO; + test_logger.error() << msg; + EXPECT_FALSE(key_is_out_of_bounds) << msg; + return; + } + + if (auto key = this->find_key(layer, position)) { + *result = key->code; + return; + } + + FAIL() << "No key is mapped for layer " << +layer << " and (column,row) " << +position.col << "," << +position.row << ")"; } void TestFixture::run_one_scan_loop() { @@ -57,3 +155,17 @@ void TestFixture::idle_for(unsigned time) { run_one_scan_loop(); } } + +void TestFixture::print_test_log() const { + const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + if (HasFailure()) { + std::cerr << test_info->test_case_name() << "." << test_info->name() << " failed!" << std::endl; + test_logger.print_log(); + } + test_logger.reset(); +} + +void TestFixture::expect_layer_state(layer_t layer_state) const { + test_logger.trace() << "Layer state: (" << +layer_state << ") Highest layer bit: (" << +get_highest_layer(layer_state) << ")" << std::endl; + EXPECT_TRUE(layer_state_is(layer_state)); +} diff --git a/tests/test_common/test_fixture.hpp b/tests/test_common/test_fixture.hpp index 340503665..73b5d8d3e 100644 --- a/tests/test_common/test_fixture.hpp +++ b/tests/test_common/test_fixture.hpp @@ -16,15 +16,34 @@ #pragma once +#include +#include +#include #include "gtest/gtest.h" +#include "keyboard.h" +#include "test_keymap_key.hpp" class TestFixture : public testing::Test { -public: + public: + static TestFixture* m_this; + TestFixture(); ~TestFixture(); static void SetUpTestCase(); static void TearDownTestCase(); + void set_keymap(std::initializer_list keycodes); + void add_key(const KeymapKey key); + + const KeymapKey* find_key(const layer_t layer_t, const keypos_t position) const; + void get_keycode(const layer_t layer, const keypos_t position, uint16_t* result) const; + void run_one_scan_loop(); void idle_for(unsigned ms); + + void expect_layer_state(layer_t layer) const; + + protected: + void print_test_log() const; + std::vector keymap; }; diff --git a/tests/test_common/test_keymap_key.cpp b/tests/test_common/test_keymap_key.cpp new file mode 100644 index 000000000..878ae097b --- /dev/null +++ b/tests/test_common/test_keymap_key.cpp @@ -0,0 +1,30 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include "test_keymap_key.hpp" +#include "test_logger.hpp" +#include "gtest/gtest-message.h" +#include "gtest/gtest.h" + +void KeymapKey::press() { + test_logger.trace() << "Key pressed: (" << +this->position.col << "," << +this->position.row << ")" << std::endl; + press_key(this->position.col, this->position.row); +} + +void KeymapKey::release() { + test_logger.trace() << "Key released: (" << +this->position.col << "," << +this->position.row << ")" << std::endl; + release_key(this->position.col, this->position.row); +} \ No newline at end of file diff --git a/tests/test_common/test_keymap_key.hpp b/tests/test_common/test_keymap_key.hpp new file mode 100644 index 000000000..7861cb4a3 --- /dev/null +++ b/tests/test_common/test_keymap_key.hpp @@ -0,0 +1,46 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +extern "C" { +#include "keyboard.h" +#include "test_matrix.h" +} + +#include + +typedef uint8_t layer_t; + +struct KeymapKey { + KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(keycode) { validate(); } + KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode, uint16_t report_code) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(report_code) { validate(); } + + void press(); + void release(); + + const layer_t layer; + const keypos_t position; + const uint16_t code; + /* Sometimes the keycode does not match the code that is send in the usb report, so we provide it here. */ + const uint16_t report_code; + + private: + void validate() { + assert(position.col <= MATRIX_COLS); + assert(position.row <= MATRIX_ROWS); + } +}; \ No newline at end of file diff --git a/tests/test_common/test_logger.cpp b/tests/test_common/test_logger.cpp new file mode 100644 index 000000000..959fdde5e --- /dev/null +++ b/tests/test_common/test_logger.cpp @@ -0,0 +1,39 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#include +#include "test_logger.hpp" + +TestLogger test_logger; + +TestLogger& TestLogger::info() { + *this << "[ INFO ] "; + return *this; +} + +TestLogger& TestLogger::trace() { + *this << "[ TRACE ] "; + return *this; +} + +TestLogger& TestLogger::error() { + *this << "[ ERROR ] "; + return *this; +} + +void TestLogger::reset() { this->m_log.str(""); }; + +void TestLogger::print_log() { std::cerr << this->m_log.str(); } diff --git a/tests/test_common/test_logger.hpp b/tests/test_common/test_logger.hpp new file mode 100644 index 000000000..348af7fab --- /dev/null +++ b/tests/test_common/test_logger.hpp @@ -0,0 +1,35 @@ +/* Copyright 2021 Stefan Kerkmann + * + * 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 . + */ + +#pragma once + +#include +#include + +class TestLogger : public std::ostream { + public: + TestLogger() : std::ostream(&m_log){}; + TestLogger& info(); + TestLogger& trace(); + TestLogger& error(); + void print_log(); + void reset(); + + private: + std::stringbuf m_log; +}; + +extern TestLogger test_logger; \ No newline at end of file