aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h23
-rw-r--r--tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk17
-rw-r--r--tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c22
-rw-r--r--tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp174
-rw-r--r--tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp911
-rw-r--r--tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp204
6 files changed, 1350 insertions, 1 deletions
diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h
new file mode 100644
index 0000000000..9267b94bec
--- /dev/null
+++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h
@@ -0,0 +1,23 @@
+/* Copyright 2022 Vladislav Kucheriavykh
+ * Copyright 2024-2025 Google LLC
+ *
+ * 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 CHORDAL_HOLD
+#define PERMISSIVE_HOLD
+#define FLOW_TAP_TERM 150
diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk
new file mode 100644
index 0000000000..2b049cea3b
--- /dev/null
+++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk
@@ -0,0 +1,17 @@
+# Copyright 2022 Vladislav Kucheriavykh
+# Copyright 2024 Google LLC
+#
+# 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/>.
+
+INTROSPECTION_KEYMAP_C = test_keymap.c
diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c
new file mode 100644
index 0000000000..8a6a2c59b0
--- /dev/null
+++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c
@@ -0,0 +1,22 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "quantum.h"
+
+const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {
+ {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
+ {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
+ {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
+ {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
+};
diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp
new file mode 100644
index 0000000000..e48cba73e2
--- /dev/null
+++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp
@@ -0,0 +1,174 @@
+/* Copyright 2021 Stefan Kerkmann
+ * Copyright 2024 Google LLC
+ *
+ * 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 "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<std::pair<KeymapKey, KeymapKey>>, public OneShot {};
+
+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_NO_REPORT(driver);
+ tap_key(osm_key);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+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_NO_REPORT(driver);
+ osm_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_REPORT(driver, (osm_key.report_code)).Times(2);
+ EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release OSM.
+ EXPECT_EMPTY_REPORT(driver);
+ osm_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(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 osl_key1 = KeymapKey{1, 0, 0, KC_X};
+ KeymapKey regular_key0 = KeymapKey{0, 1, 0, KC_Y};
+ KeymapKey regular_key1 = KeymapKey{1, 1, 0, KC_A};
+
+ set_keymap({osl_key, osl_key1, regular_key0, regular_key1});
+
+ // Press OSL key.
+ EXPECT_NO_REPORT(driver);
+ osl_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release OSL key.
+ EXPECT_NO_REPORT(driver);
+ osl_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_REPORT(driver, (regular_key1.report_code));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {
+ TestDriver driver;
+ InSequence s;
+ KeymapKey osl_key = KeymapKey{0, 0, 0, OSL(1)};
+ KeymapKey osm_key = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};
+ KeymapKey regular_key = KeymapKey{1, 1, 1, KC_A};
+ KeymapKey blank_key = KeymapKey{1, 0, 0, KC_NO};
+
+ set_keymap({osl_key, osm_key, regular_key, blank_key});
+
+ // Press OSL key.
+ EXPECT_NO_REPORT(driver);
+ osl_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release OSL key.
+ EXPECT_NO_REPORT(driver);
+ osl_key.release();
+ run_one_scan_loop();
+ EXPECT_TRUE(layer_state_is(1));
+ VERIFY_AND_CLEAR(driver);
+
+ // Press and release OSM.
+ EXPECT_NO_REPORT(driver);
+ tap_key(osm_key);
+ EXPECT_TRUE(layer_state_is(1));
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key);
+ VERIFY_AND_CLEAR(driver);
+}
diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp
new file mode 100644
index 0000000000..05acb6f416
--- /dev/null
+++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp
@@ -0,0 +1,911 @@
+// Copyright 2024-2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#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 ChordalHoldPermissiveHoldFlowTap : public TestFixture {};
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, chordal_hold_handedness) {
+ EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 0}), 'L');
+ EXPECT_EQ(chordal_hold_handedness({.col = MATRIX_COLS - 1, .row = 0}), 'R');
+ EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 2}), '*');
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, get_chordal_hold_default) {
+ auto make_record = [](uint8_t row, uint8_t col, keyevent_type_t type = KEY_EVENT) {
+ return keyrecord_t{
+ .event =
+ {
+ .key = {.col = col, .row = row},
+ .type = type,
+ .pressed = true,
+ },
+ };
+ };
+ // Create two records on the left hand.
+ keyrecord_t record_l0 = make_record(0, 0);
+ keyrecord_t record_l1 = make_record(1, 0);
+ // Create a record on the right hand.
+ keyrecord_t record_r = make_record(0, MATRIX_COLS - 1);
+
+ // Function should return true when records are on opposite hands.
+ EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_r));
+ EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_l0));
+ // ... and false when on the same hand.
+ EXPECT_FALSE(get_chordal_hold_default(&record_l0, &record_l1));
+ EXPECT_FALSE(get_chordal_hold_default(&record_l1, &record_l0));
+ // But (2, 0) has handedness '*', for which true is returned for chords
+ // with either hand.
+ keyrecord_t record_l2 = make_record(2, 0);
+ EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_l0));
+ EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_r));
+
+ // Create a record resulting from a combo.
+ keyrecord_t record_combo = make_record(0, 0, COMBO_EVENT);
+ // Function returns true in all cases.
+ EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_combo));
+ EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_combo));
+ EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_l0));
+ EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_r));
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, chord_nested_press_settled_as_hold) {
+ TestDriver driver;
+ InSequence s;
+ // Mod-tap key on the left hand.
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+ // Regular key on the right hand.
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap key.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ tap_key(regular_key);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, chord_rolled_press_settled_as_tap) {
+ TestDriver driver;
+ InSequence s;
+ // Mod-tap key on the left hand.
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+ // Regular key on the right hand.
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap key and regular key.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap key.
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_REPORT(driver, (KC_P, KC_A));
+ EXPECT_REPORT(driver, (KC_A));
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, non_chord_with_mod_tap_settled_as_tap) {
+ TestDriver driver;
+ InSequence s;
+ // Mod-tap key and regular key both on the left hand.
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+ auto regular_key = KeymapKey(0, 2, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_REPORT(driver, (KC_P, KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_REPORT(driver, (KC_P));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, tap_mod_tap_key) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM - 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, hold_mod_tap_key) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_same_hand_hold_til_timeout) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Continue holding til the tapping term.
+ EXPECT_REPORT(driver, (KC_RIGHT_CTRL));
+ EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT));
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_nested_press_opposite_hands) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_nested_press_same_hand) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, RSFT_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_same_hand_streak_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 1, 2, 3.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_same_hand_streak_orders) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 3, 2, 1.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_A));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 3, 1, 2.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 2, 3, 1.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_A, KC_C));
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_opposite_hands_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 1, 2, 3.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_two_left_one_right) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release key 3.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release key 2, then key 1.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release key 3.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release key 1, then key 2.
+ EXPECT_REPORT(driver, (KC_LEFT_CTRL));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_one_held_two_tapped) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 3, 2, 1.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys 3, 1, 2.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_one_regular_key) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_C);
+
+ set_keymap({mod_tap_key1, mod_tap_key2, regular_key});
+
+ // Press keys.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_C));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, 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, MATRIX_COLS - 1, 0, KC_A);
+ auto no_key = KeymapKey(1, 1, 0, XXXXXXX);
+ auto layer_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);
+
+ set_keymap({layer_tap_hold_key, regular_key, no_key, layer_key});
+
+ // Press layer-tap-hold key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.press();
+ run_one_scan_loop();
+ // Press regular key.
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(layer_state, 2);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release layer-tap-hold key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_hold_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(layer_state, 0);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, nested_tap_of_layer_0_layer_tap_keys) {
+ TestDriver driver;
+ InSequence s;
+ // The keys are layer-taps on layer 2 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, MATRIX_COLS - 1, 0, LT(1, KC_P));
+ auto first_key_on_layer = KeymapKey(1, 1, 0, KC_B);
+ auto second_key_on_layer = KeymapKey(1, MATRIX_COLS - 1, 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();
+ // Press second layer-tap key.
+ second_layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release second layer-tap key.
+ EXPECT_REPORT(driver, (KC_Q));
+ EXPECT_EMPTY_REPORT(driver);
+ second_layer_tap_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(layer_state, 2);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first layer-tap key.
+ EXPECT_NO_REPORT(driver);
+ first_layer_tap_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(layer_state, 0);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, lt_mt_one_regular_key) {
+ TestDriver driver;
+ InSequence s;
+ auto lt_key = KeymapKey(0, 1, 0, LT(1, KC_A));
+ auto mt_key0 = KeymapKey(0, 2, 0, SFT_T(KC_B));
+ auto mt_key1 = KeymapKey(1, 2, 0, CTL_T(KC_C));
+ auto regular_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_X);
+ auto no_key0 = KeymapKey(0, MATRIX_COLS - 1, 0, XXXXXXX);
+ auto no_key1 = KeymapKey(1, 1, 0, XXXXXXX);
+
+ set_keymap({lt_key, mt_key0, mt_key1, regular_key, no_key0, no_key1});
+
+ // Press LT, MT, and regular key.
+ EXPECT_NO_REPORT(driver);
+ lt_key.press();
+ run_one_scan_loop();
+ mt_key1.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release the regular key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_REPORT(driver, (KC_LCTL, KC_X));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ regular_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(get_mods(), MOD_BIT_LCTRL);
+ EXPECT_EQ(layer_state, 2);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release MT key.
+ EXPECT_EMPTY_REPORT(driver);
+ mt_key1.release();
+ run_one_scan_loop();
+ EXPECT_EQ(get_mods(), 0);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release LT key.
+ EXPECT_NO_REPORT(driver);
+ lt_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(layer_state, 0);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, 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, MATRIX_COLS - 1, 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, MATRIX_COLS - 1, 0, LT(2, KC_Q));
+ auto first_key_layer_2 = KeymapKey(2, 1, 0, KC_TRNS);
+ auto second_key_layer_2 = KeymapKey(2, MATRIX_COLS - 1, 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();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press second layer-tap key.
+ EXPECT_NO_REPORT(driver);
+ second_key_layer_0.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release second layer-tap key.
+ EXPECT_REPORT(driver, (KC_Q));
+ EXPECT_EMPTY_REPORT(driver);
+ second_key_layer_0.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first layer-tap key.
+ EXPECT_NO_REPORT(driver);
+ first_key_layer_0.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, 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, MATRIX_COLS - 1, 0, KC_A);
+ auto layer_key = KeymapKey(1, MATRIX_COLS - 1, 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();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release layer-tap-hold key.
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_REPORT(driver, (KC_P, KC_A));
+ EXPECT_REPORT(driver, (KC_A));
+ layer_tap_hold_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_tap_keys_stuttered_press) {
+ TestDriver driver;
+ InSequence s;
+
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, LSFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, LCTL_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Hold first mod-tap key until the tapping term.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key1.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press the second mod-tap key, then quickly release and press the first.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_B));
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_REPORT(driver, (KC_B, KC_A));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ EXPECT_EQ(get_mods(), 0); // Verify that Shift was released.
+ VERIFY_AND_CLEAR(driver);
+
+ // Release both keys.
+ EXPECT_REPORT(driver, (KC_A));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
diff --git a/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp
index a4233f0d57..d419a3b313 100644
--- a/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp
+++ b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp
@@ -98,7 +98,7 @@ TEST_F(FlowTapTest, distinct_taps) {
VERIFY_AND_CLEAR(driver);
EXPECT_EMPTY_REPORT(driver);
- idle_for(FLOW_TAP_TERM + 1);
+ idle_for(TAPPING_TERM + 1);
mod_tap_key2.release();
idle_for(FLOW_TAP_TERM + 1);
VERIFY_AND_CLEAR(driver);
@@ -640,3 +640,205 @@ TEST_F(FlowTapTest, quick_tap) {
run_one_scan_loop();
VERIFY_AND_CLEAR(driver);
}
+
+TEST_F(FlowTapTest, rolling_mt_mt) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(FlowTapTest, rolling_lt_mt_regular) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, CTL_T(KC_B));
+ auto regular_key = KeymapKey(0, 2, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(FlowTapTest, rolling_lt_regular_mt) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(FlowTapTest, rolling_mt_mt_mt) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release other mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(FlowTapTest, roll_release_132) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release other mod-tap keys.
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}