diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/caps_word/test.mk | 2 | ||||
-rw-r--r-- | tests/caps_word/test_caps_word.cpp | 52 | ||||
-rw-r--r-- | tests/repeat_key/test.mk | 1 | ||||
-rw-r--r-- | tests/repeat_key/test_repeat_key.cpp | 33 | ||||
-rw-r--r-- | tests/tap_hold_configurations/flow_tap/config.h | 22 | ||||
-rw-r--r-- | tests/tap_hold_configurations/flow_tap/test.mk | 18 | ||||
-rw-r--r-- | tests/tap_hold_configurations/flow_tap/test_keymap.c | 23 | ||||
-rw-r--r-- | tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp | 313 |
8 files changed, 457 insertions, 7 deletions
diff --git a/tests/caps_word/test.mk b/tests/caps_word/test.mk index 2509b01858..6d5664aa05 100644 --- a/tests/caps_word/test.mk +++ b/tests/caps_word/test.mk @@ -15,5 +15,7 @@ CAPS_WORD_ENABLE = yes COMMAND_ENABLE = no +LAYER_LOCK_ENABLE = yes SPACE_CADET_ENABLE = yes +TRI_LAYER_ENABLE = yes diff --git a/tests/caps_word/test_caps_word.cpp b/tests/caps_word/test_caps_word.cpp index 28d86e9324..4b58790915 100644 --- a/tests/caps_word/test_caps_word.cpp +++ b/tests/caps_word/test_caps_word.cpp @@ -156,21 +156,22 @@ TEST_F(CapsWord, IdleTimeout) { // Turn on Caps Word and tap "A". caps_word_on(); tap_key(key_a); - VERIFY_AND_CLEAR(driver); + EXPECT_EMPTY_REPORT(driver); idle_for(CAPS_WORD_IDLE_TIMEOUT); run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); // Caps Word should be off and mods should be clear. EXPECT_EQ(is_caps_word_on(), false); EXPECT_EQ(get_mods() | get_weak_mods(), 0); - EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); // Expect unshifted "A". EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); tap_key(key_a); - + run_one_scan_loop(); VERIFY_AND_CLEAR(driver); } @@ -244,6 +245,7 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) { // clang-format off EXPECT_CALL(driver, send_keyboard_mock(AnyOf( KeyboardReport(), + KeyboardReport(KC_LSFT), KeyboardReport(KC_RALT), KeyboardReport(KC_LSFT, KC_RALT)))) .Times(AnyNumber()); @@ -259,6 +261,9 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) { tap_key(key_a); run_one_scan_loop(); key_altgr.release(); + run_one_scan_loop(); + + idle_for(CAPS_WORD_IDLE_TIMEOUT); VERIFY_AND_CLEAR(driver); } @@ -274,6 +279,7 @@ TEST_F(CapsWord, ShiftsModTapAltGrSymbols) { // clang-format off EXPECT_CALL(driver, send_keyboard_mock(AnyOf( KeyboardReport(), + KeyboardReport(KC_LSFT), KeyboardReport(KC_RALT), KeyboardReport(KC_LSFT, KC_RALT)))) .Times(AnyNumber()); @@ -289,8 +295,11 @@ TEST_F(CapsWord, ShiftsModTapAltGrSymbols) { tap_key(key_a); run_one_scan_loop(); key_altgr_t.release(); - + run_one_scan_loop(); EXPECT_TRUE(is_caps_word_on()); + + idle_for(CAPS_WORD_IDLE_TIMEOUT); + VERIFY_AND_CLEAR(driver); } @@ -535,7 +544,11 @@ TEST_P(CapsWordDoubleTapShift, Activation) { // machine at this point. This due to imperfect test isolation which can't // reset the caps word double shift timer on test case setup. idle_for(CAPS_WORD_IDLE_TIMEOUT); + + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); tap_key(esc); + VERIFY_AND_CLEAR(driver); } // Double tap doesn't count if another key is pressed between the taps. @@ -589,6 +602,7 @@ TEST_P(CapsWordDoubleTapShift, SlowTaps) { EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off. clear_oneshot_mods(); + send_keyboard_report(); VERIFY_AND_CLEAR(driver); } @@ -626,7 +640,7 @@ TEST_F(CapsWord, IgnoresOSLHold) { run_one_scan_loop(); tap_key(key_b); key_osl.release(); - run_one_scan_loop(); + idle_for(CAPS_WORD_IDLE_TIMEOUT + 1); VERIFY_AND_CLEAR(driver); } @@ -645,15 +659,39 @@ TEST_F(CapsWord, IgnoresOSLTap) { KeyboardReport(), KeyboardReport(KC_LSFT)))) .Times(AnyNumber()); + // clang-format on EXPECT_REPORT(driver, (KC_LSFT, KC_B)); caps_word_on(); tap_key(key_osl); tap_key(key_b); - run_one_scan_loop(); + idle_for(CAPS_WORD_IDLE_TIMEOUT); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(CapsWord, IgnoresLayerLockKey) { + TestDriver driver; + KeymapKey key_llock(0, 1, 0, QK_LAYER_LOCK); + KeymapKey key_b(0, 0, 0, KC_B); + set_keymap({key_llock, key_b}); + + // Allow any number of reports with no keys or only modifiers. + // clang-format off + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( + KeyboardReport(), + KeyboardReport(KC_LSFT)))) + .Times(AnyNumber()); + // clang-format on + + EXPECT_REPORT(driver, (KC_LSFT, KC_B)); + caps_word_on(); + + tap_key(key_llock); + tap_key(key_b); + idle_for(CAPS_WORD_IDLE_TIMEOUT); VERIFY_AND_CLEAR(driver); } -// clang-format on } // namespace diff --git a/tests/repeat_key/test.mk b/tests/repeat_key/test.mk index aec8ff3bfb..186207ffc2 100644 --- a/tests/repeat_key/test.mk +++ b/tests/repeat_key/test.mk @@ -16,3 +16,4 @@ REPEAT_KEY_ENABLE = yes AUTO_SHIFT_ENABLE = yes +LAYER_LOCK_ENABLE = yes diff --git a/tests/repeat_key/test_repeat_key.cpp b/tests/repeat_key/test_repeat_key.cpp index eee44fc104..ed5d618761 100644 --- a/tests/repeat_key/test_repeat_key.cpp +++ b/tests/repeat_key/test_repeat_key.cpp @@ -751,4 +751,37 @@ TEST_F(RepeatKey, RepeatKeyInvoke) { testing::Mock::VerifyAndClearExpectations(&driver); } +// Check that mods and Layer Lock are not remembered. +TEST_F(RepeatKey, IgnoredKeys) { + TestDriver driver; + KeymapKey regular_key(0, 0, 0, KC_A); + KeymapKey key_repeat(0, 1, 0, QK_REP); + KeymapKey key_lsft(0, 2, 0, KC_LSFT); + KeymapKey key_lctl(0, 3, 0, KC_LCTL); + KeymapKey key_llck(0, 4, 0, QK_LAYER_LOCK); + set_keymap({regular_key, key_repeat, key_lsft, key_lctl, key_llck}); + + // Allow any number of empty reports. + EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); + { + InSequence seq; + EXPECT_REPORT(driver, (KC_A)); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LCTL)); + EXPECT_REPORT(driver, (KC_A)); + EXPECT_REPORT(driver, (KC_A)); + } + + tap_key(regular_key); // Taps the KC_A key. + + // Tap Shift, Ctrl, and Layer Lock keys, which should not be remembered. + tap_keys(key_lsft, key_lctl, key_llck); + EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A); + + // Tapping the Repeat Key should still reproduce KC_A. + tap_keys(key_repeat, key_repeat); + + testing::Mock::VerifyAndClearExpectations(&driver); +} + } // namespace diff --git a/tests/tap_hold_configurations/flow_tap/config.h b/tests/tap_hold_configurations/flow_tap/config.h new file mode 100644 index 0000000000..a17d488214 --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/config.h @@ -0,0 +1,22 @@ +/* Copyright 2022 Vladislav Kucheriavykh + * Copyright 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 FLOW_TAP_TERM 150 diff --git a/tests/tap_hold_configurations/flow_tap/test.mk b/tests/tap_hold_configurations/flow_tap/test.mk new file mode 100644 index 0000000000..81ba8da66d --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/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/>. + +COMBO_ENABLE = yes + +INTROSPECTION_KEYMAP_C = test_keymap.c diff --git a/tests/tap_hold_configurations/flow_tap/test_keymap.c b/tests/tap_hold_configurations/flow_tap/test_keymap.c new file mode 100644 index 0000000000..4dfe5e4cb6 --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/test_keymap.c @@ -0,0 +1,23 @@ +// Copyright 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 "quantum.h" + +uint16_t const mt_lt_combo[] = {SFT_T(KC_X), LT(1, KC_Y), COMBO_END}; + +// clang-format off +combo_t key_combos[] = { + COMBO(mt_lt_combo, KC_Z), +}; +// clang-format on diff --git a/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp new file mode 100644 index 0000000000..7816fcb6da --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp @@ -0,0 +1,313 @@ +// Copyright 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::AnyNumber; +using testing::InSequence; + +class FlowTapTest : public TestFixture {}; + +TEST_F(FlowTapTest, short_flow_tap_settled_as_tapped) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 0, 0, KC_A); + auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); + auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); + + set_keymap({regular_key, mod_tap_key1, mod_tap_key2}); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + // Press mod-tap key 1 quickly after regular key. The mod-tap should settle + // immediately as tapped, sending `KC_B`. + EXPECT_REPORT(driver, (KC_B)); + mod_tap_key1.press(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); + + // Press mod-tap key 2 quickly. + EXPECT_REPORT(driver, (KC_B, KC_C)); + mod_tap_key2.press(); + 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_key1.release(); + run_one_scan_loop(); + mod_tap_key2.release(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, long_flow_tap_settled_as_held) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 0, 0, KC_A); + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); + + set_keymap({regular_key, mod_tap_key}); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + EXPECT_NO_REPORT(driver); + idle_for(FLOW_TAP_TERM + 1); + VERIFY_AND_CLEAR(driver); + + // Press mod-tap key. + EXPECT_NO_REPORT(driver); + mod_tap_key.press(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); + + // Hold for the tapping term. + EXPECT_REPORT(driver, (KC_LSFT)); + idle_for(TAPPING_TERM); + 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(FlowTapTest, holding_multiple_mod_taps) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 0, 0, KC_A); + auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); + auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); + + set_keymap({regular_key, mod_tap_key1, mod_tap_key2}); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + EXPECT_NO_REPORT(driver); + idle_for(FLOW_TAP_TERM + 1); + VERIFY_AND_CLEAR(driver); + + // Press mod-tap keys. + EXPECT_NO_REPORT(driver); + mod_tap_key1.press(); + run_one_scan_loop(); + mod_tap_key2.press(); + idle_for(TAPPING_TERM - 5); // Hold almost until tapping term. + VERIFY_AND_CLEAR(driver); + + // Press regular key. + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A)); + regular_key.press(); + idle_for(10); + VERIFY_AND_CLEAR(driver); + + // Release keys. + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); + EXPECT_REPORT(driver, (KC_LCTL)); + EXPECT_EMPTY_REPORT(driver); + regular_key.release(); + run_one_scan_loop(); + mod_tap_key1.release(); + run_one_scan_loop(); + mod_tap_key2.release(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, layer_tap_key) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 0, 0, KC_A); + auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_B)); + auto regular_key2 = KeymapKey(1, 0, 0, KC_C); + + set_keymap({regular_key, layer_tap_key, regular_key2}); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + // Press layer-tap key, quickly after the regular key. + EXPECT_REPORT(driver, (KC_B)); + layer_tap_key.press(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); + + EXPECT_NO_REPORT(driver); + idle_for(TAPPING_TERM + 1); + VERIFY_AND_CLEAR(driver); + + // Release layer-tap key. + EXPECT_EMPTY_REPORT(driver); + layer_tap_key.release(); + run_one_scan_loop(); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + EXPECT_NO_REPORT(driver); + idle_for(FLOW_TAP_TERM + 1); + VERIFY_AND_CLEAR(driver); + + // Press layer-tap key, slowly after the regular key. + EXPECT_NO_REPORT(driver); + layer_tap_key.press(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); + + EXPECT_NO_REPORT(driver); + idle_for(TAPPING_TERM + 1); + EXPECT_EQ(layer_state, 1 << 1); + VERIFY_AND_CLEAR(driver); + + // Tap regular key2. + EXPECT_REPORT(driver, (KC_C)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + // Release layer-tap key. + EXPECT_NO_REPORT(driver); + layer_tap_key.release(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, combo_key) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 0, 0, KC_A); + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_X)); + auto layer_tap_key = KeymapKey(0, 2, 0, LT(1, KC_Y)); + + set_keymap({regular_key, mod_tap_key, layer_tap_key}); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + // Press combo keys quickly after regular key. + EXPECT_REPORT(driver, (KC_Z)); + EXPECT_EMPTY_REPORT(driver); + tap_combo({mod_tap_key, layer_tap_key}); + VERIFY_AND_CLEAR(driver); + + // Press mod-tap key quickly. + EXPECT_REPORT(driver, (KC_X)); + mod_tap_key.press(); + idle_for(TAPPING_TERM + 1); + 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(FlowTapTest, oneshot_mod_key) { + TestDriver driver; + InSequence s; + auto regular_key = KeymapKey(0, 0, 0, KC_A); + auto osm_key = KeymapKey(0, 1, 0, OSM(MOD_LSFT)); + + set_keymap({regular_key, osm_key}); + + // Tap regular key. + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + // Tap OSM, tap regular key. + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(osm_key); + tap_key(regular_key); + VERIFY_AND_CLEAR(driver); + + // Nested press of OSM and regular keys. + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_EMPTY_REPORT(driver); + osm_key.press(); + run_one_scan_loop(); + tap_key(regular_key); + osm_key.release(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, quick_tap) { + TestDriver driver; + InSequence s; + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_A)); + + set_keymap({mod_tap_key}); + + EXPECT_REPORT(driver, (KC_A)); + EXPECT_EMPTY_REPORT(driver); + tap_key(mod_tap_key); + VERIFY_AND_CLEAR(driver); + + EXPECT_REPORT(driver, (KC_A)); + mod_tap_key.press(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); + + EXPECT_NO_REPORT(driver); + idle_for(TAPPING_TERM + 1); + VERIFY_AND_CLEAR(driver); + + // Release mod-tap key. + EXPECT_EMPTY_REPORT(driver); + mod_tap_key.release(); + run_one_scan_loop(); + VERIFY_AND_CLEAR(driver); +} |