summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/autocorrect/config.h6
-rw-r--r--tests/autocorrect/test.mk8
-rw-r--r--tests/autocorrect/test_autocorrect.cpp204
-rw-r--r--tests/basic/test_action_layer.cpp1
-rw-r--r--tests/caps_word/caps_word_unicodemap/config.h2
-rw-r--r--tests/caps_word/test_caps_word.cpp14
-rw-r--r--tests/tap_dance/tap_dance_layers/config.h6
-rw-r--r--tests/tap_dance/tap_dance_layers/tap_dance_defs.c97
-rw-r--r--tests/tap_dance/tap_dance_layers/tap_dance_defs.h29
-rw-r--r--tests/tap_dance/tap_dance_layers/test.mk10
-rw-r--r--tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp717
-rw-r--r--tests/tap_hold_configurations/hold_on_other_key_press/config.h20
-rw-r--r--tests/tap_hold_configurations/hold_on_other_key_press/test.mk18
-rw-r--r--tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp423
-rw-r--r--tests/test_common/main.cpp2
-rw-r--r--tests/test_common/test_driver.cpp10
-rw-r--r--tests/test_common/test_driver.hpp8
17 files changed, 1553 insertions, 22 deletions
diff --git a/tests/autocorrect/config.h b/tests/autocorrect/config.h
new file mode 100644
index 0000000000..b68bf0c2d5
--- /dev/null
+++ b/tests/autocorrect/config.h
@@ -0,0 +1,6 @@
+// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "test_common.h"
diff --git a/tests/autocorrect/test.mk b/tests/autocorrect/test.mk
new file mode 100644
index 0000000000..7b97d8cce3
--- /dev/null
+++ b/tests/autocorrect/test.mk
@@ -0,0 +1,8 @@
+# Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# --------------------------------------------------------------------------------
+# Keep this file, even if it is empty, as a marker that this folder contains tests
+# --------------------------------------------------------------------------------
+
+AUTOCORRECT_ENABLE = yes
diff --git a/tests/autocorrect/test_autocorrect.cpp b/tests/autocorrect/test_autocorrect.cpp
new file mode 100644
index 0000000000..509c1c9ea4
--- /dev/null
+++ b/tests/autocorrect/test_autocorrect.cpp
@@ -0,0 +1,204 @@
+// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "keycode.h"
+#include "test_common.hpp"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::InSequence;
+
+class AutoCorrect : public TestFixture {
+ public:
+ void SetUp() override {
+ autocorrect_enable();
+ }
+ // Convenience function to tap `key`.
+ void TapKey(KeymapKey key) {
+ key.press();
+ run_one_scan_loop();
+ key.release();
+ run_one_scan_loop();
+ }
+
+ // Taps in order each key in `keys`.
+ template <typename... Ts>
+ void TapKeys(Ts... keys) {
+ for (KeymapKey key : {keys...}) {
+ TapKey(key);
+ }
+ }
+};
+
+// Test that verifies enable/disable/toggling works
+TEST_F(AutoCorrect, OnOffToggle) {
+ TestDriver driver;
+
+ EXPECT_EQ(autocorrect_is_enabled(), true);
+
+ autocorrect_disable();
+ EXPECT_EQ(autocorrect_is_enabled(), false);
+ autocorrect_disable();
+ EXPECT_EQ(autocorrect_is_enabled(), false);
+
+ autocorrect_enable();
+ EXPECT_EQ(autocorrect_is_enabled(), true);
+ autocorrect_enable();
+ EXPECT_EQ(autocorrect_is_enabled(), true);
+
+ autocorrect_toggle();
+ EXPECT_EQ(autocorrect_is_enabled(), false);
+ autocorrect_toggle();
+ EXPECT_EQ(autocorrect_is_enabled(), true);
+
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+// Test that typing "fales" autocorrects to "false"
+TEST_F(AutoCorrect, fales_to_false_autocorrection) {
+ TestDriver driver;
+ auto key_f = KeymapKey(0, 0, 0, KC_F);
+ auto key_a = KeymapKey(0, 1, 0, KC_A);
+ auto key_l = KeymapKey(0, 2, 0, KC_L);
+ auto key_e = KeymapKey(0, 3, 0, KC_E);
+ auto key_s = KeymapKey(0, 4, 0, KC_S);
+
+ set_keymap({key_f, key_a, key_l, key_e, key_s});
+
+ // Allow any number of empty reports.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());
+ { // Expect the following reports in this order.
+ InSequence s;
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_BACKSPACE)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));
+ }
+
+ TapKeys(key_f, key_a, key_l, key_e, key_s);
+
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+// Test that typing "fales" doesn't autocorrect if disabled
+TEST_F(AutoCorrect, fales_disabled_autocorrect) {
+ TestDriver driver;
+ auto key_f = KeymapKey(0, 0, 0, KC_F);
+ auto key_a = KeymapKey(0, 1, 0, KC_A);
+ auto key_l = KeymapKey(0, 2, 0, KC_L);
+ auto key_e = KeymapKey(0, 3, 0, KC_E);
+ auto key_s = KeymapKey(0, 4, 0, KC_S);
+
+ set_keymap({key_f, key_a, key_l, key_e, key_s});
+
+ // Allow any number of empty reports.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());
+ { // Expect the following reports in this order.
+ InSequence s;
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S)));
+ }
+
+ autocorrect_disable();
+ TapKeys(key_f, key_a, key_l, key_e, key_s);
+ autocorrect_enable();
+
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+// Test that typing "falsify" doesn't autocorrect if disabled
+TEST_F(AutoCorrect, falsify_should_not_autocorrect) {
+ TestDriver driver;
+ auto key_f = KeymapKey(0, 0, 0, KC_F);
+ auto key_a = KeymapKey(0, 1, 0, KC_A);
+ auto key_l = KeymapKey(0, 2, 0, KC_L);
+ auto key_s = KeymapKey(0, 3, 0, KC_S);
+ auto key_i = KeymapKey(0, 4, 0, KC_I);
+ auto key_y = KeymapKey(0, 5, 0, KC_Y);
+
+ set_keymap({key_f, key_a, key_l, key_s, key_i, key_y});
+
+ // Allow any number of empty reports.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());
+ { // Expect the following reports in this order.
+ InSequence s;
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_I)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_Y)));
+ }
+
+ TapKeys(key_f, key_a, key_l, key_s, key_i, key_f, key_y);
+
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+// Test that typing "ture" autocorrect to "true"
+TEST_F(AutoCorrect, ture_to_true_autocorrect) {
+ TestDriver driver;
+ auto key_t_code = KeymapKey(0, 0, 0, KC_T);
+ auto key_r = KeymapKey(0, 1, 0, KC_R);
+ auto key_u = KeymapKey(0, 2, 0, KC_U);
+ auto key_e = KeymapKey(0, 3, 0, KC_E);
+ auto key_space = KeymapKey(0, 4, 0, KC_SPACE);
+
+ set_keymap({key_t_code, key_r, key_u, key_e, key_space});
+
+ // Allow any number of empty reports.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());
+ { // Expect the following reports in this order.
+ InSequence s;
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_SPACE)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_T)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_BACKSPACE))).Times(2);
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));
+ }
+
+ TapKeys(key_space, key_t_code, key_u, key_r, key_e);
+
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+// Test that typing "overture" does not autocorrect
+TEST_F(AutoCorrect, overture_should_not_autocorrect) {
+ TestDriver driver;
+ auto key_t_code = KeymapKey(0, 0, 0, KC_T);
+ auto key_r = KeymapKey(0, 1, 0, KC_R);
+ auto key_u = KeymapKey(0, 2, 0, KC_U);
+ auto key_e = KeymapKey(0, 3, 0, KC_E);
+ auto key_o = KeymapKey(0, 4, 0, KC_O);
+ auto key_v = KeymapKey(0, 5, 0, KC_V);
+
+ set_keymap({key_t_code, key_r, key_u, key_e, key_o, key_v});
+
+ // Allow any number of empty reports.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());
+ { // Expect the following reports in this order.
+ InSequence s;
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_O)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_V)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_T)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));
+ }
+
+ TapKeys(key_o, key_v, key_e, key_r, key_t_code, key_u, key_r, key_e);
+
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
diff --git a/tests/basic/test_action_layer.cpp b/tests/basic/test_action_layer.cpp
index fa339a3375..d0a28d42c0 100644
--- a/tests/basic/test_action_layer.cpp
+++ b/tests/basic/test_action_layer.cpp
@@ -360,7 +360,6 @@ TEST_F(ActionLayer, LayerTapToggleWithToggleWithKeypress) {
}
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;
diff --git a/tests/caps_word/caps_word_unicodemap/config.h b/tests/caps_word/caps_word_unicodemap/config.h
index 89fd7924d4..0f770337b1 100644
--- a/tests/caps_word/caps_word_unicodemap/config.h
+++ b/tests/caps_word/caps_word_unicodemap/config.h
@@ -17,4 +17,4 @@
#include "test_common.h"
-#define UNICODE_SELECTED_MODES UC_LNX
+#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
diff --git a/tests/caps_word/test_caps_word.cpp b/tests/caps_word/test_caps_word.cpp
index 3f59ed3744..3d0735d8c2 100644
--- a/tests/caps_word/test_caps_word.cpp
+++ b/tests/caps_word/test_caps_word.cpp
@@ -118,19 +118,19 @@ TEST_F(CapsWord, DefaultCapsWordPressUserFun) {
}
}
-// Tests that `CAPSWRD` key toggles Caps Word.
+// Tests that `QK_CAPS_WORD_TOGGLE` key toggles Caps Word.
TEST_F(CapsWord, CapswrdKey) {
TestDriver driver;
- KeymapKey key_capswrd(0, 0, 0, CAPSWRD);
+ KeymapKey key_capswrd(0, 0, 0, QK_CAPS_WORD_TOGGLE);
set_keymap({key_capswrd});
// No keyboard reports should be sent.
EXPECT_NO_REPORT(driver);
- tap_key(key_capswrd); // Tap the CAPSWRD key.
+ tap_key(key_capswrd); // Tap the QK_CAPS_WORD_TOGGLE key.
EXPECT_EQ(is_caps_word_on(), true);
- tap_key(key_capswrd); // Tap the CAPSWRD key again.
+ tap_key(key_capswrd); // Tap the QK_CAPS_WORD_TOGGLE key again.
EXPECT_EQ(is_caps_word_on(), false);
testing::Mock::VerifyAndClearExpectations(&driver);
@@ -419,7 +419,7 @@ TEST_P(CapsWordBothShifts, PressLRLR) {
right_shift.press();
// For mod-tap and Space Cadet keys, wait for the tapping term.
- if (left_shift.code == LSFT_T(KC_A) || left_shift.code == KC_LSPO) {
+ if (left_shift.code == LSFT_T(KC_A) || left_shift.code == QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN) {
idle_for(TAPPING_TERM);
}
@@ -456,7 +456,7 @@ TEST_P(CapsWordBothShifts, PressLRRL) {
run_one_scan_loop();
right_shift.press();
- if (left_shift.code == LSFT_T(KC_A) || left_shift.code == KC_LSPO) {
+ if (left_shift.code == LSFT_T(KC_A) || left_shift.code == QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN) {
idle_for(TAPPING_TERM);
}
run_one_scan_loop();
@@ -481,7 +481,7 @@ INSTANTIATE_TEST_CASE_P(
CapsWordBothShiftsParams{
"OneshotShifts", OSM(MOD_LSFT), OSM(MOD_RSFT)},
CapsWordBothShiftsParams{
- "SpaceCadetShifts", KC_LSPO, KC_RSPC},
+ "SpaceCadetShifts", SC_LSPO, SC_RSPC},
CapsWordBothShiftsParams{
"ModTapShifts", LSFT_T(KC_A), RSFT_T(KC_B)}
),
diff --git a/tests/tap_dance/tap_dance_layers/config.h b/tests/tap_dance/tap_dance_layers/config.h
new file mode 100644
index 0000000000..32a19a8c68
--- /dev/null
+++ b/tests/tap_dance/tap_dance_layers/config.h
@@ -0,0 +1,6 @@
+// Copyright 2022 Sergey Vlasov (@sigprof)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "test_common.h"
diff --git a/tests/tap_dance/tap_dance_layers/tap_dance_defs.c b/tests/tap_dance/tap_dance_layers/tap_dance_defs.c
new file mode 100644
index 0000000000..5ec900c041
--- /dev/null
+++ b/tests/tap_dance/tap_dance_layers/tap_dance_defs.c
@@ -0,0 +1,97 @@
+// Copyright 2022 Sergey Vlasov (@sigprof)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "quantum.h"
+#include "tap_dance_defs.h"
+
+// Implement custom keycodes which are used to check that the layer switching
+// behaves properly.
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case FAST_AB:
+ case SLOW_AB:
+ if (record->event.pressed) {
+ tap_code(KC_A);
+ } else {
+ tap_code(KC_B);
+ }
+ return keycode == SLOW_AB;
+ case FAST_CD:
+ case SLOW_CD:
+ if (record->event.pressed) {
+ tap_code(KC_C);
+ } else {
+ tap_code(KC_D);
+ }
+ return keycode == SLOW_CD;
+ }
+ return true;
+}
+
+// Implement a custom tap dance with the following behavior:
+// - single tap: KC_APP
+// - single hold: MO(1)
+// - double tap/hold: KC_RCTL
+// (The single tap and hold actions are mostly equivalent to LT(1, KC_APP).)
+
+enum lt_app_state {
+ LTA_NONE,
+ LTA_SINGLE_TAP,
+ LTA_SINGLE_HOLD,
+ LTA_DOUBLE_HOLD,
+};
+
+static enum lt_app_state saved_lt_app_state;
+
+static enum lt_app_state get_lt_app_state(qk_tap_dance_state_t *state) {
+ if (state->count == 1) {
+ if (!state->pressed) {
+ return LTA_SINGLE_TAP;
+ } else {
+ return LTA_SINGLE_HOLD;
+ }
+ } else if (state->count == 2) {
+ return LTA_DOUBLE_HOLD;
+ } else {
+ return LTA_NONE;
+ }
+}
+
+static void lt_app_finished(qk_tap_dance_state_t *state, void *user_data) {
+ saved_lt_app_state = get_lt_app_state(state);
+ switch (saved_lt_app_state) {
+ case LTA_NONE:
+ break;
+ case LTA_SINGLE_TAP:
+ register_code(KC_APP);
+ break;
+ case LTA_SINGLE_HOLD:
+ layer_on(1);
+ break;
+ case LTA_DOUBLE_HOLD:
+ register_code(KC_RCTL);
+ break;
+ }
+}
+
+static void lt_app_reset(qk_tap_dance_state_t *state, void *user_data) {
+ switch (saved_lt_app_state) {
+ case LTA_NONE:
+ break;
+ case LTA_SINGLE_TAP:
+ unregister_code(KC_APP);
+ break;
+ case LTA_SINGLE_HOLD:
+ layer_off(1);
+ break;
+ case LTA_DOUBLE_HOLD:
+ unregister_code(KC_RCTL);
+ break;
+ }
+}
+
+qk_tap_dance_action_t tap_dance_actions[] = {
+ [TD_L_MOVE] = ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1),
+ [TD_L_TOGG] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1),
+ [TD_LT_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lt_app_finished, lt_app_reset),
+};
diff --git a/tests/tap_dance/tap_dance_layers/tap_dance_defs.h b/tests/tap_dance/tap_dance_layers/tap_dance_defs.h
new file mode 100644
index 0000000000..37cab0c2cb
--- /dev/null
+++ b/tests/tap_dance/tap_dance_layers/tap_dance_defs.h
@@ -0,0 +1,29 @@
+// Copyright 2022 Sergey Vlasov (@sigprof)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum custom_keycodes {
+ // (FAST|SLOW)_xy = tap KC_x on press, tap KC_y on release. For FAST_xy
+ // process_record_user() returns false to stop processing early; for
+ // SLOW_xy process_record_user() returns true, therefore all other key
+ // handlers are invoked.
+ FAST_AB = SAFE_RANGE,
+ FAST_CD,
+ SLOW_AB,
+ SLOW_CD,
+};
+
+enum tap_dance_ids {
+ TD_L_MOVE, // ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1)
+ TD_L_TOGG, // ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1)
+ TD_LT_APP, // similar to LT(1, KC_APP) with KC_RCTL on tap+hold or double tap
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/tap_dance/tap_dance_layers/test.mk b/tests/tap_dance/tap_dance_layers/test.mk
new file mode 100644
index 0000000000..b4cdc9b088
--- /dev/null
+++ b/tests/tap_dance/tap_dance_layers/test.mk
@@ -0,0 +1,10 @@
+# Copyright 2022 Sergey Vlasov (@sigprof)
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# --------------------------------------------------------------------------------
+# Keep this file, even if it is empty, as a marker that this folder contains tests
+# --------------------------------------------------------------------------------
+
+TAP_DANCE_ENABLE = yes
+
+SRC += tap_dance_defs.c
diff --git a/tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp b/tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp
new file mode 100644
index 0000000000..8b736b19c6
--- /dev/null
+++ b/tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp
@@ -0,0 +1,717 @@
+// Copyright 2022 Sergey Vlasov (@sigprof)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "keyboard_report_util.hpp"
+#include "keycode.h"
+#include "test_common.hpp"
+#include "action_tapping.h"
+#include "test_keymap_key.hpp"
+#include "tap_dance_defs.h"
+
+using testing::_;
+using testing::InSequence;
+
+struct TapDanceKeyParams {
+ std::string name; // Tap dance name (part of test name)
+ uint16_t keycode; // Tap dance keycode (TD(n))
+ uint16_t expect_on_tap; // Keycode for single tap
+ uint16_t expect_on_hold; // Keycode for single hold (may be MO(1))
+ uint16_t expect_on_double_tap; // Keycode for double tap (may be MO(1))
+ uint16_t expect_on_double_hold; // Keycode for double hold (may be MO(1))
+};
+
+struct OtherKeyLayerParams {
+ uint16_t keycode; // Keycode in the keymap
+ uint16_t expect_on_press; // Keycode to expect on press
+ uint16_t expect_on_release; // Keycode to expect on release (may be KC_NO if none)
+};
+
+struct OtherKeyParams {
+ std::string name; // Other key name (part of test name)
+ OtherKeyLayerParams l0; // Keycodes for layer 0
+ OtherKeyLayerParams l1; // Keycodes for layer 1
+};
+
+typedef std::tuple<TapDanceKeyParams, OtherKeyParams> TapDanceLayersParams;
+
+class TapDanceLayers : public ::testing::WithParamInterface<TapDanceLayersParams>, public TestFixture {
+ protected:
+ TapDanceKeyParams tap_dance;
+ OtherKeyParams other_key;
+
+ std::unique_ptr<KeymapKey> key_td, key_td_l1, key_other, key_other_l1;
+
+ void SetUp() override {
+ std::tie(tap_dance, other_key) = GetParam();
+
+ key_td = std::make_unique<KeymapKey>(0, 1, 0, tap_dance.keycode);
+ key_td_l1 = std::make_unique<KeymapKey>(1, 1, 0, KC_TRNS);
+ key_other = std::make_unique<KeymapKey>(0, 2, 0, other_key.l0.keycode);
+ key_other_l1 = std::make_unique<KeymapKey>(1, 2, 0, other_key.l1.keycode);
+
+ set_keymap({*key_td, *key_td_l1, *key_other, *key_other_l1});
+ }
+};
+
+static const TapDanceKeyParams tap_dance_keys[] = {
+ TapDanceKeyParams{
+ "LayerMove",
+ TD(TD_L_MOVE),
+ KC_APP,
+ KC_APP,
+ MO(1),
+ MO(1),
+ },
+ TapDanceKeyParams{
+ "LayerToggle",
+ TD(TD_L_TOGG),
+ KC_APP,
+ KC_APP,
+ MO(1),
+ MO(1),
+ },
+ TapDanceKeyParams{
+ "CustomLT",
+ TD(TD_LT_APP),
+ KC_APP,
+ MO(1),
+ KC_RCTL,
+ KC_RCTL,
+ },
+};
+
+static const OtherKeyParams other_keys[] = {
+ OtherKeyParams{
+ "Builtin",
+ OtherKeyLayerParams{KC_A, KC_A, KC_NO},
+ OtherKeyLayerParams{KC_B, KC_B, KC_NO},
+ },
+ OtherKeyParams{
+ "CustomFast",
+ OtherKeyLayerParams{FAST_AB, KC_A, KC_B},
+ OtherKeyLayerParams{FAST_CD, KC_C, KC_D},
+ },
+ OtherKeyParams{
+ "CustomSlow",
+ OtherKeyLayerParams{SLOW_AB, KC_A, KC_B},
+ OtherKeyLayerParams{SLOW_CD, KC_C, KC_D},
+ },
+};
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+ Layers,
+ TapDanceLayers,
+ ::testing::Combine(
+ ::testing::ValuesIn(tap_dance_keys),
+ ::testing::ValuesIn(other_keys)
+ ),
+ [](const ::testing::TestParamInfo<TapDanceLayersParams>& info) {
+ return std::get<0>(info.param).name + std::get<1>(info.param).name;
+ }
+);
+// clang-format on
+
+// Test single tap of the tap dance key with tapping term delay after the tap.
+TEST_P(TapDanceLayers, SingleTap) {
+ TestDriver driver;
+ InSequence s;
+
+ // The tap of the tap dance key does not result in sending a report
+ // immediately.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+
+ // After the tapping term expires, a tap event for the single tap keycode
+ // is generated.
+ idle_for(TAPPING_TERM - 1);
+ EXPECT_REPORT(driver, (tap_dance.expect_on_tap));
+ EXPECT_EMPTY_REPORT(driver);
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test single tap of the tap dance key without a delay between the tap dance
+// key and the other key.
+TEST_P(TapDanceLayers, SingleTapFast) {
+ TestDriver driver;
+ InSequence s;
+
+ // The tap of the tap dance key does not result in sending a report
+ // immediately.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+
+ // A quick press of the other key causes the tap event for the tap dance to
+ // be sent before the press event for the other key, and the layer 0
+ // mapping is used for the other key.
+ EXPECT_REPORT(driver, (tap_dance.expect_on_tap));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test single hold of the tap dance key with tapping term delay after the hold
+// (test variant for tap dances which switch the layer on hold).
+TEST_P(TapDanceLayers, SingleHoldLayer) {
+ if (tap_dance.expect_on_hold != MO(1)) {
+ // Do nothing - the SingleHoldKeycode test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the hold of the tap dance key.
+ EXPECT_NO_REPORT(driver);
+ key_td->press();
+ run_one_scan_loop();
+
+ // After the tapping term expires, the tap dance finishes and switches the
+ // layer, but does not send a report.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_press));
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key does not produce a report.
+ EXPECT_NO_REPORT(driver);
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 1 mapping of
+ // that key.
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test single hold of the tap dance key with tapping term delay after the hold
+// (test variant for tap dances which send a keycode on single hold).
+TEST_P(TapDanceLayers, SingleHoldKeycode) {
+ if (tap_dance.expect_on_hold == MO(1)) {
+ // Do nothing - the SingleHoldLayer test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the hold of the tap dance key.
+ EXPECT_NO_REPORT(driver);
+ key_td->press();
+ run_one_scan_loop();
+
+ // After the tapping term expires, the tap dance sends the report with the
+ // hold keycode.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ EXPECT_REPORT(driver, (tap_dance.expect_on_hold));
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (tap_dance.expect_on_hold, other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (tap_dance.expect_on_hold));
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key sends the release report for the
+ // corresponding hold keycode.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ } else {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ }
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test single hold of the tap dance key without tapping term delay after the
+// hold (test variant for tap dances which switch the layer on hold).
+TEST_P(TapDanceLayers, SingleHoldFastLayer) {
+ if (tap_dance.expect_on_hold != MO(1)) {
+ // Do nothing - the SingleHoldFastKeycode test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the hold of the tap dance key.
+ EXPECT_NO_REPORT(driver);
+ key_td->press();
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_press));
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key does not produce a report.
+ EXPECT_NO_REPORT(driver);
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test single hold of the tap dance key without tapping term delay after the hold
+// (test variant for tap dances which send a keycode on single hold).
+TEST_P(TapDanceLayers, SingleHoldFastKeycode) {
+ if (tap_dance.expect_on_hold == MO(1)) {
+ // Do nothing - the SingleHoldFastLayer test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the hold of the tap dance key.
+ EXPECT_NO_REPORT(driver);
+ key_td->press();
+ run_one_scan_loop();
+
+ // Pressing the other key produces first the report for the tap dance hold
+ // keycode, and then the reports for the layer 0 mapping of the other key.
+ EXPECT_REPORT(driver, (tap_dance.expect_on_hold));
+ EXPECT_REPORT(driver, (tap_dance.expect_on_hold, other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (tap_dance.expect_on_hold));
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key sends a release report for the corresponding
+ // hold keycode.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ } else {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ }
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double tap of the tap dance key with tapping term delay after the hold
+// (test variant for tap dances which switch the layer on double tap).
+TEST_P(TapDanceLayers, DoubleTapLayer) {
+ if (tap_dance.expect_on_double_tap != MO(1)) {
+ // Do nothing - the DoubleTapKeycode test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double tap of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ tap_key(*key_td);
+
+ // After the tapping term this tap dance does not send a report too.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_press));
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 1 mapping of
+ // that key.
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double tap of the tap dance key with tapping term delay after the hold
+// (test variant for tap dances which send a keycode on double tap).
+TEST_P(TapDanceLayers, DoubleTapKeycode) {
+ if (tap_dance.expect_on_double_tap == MO(1)) {
+ // Do nothing - the DoubleTapLayer test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double tap of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ tap_key(*key_td);
+
+ // After the tapping term this tap dance sends the double tap keycode.
+ idle_for(TAPPING_TERM - 1);
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_tap));
+ EXPECT_EMPTY_REPORT(driver);
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double tap of the tap dance key without tapping term delay after the
+// hold (test variant for tap dances which switch the layer on double tap).
+TEST_P(TapDanceLayers, DoubleTapFastLayer) {
+ if (tap_dance.expect_on_double_tap != MO(1)) {
+ // Do nothing - the DoubleTapFastKeycode test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double tap of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ tap_key(*key_td);
+
+ // Pressing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_press));
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 1 mapping of
+ // that key.
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double tap of the tap dance key without tapping term delay after the
+// hold (test variant for tap dances which send a keycode on double tap).
+TEST_P(TapDanceLayers, DoubleTapFastKeycode) {
+ if (tap_dance.expect_on_double_tap == MO(1)) {
+ // Do nothing - the DoubleTapFastLayer test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double tap of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ tap_key(*key_td);
+
+ // Pressing the other key produces first the report for the tap dance
+ // double tap keycode, and then the reports for the layer 0 mapping of the
+ // other key.
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_tap));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double hold of the tap dance key with tapping term delay after the hold
+// (test variant for tap dances which switch the layer on double hold).
+TEST_P(TapDanceLayers, DoubleHoldLayer) {
+ if (tap_dance.expect_on_double_hold != MO(1)) {
+ // Do nothing - the DoubleHoldKeycode test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double hold of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ key_td->press();
+ run_one_scan_loop();
+
+ // After the tapping term expires, the tap dance finishes and switches the
+ // layer, but does not send a report.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_press));
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key does not produce a report.
+ EXPECT_NO_REPORT(driver);
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 1 mapping of
+ // that key.
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double hold of the tap dance key with tapping term delay after the hold
+// (test variant for tap dances which send a keycode on double hold).
+TEST_P(TapDanceLayers, DoubleHoldKeycode) {
+ if (tap_dance.expect_on_double_hold == MO(1)) {
+ // Do nothing - the DoubleHoldLayer test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double hold of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ key_td->press();
+ run_one_scan_loop();
+
+ // After the tapping term expires, the tap dance sends the report with the
+ // double hold keycode.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold, other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key sends the release report for the
+ // corresponding double hold keycode.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ } else {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ }
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the reports for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double hold of the tap dance key without tapping term delay after the
+// hold (test variant for tap dances which switch the layer on double hold).
+TEST_P(TapDanceLayers, DoubleHoldFastLayer) {
+ if (tap_dance.expect_on_double_hold != MO(1)) {
+ // Do nothing - the DoubleHoldFastKeycode test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double hold of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ key_td->press();
+ run_one_scan_loop();
+
+ // Pressing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_press));
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key does not produce a report.
+ EXPECT_NO_REPORT(driver);
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the reports for the layer 1 mapping of
+ // that key.
+ if (other_key.l1.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l1.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
+
+// Test double hold of the tap dance key without tapping term delay after the hold
+// (test variant for tap dances which send a keycode on double hold).
+TEST_P(TapDanceLayers, DoubleHoldFastKeycode) {
+ if (tap_dance.expect_on_double_hold == MO(1)) {
+ // Do nothing - the DoubleHoldFastLayer test would run instead.
+ return;
+ }
+
+ TestDriver driver;
+ InSequence s;
+
+ // No report gets sent immediately after the double hold of the tap dance
+ // key.
+ EXPECT_NO_REPORT(driver);
+ tap_key(*key_td);
+ key_td->press();
+ run_one_scan_loop();
+
+ // Pressing the other key produces first the report for the tap dance
+ // double hold keycode, and then the reports for the layer 0 mapping of the
+ // other key.
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold, other_key.l0.expect_on_press));
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));
+ }
+ key_other->press();
+ run_one_scan_loop();
+
+ // Releasing the tap dance key sends a release report for the corresponding
+ // double hold keycode.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_EMPTY_REPORT(driver);
+ } else {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_press));
+ }
+ key_td->release();
+ run_one_scan_loop();
+
+ // Releasing the other key produces the report for the layer 0 mapping of
+ // that key.
+ if (other_key.l0.expect_on_release != KC_NO) {
+ EXPECT_REPORT(driver, (other_key.l0.expect_on_release));
+ }
+ EXPECT_EMPTY_REPORT(driver);
+ key_other->release();
+ run_one_scan_loop();
+}
diff --git a/tests/tap_hold_configurations/hold_on_other_key_press/config.h b/tests/tap_hold_configurations/hold_on_other_key_press/config.h
new file mode 100644
index 0000000000..98a72ec81f
--- /dev/null
+++ b/tests/tap_hold_configurations/hold_on_other_key_press/config.h
@@ -0,0 +1,20 @@
+/* Copyright 2022 Vladislav Kucheriavykh
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "test_common.h"
+#define HOLD_ON_OTHER_KEY_PRESS
diff --git a/tests/tap_hold_configurations/hold_on_other_key_press/test.mk b/tests/tap_hold_configurations/hold_on_other_key_press/test.mk
new file mode 100644
index 0000000000..6b5968df16
--- /dev/null
+++ b/tests/tap_hold_configurations/hold_on_other_key_press/test.mk
@@ -0,0 +1,18 @@
+# Copyright 2022 Vladislav Kucheriavykh
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# --------------------------------------------------------------------------------
+# Keep this file, even if it is empty, as a marker that this folder contains tests
+# --------------------------------------------------------------------------------
diff --git a/tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp b/tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp
new file mode 100644
index 0000000000..e77c756624
--- /dev/null
+++ b/tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp
@@ -0,0 +1,423 @@
+/* Copyright 2022 Vladislav Kucheriavykh
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 HoldOnOtherKeyPress : public TestFixture {};
+
+TEST_F(HoldOnOtherKeyPress, short_distinct_taps_of_mod_tap_key_and_regular_key) {
+ 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_NO_REPORT(driver);
+ mod_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release mod-tap-hold key. */
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key. */
+ EXPECT_REPORT(driver, (KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key. */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, long_distinct_taps_of_mod_tap_key_and_regular_key) {
+ 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_NO_REPORT(driver);
+ mod_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Idle for tapping term of mod tap hold key. */
+ EXPECT_REPORT(driver, (KC_LSFT));
+ idle_for(TAPPING_TERM + 1);
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release mod-tap-hold key. */
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key. */
+ EXPECT_REPORT(driver, (KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key. */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, short_distinct_taps_of_layer_tap_key_and_regular_key) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+ auto regular_key = KeymapKey(0, 2, 0, KC_A);
+ auto layer_key = KeymapKey(0, 2, 0, KC_B);
+
+ set_keymap({layer_tap_hold_key, regular_key});
+
+ /* Press layer-tap-hold key. */
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release layer-tap-hold key. */
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key. */
+ EXPECT_REPORT(driver, (KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key. */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, long_distinct_taps_of_layer_tap_key_and_regular_key) {
+ 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(0, 2, 0, KC_B);
+
+ set_keymap({layer_tap_hold_key, regular_key});
+
+ /* Press layer-tap-hold key. */
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Idle for tapping term of layer tap hold key. */
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release layer-tap-hold key. */
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key. */
+ EXPECT_REPORT(driver, (KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key. */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, 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_NO_REPORT(driver);
+ mod_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key. */
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_A, KC_LSFT));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key. */
+ EXPECT_REPORT(driver, (KC_LSFT));
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release mod-tap-hold key. */
+ EXPECT_EMPTY_REPORT(driver);
+ 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(HoldOnOtherKeyPress, tap_a_mod_tap_key_while_another_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_NO_REPORT(driver);
+ first_mod_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press second tap-hold key */
+ EXPECT_REPORT(driver, (KC_LSFT));
+ second_mod_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release second tap-hold key */
+ EXPECT_REPORT(driver, (KC_A, KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ second_mod_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release first mod-tap-hold key */
+ EXPECT_EMPTY_REPORT(driver);
+ first_mod_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, 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_NO_REPORT(driver);
+ layer_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key */
+ EXPECT_REPORT(driver, (KC_B));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release layer-tap-hold key */
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, nested_tap_of_layer_0_layer_tap_keys) {
+ TestDriver driver;
+ InSequence s;
+ /* The keys are layer-taps on layer 0 but regular keys on layer 1 */
+ auto first_layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A));
+ auto second_layer_tap_key = KeymapKey(0, 2, 0, LT(1, KC_P));
+ auto first_key_on_layer = KeymapKey(1, 1, 0, KC_B);
+ auto second_key_on_layer = KeymapKey(1, 2, 0, KC_Q);
+
+ set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer});
+
+ /* Press first layer-tap key */
+ EXPECT_NO_REPORT(driver);
+ first_layer_tap_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press second layer-tap key */
+ EXPECT_REPORT(driver, (KC_Q));
+ second_layer_tap_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release second layer-tap key */
+ EXPECT_EMPTY_REPORT(driver);
+ second_layer_tap_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release first layer-tap key */
+ EXPECT_NO_REPORT(driver);
+ first_layer_tap_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, nested_tap_of_layer_tap_keys) {
+ TestDriver driver;
+ InSequence s;
+ /* The keys are layer-taps on all layers */
+ auto first_key_layer_0 = KeymapKey(0, 1, 0, LT(1, KC_A));
+ auto second_key_layer_0 = KeymapKey(0, 2, 0, LT(1, KC_P));
+ auto first_key_layer_1 = KeymapKey(1, 1, 0, LT(2, KC_B));
+ auto second_key_layer_1 = KeymapKey(1, 2, 0, LT(2, KC_Q));
+ auto first_key_layer_2 = KeymapKey(2, 1, 0, KC_TRNS);
+ auto second_key_layer_2 = KeymapKey(2, 2, 0, KC_TRNS);
+
+ set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2});
+
+ /* Press first layer-tap key */
+ EXPECT_NO_REPORT(driver);
+ first_key_layer_0.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press second layer-tap key */
+ EXPECT_NO_REPORT(driver);
+ second_key_layer_0.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release second layer-tap key */
+ EXPECT_REPORT(driver, (KC_Q));
+ EXPECT_EMPTY_REPORT(driver);
+ second_key_layer_0.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release first layer-tap key */
+ EXPECT_NO_REPORT(driver);
+ first_key_layer_0.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, roll_mod_tap_key_with_regular_key) {
+ 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_NO_REPORT(driver);
+ mod_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key. */
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_A, KC_LSFT));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release mod-tap-hold key. */
+ EXPECT_REPORT(driver, (KC_A));
+ mod_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key. */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
+
+TEST_F(HoldOnOtherKeyPress, roll_layer_tap_key_with_regular_key) {
+ 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_NO_REPORT(driver);
+ layer_tap_hold_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Press regular key */
+ EXPECT_REPORT(driver, (KC_B));
+ regular_key.press();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release layer-tap-hold key */
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+
+ /* Release regular key */
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ testing::Mock::VerifyAndClearExpectations(&driver);
+}
diff --git a/tests/test_common/main.cpp b/tests/test_common/main.cpp
index 3e4b4c0f95..0f4e320b07 100644
--- a/tests/test_common/main.cpp
+++ b/tests/test_common/main.cpp
@@ -5,7 +5,7 @@ extern "C" {
#include "debug.h"
int8_t sendchar(uint8_t c) {
- fprintf(stderr, "%c", c);
+ fprintf(stdout, "%c", c);
return 0;
}
diff --git a/tests/test_common/test_driver.cpp b/tests/test_common/test_driver.cpp
index 86644ab6bd..f1c52cb7b6 100644
--- a/tests/test_common/test_driver.cpp
+++ b/tests/test_common/test_driver.cpp
@@ -31,7 +31,7 @@ uint8_t hex_digit_to_keycode(uint8_t digit) {
}
} // namespace
-TestDriver::TestDriver() : m_driver{&TestDriver::keyboard_leds, &TestDriver::send_keyboard, &TestDriver::send_mouse, &TestDriver::send_system, &TestDriver::send_consumer} {
+TestDriver::TestDriver() : m_driver{&TestDriver::keyboard_leds, &TestDriver::send_keyboard, &TestDriver::send_mouse, &TestDriver::send_extra} {
host_set_driver(&m_driver);
m_this = this;
}
@@ -53,12 +53,8 @@ void TestDriver::send_mouse(report_mouse_t* report) {
m_this->send_mouse_mock(*report);
}
-void TestDriver::send_system(uint16_t data) {
- m_this->send_system_mock(data);
-}
-
-void TestDriver::send_consumer(uint16_t data) {
- m_this->send_consumer(data);
+void TestDriver::send_extra(report_extra_t* report) {
+ m_this->send_extra_mock(*report);
}
namespace internal {
diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp
index b58cfd1ebc..982aec6c83 100644
--- a/tests/test_common/test_driver.hpp
+++ b/tests/test_common/test_driver.hpp
@@ -32,15 +32,13 @@ class TestDriver {
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));
+ MOCK_METHOD1(send_extra_mock, void(report_extra_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);
+ static void send_extra(report_extra_t* report);
host_driver_t m_driver;
uint8_t m_leds = 0;
static TestDriver* m_this;
@@ -66,7 +64,7 @@ class TestDriver {
#define EXPECT_REPORT(driver, report) EXPECT_CALL((driver), send_keyboard_mock(KeyboardReport report))
/**
- * @brief Sets gmock expectation that Unicode `code_point` is sent with UC_LNX input
+ * @brief Sets gmock expectation that Unicode `code_point` is sent with UNICODE_MODE_LINUX input
* mode. For instance for U+2013,
*
* EXPECT_UNICODE(driver, 0x2013);