test_caps_word.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // Copyright 2022 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. #include "keyboard_report_util.hpp"
  16. #include "keycode.h"
  17. #include "test_common.hpp"
  18. #include "test_fixture.hpp"
  19. #include "test_keymap_key.hpp"
  20. using ::testing::_;
  21. using ::testing::AnyNumber;
  22. using ::testing::AnyOf;
  23. using ::testing::InSequence;
  24. using ::testing::TestParamInfo;
  25. class CapsWord : public TestFixture {
  26. public:
  27. void SetUp() override {
  28. caps_word_off();
  29. }
  30. // Convenience function to tap `key`.
  31. void TapKey(KeymapKey key) {
  32. key.press();
  33. run_one_scan_loop();
  34. key.release();
  35. run_one_scan_loop();
  36. }
  37. // Taps in order each key in `keys`.
  38. template <typename... Ts>
  39. void TapKeys(Ts... keys) {
  40. for (KeymapKey key : {keys...}) {
  41. TapKey(key);
  42. }
  43. }
  44. };
  45. // Tests caps_word_on(), _off(), and _toggle() functions.
  46. TEST_F(CapsWord, OnOffToggleFuns) {
  47. TestDriver driver;
  48. EXPECT_EQ(is_caps_word_on(), false);
  49. caps_word_on();
  50. EXPECT_EQ(is_caps_word_on(), true);
  51. caps_word_on();
  52. EXPECT_EQ(is_caps_word_on(), true);
  53. caps_word_off();
  54. EXPECT_EQ(is_caps_word_on(), false);
  55. caps_word_off();
  56. EXPECT_EQ(is_caps_word_on(), false);
  57. caps_word_toggle();
  58. EXPECT_EQ(is_caps_word_on(), true);
  59. caps_word_toggle();
  60. EXPECT_EQ(is_caps_word_on(), false);
  61. testing::Mock::VerifyAndClearExpectations(&driver);
  62. }
  63. // Tests the default `caps_word_press_user()` function.
  64. TEST_F(CapsWord, DefaultCapsWordPressUserFun) {
  65. // Spot check some keycodes that continue Caps Word, with shift applied.
  66. for (uint16_t keycode : {KC_A, KC_B, KC_Z, KC_MINS}) {
  67. SCOPED_TRACE("keycode: " + testing::PrintToString(keycode));
  68. clear_weak_mods();
  69. EXPECT_TRUE(caps_word_press_user(keycode));
  70. EXPECT_EQ(get_weak_mods(), MOD_BIT(KC_LSFT));
  71. }
  72. // Some keycodes that continue Caps Word, without shifting.
  73. for (uint16_t keycode : {KC_1, KC_9, KC_0, KC_BSPC, KC_DEL}) {
  74. SCOPED_TRACE("keycode: " + testing::PrintToString(keycode));
  75. clear_weak_mods();
  76. EXPECT_TRUE(caps_word_press_user(keycode));
  77. EXPECT_EQ(get_weak_mods(), 0);
  78. }
  79. // Some keycodes that turn off Caps Word.
  80. for (uint16_t keycode : {KC_SPC, KC_DOT, KC_COMM, KC_TAB, KC_ESC, KC_ENT}) {
  81. SCOPED_TRACE("keycode: " + testing::PrintToString(keycode));
  82. EXPECT_FALSE(caps_word_press_user(keycode));
  83. }
  84. }
  85. // Tests that `CAPSWRD` key toggles Caps Word.
  86. TEST_F(CapsWord, CapswrdKey) {
  87. TestDriver driver;
  88. KeymapKey key_capswrd(0, 0, 0, CAPSWRD);
  89. set_keymap({key_capswrd});
  90. // No keyboard reports should be sent.
  91. EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0);
  92. TapKey(key_capswrd); // Tap the CAPSWRD key.
  93. EXPECT_EQ(is_caps_word_on(), true);
  94. TapKey(key_capswrd); // Tap the CAPSWRD key again.
  95. EXPECT_EQ(is_caps_word_on(), false);
  96. testing::Mock::VerifyAndClearExpectations(&driver);
  97. }
  98. // Tests that being idle for CAPS_WORD_IDLE_TIMEOUT turns off Caps Word.
  99. TEST_F(CapsWord, IdleTimeout) {
  100. TestDriver driver;
  101. KeymapKey key_a(0, 0, 0, KC_A);
  102. set_keymap({key_a});
  103. // Allow any number of reports with no keys or only KC_LSFT.
  104. // clang-format off
  105. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  106. KeyboardReport(),
  107. KeyboardReport(KC_LSFT))))
  108. .Times(AnyNumber());
  109. // clang-format on
  110. // Expect "Shift+A".
  111. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A)));
  112. // Turn on Caps Word and tap "A".
  113. caps_word_on();
  114. TapKey(key_a);
  115. testing::Mock::VerifyAndClearExpectations(&driver);
  116. idle_for(CAPS_WORD_IDLE_TIMEOUT);
  117. run_one_scan_loop();
  118. // Caps Word should be off and mods should be clear.
  119. EXPECT_EQ(is_caps_word_on(), false);
  120. EXPECT_EQ(get_mods() | get_weak_mods(), 0);
  121. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());
  122. // Expect unshifted "A".
  123. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));
  124. TapKey(key_a);
  125. testing::Mock::VerifyAndClearExpectations(&driver);
  126. }
  127. // Tests that typing "A, 4, A, 4" produces "Shift+A, 4, Shift+A, 4".
  128. TEST_F(CapsWord, ShiftsLettersButNotDigits) {
  129. TestDriver driver;
  130. KeymapKey key_a(0, 0, 0, KC_A);
  131. KeymapKey key_4(0, 1, 0, KC_4);
  132. set_keymap({key_a, key_4});
  133. // Allow any number of reports with no keys or only KC_LSFT.
  134. // clang-format off
  135. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  136. KeyboardReport(),
  137. KeyboardReport(KC_LSFT))))
  138. .Times(AnyNumber());
  139. // clang-format on
  140. { // Expect: "Shift+A, 4, Shift+A, 4".
  141. InSequence s;
  142. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A)));
  143. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_4)));
  144. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A)));
  145. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_4)));
  146. }
  147. // Turn on Caps Word and tap "A, 4, A, 4".
  148. caps_word_on();
  149. TapKeys(key_a, key_4, key_a, key_4);
  150. testing::Mock::VerifyAndClearExpectations(&driver);
  151. }
  152. // Tests that typing "A, Space, A" produces "Shift+A, Space, A".
  153. TEST_F(CapsWord, SpaceTurnsOffCapsWord) {
  154. TestDriver driver;
  155. KeymapKey key_a(0, 0, 0, KC_A);
  156. KeymapKey key_spc(0, 1, 0, KC_SPC);
  157. set_keymap({key_a, key_spc});
  158. // Allow any number of reports with no keys or only KC_LSFT.
  159. // clang-format off
  160. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  161. KeyboardReport(),
  162. KeyboardReport(KC_LSFT))))
  163. .Times(AnyNumber());
  164. // clang-format on
  165. { // Expect: "Shift+A, Space, A".
  166. InSequence seq;
  167. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A)));
  168. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_SPC)));
  169. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));
  170. }
  171. // Turn on Caps Word and tap "A, Space, A".
  172. caps_word_on();
  173. TapKeys(key_a, key_spc, key_a);
  174. testing::Mock::VerifyAndClearExpectations(&driver);
  175. }
  176. // Tests that typing "AltGr + A" produces "Shift + AltGr + A".
  177. TEST_F(CapsWord, ShiftsAltGrSymbols) {
  178. TestDriver driver;
  179. KeymapKey key_a(0, 0, 0, KC_A);
  180. KeymapKey key_altgr(0, 1, 0, KC_RALT);
  181. set_keymap({key_a, key_altgr});
  182. // Allow any number of reports with no keys or only modifiers.
  183. // clang-format off
  184. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  185. KeyboardReport(),
  186. KeyboardReport(KC_RALT),
  187. KeyboardReport(KC_LSFT, KC_RALT))))
  188. .Times(AnyNumber());
  189. // Expect "Shift + AltGr + A, Space".
  190. EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_RALT, KC_A)));
  191. // clang-format on
  192. // Turn on Caps Word and type "AltGr + A".
  193. caps_word_on();
  194. key_altgr.press();
  195. run_one_scan_loop();
  196. TapKeys(key_a);
  197. run_one_scan_loop();
  198. key_altgr.release();
  199. testing::Mock::VerifyAndClearExpectations(&driver);
  200. }
  201. struct CapsWordBothShiftsParams {
  202. std::string name;
  203. uint16_t left_shift_keycode;
  204. uint16_t right_shift_keycode;
  205. static const std::string& GetName(const TestParamInfo<CapsWordBothShiftsParams>& info) {
  206. return info.param.name;
  207. }
  208. };
  209. // Tests the BOTH_SHIFTS_TURNS_ON_CAPS_WORD method to turn on Caps Word.
  210. class CapsWordBothShifts : public ::testing::WithParamInterface<CapsWordBothShiftsParams>, public CapsWord {};
  211. // Pressing shifts as "Left down, Right down, Left up, Right up".
  212. TEST_P(CapsWordBothShifts, PressLRLR) {
  213. TestDriver driver;
  214. KeymapKey left_shift(0, 0, 0, GetParam().left_shift_keycode);
  215. KeymapKey right_shift(0, 1, 0, GetParam().right_shift_keycode);
  216. set_keymap({left_shift, right_shift});
  217. // clang-format off
  218. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  219. KeyboardReport(),
  220. KeyboardReport(KC_LSFT),
  221. KeyboardReport(KC_RSFT),
  222. KeyboardReport(KC_LSFT, KC_RSFT))))
  223. .Times(AnyNumber());
  224. // clang-format on
  225. EXPECT_EQ(is_caps_word_on(), false);
  226. left_shift.press(); // Press both shifts.
  227. run_one_scan_loop();
  228. right_shift.press();
  229. // For mod-tap and Space Cadet keys, wait for the tapping term.
  230. if (left_shift.code == LSFT_T(KC_A) || left_shift.code == KC_LSPO) {
  231. idle_for(TAPPING_TERM);
  232. }
  233. run_one_scan_loop();
  234. left_shift.release(); // Release both.
  235. run_one_scan_loop();
  236. right_shift.release();
  237. run_one_scan_loop();
  238. EXPECT_EQ(is_caps_word_on(), true);
  239. testing::Mock::VerifyAndClearExpectations(&driver);
  240. }
  241. // Pressing shifts as "Left down, Right down, Right up, Left up".
  242. TEST_P(CapsWordBothShifts, PressLRRL) {
  243. TestDriver driver;
  244. KeymapKey left_shift(0, 0, 0, GetParam().left_shift_keycode);
  245. KeymapKey right_shift(0, 1, 0, GetParam().right_shift_keycode);
  246. set_keymap({left_shift, right_shift});
  247. // clang-format off
  248. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  249. KeyboardReport(),
  250. KeyboardReport(KC_LSFT),
  251. KeyboardReport(KC_RSFT),
  252. KeyboardReport(KC_LSFT, KC_RSFT))))
  253. .Times(AnyNumber());
  254. // clang-format on
  255. EXPECT_EQ(is_caps_word_on(), false);
  256. left_shift.press(); // Press both shifts.
  257. run_one_scan_loop();
  258. right_shift.press();
  259. if (left_shift.code == LSFT_T(KC_A) || left_shift.code == KC_LSPO) {
  260. idle_for(TAPPING_TERM);
  261. }
  262. run_one_scan_loop();
  263. right_shift.release(); // Release both.
  264. run_one_scan_loop();
  265. left_shift.release();
  266. run_one_scan_loop();
  267. EXPECT_EQ(is_caps_word_on(), true);
  268. testing::Mock::VerifyAndClearExpectations(&driver);
  269. }
  270. // clang-format off
  271. INSTANTIATE_TEST_CASE_P(
  272. ShiftPairs,
  273. CapsWordBothShifts,
  274. ::testing::Values(
  275. CapsWordBothShiftsParams{
  276. "PlainShifts", KC_LSFT, KC_RSFT},
  277. CapsWordBothShiftsParams{
  278. "OneshotShifts", OSM(MOD_LSFT), OSM(MOD_RSFT)},
  279. CapsWordBothShiftsParams{
  280. "SpaceCadetShifts", KC_LSPO, KC_RSPC},
  281. CapsWordBothShiftsParams{
  282. "ModTapShifts", LSFT_T(KC_A), RSFT_T(KC_B)}
  283. ),
  284. CapsWordBothShiftsParams::GetName
  285. );
  286. // clang-format on
  287. struct CapsWordDoubleTapShiftParams {
  288. std::string name;
  289. uint16_t left_shift_keycode;
  290. static const std::string& GetName(const TestParamInfo<CapsWordDoubleTapShiftParams>& info) {
  291. return info.param.name;
  292. }
  293. };
  294. // Tests the DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD method to turn on Caps Word.
  295. class CapsWordDoubleTapShift : public ::testing::WithParamInterface<CapsWordDoubleTapShiftParams>, public CapsWord {};
  296. // Tests that double tapping activates Caps Word.
  297. TEST_P(CapsWordDoubleTapShift, Activation) {
  298. TestDriver driver;
  299. KeymapKey left_shift(0, 0, 0, GetParam().left_shift_keycode);
  300. set_keymap({left_shift});
  301. // clang-format off
  302. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  303. KeyboardReport(),
  304. KeyboardReport(KC_LSFT))))
  305. .Times(AnyNumber());
  306. // clang-format on
  307. EXPECT_EQ(is_caps_word_on(), false);
  308. // Tapping shift twice within the tapping term turns on Caps Word.
  309. TapKey(left_shift);
  310. idle_for(TAPPING_TERM - 10);
  311. TapKey(left_shift);
  312. EXPECT_EQ(is_caps_word_on(), true);
  313. testing::Mock::VerifyAndClearExpectations(&driver);
  314. }
  315. // Double tap doesn't count if another key is pressed between the taps.
  316. TEST_P(CapsWordDoubleTapShift, Interrupted) {
  317. TestDriver driver;
  318. KeymapKey left_shift(0, 0, 0, GetParam().left_shift_keycode);
  319. KeymapKey key_a(0, 1, 0, KC_A);
  320. set_keymap({left_shift, key_a});
  321. // clang-format off
  322. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  323. KeyboardReport(),
  324. KeyboardReport(KC_LSFT),
  325. KeyboardReport(KC_LSFT, KC_A))))
  326. .Times(AnyNumber());
  327. // clang-format on
  328. left_shift.press();
  329. run_one_scan_loop();
  330. TapKey(key_a); // 'A' key interrupts the double tap.
  331. left_shift.release();
  332. run_one_scan_loop();
  333. idle_for(TAPPING_TERM - 10);
  334. TapKey(left_shift);
  335. EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off.
  336. clear_oneshot_mods();
  337. testing::Mock::VerifyAndClearExpectations(&driver);
  338. }
  339. // Double tap doesn't count if taps are more than tapping term apart.
  340. TEST_P(CapsWordDoubleTapShift, SlowTaps) {
  341. TestDriver driver;
  342. KeymapKey left_shift(0, 0, 0, GetParam().left_shift_keycode);
  343. set_keymap({left_shift});
  344. // clang-format off
  345. EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
  346. KeyboardReport(),
  347. KeyboardReport(KC_LSFT))))
  348. .Times(AnyNumber());
  349. // clang-format on
  350. TapKey(left_shift);
  351. idle_for(TAPPING_TERM + 1);
  352. TapKey(left_shift);
  353. EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off.
  354. clear_oneshot_mods();
  355. testing::Mock::VerifyAndClearExpectations(&driver);
  356. }
  357. // clang-format off
  358. INSTANTIATE_TEST_CASE_P(
  359. Shifts,
  360. CapsWordDoubleTapShift,
  361. ::testing::Values(
  362. CapsWordDoubleTapShiftParams{"PlainShift", KC_LSFT},
  363. CapsWordDoubleTapShiftParams{"OneshotShift", OSM(MOD_LSFT)}
  364. ),
  365. CapsWordDoubleTapShiftParams::GetName
  366. );
  367. // clang-format on