diff options
| author | Pascal Getreuer <50221757+getreuer@users.noreply.github.com> | 2023-05-20 05:35:06 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-20 22:35:06 +1000 |
| commit | 3993b15f054265071730cdb450f43457dcf4c64a (patch) | |
| tree | 61c5b980ed14428bae3c0278c27937dbb5d33627 /tests | |
| parent | e1766df185869d8a591228d37f3f7b6d5b4049b4 (diff) | |
[Core] Add Repeat Key ("repeat last key") as a core feature. (#19700)
Co-authored-by: casuanoob <96005765+casuanoob@users.noreply.github.com>
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/repeat_key/alt_repeat_key/config.h | 18 | ||||
| -rw-r--r-- | tests/repeat_key/alt_repeat_key/test.mk | 18 | ||||
| -rw-r--r-- | tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp | 523 | ||||
| -rw-r--r-- | tests/repeat_key/config.h | 20 | ||||
| -rw-r--r-- | tests/repeat_key/repeat_key_combo/config.h | 18 | ||||
| -rw-r--r-- | tests/repeat_key/repeat_key_combo/test.mk | 18 | ||||
| -rw-r--r-- | tests/repeat_key/repeat_key_combo/test_repeat_key_combo.cpp | 67 | ||||
| -rw-r--r-- | tests/repeat_key/test.mk | 18 | ||||
| -rw-r--r-- | tests/repeat_key/test_repeat_key.cpp | 754 | ||||
| -rw-r--r-- | tests/test_common/keycode_table.cpp | 2 | ||||
| -rw-r--r-- | tests/test_common/test_driver.hpp | 12 |
11 files changed, 1468 insertions, 0 deletions
diff --git a/tests/repeat_key/alt_repeat_key/config.h b/tests/repeat_key/alt_repeat_key/config.h new file mode 100644 index 0000000000..d0c4ddadbd --- /dev/null +++ b/tests/repeat_key/alt_repeat_key/config.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // Copyright 2023 Google LLC | ||
| 2 | // | ||
| 3 | // This program is free software: you can redistribute it and/or modify | ||
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, either version 2 of the License, or | ||
| 6 | // (at your option) any later version. | ||
| 7 | // | ||
| 8 | // This program is distributed in the hope that it will be useful, | ||
| 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | // GNU General Public License for more details. | ||
| 12 | // | ||
| 13 | // You should have received a copy of the GNU General Public License | ||
| 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | #pragma once | ||
| 17 | |||
| 18 | #include "test_common.h" | ||
diff --git a/tests/repeat_key/alt_repeat_key/test.mk b/tests/repeat_key/alt_repeat_key/test.mk new file mode 100644 index 0000000000..080871c816 --- /dev/null +++ b/tests/repeat_key/alt_repeat_key/test.mk | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # Copyright 2023 Google LLC | ||
| 2 | # | ||
| 3 | # This program is free software: you can redistribute it and/or modify | ||
| 4 | # it under the terms of the GNU General Public License as published by | ||
| 5 | # the Free Software Foundation, either version 2 of the License, or | ||
| 6 | # (at your option) any later version. | ||
| 7 | # | ||
| 8 | # This program is distributed in the hope that it will be useful, | ||
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | # GNU General Public License for more details. | ||
| 12 | # | ||
| 13 | # You should have received a copy of the GNU General Public License | ||
| 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | REPEAT_KEY_ENABLE = yes | ||
| 17 | |||
| 18 | EXTRAKEY_ENABLE = yes | ||
diff --git a/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp b/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp new file mode 100644 index 0000000000..ae525acb45 --- /dev/null +++ b/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp | |||
| @@ -0,0 +1,523 @@ | |||
| 1 | // Copyright 2023 Google LLC | ||
| 2 | // | ||
| 3 | // This program is free software: you can redistribute it and/or modify | ||
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, either version 2 of the License, or | ||
| 6 | // (at your option) any later version. | ||
| 7 | // | ||
| 8 | // This program is distributed in the hope that it will be useful, | ||
| 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | // GNU General Public License for more details. | ||
| 12 | // | ||
| 13 | // You should have received a copy of the GNU General Public License | ||
| 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | #include <functional> | ||
| 17 | |||
| 18 | #include "keyboard_report_util.hpp" | ||
| 19 | #include "keycode.h" | ||
| 20 | #include "test_common.hpp" | ||
| 21 | #include "test_fixture.hpp" | ||
| 22 | #include "test_keymap_key.hpp" | ||
| 23 | |||
| 24 | using ::testing::AnyNumber; | ||
| 25 | using ::testing::InSequence; | ||
| 26 | |||
| 27 | namespace { | ||
| 28 | |||
| 29 | bool process_record_user_default(uint16_t keycode, keyrecord_t* record) { | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool remember_last_key_user_default(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) { | ||
| 34 | return true; | ||
| 35 | } | ||
| 36 | |||
| 37 | uint16_t get_alt_repeat_key_keycode_user_default(uint16_t keycode, uint8_t mods) { | ||
| 38 | return KC_TRNS; | ||
| 39 | } | ||
| 40 | |||
| 41 | // Indirections so that process_record_user() can be replaced with other | ||
| 42 | // functions in the test cases below. | ||
| 43 | std::function<bool(uint16_t, keyrecord_t*)> process_record_user_fun = process_record_user_default; | ||
| 44 | std::function<bool(uint16_t, keyrecord_t*, uint8_t*)> remember_last_key_user_fun = remember_last_key_user_default; | ||
| 45 | std::function<uint16_t(uint16_t, uint8_t)> get_alt_repeat_key_keycode_user_fun = get_alt_repeat_key_keycode_user_default; | ||
| 46 | |||
| 47 | extern "C" bool process_record_user(uint16_t keycode, keyrecord_t* record) { | ||
| 48 | return process_record_user_fun(keycode, record); | ||
| 49 | } | ||
| 50 | |||
| 51 | extern "C" bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) { | ||
| 52 | return remember_last_key_user_fun(keycode, record, remembered_mods); | ||
| 53 | } | ||
| 54 | |||
| 55 | extern "C" uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { | ||
| 56 | return get_alt_repeat_key_keycode_user_fun(keycode, mods); | ||
| 57 | } | ||
| 58 | |||
| 59 | class AltRepeatKey : public TestFixture { | ||
| 60 | public: | ||
| 61 | bool process_record_user_was_called_; | ||
| 62 | |||
| 63 | void SetUp() override { | ||
| 64 | process_record_user_fun = process_record_user_default; | ||
| 65 | remember_last_key_user_fun = remember_last_key_user_default; | ||
| 66 | get_alt_repeat_key_keycode_user_fun = get_alt_repeat_key_keycode_user_default; | ||
| 67 | } | ||
| 68 | |||
| 69 | void ExpectProcessRecordUserCalledWith(bool expected_press, uint16_t expected_keycode, int8_t expected_repeat_key_count) { | ||
| 70 | process_record_user_was_called_ = false; | ||
| 71 | process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) { | ||
| 72 | EXPECT_EQ(record->event.pressed, expected_press); | ||
| 73 | EXPECT_KEYCODE_EQ(keycode, expected_keycode); | ||
| 74 | EXPECT_EQ(get_repeat_key_count(), expected_repeat_key_count); | ||
| 75 | // Tests below use this to verify process_record_user() was called. | ||
| 76 | process_record_user_was_called_ = true; | ||
| 77 | return true; | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | // Expects that the characters of `s` are sent. | ||
| 82 | // NOTE: This implementation is limited to chars a-z, A-Z. | ||
| 83 | void ExpectString(TestDriver& driver, const std::string& s) { | ||
| 84 | InSequence seq; | ||
| 85 | for (int c : s) { | ||
| 86 | switch (c) { | ||
| 87 | case 'a' ... 'z': { // Lowercase letter. | ||
| 88 | uint16_t keycode = c - ('a' - KC_A); | ||
| 89 | EXPECT_REPORT(driver, (keycode)); | ||
| 90 | } break; | ||
| 91 | |||
| 92 | case 'A' ... 'Z': { // Capital letter = KC_LSFT + letter key. | ||
| 93 | uint16_t keycode = c - ('A' - KC_A); | ||
| 94 | EXPECT_REPORT(driver, (KC_LSFT, keycode)); | ||
| 95 | } break; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | |||
| 101 | TEST_F(AltRepeatKey, AlternateBasic) { | ||
| 102 | TestDriver driver; | ||
| 103 | KeymapKey key_bspc(0, 0, 0, KC_BSPC); | ||
| 104 | KeymapKey key_pgdn(0, 1, 0, KC_PGDN); | ||
| 105 | KeymapKey key_pgup(0, 2, 0, KC_PGUP); | ||
| 106 | KeymapKey key_repeat(0, 4, 0, QK_REP); | ||
| 107 | KeymapKey key_alt_repeat(0, 5, 0, QK_AREP); | ||
| 108 | set_keymap({key_bspc, key_pgdn, key_pgup, key_repeat, key_alt_repeat}); | ||
| 109 | |||
| 110 | // Allow any number of empty reports. | ||
| 111 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 112 | { | ||
| 113 | InSequence seq; | ||
| 114 | EXPECT_REPORT(driver, (KC_BSPC)); | ||
| 115 | EXPECT_REPORT(driver, (KC_DEL)); | ||
| 116 | EXPECT_REPORT(driver, (KC_DEL)); | ||
| 117 | EXPECT_REPORT(driver, (KC_BSPC)); | ||
| 118 | EXPECT_REPORT(driver, (KC_DEL)); | ||
| 119 | EXPECT_REPORT(driver, (KC_PGDN)); | ||
| 120 | EXPECT_REPORT(driver, (KC_PGUP)); | ||
| 121 | EXPECT_REPORT(driver, (KC_PGUP)); | ||
| 122 | EXPECT_REPORT(driver, (KC_PGDN)); | ||
| 123 | } | ||
| 124 | |||
| 125 | tap_key(key_bspc); | ||
| 126 | |||
| 127 | for (int n = 1; n <= 2; ++n) { // Tap the Alternate Repeat Key twice. | ||
| 128 | ExpectProcessRecordUserCalledWith(true, KC_DEL, -n); | ||
| 129 | key_alt_repeat.press(); // Press the Alternate Repeat Key. | ||
| 130 | run_one_scan_loop(); | ||
| 131 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 132 | |||
| 133 | // Expect the corresponding release event. | ||
| 134 | ExpectProcessRecordUserCalledWith(false, KC_DEL, -n); | ||
| 135 | key_alt_repeat.release(); // Release the Repeat Key. | ||
| 136 | run_one_scan_loop(); | ||
| 137 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 138 | } | ||
| 139 | |||
| 140 | process_record_user_fun = process_record_user_default; | ||
| 141 | tap_keys(key_repeat, key_alt_repeat); | ||
| 142 | tap_keys(key_pgdn, key_alt_repeat); | ||
| 143 | tap_keys(key_pgup, key_alt_repeat); | ||
| 144 | |||
| 145 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 146 | } | ||
| 147 | |||
| 148 | struct TestParamsAlternateKeyCodes { | ||
| 149 | uint16_t keycode; | ||
| 150 | uint8_t mods; | ||
| 151 | uint16_t expected_alt_keycode; | ||
| 152 | }; | ||
| 153 | |||
| 154 | // Tests `get_alt_repeat_key_keycode()` for various keycodes. | ||
| 155 | TEST_F(AltRepeatKey, GetAltRepeatKeyKeycode) { | ||
| 156 | for (const auto& params : std::vector<TestParamsAlternateKeyCodes>({ | ||
| 157 | // clang-format off | ||
| 158 | // Each line tests one call to `get_alt_repeat_key_keycode()`: | ||
| 159 | // {keycode, mods, expected_alt_keycode}. | ||
| 160 | // Arrows. | ||
| 161 | {KC_LEFT, 0, KC_RGHT}, | ||
| 162 | {KC_RGHT, 0, KC_LEFT}, | ||
| 163 | {KC_LEFT, MOD_BIT(KC_LSFT), LSFT(KC_RGHT)}, | ||
| 164 | {KC_LEFT, MOD_BIT(KC_RSFT), RSFT(KC_RGHT)}, | ||
| 165 | {KC_LEFT, MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT), C(S(KC_RGHT))}, | ||
| 166 | {KC_LEFT, MOD_BIT(KC_LGUI), LGUI(KC_RGHT)}, | ||
| 167 | {C(KC_LEFT), MOD_BIT(KC_LSFT), C(S(KC_RGHT))}, | ||
| 168 | {KC_UP, 0, KC_DOWN}, | ||
| 169 | // Navigation keys. | ||
| 170 | {KC_PGUP, 0, KC_PGDN}, | ||
| 171 | {KC_HOME, 0, KC_END }, | ||
| 172 | // Media keys. | ||
| 173 | {KC_WBAK, 0, KC_WFWD}, | ||
| 174 | {KC_MNXT, 0, KC_MPRV}, | ||
| 175 | {KC_MRWD, 0, KC_MFFD}, | ||
| 176 | {KC_VOLU, 0, KC_VOLD}, | ||
| 177 | {KC_BRIU, 0, KC_BRID}, | ||
| 178 | // Emacs navigation. | ||
| 179 | {KC_N, MOD_BIT(KC_LCTL), C(KC_P)}, | ||
| 180 | {KC_B, MOD_BIT(KC_LCTL), LCTL(KC_F)}, | ||
| 181 | {KC_B, MOD_BIT(KC_RCTL), RCTL(KC_F)}, | ||
| 182 | {KC_B, MOD_BIT(KC_LALT), LALT(KC_F)}, | ||
| 183 | {KC_F, MOD_BIT(KC_LCTL), C(KC_B)}, | ||
| 184 | {KC_A, MOD_BIT(KC_LCTL), C(KC_E)}, | ||
| 185 | {KC_D, MOD_BIT(KC_LCTL), C(KC_U)}, | ||
| 186 | // Vim navigation. | ||
| 187 | {KC_J, 0, KC_K}, | ||
| 188 | {KC_K, 0, KC_J}, | ||
| 189 | {KC_H, 0, KC_L}, | ||
| 190 | {KC_B, 0, KC_W}, | ||
| 191 | {KC_W, 0, KC_B}, | ||
| 192 | {KC_E, 0, KC_B}, | ||
| 193 | {KC_B, MOD_BIT(KC_LSFT), S(KC_W)}, | ||
| 194 | {KC_W, MOD_BIT(KC_LSFT), S(KC_B)}, | ||
| 195 | {KC_E, MOD_BIT(KC_LSFT), S(KC_B)}, | ||
| 196 | {KC_O, MOD_BIT(KC_LCTL), C(KC_I)}, | ||
| 197 | {KC_I, MOD_BIT(KC_LCTL), C(KC_O)}, | ||
| 198 | // Other. | ||
| 199 | {KC_DEL, 0, KC_BSPC}, | ||
| 200 | {KC_LBRC, 0, KC_RBRC}, | ||
| 201 | {KC_LCBR, 0, KC_RCBR}, | ||
| 202 | // Some keys where the last key is a tap-hold key. | ||
| 203 | {LSFT_T(KC_F), MOD_BIT(KC_RCTL), RCTL(KC_B)}, | ||
| 204 | {LT(1, KC_A), MOD_BIT(KC_RGUI), RGUI(KC_E)}, | ||
| 205 | {RALT_T(KC_J), 0, KC_K}, | ||
| 206 | // Some keys where no alternate is defined. | ||
| 207 | {KC_A, 0, KC_NO}, | ||
| 208 | {KC_F1, 0, KC_NO}, | ||
| 209 | {QK_LEAD, 0, KC_NO}, | ||
| 210 | {MO(1), 0, KC_NO}, | ||
| 211 | // clang-format on | ||
| 212 | })) { | ||
| 213 | SCOPED_TRACE(std::string("Input keycode: ") + get_keycode_identifier_or_default(params.keycode)); | ||
| 214 | set_last_keycode(params.keycode); | ||
| 215 | set_last_mods(params.mods); | ||
| 216 | |||
| 217 | const uint16_t actual = get_alt_repeat_key_keycode(); | ||
| 218 | |||
| 219 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), params.expected_alt_keycode); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | // Test adding to and overriding the above through the | ||
| 224 | // `get_alt_repeat_key_keycode_user()` callback. | ||
| 225 | TEST_F(AltRepeatKey, GetAltRepeatKeyKeycodeUser) { | ||
| 226 | get_alt_repeat_key_keycode_user_fun = [](uint16_t keycode, uint8_t mods) -> uint16_t { | ||
| 227 | bool shifted = (mods & MOD_MASK_SHIFT); | ||
| 228 | switch (keycode) { | ||
| 229 | case KC_LEFT: | ||
| 230 | return KC_ENT; | ||
| 231 | case MO(1): | ||
| 232 | return TG(1); | ||
| 233 | case KC_TAB: // Tab <-> Shift + Tab example. | ||
| 234 | if (shifted) { | ||
| 235 | return KC_TAB; | ||
| 236 | } else { | ||
| 237 | return S(KC_TAB); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | // Ctrl + Y <-> Ctrl + Z example. | ||
| 242 | if ((mods & MOD_MASK_CTRL)) { | ||
| 243 | switch (keycode) { | ||
| 244 | case KC_Y: | ||
| 245 | return C(KC_Z); | ||
| 246 | case KC_Z: | ||
| 247 | return C(KC_Y); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | return KC_NO; | ||
| 252 | }; | ||
| 253 | |||
| 254 | set_last_keycode(KC_LEFT); | ||
| 255 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), KC_ENT); | ||
| 256 | |||
| 257 | set_last_keycode(MO(1)); | ||
| 258 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), TG(1)); | ||
| 259 | |||
| 260 | set_last_keycode(KC_TAB); | ||
| 261 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), S(KC_TAB)); | ||
| 262 | |||
| 263 | set_last_keycode(KC_TAB); | ||
| 264 | set_last_mods(MOD_BIT(KC_LSFT)); | ||
| 265 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), KC_TAB); | ||
| 266 | |||
| 267 | set_last_keycode(KC_Z); | ||
| 268 | set_last_mods(MOD_BIT(KC_LCTL)); | ||
| 269 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), C(KC_Y)); | ||
| 270 | |||
| 271 | set_last_keycode(KC_Y); | ||
| 272 | set_last_mods(MOD_BIT(KC_LCTL)); | ||
| 273 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), C(KC_Z)); | ||
| 274 | } | ||
| 275 | |||
| 276 | // Tests rolling from a key to Alternate Repeat. | ||
| 277 | TEST_F(AltRepeatKey, RollingToAltRepeat) { | ||
| 278 | TestDriver driver; | ||
| 279 | KeymapKey key_left(0, 0, 0, KC_LEFT); | ||
| 280 | KeymapKey key_alt_repeat(0, 1, 0, QK_AREP); | ||
| 281 | set_keymap({key_left, key_alt_repeat}); | ||
| 282 | |||
| 283 | { | ||
| 284 | InSequence seq; | ||
| 285 | EXPECT_REPORT(driver, (KC_LEFT)); | ||
| 286 | EXPECT_REPORT(driver, (KC_LEFT, KC_RGHT)); | ||
| 287 | EXPECT_REPORT(driver, (KC_RGHT)); | ||
| 288 | EXPECT_EMPTY_REPORT(driver); | ||
| 289 | EXPECT_REPORT(driver, (KC_RGHT)); | ||
| 290 | EXPECT_EMPTY_REPORT(driver); | ||
| 291 | } | ||
| 292 | |||
| 293 | // Perform a rolled press from Left to Alternate Repeat. | ||
| 294 | |||
| 295 | ExpectProcessRecordUserCalledWith(true, KC_LEFT, 0); | ||
| 296 | key_left.press(); | ||
| 297 | run_one_scan_loop(); | ||
| 298 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 299 | |||
| 300 | ExpectProcessRecordUserCalledWith(true, KC_RGHT, -1); | ||
| 301 | key_alt_repeat.press(); // Press the Alternate Repeat Key. | ||
| 302 | run_one_scan_loop(); | ||
| 303 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 304 | |||
| 305 | ExpectProcessRecordUserCalledWith(false, KC_LEFT, 0); | ||
| 306 | key_left.release(); | ||
| 307 | run_one_scan_loop(); | ||
| 308 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 309 | |||
| 310 | ExpectProcessRecordUserCalledWith(false, KC_RGHT, -1); | ||
| 311 | key_alt_repeat.release(); // Release the Alternate Repeat Key. | ||
| 312 | run_one_scan_loop(); | ||
| 313 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 314 | |||
| 315 | process_record_user_fun = process_record_user_default; | ||
| 316 | tap_key(key_alt_repeat); | ||
| 317 | |||
| 318 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 319 | } | ||
| 320 | |||
| 321 | // Tests rolling from Alternate Repeat to another key. | ||
| 322 | TEST_F(AltRepeatKey, RollingFromAltRepeat) { | ||
| 323 | TestDriver driver; | ||
| 324 | KeymapKey key_left(0, 0, 0, KC_LEFT); | ||
| 325 | KeymapKey key_up(0, 1, 0, KC_UP); | ||
| 326 | KeymapKey key_alt_repeat(0, 2, 0, QK_AREP); | ||
| 327 | set_keymap({key_left, key_up, key_alt_repeat}); | ||
| 328 | |||
| 329 | { | ||
| 330 | InSequence seq; | ||
| 331 | EXPECT_REPORT(driver, (KC_LEFT)); | ||
| 332 | EXPECT_EMPTY_REPORT(driver); | ||
| 333 | EXPECT_REPORT(driver, (KC_RGHT)); | ||
| 334 | EXPECT_REPORT(driver, (KC_RGHT, KC_UP)); | ||
| 335 | EXPECT_REPORT(driver, (KC_UP)); | ||
| 336 | EXPECT_EMPTY_REPORT(driver); | ||
| 337 | EXPECT_REPORT(driver, (KC_DOWN)); | ||
| 338 | EXPECT_EMPTY_REPORT(driver); | ||
| 339 | } | ||
| 340 | |||
| 341 | tap_key(key_left); | ||
| 342 | |||
| 343 | // Perform a rolled press from Alternate Repeat to Up. | ||
| 344 | |||
| 345 | ExpectProcessRecordUserCalledWith(true, KC_RGHT, -1); | ||
| 346 | key_alt_repeat.press(); // Press the Alternate Repeat Key. | ||
| 347 | run_one_scan_loop(); | ||
| 348 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 349 | |||
| 350 | ExpectProcessRecordUserCalledWith(true, KC_UP, 0); | ||
| 351 | key_up.press(); | ||
| 352 | run_one_scan_loop(); | ||
| 353 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 354 | |||
| 355 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_UP); | ||
| 356 | |||
| 357 | ExpectProcessRecordUserCalledWith(false, KC_RGHT, -1); | ||
| 358 | key_alt_repeat.release(); // Release the Alternate Repeat Key. | ||
| 359 | run_one_scan_loop(); | ||
| 360 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 361 | |||
| 362 | ExpectProcessRecordUserCalledWith(false, KC_UP, 0); | ||
| 363 | key_up.release(); | ||
| 364 | run_one_scan_loop(); | ||
| 365 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 366 | |||
| 367 | process_record_user_fun = process_record_user_default; | ||
| 368 | tap_key(key_alt_repeat); | ||
| 369 | |||
| 370 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 371 | } | ||
| 372 | |||
| 373 | // Tests using the Alternate Repeat Key on a macro that doesn't have an | ||
| 374 | // alternate keycode defined. | ||
| 375 | TEST_F(AltRepeatKey, AlternateUnsupportedMacro) { | ||
| 376 | TestDriver driver; | ||
| 377 | KeymapKey key_foo(0, 0, 0, QK_USER_0); | ||
| 378 | KeymapKey key_alt_repeat(0, 1, 0, QK_AREP); | ||
| 379 | set_keymap({key_foo, key_alt_repeat}); | ||
| 380 | |||
| 381 | process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) { | ||
| 382 | process_record_user_was_called_ = true; | ||
| 383 | switch (keycode) { | ||
| 384 | case QK_USER_0: | ||
| 385 | if (record->event.pressed) { | ||
| 386 | SEND_STRING("foo"); | ||
| 387 | } | ||
| 388 | break; | ||
| 389 | } | ||
| 390 | return true; | ||
| 391 | }; | ||
| 392 | |||
| 393 | // Allow any number of empty reports. | ||
| 394 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 395 | ExpectString(driver, "foofoo"); | ||
| 396 | |||
| 397 | process_record_user_was_called_ = false; | ||
| 398 | tap_key(key_foo); | ||
| 399 | |||
| 400 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 401 | EXPECT_KEYCODE_EQ(get_last_keycode(), QK_USER_0); | ||
| 402 | EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), KC_NO); | ||
| 403 | |||
| 404 | process_record_user_was_called_ = false; | ||
| 405 | key_alt_repeat.press(); // Press Alternate Repeat. | ||
| 406 | run_one_scan_loop(); | ||
| 407 | |||
| 408 | EXPECT_FALSE(process_record_user_was_called_); | ||
| 409 | |||
| 410 | process_record_user_was_called_ = false; | ||
| 411 | key_alt_repeat.release(); // Release Alternate Repeat. | ||
| 412 | run_one_scan_loop(); | ||
| 413 | |||
| 414 | EXPECT_FALSE(process_record_user_was_called_); | ||
| 415 | |||
| 416 | process_record_user_was_called_ = false; | ||
| 417 | tap_key(key_foo); | ||
| 418 | |||
| 419 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 420 | |||
| 421 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 422 | } | ||
| 423 | |||
| 424 | // Tests a macro with custom alternate behavior. | ||
| 425 | TEST_F(AltRepeatKey, MacroCustomAlternate) { | ||
| 426 | TestDriver driver; | ||
| 427 | KeymapKey key_foo(0, 0, 0, QK_USER_0); | ||
| 428 | KeymapKey key_alt_repeat(0, 1, 0, QK_AREP); | ||
| 429 | set_keymap({key_foo, key_alt_repeat}); | ||
| 430 | |||
| 431 | get_alt_repeat_key_keycode_user_fun = [](uint16_t keycode, uint8_t mods) -> uint16_t { | ||
| 432 | switch (keycode) { | ||
| 433 | case QK_USER_0: | ||
| 434 | return QK_USER_0; // QK_USER_0 handles its own alternate. | ||
| 435 | default: | ||
| 436 | return KC_NO; // No key by default. | ||
| 437 | } | ||
| 438 | }; | ||
| 439 | process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) { | ||
| 440 | process_record_user_was_called_ = true; | ||
| 441 | switch (keycode) { | ||
| 442 | case QK_USER_0: | ||
| 443 | if (record->event.pressed) { | ||
| 444 | if (get_repeat_key_count() >= 0) { | ||
| 445 | SEND_STRING("foo"); | ||
| 446 | } else { // Key is being alternate repeated. | ||
| 447 | SEND_STRING("bar"); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | break; | ||
| 451 | } | ||
| 452 | return true; | ||
| 453 | }; | ||
| 454 | |||
| 455 | // Allow any number of empty reports. | ||
| 456 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 457 | ExpectString(driver, "foobarbar"); | ||
| 458 | |||
| 459 | tap_keys(key_foo, key_alt_repeat, key_alt_repeat); | ||
| 460 | |||
| 461 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 462 | } | ||
| 463 | |||
| 464 | // Tests the Additional "Alternate" keys example from the documentation page. | ||
| 465 | TEST_F(AltRepeatKey, AdditionalAlternateKeysExample) { | ||
| 466 | TestDriver driver; | ||
| 467 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 468 | KeymapKey key_w(0, 1, 0, KC_W); | ||
| 469 | KeymapKey key_altrep2(0, 2, 0, QK_USER_0); | ||
| 470 | KeymapKey key_altrep3(0, 3, 0, QK_USER_1); | ||
| 471 | set_keymap({key_a, key_w, key_altrep2, key_altrep3}); | ||
| 472 | |||
| 473 | remember_last_key_user_fun = [](uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) { | ||
| 474 | switch (keycode) { | ||
| 475 | case QK_USER_0: | ||
| 476 | case QK_USER_1: | ||
| 477 | return false; // Ignore ALTREP keys. | ||
| 478 | } | ||
| 479 | return true; // Other keys can be repeated. | ||
| 480 | }; | ||
| 481 | process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) { | ||
| 482 | switch (keycode) { | ||
| 483 | case QK_USER_0: | ||
| 484 | if (record->event.pressed) { | ||
| 485 | const uint16_t last_key = get_last_keycode(); | ||
| 486 | switch (last_key) { | ||
| 487 | case KC_A: | ||
| 488 | SEND_STRING(/*a*/ "tion"); | ||
| 489 | break; | ||
| 490 | case KC_W: | ||
| 491 | SEND_STRING(/*w*/ "hich"); | ||
| 492 | break; | ||
| 493 | } | ||
| 494 | } | ||
| 495 | return false; | ||
| 496 | case QK_USER_1: | ||
| 497 | if (record->event.pressed) { | ||
| 498 | const uint16_t last_key = get_last_keycode(); | ||
| 499 | switch (last_key) { | ||
| 500 | case KC_A: | ||
| 501 | SEND_STRING(/*a*/ "bout"); | ||
| 502 | break; | ||
| 503 | case KC_W: | ||
| 504 | SEND_STRING(/*w*/ "ould"); | ||
| 505 | break; | ||
| 506 | } | ||
| 507 | } | ||
| 508 | return false; | ||
| 509 | } | ||
| 510 | return true; | ||
| 511 | }; | ||
| 512 | |||
| 513 | // Allow any number of empty reports. | ||
| 514 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 515 | ExpectString(driver, "ationwhichaboutwould"); | ||
| 516 | |||
| 517 | tap_keys(key_a, key_altrep2, key_w, key_altrep2); | ||
| 518 | tap_keys(key_a, key_altrep3, key_w, key_altrep3); | ||
| 519 | |||
| 520 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 521 | } | ||
| 522 | |||
| 523 | } // namespace | ||
diff --git a/tests/repeat_key/config.h b/tests/repeat_key/config.h new file mode 100644 index 0000000000..003d980c82 --- /dev/null +++ b/tests/repeat_key/config.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // Copyright 2023 Google LLC | ||
| 2 | // | ||
| 3 | // This program is free software: you can redistribute it and/or modify | ||
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, either version 2 of the License, or | ||
| 6 | // (at your option) any later version. | ||
| 7 | // | ||
| 8 | // This program is distributed in the hope that it will be useful, | ||
| 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | // GNU General Public License for more details. | ||
| 12 | // | ||
| 13 | // You should have received a copy of the GNU General Public License | ||
| 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | #pragma once | ||
| 17 | |||
| 18 | #include "test_common.h" | ||
| 19 | |||
| 20 | #define NO_ALT_REPEAT_KEY | ||
diff --git a/tests/repeat_key/repeat_key_combo/config.h b/tests/repeat_key/repeat_key_combo/config.h new file mode 100644 index 0000000000..d0c4ddadbd --- /dev/null +++ b/tests/repeat_key/repeat_key_combo/config.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // Copyright 2023 Google LLC | ||
| 2 | // | ||
| 3 | // This program is free software: you can redistribute it and/or modify | ||
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, either version 2 of the License, or | ||
| 6 | // (at your option) any later version. | ||
| 7 | // | ||
| 8 | // This program is distributed in the hope that it will be useful, | ||
| 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | // GNU General Public License for more details. | ||
| 12 | // | ||
| 13 | // You should have received a copy of the GNU General Public License | ||
| 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | #pragma once | ||
| 17 | |||
| 18 | #include "test_common.h" | ||
diff --git a/tests/repeat_key/repeat_key_combo/test.mk b/tests/repeat_key/repeat_key_combo/test.mk new file mode 100644 index 0000000000..db6ea7789a --- /dev/null +++ b/tests/repeat_key/repeat_key_combo/test.mk | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # Copyright 2023 Google LLC | ||
| 2 | # | ||
| 3 | # This program is free software: you can redistribute it and/or modify | ||
| 4 | # it under the terms of the GNU General Public License as published by | ||
| 5 | # the Free Software Foundation, either version 2 of the License, or | ||
| 6 | # (at your option) any later version. | ||
| 7 | # | ||
| 8 | # This program is distributed in the hope that it will be useful, | ||
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | # GNU General Public License for more details. | ||
| 12 | # | ||
| 13 | # You should have received a copy of the GNU General Public License | ||
| 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | REPEAT_KEY_ENABLE = yes | ||
| 17 | |||
| 18 | COMBO_ENABLE = yes | ||
diff --git a/tests/repeat_key/repeat_key_combo/test_repeat_key_combo.cpp b/tests/repeat_key/repeat_key_combo/test_repeat_key_combo.cpp new file mode 100644 index 0000000000..2d2fbaa966 --- /dev/null +++ b/tests/repeat_key/repeat_key_combo/test_repeat_key_combo.cpp | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | // Copyright 2023 Google LLC | ||
| 2 | // | ||
| 3 | // This program is free software: you can redistribute it and/or modify | ||
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, either version 2 of the License, or | ||
| 6 | // (at your option) any later version. | ||
| 7 | // | ||
| 8 | // This program is distributed in the hope that it will be useful, | ||
| 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | // GNU General Public License for more details. | ||
| 12 | // | ||
| 13 | // You should have received a copy of the GNU General Public License | ||
| 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | #include "keyboard_report_util.hpp" | ||
| 17 | #include "keycode.h" | ||
| 18 | #include "test_common.hpp" | ||
| 19 | #include "test_fixture.hpp" | ||
| 20 | #include "test_keymap_key.hpp" | ||
| 21 | |||
| 22 | using ::testing::AnyNumber; | ||
| 23 | using ::testing::InSequence; | ||
| 24 | |||
| 25 | namespace { | ||
| 26 | |||
| 27 | extern "C" { | ||
| 28 | // Define a combo: KC_X + KC_Y = KC_Q. | ||
| 29 | const uint16_t xy_combo[] PROGMEM = {KC_X, KC_Y, COMBO_END}; | ||
| 30 | combo_t key_combos[] = {COMBO(xy_combo, KC_Q)}; | ||
| 31 | uint16_t COMBO_LEN = sizeof(key_combos) / sizeof(*key_combos); | ||
| 32 | } // extern "C" | ||
| 33 | |||
| 34 | class RepeatKey : public TestFixture {}; | ||
| 35 | |||
| 36 | // Tests repeating a combo, KC_X + KC_Y = KC_Q, by typing | ||
| 37 | // "X, Repeat, Repeat, {X Y}, Repeat, Repeat". This produces "xxxqqq". | ||
| 38 | TEST_F(RepeatKey, Combo) { | ||
| 39 | TestDriver driver; | ||
| 40 | KeymapKey key_x(0, 0, 0, KC_X); | ||
| 41 | KeymapKey key_y(0, 1, 0, KC_Y); | ||
| 42 | KeymapKey key_repeat(0, 2, 0, QK_REP); | ||
| 43 | set_keymap({key_x, key_y, key_repeat}); | ||
| 44 | |||
| 45 | // Allow any number of empty reports. | ||
| 46 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 47 | { | ||
| 48 | InSequence seq; | ||
| 49 | EXPECT_REPORT(driver, (KC_X)); | ||
| 50 | EXPECT_REPORT(driver, (KC_X)); | ||
| 51 | EXPECT_REPORT(driver, (KC_X)); | ||
| 52 | EXPECT_REPORT(driver, (KC_Q)); | ||
| 53 | EXPECT_REPORT(driver, (KC_Q)); | ||
| 54 | EXPECT_REPORT(driver, (KC_Q)); | ||
| 55 | } | ||
| 56 | |||
| 57 | tap_keys(key_x, key_repeat, key_repeat); | ||
| 58 | tap_combo({key_x, key_y}); | ||
| 59 | |||
| 60 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_Q); | ||
| 61 | |||
| 62 | tap_keys(key_repeat, key_repeat); | ||
| 63 | |||
| 64 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 65 | } | ||
| 66 | |||
| 67 | } // namespace | ||
diff --git a/tests/repeat_key/test.mk b/tests/repeat_key/test.mk new file mode 100644 index 0000000000..aec8ff3bfb --- /dev/null +++ b/tests/repeat_key/test.mk | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # Copyright 2023 Google LLC | ||
| 2 | # | ||
| 3 | # This program is free software: you can redistribute it and/or modify | ||
| 4 | # it under the terms of the GNU General Public License as published by | ||
| 5 | # the Free Software Foundation, either version 2 of the License, or | ||
| 6 | # (at your option) any later version. | ||
| 7 | # | ||
| 8 | # This program is distributed in the hope that it will be useful, | ||
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | # GNU General Public License for more details. | ||
| 12 | # | ||
| 13 | # You should have received a copy of the GNU General Public License | ||
| 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | REPEAT_KEY_ENABLE = yes | ||
| 17 | |||
| 18 | AUTO_SHIFT_ENABLE = yes | ||
diff --git a/tests/repeat_key/test_repeat_key.cpp b/tests/repeat_key/test_repeat_key.cpp new file mode 100644 index 0000000000..eee44fc104 --- /dev/null +++ b/tests/repeat_key/test_repeat_key.cpp | |||
| @@ -0,0 +1,754 @@ | |||
| 1 | // Copyright 2023 Google LLC | ||
| 2 | // | ||
| 3 | // This program is free software: you can redistribute it and/or modify | ||
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, either version 2 of the License, or | ||
| 6 | // (at your option) any later version. | ||
| 7 | // | ||
| 8 | // This program is distributed in the hope that it will be useful, | ||
| 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | // GNU General Public License for more details. | ||
| 12 | // | ||
| 13 | // You should have received a copy of the GNU General Public License | ||
| 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | |||
| 16 | #include <functional> | ||
| 17 | |||
| 18 | #include "keyboard_report_util.hpp" | ||
| 19 | #include "keycode.h" | ||
| 20 | #include "test_common.hpp" | ||
| 21 | #include "test_fixture.hpp" | ||
| 22 | #include "test_keymap_key.hpp" | ||
| 23 | |||
| 24 | using ::testing::AnyNumber; | ||
| 25 | using ::testing::AnyOf; | ||
| 26 | using ::testing::InSequence; | ||
| 27 | |||
| 28 | #define FOO_MACRO SAFE_RANGE | ||
| 29 | |||
| 30 | namespace { | ||
| 31 | |||
| 32 | bool process_record_user_default(uint16_t keycode, keyrecord_t* record) { | ||
| 33 | return true; | ||
| 34 | } | ||
| 35 | |||
| 36 | bool remember_last_key_user_default(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) { | ||
| 37 | return true; | ||
| 38 | } | ||
| 39 | |||
| 40 | // Indirection so that process_record_user() and remember_last_key_user() | ||
| 41 | // can be replaced with other functions in the test cases below. | ||
| 42 | std::function<bool(uint16_t, keyrecord_t*)> process_record_user_fun = process_record_user_default; | ||
| 43 | std::function<bool(uint16_t, keyrecord_t*, uint8_t*)> remember_last_key_user_fun = remember_last_key_user_default; | ||
| 44 | |||
| 45 | extern "C" bool process_record_user(uint16_t keycode, keyrecord_t* record) { | ||
| 46 | return process_record_user_fun(keycode, record); | ||
| 47 | } | ||
| 48 | extern "C" bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) { | ||
| 49 | return remember_last_key_user_fun(keycode, record, remembered_mods); | ||
| 50 | } | ||
| 51 | |||
| 52 | class RepeatKey : public TestFixture { | ||
| 53 | public: | ||
| 54 | bool process_record_user_was_called_; | ||
| 55 | |||
| 56 | void SetUp() override { | ||
| 57 | autoshift_disable(); | ||
| 58 | process_record_user_fun = process_record_user_default; | ||
| 59 | remember_last_key_user_fun = remember_last_key_user_default; | ||
| 60 | } | ||
| 61 | |||
| 62 | void ExpectProcessRecordUserCalledWith(bool expected_press, uint16_t expected_keycode, int8_t expected_repeat_key_count) { | ||
| 63 | process_record_user_was_called_ = false; | ||
| 64 | process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) { | ||
| 65 | EXPECT_EQ(record->event.pressed, expected_press); | ||
| 66 | EXPECT_KEYCODE_EQ(keycode, expected_keycode); | ||
| 67 | EXPECT_EQ(get_repeat_key_count(), expected_repeat_key_count); | ||
| 68 | // Tests below use this to verify process_record_user() was called. | ||
| 69 | process_record_user_was_called_ = true; | ||
| 70 | return true; | ||
| 71 | }; | ||
| 72 | } | ||
| 73 | |||
| 74 | // Expects that the characters of `s` are sent. | ||
| 75 | // NOTE: This implementation is limited to chars a-z, A-Z. | ||
| 76 | void ExpectString(TestDriver& driver, const std::string& s) { | ||
| 77 | InSequence seq; | ||
| 78 | for (int c : s) { | ||
| 79 | switch (c) { | ||
| 80 | case 'a' ... 'z': { // Lowercase letter. | ||
| 81 | uint16_t keycode = c - ('a' - KC_A); | ||
| 82 | EXPECT_REPORT(driver, (keycode)); | ||
| 83 | } break; | ||
| 84 | |||
| 85 | case 'A' ... 'Z': { // Capital letter = KC_LSFT + letter key. | ||
| 86 | uint16_t keycode = c - ('A' - KC_A); | ||
| 87 | EXPECT_REPORT(driver, (KC_LSFT, keycode)); | ||
| 88 | } break; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | |||
| 94 | // Tests that "A, Repeat, Repeat, B, Repeat" produces "aaabb". | ||
| 95 | TEST_F(RepeatKey, Basic) { | ||
| 96 | TestDriver driver; | ||
| 97 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 98 | KeymapKey key_b(0, 1, 0, KC_B); | ||
| 99 | KeymapKey key_repeat(0, 2, 0, QK_REP); | ||
| 100 | set_keymap({key_a, key_b, key_repeat}); | ||
| 101 | |||
| 102 | // Allow any number of empty reports. | ||
| 103 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 104 | ExpectString(driver, "aaabb"); | ||
| 105 | |||
| 106 | // When KC_A is pressed, process_record_user() should be called | ||
| 107 | // with a press event with keycode == KC_A and repeat_key_count() == 0. | ||
| 108 | ExpectProcessRecordUserCalledWith(true, KC_A, 0); | ||
| 109 | key_a.press(); | ||
| 110 | run_one_scan_loop(); | ||
| 111 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 112 | |||
| 113 | // After pressing A, the keycode of the key to be repeated is KC_A. | ||
| 114 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A); | ||
| 115 | EXPECT_EQ(get_last_mods(), 0); | ||
| 116 | |||
| 117 | // Expect the corresponding release event when A is released. | ||
| 118 | ExpectProcessRecordUserCalledWith(false, KC_A, 0); | ||
| 119 | key_a.release(); | ||
| 120 | run_one_scan_loop(); | ||
| 121 | |||
| 122 | for (int n = 1; n <= 2; ++n) { // Tap the Repeat Key twice. | ||
| 123 | // When Repeat is pressed, process_record_user() should be called with a | ||
| 124 | // press event with keycode == KC_A and repeat_key_count() == n. | ||
| 125 | ExpectProcessRecordUserCalledWith(true, KC_A, n); | ||
| 126 | key_repeat.press(); // Press the Repeat Key. | ||
| 127 | run_one_scan_loop(); | ||
| 128 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 129 | |||
| 130 | // Expect the corresponding release event. | ||
| 131 | ExpectProcessRecordUserCalledWith(false, KC_A, n); | ||
| 132 | key_repeat.release(); // Release the Repeat Key. | ||
| 133 | run_one_scan_loop(); | ||
| 134 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 135 | } | ||
| 136 | |||
| 137 | process_record_user_fun = process_record_user_default; | ||
| 138 | tap_key(key_b); | ||
| 139 | // Then after tapping key_b, the keycode to be repeated becomes KC_B. | ||
| 140 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B); | ||
| 141 | |||
| 142 | tap_key(key_repeat); | ||
| 143 | |||
| 144 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 145 | } | ||
| 146 | |||
| 147 | // Tests repeating a macro. The keycode FOO_MACRO sends "foo" when pressed. The | ||
| 148 | // test taps "FOO_MACRO, Repeat, Repeat", producing "foofoofoo". | ||
| 149 | TEST_F(RepeatKey, Macro) { | ||
| 150 | TestDriver driver; | ||
| 151 | KeymapKey key_foo(0, 0, 0, FOO_MACRO); | ||
| 152 | KeymapKey key_repeat(0, 1, 0, QK_REP); | ||
| 153 | set_keymap({key_foo, key_repeat}); | ||
| 154 | |||
| 155 | // Define process_record_user() to handle FOO_MACRO. | ||
| 156 | process_record_user_fun = [](uint16_t keycode, keyrecord_t* record) { | ||
| 157 | switch (keycode) { | ||
| 158 | case FOO_MACRO: | ||
| 159 | if (record->event.pressed) { | ||
| 160 | SEND_STRING("foo"); | ||
| 161 | } | ||
| 162 | break; | ||
| 163 | } | ||
| 164 | return true; | ||
| 165 | }; | ||
| 166 | |||
| 167 | // Allow any number of empty reports. | ||
| 168 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 169 | ExpectString(driver, "foofoofoo"); | ||
| 170 | |||
| 171 | tap_key(key_foo); | ||
| 172 | |||
| 173 | EXPECT_KEYCODE_EQ(get_last_keycode(), FOO_MACRO); | ||
| 174 | |||
| 175 | tap_keys(key_repeat, key_repeat); | ||
| 176 | |||
| 177 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 178 | } | ||
| 179 | |||
| 180 | // Tests a macro with customized repeat behavior: "foo" is sent normally, "bar" | ||
| 181 | // on the first repeat, and "baz" on subsequent repeats. The test taps | ||
| 182 | // "FOO_MACRO, Repeat, Repeat, FOO_MACRO, Repeat", producing "foobarbazfoobar". | ||
| 183 | TEST_F(RepeatKey, MacroCustomRepeat) { | ||
| 184 | TestDriver driver; | ||
| 185 | KeymapKey key_foo(0, 0, 0, FOO_MACRO); | ||
| 186 | KeymapKey key_repeat(0, 1, 0, QK_REP); | ||
| 187 | set_keymap({key_foo, key_repeat}); | ||
| 188 | |||
| 189 | process_record_user_fun = [](uint16_t keycode, keyrecord_t* record) { | ||
| 190 | switch (keycode) { | ||
| 191 | case FOO_MACRO: | ||
| 192 | if (record->event.pressed) { | ||
| 193 | switch (get_repeat_key_count()) { | ||
| 194 | case 0: // When pressed normally. | ||
| 195 | SEND_STRING("foo"); | ||
| 196 | break; | ||
| 197 | case 1: // On first repeat. | ||
| 198 | SEND_STRING("bar"); | ||
| 199 | break; | ||
| 200 | default: // On subsequent repeats. | ||
| 201 | SEND_STRING("baz"); | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | break; | ||
| 206 | } | ||
| 207 | return true; | ||
| 208 | }; | ||
| 209 | |||
| 210 | // Allow any number of empty reports. | ||
| 211 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 212 | ExpectString(driver, "foobarbazfoobar"); | ||
| 213 | |||
| 214 | tap_key(key_foo); | ||
| 215 | |||
| 216 | EXPECT_KEYCODE_EQ(get_last_keycode(), FOO_MACRO); | ||
| 217 | |||
| 218 | tap_keys(key_repeat, key_repeat, key_foo, key_repeat); | ||
| 219 | |||
| 220 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 221 | } | ||
| 222 | |||
| 223 | // Tests repeating keys on different layers. A 2-layer keymap is defined: | ||
| 224 | // Layer 0: QK_REP , MO(1) , KC_A | ||
| 225 | // Layer 1: KC_TRNS, KC_TRNS, KC_B | ||
| 226 | // The test does the following, which should produce "bbbaaa": | ||
| 227 | // 1. Hold MO(1), switching to layer 1. | ||
| 228 | // 2. Tap KC_B on layer 1. | ||
| 229 | // 3. Release MO(1), switching back to layer 0. | ||
| 230 | // 4. Tap Repeat twice. | ||
| 231 | // 5. Tap KC_A on layer 0. | ||
| 232 | // 6. Hold MO(1), switching to layer 1. | ||
| 233 | // 7. Tap Repeat twice. | ||
| 234 | TEST_F(RepeatKey, AcrossLayers) { | ||
| 235 | TestDriver driver; | ||
| 236 | KeymapKey key_repeat(0, 0, 0, QK_REP); | ||
| 237 | KeymapKey key_mo_1(0, 1, 0, MO(1)); | ||
| 238 | KeymapKey regular_key(0, 2, 0, KC_A); | ||
| 239 | set_keymap({// Layer 0. | ||
| 240 | key_repeat, key_mo_1, regular_key, | ||
| 241 | // Layer 1. | ||
| 242 | KeymapKey{1, 0, 0, KC_TRNS}, KeymapKey{1, 1, 0, KC_TRNS}, KeymapKey{1, 2, 0, KC_B}}); | ||
| 243 | |||
| 244 | // Allow any number of empty reports. | ||
| 245 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 246 | ExpectString(driver, "bbbaaa"); | ||
| 247 | |||
| 248 | key_mo_1.press(); // Hold the MO(1) layer key. | ||
| 249 | run_one_scan_loop(); | ||
| 250 | tap_key(regular_key); // Taps the KC_B key on layer 1. | ||
| 251 | |||
| 252 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B); | ||
| 253 | |||
| 254 | key_mo_1.release(); // Release the layer key. | ||
| 255 | run_one_scan_loop(); | ||
| 256 | tap_keys(key_repeat, key_repeat); | ||
| 257 | tap_key(regular_key); // Taps the KC_A key on layer 0. | ||
| 258 | |||
| 259 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A); | ||
| 260 | |||
| 261 | key_mo_1.press(); // Hold the layer key. | ||
| 262 | run_one_scan_loop(); | ||
| 263 | tap_keys(key_repeat, key_repeat); | ||
| 264 | |||
| 265 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 266 | } | ||
| 267 | |||
| 268 | // Tests "A(down), Repeat(down), A(up), Repeat(up), Repeat" produces "aaa". | ||
| 269 | TEST_F(RepeatKey, RollingToRepeat) { | ||
| 270 | TestDriver driver; | ||
| 271 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 272 | KeymapKey key_repeat(0, 1, 0, QK_REP); | ||
| 273 | set_keymap({key_a, key_repeat}); | ||
| 274 | |||
| 275 | { | ||
| 276 | InSequence seq; | ||
| 277 | EXPECT_REPORT(driver, (KC_A)); | ||
| 278 | EXPECT_EMPTY_REPORT(driver); | ||
| 279 | EXPECT_REPORT(driver, (KC_A)); | ||
| 280 | EXPECT_EMPTY_REPORT(driver); | ||
| 281 | EXPECT_REPORT(driver, (KC_A)); | ||
| 282 | EXPECT_EMPTY_REPORT(driver); | ||
| 283 | } | ||
| 284 | |||
| 285 | // Perform a rolled press from A to Repeat. | ||
| 286 | |||
| 287 | ExpectProcessRecordUserCalledWith(true, KC_A, 0); | ||
| 288 | key_a.press(); | ||
| 289 | run_one_scan_loop(); | ||
| 290 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 291 | |||
| 292 | ExpectProcessRecordUserCalledWith(true, KC_A, 1); | ||
| 293 | key_repeat.press(); // Press the Repeat Key. | ||
| 294 | run_one_scan_loop(); | ||
| 295 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 296 | |||
| 297 | ExpectProcessRecordUserCalledWith(false, KC_A, 0); | ||
| 298 | key_a.release(); | ||
| 299 | run_one_scan_loop(); | ||
| 300 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 301 | |||
| 302 | ExpectProcessRecordUserCalledWith(false, KC_A, 1); | ||
| 303 | key_repeat.release(); // Release the Repeat Key. | ||
| 304 | run_one_scan_loop(); | ||
| 305 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 306 | |||
| 307 | process_record_user_fun = process_record_user_default; | ||
| 308 | tap_key(key_repeat); | ||
| 309 | |||
| 310 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 311 | } | ||
| 312 | |||
| 313 | // Tests "A, Repeat(down), B(down), Repeat(up), B(up), Repeat" produces "aabb". | ||
| 314 | TEST_F(RepeatKey, RollingFromRepeat) { | ||
| 315 | TestDriver driver; | ||
| 316 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 317 | KeymapKey key_b(0, 1, 0, KC_B); | ||
| 318 | KeymapKey key_repeat(0, 2, 0, QK_REP); | ||
| 319 | set_keymap({key_a, key_b, key_repeat}); | ||
| 320 | |||
| 321 | { | ||
| 322 | InSequence seq; | ||
| 323 | EXPECT_REPORT(driver, (KC_A)); | ||
| 324 | EXPECT_EMPTY_REPORT(driver); | ||
| 325 | EXPECT_REPORT(driver, (KC_A)); | ||
| 326 | EXPECT_REPORT(driver, (KC_A, KC_B)); | ||
| 327 | EXPECT_REPORT(driver, (KC_B)); | ||
| 328 | EXPECT_EMPTY_REPORT(driver); | ||
| 329 | EXPECT_REPORT(driver, (KC_B)); | ||
| 330 | EXPECT_EMPTY_REPORT(driver); | ||
| 331 | } | ||
| 332 | |||
| 333 | tap_key(key_a); | ||
| 334 | |||
| 335 | // Perform a rolled press from Repeat to B. | ||
| 336 | |||
| 337 | ExpectProcessRecordUserCalledWith(true, KC_A, 1); | ||
| 338 | key_repeat.press(); // Press the Repeat Key. | ||
| 339 | run_one_scan_loop(); | ||
| 340 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 341 | |||
| 342 | ExpectProcessRecordUserCalledWith(true, KC_B, 0); | ||
| 343 | key_b.press(); | ||
| 344 | run_one_scan_loop(); | ||
| 345 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 346 | |||
| 347 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B); | ||
| 348 | |||
| 349 | ExpectProcessRecordUserCalledWith(false, KC_A, 1); | ||
| 350 | key_repeat.release(); // Release the Repeat Key. | ||
| 351 | run_one_scan_loop(); | ||
| 352 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 353 | |||
| 354 | ExpectProcessRecordUserCalledWith(false, KC_B, 0); | ||
| 355 | key_b.release(); | ||
| 356 | run_one_scan_loop(); | ||
| 357 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 358 | |||
| 359 | process_record_user_fun = process_record_user_default; | ||
| 360 | tap_key(key_repeat); | ||
| 361 | |||
| 362 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 363 | } | ||
| 364 | |||
| 365 | // Tests Repeat Key with a modifier, types "AltGr+C, Repeat, Repeat, C". | ||
| 366 | TEST_F(RepeatKey, RecallMods) { | ||
| 367 | TestDriver driver; | ||
| 368 | KeymapKey key_c(0, 0, 0, KC_C); | ||
| 369 | KeymapKey key_altgr(0, 1, 0, KC_RALT); | ||
| 370 | KeymapKey key_repeat(0, 2, 0, QK_REP); | ||
| 371 | set_keymap({key_c, key_altgr, key_repeat}); | ||
| 372 | |||
| 373 | // Allow any number of reports with no keys or only KC_RALT. | ||
| 374 | // clang-format off | ||
| 375 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 376 | KeyboardReport(), | ||
| 377 | KeyboardReport(KC_RALT)))) | ||
| 378 | .Times(AnyNumber()); | ||
| 379 | // clang-format on | ||
| 380 | |||
| 381 | { // Expect: "AltGr+C, AltGr+C, AltGr+C, C". | ||
| 382 | InSequence seq; | ||
| 383 | EXPECT_REPORT(driver, (KC_RALT, KC_C)); | ||
| 384 | EXPECT_REPORT(driver, (KC_RALT, KC_C)); | ||
| 385 | EXPECT_REPORT(driver, (KC_RALT, KC_C)); | ||
| 386 | EXPECT_REPORT(driver, (KC_C)); | ||
| 387 | } | ||
| 388 | |||
| 389 | key_altgr.press(); | ||
| 390 | run_one_scan_loop(); | ||
| 391 | tap_key(key_c); | ||
| 392 | key_altgr.release(); | ||
| 393 | run_one_scan_loop(); | ||
| 394 | |||
| 395 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_C); | ||
| 396 | EXPECT_EQ(get_last_mods(), MOD_BIT(KC_RALT)); | ||
| 397 | |||
| 398 | tap_keys(key_repeat, key_repeat, key_c); | ||
| 399 | |||
| 400 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 401 | } | ||
| 402 | |||
| 403 | // Tests that Repeat Key stacks mods, types | ||
| 404 | // "Ctrl+Left, Repeat, Shift+Repeat, Shift+Repeat, Repeat, Left". | ||
| 405 | TEST_F(RepeatKey, StackMods) { | ||
| 406 | TestDriver driver; | ||
| 407 | KeymapKey key_left(0, 0, 0, KC_LEFT); | ||
| 408 | KeymapKey key_shift(0, 1, 0, KC_LSFT); | ||
| 409 | KeymapKey key_ctrl(0, 2, 0, KC_LCTL); | ||
| 410 | KeymapKey key_repeat(0, 3, 0, QK_REP); | ||
| 411 | set_keymap({key_left, key_shift, key_ctrl, key_repeat}); | ||
| 412 | |||
| 413 | // Allow any number of reports with no keys or only mods. | ||
| 414 | // clang-format off | ||
| 415 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 416 | KeyboardReport(), | ||
| 417 | KeyboardReport(KC_LCTL), | ||
| 418 | KeyboardReport(KC_LSFT), | ||
| 419 | KeyboardReport(KC_LCTL, KC_LSFT)))) | ||
| 420 | .Times(AnyNumber()); | ||
| 421 | // clang-format on | ||
| 422 | |||
| 423 | { // Expect: "Ctrl+Left, Ctrl+Shift+Left". | ||
| 424 | InSequence seq; | ||
| 425 | EXPECT_REPORT(driver, (KC_LCTL, KC_LEFT)); | ||
| 426 | EXPECT_REPORT(driver, (KC_LCTL, KC_LEFT)); | ||
| 427 | EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_LEFT)); | ||
| 428 | EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_LEFT)); | ||
| 429 | EXPECT_REPORT(driver, (KC_LCTL, KC_LEFT)); | ||
| 430 | EXPECT_REPORT(driver, (KC_LEFT)); | ||
| 431 | } | ||
| 432 | |||
| 433 | key_ctrl.press(); | ||
| 434 | run_one_scan_loop(); | ||
| 435 | tap_key(key_left); | ||
| 436 | run_one_scan_loop(); | ||
| 437 | key_ctrl.release(); | ||
| 438 | run_one_scan_loop(); | ||
| 439 | |||
| 440 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_LEFT); | ||
| 441 | EXPECT_EQ(get_last_mods(), MOD_BIT(KC_LCTL)); | ||
| 442 | |||
| 443 | tap_key(key_repeat); | ||
| 444 | |||
| 445 | key_shift.press(); | ||
| 446 | run_one_scan_loop(); | ||
| 447 | tap_keys(key_repeat, key_repeat); | ||
| 448 | key_shift.release(); | ||
| 449 | run_one_scan_loop(); | ||
| 450 | |||
| 451 | EXPECT_EQ(get_last_mods(), MOD_BIT(KC_LCTL)); | ||
| 452 | |||
| 453 | tap_keys(key_repeat, key_left); | ||
| 454 | |||
| 455 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 456 | } | ||
| 457 | |||
| 458 | // Types: "S(KC_1), Repeat, Ctrl+Repeat, Ctrl+Repeat, Repeat, KC_2". | ||
| 459 | TEST_F(RepeatKey, ShiftedKeycode) { | ||
| 460 | TestDriver driver; | ||
| 461 | KeymapKey key_exlm(0, 0, 0, S(KC_1)); | ||
| 462 | KeymapKey key_2(0, 1, 0, KC_2); | ||
| 463 | KeymapKey key_ctrl(0, 2, 0, KC_LCTL); | ||
| 464 | KeymapKey key_repeat(0, 3, 0, QK_REP); | ||
| 465 | set_keymap({key_exlm, key_2, key_ctrl, key_repeat}); | ||
| 466 | |||
| 467 | // Allow any number of reports with no keys or only mods. | ||
| 468 | // clang-format off | ||
| 469 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 470 | KeyboardReport(), | ||
| 471 | KeyboardReport(KC_LCTL), | ||
| 472 | KeyboardReport(KC_LSFT), | ||
| 473 | KeyboardReport(KC_LCTL, KC_LSFT)))) | ||
| 474 | .Times(AnyNumber()); | ||
| 475 | // clang-format on | ||
| 476 | |||
| 477 | { // Expect: "Shift+1, Shift+1, Ctrl+Shift+1, Ctrl+Shift+1, Shift+1, 2". | ||
| 478 | InSequence seq; | ||
| 479 | EXPECT_REPORT(driver, (KC_LSFT, KC_1)); | ||
| 480 | EXPECT_REPORT(driver, (KC_LSFT, KC_1)); | ||
| 481 | EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_1)); | ||
| 482 | EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_1)); | ||
| 483 | EXPECT_REPORT(driver, (KC_LSFT, KC_1)); | ||
| 484 | EXPECT_REPORT(driver, (KC_2)); | ||
| 485 | } | ||
| 486 | |||
| 487 | tap_key(key_exlm); | ||
| 488 | |||
| 489 | EXPECT_KEYCODE_EQ(get_last_keycode(), S(KC_1)); | ||
| 490 | |||
| 491 | tap_key(key_repeat); | ||
| 492 | |||
| 493 | key_ctrl.press(); | ||
| 494 | run_one_scan_loop(); | ||
| 495 | tap_keys(key_repeat, key_repeat); | ||
| 496 | key_ctrl.release(); | ||
| 497 | run_one_scan_loop(); | ||
| 498 | |||
| 499 | tap_keys(key_repeat, key_2); | ||
| 500 | |||
| 501 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 502 | } | ||
| 503 | |||
| 504 | // Tests Repeat Key with a one-shot Shift, types | ||
| 505 | // "A, OSM(MOD_LSFT), Repeat, Repeat". | ||
| 506 | TEST_F(RepeatKey, WithOneShotShift) { | ||
| 507 | TestDriver driver; | ||
| 508 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 509 | KeymapKey key_oneshot_shift(0, 1, 0, OSM(MOD_LSFT)); | ||
| 510 | KeymapKey key_repeat(0, 2, 0, QK_REP); | ||
| 511 | set_keymap({key_a, key_oneshot_shift, key_repeat}); | ||
| 512 | |||
| 513 | // Allow any number of reports with no keys or only KC_RALT. | ||
| 514 | // clang-format off | ||
| 515 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 516 | KeyboardReport(), | ||
| 517 | KeyboardReport(KC_LSFT)))) | ||
| 518 | .Times(AnyNumber()); | ||
| 519 | // clang-format on | ||
| 520 | ExpectString(driver, "aAa"); | ||
| 521 | |||
| 522 | tap_keys(key_a, key_oneshot_shift, key_repeat, key_repeat); | ||
| 523 | |||
| 524 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 525 | } | ||
| 526 | |||
| 527 | // Tests Repeat Key with a mod-tap key, types | ||
| 528 | // "A, Repeat, Repeat, A(down), Repeat, Repeat, A(up), Repeat". | ||
| 529 | TEST_F(RepeatKey, ModTap) { | ||
| 530 | TestDriver driver; | ||
| 531 | KeymapKey key_mt_a(0, 0, 0, LSFT_T(KC_A)); | ||
| 532 | KeymapKey key_repeat(0, 1, 0, QK_REP); | ||
| 533 | set_keymap({key_mt_a, key_repeat}); | ||
| 534 | |||
| 535 | // Allow any number of reports with no keys or only KC_LSFT. | ||
| 536 | // clang-format off | ||
| 537 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 538 | KeyboardReport(), | ||
| 539 | KeyboardReport(KC_LSFT)))) | ||
| 540 | .Times(AnyNumber()); | ||
| 541 | // clang-format on | ||
| 542 | ExpectString(driver, "aaaAAa"); | ||
| 543 | |||
| 544 | tap_key(key_mt_a); | ||
| 545 | |||
| 546 | EXPECT_KEYCODE_EQ(get_last_keycode(), LSFT_T(KC_A)); | ||
| 547 | |||
| 548 | tap_keys(key_repeat, key_repeat); | ||
| 549 | key_mt_a.press(); | ||
| 550 | run_one_scan_loop(); | ||
| 551 | tap_key(key_repeat, TAPPING_TERM + 1); | ||
| 552 | tap_key(key_repeat); | ||
| 553 | key_mt_a.release(); | ||
| 554 | run_one_scan_loop(); | ||
| 555 | tap_key(key_repeat); | ||
| 556 | |||
| 557 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 558 | } | ||
| 559 | |||
| 560 | // Tests with Auto Shift. When repeating an autoshiftable key, it does not | ||
| 561 | // matter how long the original key was held, rather, quickly tapping vs. | ||
| 562 | // long-pressing the Repeat Key determines whether the shifted key is repeated. | ||
| 563 | // | ||
| 564 | // The test does the following, which should produce "aaABbB": | ||
| 565 | // 1. Tap KC_A quickly. | ||
| 566 | // 2. Tap Repeat Key quickly. | ||
| 567 | // 3. Long-press Repeat Key. | ||
| 568 | // 4. Long-press KC_B. | ||
| 569 | // 5. Tap Repeat Key quickly. | ||
| 570 | // 6. Long-press Repeat Key. | ||
| 571 | TEST_F(RepeatKey, AutoShift) { | ||
| 572 | TestDriver driver; | ||
| 573 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 574 | KeymapKey key_b(0, 1, 0, KC_B); | ||
| 575 | KeymapKey key_repeat(0, 2, 0, QK_REP); | ||
| 576 | set_keymap({key_a, key_b, key_repeat}); | ||
| 577 | |||
| 578 | autoshift_enable(); | ||
| 579 | |||
| 580 | // Allow any number of reports with no keys or only KC_LSFT. | ||
| 581 | // clang-format off | ||
| 582 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 583 | KeyboardReport(), | ||
| 584 | KeyboardReport(KC_LSFT)))) | ||
| 585 | .Times(AnyNumber()); | ||
| 586 | // clang-format on | ||
| 587 | ExpectString(driver, "aaABbB"); | ||
| 588 | |||
| 589 | tap_key(key_a); // Tap A quickly. | ||
| 590 | |||
| 591 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A); | ||
| 592 | EXPECT_EQ(get_last_mods(), 0); | ||
| 593 | |||
| 594 | tap_key(key_repeat); | ||
| 595 | tap_key(key_repeat, AUTO_SHIFT_TIMEOUT + 1); | ||
| 596 | |||
| 597 | tap_key(key_b, AUTO_SHIFT_TIMEOUT + 1); // Long press B. | ||
| 598 | |||
| 599 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B); | ||
| 600 | EXPECT_EQ(get_last_mods(), 0); | ||
| 601 | |||
| 602 | tap_key(key_repeat); | ||
| 603 | tap_key(key_repeat, AUTO_SHIFT_TIMEOUT + 1); | ||
| 604 | |||
| 605 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 606 | } | ||
| 607 | |||
| 608 | // Defines `remember_last_key_user()` to forget the Shift mod and types: | ||
| 609 | // "Ctrl+A, Repeat, Shift+A, Repeat, Shift+Repeat". | ||
| 610 | TEST_F(RepeatKey, FilterRememberedMods) { | ||
| 611 | TestDriver driver; | ||
| 612 | KeymapKey key_a(0, 0, 0, KC_A); | ||
| 613 | KeymapKey key_ctrl(0, 1, 0, KC_LCTL); | ||
| 614 | KeymapKey key_shift(0, 2, 0, KC_LSFT); | ||
| 615 | KeymapKey key_repeat(0, 3, 0, QK_REP); | ||
| 616 | set_keymap({key_a, key_ctrl, key_shift, key_repeat}); | ||
| 617 | |||
| 618 | remember_last_key_user_fun = [](uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) { | ||
| 619 | *remembered_mods &= ~MOD_MASK_SHIFT; | ||
| 620 | return true; | ||
| 621 | }; | ||
| 622 | |||
| 623 | // Allow any number of reports with no keys or only mods. | ||
| 624 | // clang-format off | ||
| 625 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 626 | KeyboardReport(), | ||
| 627 | KeyboardReport(KC_LCTL), | ||
| 628 | KeyboardReport(KC_LSFT), | ||
| 629 | KeyboardReport(KC_LCTL, KC_LSFT)))) | ||
| 630 | .Times(AnyNumber()); | ||
| 631 | // clang-format on | ||
| 632 | |||
| 633 | { // Expect: "Ctrl+A, Ctrl+A, Shift+A, A, Shift+A". | ||
| 634 | InSequence seq; | ||
| 635 | EXPECT_REPORT(driver, (KC_LCTL, KC_A)); | ||
| 636 | EXPECT_REPORT(driver, (KC_LCTL, KC_A)); | ||
| 637 | EXPECT_REPORT(driver, (KC_LSFT, KC_A)); | ||
| 638 | EXPECT_REPORT(driver, (KC_A)); | ||
| 639 | EXPECT_REPORT(driver, (KC_LSFT, KC_A)); | ||
| 640 | } | ||
| 641 | |||
| 642 | key_ctrl.press(); | ||
| 643 | run_one_scan_loop(); | ||
| 644 | tap_key(key_a); | ||
| 645 | |||
| 646 | EXPECT_EQ(get_last_mods(), MOD_BIT(KC_LCTL)); | ||
| 647 | |||
| 648 | key_ctrl.release(); | ||
| 649 | run_one_scan_loop(); | ||
| 650 | |||
| 651 | tap_key(key_repeat); | ||
| 652 | key_shift.press(); | ||
| 653 | run_one_scan_loop(); | ||
| 654 | tap_key(key_a); | ||
| 655 | |||
| 656 | EXPECT_EQ(get_last_mods(), 0); // Shift should be forgotten. | ||
| 657 | |||
| 658 | key_shift.release(); | ||
| 659 | run_one_scan_loop(); | ||
| 660 | |||
| 661 | tap_key(key_repeat); | ||
| 662 | |||
| 663 | key_shift.press(); | ||
| 664 | run_one_scan_loop(); | ||
| 665 | tap_key(key_repeat); | ||
| 666 | key_shift.release(); | ||
| 667 | run_one_scan_loop(); | ||
| 668 | |||
| 669 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 670 | } | ||
| 671 | |||
| 672 | // Tests set_last_keycode() and set_last_mods(). | ||
| 673 | TEST_F(RepeatKey, SetRepeatKeyKeycode) { | ||
| 674 | TestDriver driver; | ||
| 675 | KeymapKey key_repeat(0, 0, 0, QK_REP); | ||
| 676 | set_keymap({key_repeat}); | ||
| 677 | |||
| 678 | // Allow any number of reports with no keys or only KC_LSFT. | ||
| 679 | // clang-format off | ||
| 680 | EXPECT_CALL(driver, send_keyboard_mock(AnyOf( | ||
| 681 | KeyboardReport(), | ||
| 682 | KeyboardReport(KC_LSFT)))) | ||
| 683 | .Times(AnyNumber()); | ||
| 684 | // clang-format on | ||
| 685 | ExpectString(driver, "aaBB"); | ||
| 686 | |||
| 687 | set_last_keycode(KC_A); | ||
| 688 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A); | ||
| 689 | |||
| 690 | for (int n = 1; n <= 2; ++n) { // Tap the Repeat Key twice. | ||
| 691 | // When Repeat is pressed, process_record_user() should be called with a | ||
| 692 | // press event with keycode == KC_A and repeat_key_count() == n. | ||
| 693 | ExpectProcessRecordUserCalledWith(true, KC_A, n); | ||
| 694 | key_repeat.press(); // Press the Repeat Key. | ||
| 695 | run_one_scan_loop(); | ||
| 696 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 697 | |||
| 698 | // Expect the corresponding release event. | ||
| 699 | ExpectProcessRecordUserCalledWith(false, KC_A, n); | ||
| 700 | key_repeat.release(); // Release the Repeat Key. | ||
| 701 | run_one_scan_loop(); | ||
| 702 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 703 | } | ||
| 704 | |||
| 705 | process_record_user_fun = process_record_user_default; | ||
| 706 | set_last_keycode(KC_B); | ||
| 707 | set_last_mods(MOD_BIT(KC_LSFT)); | ||
| 708 | |||
| 709 | tap_keys(key_repeat, key_repeat); | ||
| 710 | |||
| 711 | set_last_keycode(KC_NO); | ||
| 712 | tap_keys(key_repeat, key_repeat); // Has no effect. | ||
| 713 | |||
| 714 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 715 | } | ||
| 716 | |||
| 717 | // Tests the `repeat_key_invoke()` function. | ||
| 718 | TEST_F(RepeatKey, RepeatKeyInvoke) { | ||
| 719 | TestDriver driver; | ||
| 720 | KeymapKey key_s(0, 0, 0, KC_S); | ||
| 721 | set_keymap({key_s}); | ||
| 722 | |||
| 723 | // Allow any number of empty reports. | ||
| 724 | EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); | ||
| 725 | ExpectString(driver, "ss"); | ||
| 726 | |||
| 727 | tap_key(key_s); | ||
| 728 | |||
| 729 | EXPECT_KEYCODE_EQ(get_last_keycode(), KC_S); | ||
| 730 | |||
| 731 | // Calling repeat_key_invoke() should result in process_record_user() | ||
| 732 | // getting a press event with keycode KC_S. | ||
| 733 | ExpectProcessRecordUserCalledWith(true, KC_S, 1); | ||
| 734 | keyevent_t event; | ||
| 735 | event.key = {0, 0}; | ||
| 736 | event.pressed = true; | ||
| 737 | event.time = timer_read(); | ||
| 738 | event.type = KEY_EVENT; | ||
| 739 | repeat_key_invoke(&event); | ||
| 740 | run_one_scan_loop(); | ||
| 741 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 742 | |||
| 743 | // Make the release event. | ||
| 744 | ExpectProcessRecordUserCalledWith(false, KC_S, 1); | ||
| 745 | event.pressed = false; | ||
| 746 | event.time = timer_read(); | ||
| 747 | repeat_key_invoke(&event); | ||
| 748 | run_one_scan_loop(); | ||
| 749 | EXPECT_TRUE(process_record_user_was_called_); | ||
| 750 | |||
| 751 | testing::Mock::VerifyAndClearExpectations(&driver); | ||
| 752 | } | ||
| 753 | |||
| 754 | } // namespace | ||
diff --git a/tests/test_common/keycode_table.cpp b/tests/test_common/keycode_table.cpp index d21630c01b..9ed80cdbcf 100644 --- a/tests/test_common/keycode_table.cpp +++ b/tests/test_common/keycode_table.cpp | |||
| @@ -663,6 +663,8 @@ std::map<uint16_t, std::string> KEYCODE_ID_TABLE = { | |||
| 663 | {QK_AUTOCORRECT_TOGGLE, "QK_AUTOCORRECT_TOGGLE"}, | 663 | {QK_AUTOCORRECT_TOGGLE, "QK_AUTOCORRECT_TOGGLE"}, |
| 664 | {QK_TRI_LAYER_LOWER, "QK_TRI_LAYER_LOWER"}, | 664 | {QK_TRI_LAYER_LOWER, "QK_TRI_LAYER_LOWER"}, |
| 665 | {QK_TRI_LAYER_UPPER, "QK_TRI_LAYER_UPPER"}, | 665 | {QK_TRI_LAYER_UPPER, "QK_TRI_LAYER_UPPER"}, |
| 666 | {QK_REPEAT_KEY, "QK_REPEAT_KEY"}, | ||
| 667 | {QK_ALT_REPEAT_KEY, "QK_ALT_REPEAT_KEY"}, | ||
| 666 | {QK_KB_0, "QK_KB_0"}, | 668 | {QK_KB_0, "QK_KB_0"}, |
| 667 | {QK_KB_1, "QK_KB_1"}, | 669 | {QK_KB_1, "QK_KB_1"}, |
| 668 | {QK_KB_2, "QK_KB_2"}, | 670 | {QK_KB_2, "QK_KB_2"}, |
diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp index 8d09e44840..d8a6885d0f 100644 --- a/tests/test_common/test_driver.hpp +++ b/tests/test_common/test_driver.hpp | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <stdint.h> | 20 | #include <stdint.h> |
| 21 | #include "host.h" | 21 | #include "host.h" |
| 22 | #include "keyboard_report_util.hpp" | 22 | #include "keyboard_report_util.hpp" |
| 23 | #include "keycode_util.hpp" | ||
| 23 | #include "test_logger.hpp" | 24 | #include "test_logger.hpp" |
| 24 | 25 | ||
| 25 | class TestDriver { | 26 | class TestDriver { |
| @@ -98,6 +99,17 @@ class TestDriver { | |||
| 98 | */ | 99 | */ |
| 99 | #define EXPECT_NO_REPORT(driver) EXPECT_ANY_REPORT(driver).Times(0) | 100 | #define EXPECT_NO_REPORT(driver) EXPECT_ANY_REPORT(driver).Times(0) |
| 100 | 101 | ||
| 102 | /** @brief Tests whether keycode `actual` is equal to `expected`. */ | ||
| 103 | #define EXPECT_KEYCODE_EQ(actual, expected) EXPECT_THAT((actual), KeycodeEq((expected))) | ||
| 104 | |||
| 105 | MATCHER_P(KeycodeEq, expected_keycode, "is equal to " + testing::PrintToString(expected_keycode) + ", keycode " + get_keycode_identifier_or_default(expected_keycode)) { | ||
| 106 | if (arg == expected_keycode) { | ||
| 107 | return true; | ||
| 108 | } | ||
| 109 | *result_listener << "keycode " << get_keycode_identifier_or_default(arg); | ||
| 110 | return false; | ||
| 111 | } | ||
| 112 | |||
| 101 | /** | 113 | /** |
| 102 | * @brief Verify and clear all gmock expectations that have been setup until | 114 | * @brief Verify and clear all gmock expectations that have been setup until |
| 103 | * this point. | 115 | * this point. |
