From 95d20e6d8bb1ffaf3024af793daf789ee0b75727 Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Sun, 5 Jun 2022 00:14:02 -0700 Subject: Fix and add unit tests for Caps Word to work with Unicode Map, Auto Shift, Retro Shift. (#17284) * Fix Caps Word and Unicode Map * Tests for Caps Word + Auto Shift and Unicode Map. * Fix formatting * Add additional keyboard report expectation macros This commit defines five test utilities, EXPECT_REPORT, EXPECT_UNICODE, EXPECT_EMPTY_REPORT, EXPECT_ANY_REPORT and EXPECT_NO_REPORT for use with TestDriver. EXPECT_REPORT sets a gmock expectation that a given keyboard report will be sent. For instance, EXPECT_REPORT(driver, (KC_LSFT, KC_A)); is shorthand for EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); EXPECT_UNICODE sets a gmock expectation that a given Unicode code point will be sent using UC_LNX input mode. For instance for U+2013, EXPECT_UNICODE(driver, 0x2013); expects the sequence of keys: "Ctrl+Shift+U, 2, 0, 1, 3, space". EXPECT_EMPTY_REPORT sets a gmock expectation that a given keyboard report will be sent. For instance EXPECT_EMPTY_REPORT(driver); expects a single report without keypresses or modifiers. EXPECT_ANY_REPORT sets a gmock expectation that a arbitrary keyboard report will be sent, without matching its contents. For instance EXPECT_ANY_REPORT(driver).Times(1); expects a single arbitrary keyboard report will be sent. EXPECT_NO_REPORT sets a gmock expectation that no keyboard report will be sent at all. * Add tap_key() and tap_keys() to TestFixture. This commit adds a `tap_key(key)` method to TestFixture that taps a given KeymapKey, optionally with a specified delay between press and release. Similarly, the method `tap_keys(key_a, key_b, key_c)` taps a sequence of KeymapKeys. * Use EXPECT_REPORT, tap_keys, etc. in most tests. This commit uses EXPECT_REPORT, EXPECT_UNICODE, EXPECT_EMPTY_REPORT, EXPECT_NO_REPORT, tap_key() and tap_keys() test utilities from the previous two commits in most tests. Particularly the EXPECT_REPORT macro is frequently useful and makes a nice reduction in boilerplate needed to express many tests. Co-authored-by: David Kosorin --- tests/caps_word/caps_word_autoshift/config.h | 22 ++++ tests/caps_word/caps_word_autoshift/test.mk | 18 +++ .../test_caps_word_autoshift.cpp | 101 +++++++++++++++++ tests/caps_word/caps_word_unicodemap/config.h | 20 ++++ tests/caps_word/caps_word_unicodemap/test.mk | 18 +++ .../test_caps_word_unicodemap.cpp | 121 +++++++++++++++++++++ tests/caps_word/test_caps_word.cpp | 68 +++++------- 7 files changed, 326 insertions(+), 42 deletions(-) create mode 100644 tests/caps_word/caps_word_autoshift/config.h create mode 100644 tests/caps_word/caps_word_autoshift/test.mk create mode 100644 tests/caps_word/caps_word_autoshift/test_caps_word_autoshift.cpp create mode 100644 tests/caps_word/caps_word_unicodemap/config.h create mode 100644 tests/caps_word/caps_word_unicodemap/test.mk create mode 100644 tests/caps_word/caps_word_unicodemap/test_caps_word_unicodemap.cpp (limited to 'tests/caps_word') diff --git a/tests/caps_word/caps_word_autoshift/config.h b/tests/caps_word/caps_word_autoshift/config.h new file mode 100644 index 0000000000..b80f53b9dd --- /dev/null +++ b/tests/caps_word/caps_word_autoshift/config.h @@ -0,0 +1,22 @@ +// Copyright 2022 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 . + +#pragma once + +#include "test_common.h" + +#define TAPPING_TERM 200 +#define AUTO_SHIFT_TIMEOUT 150 +#define RETRO_SHIFT 500 diff --git a/tests/caps_word/caps_word_autoshift/test.mk b/tests/caps_word/caps_word_autoshift/test.mk new file mode 100644 index 0000000000..7f717d7fc1 --- /dev/null +++ b/tests/caps_word/caps_word_autoshift/test.mk @@ -0,0 +1,18 @@ +# Copyright 2022 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 . + +CAPS_WORD_ENABLE = yes +AUTO_SHIFT_ENABLE = yes + diff --git a/tests/caps_word/caps_word_autoshift/test_caps_word_autoshift.cpp b/tests/caps_word/caps_word_autoshift/test_caps_word_autoshift.cpp new file mode 100644 index 0000000000..deb4d95766 --- /dev/null +++ b/tests/caps_word/caps_word_autoshift/test_caps_word_autoshift.cpp @@ -0,0 +1,101 @@ +// Copyright 2022 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 . + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::AnyOf; +using ::testing::InSequence; + +class CapsWord : public TestFixture { + public: + void SetUp() override { + caps_word_off(); + } +}; + +// Tests that with Auto Shift, letter keys are shifted by Caps Word +// regardless of whether they are released before AUTO_SHIFT_TIMEOUT. +TEST_F(CapsWord, AutoShiftKeys) { + TestDriver driver; + KeymapKey key_a(0, 0, 0, KC_A); + KeymapKey key_spc(0, 1, 0, KC_SPC); + set_keymap({key_a, key_spc}); + + // Allow any number of reports with no keys or only KC_LSFT. + // clang-format off + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( + KeyboardReport(), + KeyboardReport(KC_LSFT)))) + .Times(AnyNumber()); + // clang-format on + { // Expect: "A, A, space, a". + InSequence s; + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_REPORT(driver, (KC_SPC)); + EXPECT_REPORT(driver, (KC_A)); + } + + // Turn on Caps Word and type "A (quick tap), A (long press), space, A". + caps_word_on(); + + tap_key(key_a); // Tap A quickly. + tap_key(key_a, AUTO_SHIFT_TIMEOUT + 1); // Long press A. + tap_key(key_spc); + tap_key(key_a); + + testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Tests that with tap-hold keys with Retro Shift, letter keys are shifted by +// Caps Word regardless of whether they are retroshifted. +TEST_F(CapsWord, RetroShiftKeys) { + TestDriver driver; + KeymapKey key_modtap_a(0, 0, 0, LCTL_T(KC_A)); + KeymapKey key_layertap_b(0, 1, 0, LT(1, KC_B)); + set_keymap({key_modtap_a, key_layertap_b}); + + // Allow any number of reports with no keys or only KC_LSFT. + // clang-format off + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( + KeyboardReport(), + KeyboardReport(KC_LSFT)))) + .Times(AnyNumber()); + // clang-format on + { // Expect: "B, A, B, A". + InSequence s; + EXPECT_REPORT(driver, (KC_LSFT, KC_B)); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_REPORT(driver, (KC_LSFT, KC_B)); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + } + + // Turn on Caps Word and type "B, A (long press), B (long press), A". + caps_word_on(); + + tap_key(key_layertap_b); // Tap B quickly. + tap_key(key_modtap_a, TAPPING_TERM + 1); // Long press A. + tap_key(key_layertap_b, TAPPING_TERM + 1); // Long press B. + tap_key(key_modtap_a); // Tap A quickly. + + EXPECT_EQ(is_caps_word_on(), true); + testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/caps_word/caps_word_unicodemap/config.h b/tests/caps_word/caps_word_unicodemap/config.h new file mode 100644 index 0000000000..89fd7924d4 --- /dev/null +++ b/tests/caps_word/caps_word_unicodemap/config.h @@ -0,0 +1,20 @@ +// Copyright 2022 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 . + +#pragma once + +#include "test_common.h" + +#define UNICODE_SELECTED_MODES UC_LNX diff --git a/tests/caps_word/caps_word_unicodemap/test.mk b/tests/caps_word/caps_word_unicodemap/test.mk new file mode 100644 index 0000000000..92bcba762c --- /dev/null +++ b/tests/caps_word/caps_word_unicodemap/test.mk @@ -0,0 +1,18 @@ +# Copyright 2022 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 . + +CAPS_WORD_ENABLE = yes +UNICODEMAP_ENABLE = yes + diff --git a/tests/caps_word/caps_word_unicodemap/test_caps_word_unicodemap.cpp b/tests/caps_word/caps_word_unicodemap/test_caps_word_unicodemap.cpp new file mode 100644 index 0000000000..fb8f9333bb --- /dev/null +++ b/tests/caps_word/caps_word_unicodemap/test_caps_word_unicodemap.cpp @@ -0,0 +1,121 @@ +// Copyright 2022 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 . + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::AnyOf; +using ::testing::InSequence; + +extern "C" { +enum unicode_names { + ENDASH, + EMDASH, + DELTA_LOWERCASE, + DELTA_UPPERCASE, +}; + +const uint32_t unicode_map[] PROGMEM = { + [ENDASH] = 0x2013, + [EMDASH] = 0x2014, + [DELTA_LOWERCASE] = 0x03b4, + [DELTA_UPPERCASE] = 0x0394, +}; + +#define U_DASH XP(ENDASH, EMDASH) +#define U_DELTA XP(DELTA_LOWERCASE, DELTA_UPPERCASE) + +bool caps_word_press_user(uint16_t keycode) { + switch (keycode) { + // Keycodes that continue Caps Word, with shift applied. + case U_DELTA: + add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key. + return true; + + // Keycodes that continue Caps Word, without shifting. + case U_DASH: + return true; + + default: + return false; // Deactivate Caps Word. + } +} +} // extern "C" + +class CapsWord : public TestFixture { + public: + void SetUp() override { + caps_word_off(); + } +}; + +// Tests that typing U_DELTA while Caps Word is on sends the uppercase Delta. +TEST_F(CapsWord, ShiftedUnicodeMapKey) { + TestDriver driver; + KeymapKey key_delta(0, 0, 0, U_DELTA); + KeymapKey key_spc(0, 1, 0, KC_SPC); + set_keymap({key_delta, key_spc}); + + // Allow any number of reports with no keys or only KC_LSFT and KC_LCTL. + // clang-format off + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( + KeyboardReport(), + KeyboardReport(KC_LSFT), + KeyboardReport(KC_LCTL, KC_LSFT)))) + .Times(AnyNumber()); + // clang-format on + { // Expect: "Uppercase Delta, space, lowercase delta". + InSequence s; + EXPECT_UNICODE(driver, unicode_map[DELTA_UPPERCASE]); + EXPECT_REPORT(driver, (KC_SPC)); + EXPECT_UNICODE(driver, unicode_map[DELTA_LOWERCASE]); + } + + // Turn on Caps Word and tap "delta, space, delta". + caps_word_on(); + tap_keys(key_delta, key_spc, key_delta); + + EXPECT_EQ(is_caps_word_on(), false); + testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Tests typing U_ENDASH while Caps Word is on. +TEST_F(CapsWord, UnshiftedUnicodeMapKey) { + TestDriver driver; + KeymapKey key_dash(0, 0, 0, U_DASH); + set_keymap({key_dash}); + + // Allow any number of reports with no keys or only KC_LSFT and KC_LCTL. + // clang-format off + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( + KeyboardReport(), + KeyboardReport(KC_LSFT), + KeyboardReport(KC_LCTL, KC_LSFT)))) + .Times(AnyNumber()); + // clang-format on + EXPECT_UNICODE(driver, unicode_map[ENDASH]); + + // Turn on Caps Word and tap U_DASH key. + caps_word_on(); + tap_key(key_dash); + + EXPECT_EQ(is_caps_word_on(), true); + testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/caps_word/test_caps_word.cpp b/tests/caps_word/test_caps_word.cpp index f611d4c104..0af4b0175d 100644 --- a/tests/caps_word/test_caps_word.cpp +++ b/tests/caps_word/test_caps_word.cpp @@ -30,22 +30,6 @@ class CapsWord : public TestFixture { void SetUp() override { caps_word_off(); } - - // Convenience function to tap `key`. - void TapKey(KeymapKey key) { - key.press(); - run_one_scan_loop(); - key.release(); - run_one_scan_loop(); - } - - // Taps in order each key in `keys`. - template - void TapKeys(Ts... keys) { - for (KeymapKey key : {keys...}) { - TapKey(key); - } - } }; // Tests caps_word_on(), _off(), and _toggle() functions. @@ -104,12 +88,12 @@ TEST_F(CapsWord, CapswrdKey) { set_keymap({key_capswrd}); // No keyboard reports should be sent. - EXPECT_CALL(driver, send_keyboard_mock(_)).Times(0); + EXPECT_NO_REPORT(driver); - TapKey(key_capswrd); // Tap the CAPSWRD key. + tap_key(key_capswrd); // Tap the CAPSWRD key. EXPECT_EQ(is_caps_word_on(), true); - TapKey(key_capswrd); // Tap the CAPSWRD key again. + tap_key(key_capswrd); // Tap the CAPSWRD key again. EXPECT_EQ(is_caps_word_on(), false); testing::Mock::VerifyAndClearExpectations(&driver); @@ -130,11 +114,11 @@ TEST_F(CapsWord, IdleTimeout) { // clang-format on // Expect "Shift+A". - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); // Turn on Caps Word and tap "A". caps_word_on(); - TapKey(key_a); + tap_key(key_a); testing::Mock::VerifyAndClearExpectations(&driver); @@ -145,10 +129,10 @@ TEST_F(CapsWord, IdleTimeout) { EXPECT_EQ(is_caps_word_on(), false); EXPECT_EQ(get_mods() | get_weak_mods(), 0); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber()); + EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); // Expect unshifted "A". - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); - TapKey(key_a); + EXPECT_REPORT(driver, (KC_A)); + tap_key(key_a); testing::Mock::VerifyAndClearExpectations(&driver); } @@ -170,15 +154,15 @@ TEST_F(CapsWord, ShiftsLettersButNotDigits) { { // Expect: "Shift+A, 4, Shift+A, 4". InSequence s; - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_4))); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_4))); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_REPORT(driver, (KC_4)); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_REPORT(driver, (KC_4)); } // Turn on Caps Word and tap "A, 4, A, 4". caps_word_on(); - TapKeys(key_a, key_4, key_a, key_4); + tap_keys(key_a, key_4, key_a, key_4); testing::Mock::VerifyAndClearExpectations(&driver); } @@ -200,14 +184,14 @@ TEST_F(CapsWord, SpaceTurnsOffCapsWord) { { // Expect: "Shift+A, Space, A". InSequence seq; - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A))); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_SPC))); - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); + EXPECT_REPORT(driver, (KC_SPC)); + EXPECT_REPORT(driver, (KC_A)); } // Turn on Caps Word and tap "A, Space, A". caps_word_on(); - TapKeys(key_a, key_spc, key_a); + tap_keys(key_a, key_spc, key_a); testing::Mock::VerifyAndClearExpectations(&driver); } @@ -226,8 +210,8 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) { KeyboardReport(KC_RALT), KeyboardReport(KC_LSFT, KC_RALT)))) .Times(AnyNumber()); - // Expect "Shift + AltGr + A, Space". - EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_RALT, KC_A))); + // Expect "Shift + AltGr + A". + EXPECT_REPORT(driver, (KC_LSFT, KC_RALT, KC_A)); // clang-format on // Turn on Caps Word and type "AltGr + A". @@ -235,7 +219,7 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) { key_altgr.press(); run_one_scan_loop(); - TapKeys(key_a); + tap_key(key_a); run_one_scan_loop(); key_altgr.release(); @@ -376,9 +360,9 @@ TEST_P(CapsWordDoubleTapShift, Activation) { EXPECT_EQ(is_caps_word_on(), false); // Tapping shift twice within the tapping term turns on Caps Word. - TapKey(left_shift); + tap_key(left_shift); idle_for(TAPPING_TERM - 10); - TapKey(left_shift); + tap_key(left_shift); EXPECT_EQ(is_caps_word_on(), true); @@ -403,13 +387,13 @@ TEST_P(CapsWordDoubleTapShift, Interrupted) { left_shift.press(); run_one_scan_loop(); - TapKey(key_a); // 'A' key interrupts the double tap. + tap_key(key_a); // 'A' key interrupts the double tap. left_shift.release(); run_one_scan_loop(); idle_for(TAPPING_TERM - 10); - TapKey(left_shift); + tap_key(left_shift); EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off. clear_oneshot_mods(); @@ -430,9 +414,9 @@ TEST_P(CapsWordDoubleTapShift, SlowTaps) { .Times(AnyNumber()); // clang-format on - TapKey(left_shift); + tap_key(left_shift); idle_for(TAPPING_TERM + 1); - TapKey(left_shift); + tap_key(left_shift); EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off. clear_oneshot_mods(); -- cgit v1.2.3