diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/autocorrect/config.h | 6 | ||||
| -rw-r--r-- | tests/autocorrect/test.mk | 8 | ||||
| -rw-r--r-- | tests/autocorrect/test_autocorrect.cpp | 204 | ||||
| -rw-r--r-- | tests/basic/test_action_layer.cpp | 1 | ||||
| -rw-r--r-- | tests/caps_word/caps_word_unicodemap/config.h | 2 | ||||
| -rw-r--r-- | tests/caps_word/test_caps_word.cpp | 14 | ||||
| -rw-r--r-- | tests/tap_dance/tap_dance_layers/config.h | 6 | ||||
| -rw-r--r-- | tests/tap_dance/tap_dance_layers/tap_dance_defs.c | 97 | ||||
| -rw-r--r-- | tests/tap_dance/tap_dance_layers/tap_dance_defs.h | 29 | ||||
| -rw-r--r-- | tests/tap_dance/tap_dance_layers/test.mk | 10 | ||||
| -rw-r--r-- | tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp | 717 | ||||
| -rw-r--r-- | tests/tap_hold_configurations/hold_on_other_key_press/config.h | 20 | ||||
| -rw-r--r-- | tests/tap_hold_configurations/hold_on_other_key_press/test.mk | 18 | ||||
| -rw-r--r-- | tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp | 423 | ||||
| -rw-r--r-- | tests/test_common/main.cpp | 2 | ||||
| -rw-r--r-- | tests/test_common/test_driver.cpp | 10 | ||||
| -rw-r--r-- | tests/test_common/test_driver.hpp | 8 | 
17 files changed, 1553 insertions, 22 deletions
| diff --git a/tests/autocorrect/config.h b/tests/autocorrect/config.h new file mode 100644 index 0000000000..b68bf0c2d5 --- /dev/null +++ b/tests/autocorrect/config.h @@ -0,0 +1,6 @@ +// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" diff --git a/tests/autocorrect/test.mk b/tests/autocorrect/test.mk new file mode 100644 index 0000000000..7b97d8cce3 --- /dev/null +++ b/tests/autocorrect/test.mk @@ -0,0 +1,8 @@ +# Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com> +# SPDX-License-Identifier: GPL-2.0-or-later + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- + +AUTOCORRECT_ENABLE = yes diff --git a/tests/autocorrect/test_autocorrect.cpp b/tests/autocorrect/test_autocorrect.cpp new file mode 100644 index 0000000000..509c1c9ea4 --- /dev/null +++ b/tests/autocorrect/test_autocorrect.cpp @@ -0,0 +1,204 @@ +// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "keycode.h" +#include "test_common.hpp" + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::InSequence; + +class AutoCorrect : public TestFixture { +   public: +    void SetUp() override { +        autocorrect_enable(); +    } +    // 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 <typename... Ts> +    void TapKeys(Ts... keys) { +        for (KeymapKey key : {keys...}) { +            TapKey(key); +        } +    } +}; + +// Test that verifies enable/disable/toggling works +TEST_F(AutoCorrect, OnOffToggle) { +    TestDriver driver; + +    EXPECT_EQ(autocorrect_is_enabled(), true); + +    autocorrect_disable(); +    EXPECT_EQ(autocorrect_is_enabled(), false); +    autocorrect_disable(); +    EXPECT_EQ(autocorrect_is_enabled(), false); + +    autocorrect_enable(); +    EXPECT_EQ(autocorrect_is_enabled(), true); +    autocorrect_enable(); +    EXPECT_EQ(autocorrect_is_enabled(), true); + +    autocorrect_toggle(); +    EXPECT_EQ(autocorrect_is_enabled(), false); +    autocorrect_toggle(); +    EXPECT_EQ(autocorrect_is_enabled(), true); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Test that typing "fales" autocorrects to "false" +TEST_F(AutoCorrect, fales_to_false_autocorrection) { +    TestDriver driver; +    auto       key_f = KeymapKey(0, 0, 0, KC_F); +    auto       key_a = KeymapKey(0, 1, 0, KC_A); +    auto       key_l = KeymapKey(0, 2, 0, KC_L); +    auto       key_e = KeymapKey(0, 3, 0, KC_E); +    auto       key_s = KeymapKey(0, 4, 0, KC_S); + +    set_keymap({key_f, key_a, key_l, key_e, key_s}); + +    // Allow any number of empty reports. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber()); +    { // Expect the following reports in this order. +        InSequence s; +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_BACKSPACE))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))); +    } + +    TapKeys(key_f, key_a, key_l, key_e, key_s); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Test that typing "fales" doesn't autocorrect if disabled +TEST_F(AutoCorrect, fales_disabled_autocorrect) { +    TestDriver driver; +    auto       key_f = KeymapKey(0, 0, 0, KC_F); +    auto       key_a = KeymapKey(0, 1, 0, KC_A); +    auto       key_l = KeymapKey(0, 2, 0, KC_L); +    auto       key_e = KeymapKey(0, 3, 0, KC_E); +    auto       key_s = KeymapKey(0, 4, 0, KC_S); + +    set_keymap({key_f, key_a, key_l, key_e, key_s}); + +    // Allow any number of empty reports. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber()); +    { // Expect the following reports in this order. +        InSequence s; +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S))); +    } + +    autocorrect_disable(); +    TapKeys(key_f, key_a, key_l, key_e, key_s); +    autocorrect_enable(); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Test that typing "falsify" doesn't autocorrect if disabled +TEST_F(AutoCorrect, falsify_should_not_autocorrect) { +    TestDriver driver; +    auto       key_f = KeymapKey(0, 0, 0, KC_F); +    auto       key_a = KeymapKey(0, 1, 0, KC_A); +    auto       key_l = KeymapKey(0, 2, 0, KC_L); +    auto       key_s = KeymapKey(0, 3, 0, KC_S); +    auto       key_i = KeymapKey(0, 4, 0, KC_I); +    auto       key_y = KeymapKey(0, 5, 0, KC_Y); + +    set_keymap({key_f, key_a, key_l, key_s, key_i, key_y}); + +    // Allow any number of empty reports. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber()); +    { // Expect the following reports in this order. +        InSequence s; +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_I))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_Y))); +    } + +    TapKeys(key_f, key_a, key_l, key_s, key_i, key_f, key_y); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Test that  typing "ture" autocorrect to "true" +TEST_F(AutoCorrect, ture_to_true_autocorrect) { +    TestDriver driver; +    auto       key_t_code = KeymapKey(0, 0, 0, KC_T); +    auto       key_r      = KeymapKey(0, 1, 0, KC_R); +    auto       key_u      = KeymapKey(0, 2, 0, KC_U); +    auto       key_e      = KeymapKey(0, 3, 0, KC_E); +    auto       key_space  = KeymapKey(0, 4, 0, KC_SPACE); + +    set_keymap({key_t_code, key_r, key_u, key_e, key_space}); + +    // Allow any number of empty reports. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber()); +    { // Expect the following reports in this order. +        InSequence s; +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_SPACE))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_T))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_BACKSPACE))).Times(2); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))); +    } + +    TapKeys(key_space, key_t_code, key_u, key_r, key_e); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +// Test that  typing "overture" does not autocorrect +TEST_F(AutoCorrect, overture_should_not_autocorrect) { +    TestDriver driver; +    auto       key_t_code = KeymapKey(0, 0, 0, KC_T); +    auto       key_r      = KeymapKey(0, 1, 0, KC_R); +    auto       key_u      = KeymapKey(0, 2, 0, KC_U); +    auto       key_e      = KeymapKey(0, 3, 0, KC_E); +    auto       key_o      = KeymapKey(0, 4, 0, KC_O); +    auto       key_v      = KeymapKey(0, 5, 0, KC_V); + +    set_keymap({key_t_code, key_r, key_u, key_e, key_o, key_v}); + +    // Allow any number of empty reports. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber()); +    { // Expect the following reports in this order. +        InSequence s; +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_O))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_V))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_T))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R))); +        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E))); +    } + +    TapKeys(key_o, key_v, key_e, key_r, key_t_code, key_u, key_r, key_e); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/basic/test_action_layer.cpp b/tests/basic/test_action_layer.cpp index fa339a3375..d0a28d42c0 100644 --- a/tests/basic/test_action_layer.cpp +++ b/tests/basic/test_action_layer.cpp @@ -360,7 +360,6 @@ TEST_F(ActionLayer, LayerTapToggleWithToggleWithKeypress) {  }  TEST_F(ActionLayer, LayerTapReleasedBeforeKeypressReleaseWithModifiers) { -    GTEST_SKIP() << "TODO: Modifiers are erroneously discarded on layer changes, although a key that introduced the modifier is still held.";      TestDriver driver;      InSequence s; diff --git a/tests/caps_word/caps_word_unicodemap/config.h b/tests/caps_word/caps_word_unicodemap/config.h index 89fd7924d4..0f770337b1 100644 --- a/tests/caps_word/caps_word_unicodemap/config.h +++ b/tests/caps_word/caps_word_unicodemap/config.h @@ -17,4 +17,4 @@  #include "test_common.h" -#define UNICODE_SELECTED_MODES UC_LNX +#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX diff --git a/tests/caps_word/test_caps_word.cpp b/tests/caps_word/test_caps_word.cpp index 3f59ed3744..3d0735d8c2 100644 --- a/tests/caps_word/test_caps_word.cpp +++ b/tests/caps_word/test_caps_word.cpp @@ -118,19 +118,19 @@ TEST_F(CapsWord, DefaultCapsWordPressUserFun) {      }  } -// Tests that `CAPSWRD` key toggles Caps Word. +// Tests that `QK_CAPS_WORD_TOGGLE` key toggles Caps Word.  TEST_F(CapsWord, CapswrdKey) {      TestDriver driver; -    KeymapKey  key_capswrd(0, 0, 0, CAPSWRD); +    KeymapKey  key_capswrd(0, 0, 0, QK_CAPS_WORD_TOGGLE);      set_keymap({key_capswrd});      // No keyboard reports should be sent.      EXPECT_NO_REPORT(driver); -    tap_key(key_capswrd); // Tap the CAPSWRD key. +    tap_key(key_capswrd); // Tap the QK_CAPS_WORD_TOGGLE key.      EXPECT_EQ(is_caps_word_on(), true); -    tap_key(key_capswrd); // Tap the CAPSWRD key again. +    tap_key(key_capswrd); // Tap the QK_CAPS_WORD_TOGGLE key again.      EXPECT_EQ(is_caps_word_on(), false);      testing::Mock::VerifyAndClearExpectations(&driver); @@ -419,7 +419,7 @@ TEST_P(CapsWordBothShifts, PressLRLR) {      right_shift.press();      // For mod-tap and Space Cadet keys, wait for the tapping term. -    if (left_shift.code == LSFT_T(KC_A) || left_shift.code == KC_LSPO) { +    if (left_shift.code == LSFT_T(KC_A) || left_shift.code == QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN) {          idle_for(TAPPING_TERM);      } @@ -456,7 +456,7 @@ TEST_P(CapsWordBothShifts, PressLRRL) {      run_one_scan_loop();      right_shift.press(); -    if (left_shift.code == LSFT_T(KC_A) || left_shift.code == KC_LSPO) { +    if (left_shift.code == LSFT_T(KC_A) || left_shift.code == QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN) {          idle_for(TAPPING_TERM);      }      run_one_scan_loop(); @@ -481,7 +481,7 @@ INSTANTIATE_TEST_CASE_P(          CapsWordBothShiftsParams{              "OneshotShifts", OSM(MOD_LSFT), OSM(MOD_RSFT)},          CapsWordBothShiftsParams{ -            "SpaceCadetShifts", KC_LSPO, KC_RSPC}, +            "SpaceCadetShifts", SC_LSPO, SC_RSPC},          CapsWordBothShiftsParams{              "ModTapShifts", LSFT_T(KC_A), RSFT_T(KC_B)}          ), diff --git a/tests/tap_dance/tap_dance_layers/config.h b/tests/tap_dance/tap_dance_layers/config.h new file mode 100644 index 0000000000..32a19a8c68 --- /dev/null +++ b/tests/tap_dance/tap_dance_layers/config.h @@ -0,0 +1,6 @@ +// Copyright 2022 Sergey Vlasov (@sigprof) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" diff --git a/tests/tap_dance/tap_dance_layers/tap_dance_defs.c b/tests/tap_dance/tap_dance_layers/tap_dance_defs.c new file mode 100644 index 0000000000..5ec900c041 --- /dev/null +++ b/tests/tap_dance/tap_dance_layers/tap_dance_defs.c @@ -0,0 +1,97 @@ +// Copyright 2022 Sergey Vlasov (@sigprof) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "tap_dance_defs.h" + +// Implement custom keycodes which are used to check that the layer switching +// behaves properly. +bool process_record_user(uint16_t keycode, keyrecord_t *record) { +    switch (keycode) { +        case FAST_AB: +        case SLOW_AB: +            if (record->event.pressed) { +                tap_code(KC_A); +            } else { +                tap_code(KC_B); +            } +            return keycode == SLOW_AB; +        case FAST_CD: +        case SLOW_CD: +            if (record->event.pressed) { +                tap_code(KC_C); +            } else { +                tap_code(KC_D); +            } +            return keycode == SLOW_CD; +    } +    return true; +} + +// Implement a custom tap dance with the following behavior: +// - single tap: KC_APP +// - single hold: MO(1) +// - double tap/hold: KC_RCTL +// (The single tap and hold actions are mostly equivalent to LT(1, KC_APP).) + +enum lt_app_state { +    LTA_NONE, +    LTA_SINGLE_TAP, +    LTA_SINGLE_HOLD, +    LTA_DOUBLE_HOLD, +}; + +static enum lt_app_state saved_lt_app_state; + +static enum lt_app_state get_lt_app_state(qk_tap_dance_state_t *state) { +    if (state->count == 1) { +        if (!state->pressed) { +            return LTA_SINGLE_TAP; +        } else { +            return LTA_SINGLE_HOLD; +        } +    } else if (state->count == 2) { +        return LTA_DOUBLE_HOLD; +    } else { +        return LTA_NONE; +    } +} + +static void lt_app_finished(qk_tap_dance_state_t *state, void *user_data) { +    saved_lt_app_state = get_lt_app_state(state); +    switch (saved_lt_app_state) { +        case LTA_NONE: +            break; +        case LTA_SINGLE_TAP: +            register_code(KC_APP); +            break; +        case LTA_SINGLE_HOLD: +            layer_on(1); +            break; +        case LTA_DOUBLE_HOLD: +            register_code(KC_RCTL); +            break; +    } +} + +static void lt_app_reset(qk_tap_dance_state_t *state, void *user_data) { +    switch (saved_lt_app_state) { +        case LTA_NONE: +            break; +        case LTA_SINGLE_TAP: +            unregister_code(KC_APP); +            break; +        case LTA_SINGLE_HOLD: +            layer_off(1); +            break; +        case LTA_DOUBLE_HOLD: +            unregister_code(KC_RCTL); +            break; +    } +} + +qk_tap_dance_action_t tap_dance_actions[] = { +    [TD_L_MOVE] = ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1), +    [TD_L_TOGG] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1), +    [TD_LT_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lt_app_finished, lt_app_reset), +}; diff --git a/tests/tap_dance/tap_dance_layers/tap_dance_defs.h b/tests/tap_dance/tap_dance_layers/tap_dance_defs.h new file mode 100644 index 0000000000..37cab0c2cb --- /dev/null +++ b/tests/tap_dance/tap_dance_layers/tap_dance_defs.h @@ -0,0 +1,29 @@ +// Copyright 2022 Sergey Vlasov (@sigprof) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +enum custom_keycodes { +    // (FAST|SLOW)_xy = tap KC_x on press, tap KC_y on release.  For FAST_xy +    // process_record_user() returns false to stop processing early; for +    // SLOW_xy process_record_user() returns true, therefore all other key +    // handlers are invoked. +    FAST_AB = SAFE_RANGE, +    FAST_CD, +    SLOW_AB, +    SLOW_CD, +}; + +enum tap_dance_ids { +    TD_L_MOVE, // ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1) +    TD_L_TOGG, // ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1) +    TD_LT_APP, // similar to LT(1, KC_APP) with KC_RCTL on tap+hold or double tap +}; + +#ifdef __cplusplus +} +#endif diff --git a/tests/tap_dance/tap_dance_layers/test.mk b/tests/tap_dance/tap_dance_layers/test.mk new file mode 100644 index 0000000000..b4cdc9b088 --- /dev/null +++ b/tests/tap_dance/tap_dance_layers/test.mk @@ -0,0 +1,10 @@ +# Copyright 2022 Sergey Vlasov (@sigprof) +# SPDX-License-Identifier: GPL-2.0-or-later + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- + +TAP_DANCE_ENABLE = yes + +SRC += tap_dance_defs.c diff --git a/tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp b/tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp new file mode 100644 index 0000000000..8b736b19c6 --- /dev/null +++ b/tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp @@ -0,0 +1,717 @@ +// Copyright 2022 Sergey Vlasov (@sigprof) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_keymap_key.hpp" +#include "tap_dance_defs.h" + +using testing::_; +using testing::InSequence; + +struct TapDanceKeyParams { +    std::string name;                  // Tap dance name (part of test name) +    uint16_t    keycode;               // Tap dance keycode (TD(n)) +    uint16_t    expect_on_tap;         // Keycode for single tap +    uint16_t    expect_on_hold;        // Keycode for single hold (may be MO(1)) +    uint16_t    expect_on_double_tap;  // Keycode for double tap (may be MO(1)) +    uint16_t    expect_on_double_hold; // Keycode for double hold (may be MO(1)) +}; + +struct OtherKeyLayerParams { +    uint16_t keycode;           // Keycode in the keymap +    uint16_t expect_on_press;   // Keycode to expect on press +    uint16_t expect_on_release; // Keycode to expect on release (may be KC_NO if none) +}; + +struct OtherKeyParams { +    std::string         name; // Other key name (part of test name) +    OtherKeyLayerParams l0;   // Keycodes for layer 0 +    OtherKeyLayerParams l1;   // Keycodes for layer 1 +}; + +typedef std::tuple<TapDanceKeyParams, OtherKeyParams> TapDanceLayersParams; + +class TapDanceLayers : public ::testing::WithParamInterface<TapDanceLayersParams>, public TestFixture { +   protected: +    TapDanceKeyParams tap_dance; +    OtherKeyParams    other_key; + +    std::unique_ptr<KeymapKey> key_td, key_td_l1, key_other, key_other_l1; + +    void SetUp() override { +        std::tie(tap_dance, other_key) = GetParam(); + +        key_td       = std::make_unique<KeymapKey>(0, 1, 0, tap_dance.keycode); +        key_td_l1    = std::make_unique<KeymapKey>(1, 1, 0, KC_TRNS); +        key_other    = std::make_unique<KeymapKey>(0, 2, 0, other_key.l0.keycode); +        key_other_l1 = std::make_unique<KeymapKey>(1, 2, 0, other_key.l1.keycode); + +        set_keymap({*key_td, *key_td_l1, *key_other, *key_other_l1}); +    } +}; + +static const TapDanceKeyParams tap_dance_keys[] = { +    TapDanceKeyParams{ +        "LayerMove", +        TD(TD_L_MOVE), +        KC_APP, +        KC_APP, +        MO(1), +        MO(1), +    }, +    TapDanceKeyParams{ +        "LayerToggle", +        TD(TD_L_TOGG), +        KC_APP, +        KC_APP, +        MO(1), +        MO(1), +    }, +    TapDanceKeyParams{ +        "CustomLT", +        TD(TD_LT_APP), +        KC_APP, +        MO(1), +        KC_RCTL, +        KC_RCTL, +    }, +}; + +static const OtherKeyParams other_keys[] = { +    OtherKeyParams{ +        "Builtin", +        OtherKeyLayerParams{KC_A, KC_A, KC_NO}, +        OtherKeyLayerParams{KC_B, KC_B, KC_NO}, +    }, +    OtherKeyParams{ +        "CustomFast", +        OtherKeyLayerParams{FAST_AB, KC_A, KC_B}, +        OtherKeyLayerParams{FAST_CD, KC_C, KC_D}, +    }, +    OtherKeyParams{ +        "CustomSlow", +        OtherKeyLayerParams{SLOW_AB, KC_A, KC_B}, +        OtherKeyLayerParams{SLOW_CD, KC_C, KC_D}, +    }, +}; + +// clang-format off +INSTANTIATE_TEST_CASE_P( +    Layers, +    TapDanceLayers, +    ::testing::Combine( +        ::testing::ValuesIn(tap_dance_keys), +        ::testing::ValuesIn(other_keys) +    ), +    [](const ::testing::TestParamInfo<TapDanceLayersParams>& info) { +        return std::get<0>(info.param).name + std::get<1>(info.param).name; +    } +); +// clang-format on + +// Test single tap of the tap dance key with tapping term delay after the tap. +TEST_P(TapDanceLayers, SingleTap) { +    TestDriver driver; +    InSequence s; + +    // The tap of the tap dance key does not result in sending a report +    // immediately. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); + +    // After the tapping term expires, a tap event for the single tap keycode +    // is generated. +    idle_for(TAPPING_TERM - 1); +    EXPECT_REPORT(driver, (tap_dance.expect_on_tap)); +    EXPECT_EMPTY_REPORT(driver); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 0 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the other key produces the reports for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test single tap of the tap dance key without a delay between the tap dance +// key and the other key. +TEST_P(TapDanceLayers, SingleTapFast) { +    TestDriver driver; +    InSequence s; + +    // The tap of the tap dance key does not result in sending a report +    // immediately. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); + +    // A quick press of the other key causes the tap event for the tap dance to +    // be sent before the press event for the other key, and the layer 0 +    // mapping is used for the other key. +    EXPECT_REPORT(driver, (tap_dance.expect_on_tap)); +    EXPECT_EMPTY_REPORT(driver); +    EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the other key produces the reports for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test single hold of the tap dance key with tapping term delay after the hold +// (test variant for tap dances which switch the layer on hold). +TEST_P(TapDanceLayers, SingleHoldLayer) { +    if (tap_dance.expect_on_hold != MO(1)) { +        // Do nothing - the SingleHoldKeycode test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the hold of the tap dance key. +    EXPECT_NO_REPORT(driver); +    key_td->press(); +    run_one_scan_loop(); + +    // After the tapping term expires, the tap dance finishes and switches the +    // layer, but does not send a report. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 1 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l1.expect_on_press)); +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key does not produce a report. +    EXPECT_NO_REPORT(driver); +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 1 mapping of +    // that key. +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l1.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test single hold of the tap dance key with tapping term delay after the hold +// (test variant for tap dances which send a keycode on single hold). +TEST_P(TapDanceLayers, SingleHoldKeycode) { +    if (tap_dance.expect_on_hold == MO(1)) { +        // Do nothing - the SingleHoldLayer test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the hold of the tap dance key. +    EXPECT_NO_REPORT(driver); +    key_td->press(); +    run_one_scan_loop(); + +    // After the tapping term expires, the tap dance sends the report with the +    // hold keycode. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    EXPECT_REPORT(driver, (tap_dance.expect_on_hold)); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 0 mapping of +    // that key. +    EXPECT_REPORT(driver, (tap_dance.expect_on_hold, other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (tap_dance.expect_on_hold)); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key sends the release report for the +    // corresponding hold keycode. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } else { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    } +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the reports for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test single hold of the tap dance key without tapping term delay after the +// hold (test variant for tap dances which switch the layer on hold). +TEST_P(TapDanceLayers, SingleHoldFastLayer) { +    if (tap_dance.expect_on_hold != MO(1)) { +        // Do nothing - the SingleHoldFastKeycode test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the hold of the tap dance key. +    EXPECT_NO_REPORT(driver); +    key_td->press(); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 1 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l1.expect_on_press)); +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key does not produce a report. +    EXPECT_NO_REPORT(driver); +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the reports for the layer 1 mapping of +    // that key. +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l1.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test single hold of the tap dance key without tapping term delay after the hold +// (test variant for tap dances which send a keycode on single hold). +TEST_P(TapDanceLayers, SingleHoldFastKeycode) { +    if (tap_dance.expect_on_hold == MO(1)) { +        // Do nothing - the SingleHoldFastLayer test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the hold of the tap dance key. +    EXPECT_NO_REPORT(driver); +    key_td->press(); +    run_one_scan_loop(); + +    // Pressing the other key produces first the report for the tap dance hold +    // keycode, and then the reports for the layer 0 mapping of the other key. +    EXPECT_REPORT(driver, (tap_dance.expect_on_hold)); +    EXPECT_REPORT(driver, (tap_dance.expect_on_hold, other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (tap_dance.expect_on_hold)); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key sends a release report for the corresponding +    // hold keycode. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } else { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    } +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double tap of the tap dance key with tapping term delay after the hold +// (test variant for tap dances which switch the layer on double tap). +TEST_P(TapDanceLayers, DoubleTapLayer) { +    if (tap_dance.expect_on_double_tap != MO(1)) { +        // Do nothing - the DoubleTapKeycode test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double tap of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    tap_key(*key_td); + +    // After the tapping term this tap dance does not send a report too. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 1 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l1.expect_on_press)); +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 1 mapping of +    // that key. +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l1.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double tap of the tap dance key with tapping term delay after the hold +// (test variant for tap dances which send a keycode on double tap). +TEST_P(TapDanceLayers, DoubleTapKeycode) { +    if (tap_dance.expect_on_double_tap == MO(1)) { +        // Do nothing - the DoubleTapLayer test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double tap of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    tap_key(*key_td); + +    // After the tapping term this tap dance sends the double tap keycode. +    idle_for(TAPPING_TERM - 1); +    EXPECT_REPORT(driver, (tap_dance.expect_on_double_tap)); +    EXPECT_EMPTY_REPORT(driver); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 0 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double tap of the tap dance key without tapping term delay after the +// hold (test variant for tap dances which switch the layer on double tap). +TEST_P(TapDanceLayers, DoubleTapFastLayer) { +    if (tap_dance.expect_on_double_tap != MO(1)) { +        // Do nothing - the DoubleTapFastKeycode test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double tap of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    tap_key(*key_td); + +    // Pressing the other key produces the reports for the layer 1 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l1.expect_on_press)); +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 1 mapping of +    // that key. +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l1.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double tap of the tap dance key without tapping term delay after the +// hold (test variant for tap dances which send a keycode on double tap). +TEST_P(TapDanceLayers, DoubleTapFastKeycode) { +    if (tap_dance.expect_on_double_tap == MO(1)) { +        // Do nothing - the DoubleTapFastLayer test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double tap of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    tap_key(*key_td); + +    // Pressing the other key produces first the report for the tap dance +    // double tap keycode, and then the reports for the layer 0 mapping of the +    // other key. +    EXPECT_REPORT(driver, (tap_dance.expect_on_double_tap)); +    EXPECT_EMPTY_REPORT(driver); +    EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double hold of the tap dance key with tapping term delay after the hold +// (test variant for tap dances which switch the layer on double hold). +TEST_P(TapDanceLayers, DoubleHoldLayer) { +    if (tap_dance.expect_on_double_hold != MO(1)) { +        // Do nothing - the DoubleHoldKeycode test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double hold of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    key_td->press(); +    run_one_scan_loop(); + +    // After the tapping term expires, the tap dance finishes and switches the +    // layer, but does not send a report. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 1 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l1.expect_on_press)); +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key does not produce a report. +    EXPECT_NO_REPORT(driver); +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 1 mapping of +    // that key. +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l1.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double hold of the tap dance key with tapping term delay after the hold +// (test variant for tap dances which send a keycode on double hold). +TEST_P(TapDanceLayers, DoubleHoldKeycode) { +    if (tap_dance.expect_on_double_hold == MO(1)) { +        // Do nothing - the DoubleHoldLayer test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double hold of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    key_td->press(); +    run_one_scan_loop(); + +    // After the tapping term expires, the tap dance sends the report with the +    // double hold keycode. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold)); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 0 mapping of +    // that key. +    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold, other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold)); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key sends the release report for the +    // corresponding double hold keycode. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } else { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    } +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the reports for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double hold of the tap dance key without tapping term delay after the +// hold (test variant for tap dances which switch the layer on double hold). +TEST_P(TapDanceLayers, DoubleHoldFastLayer) { +    if (tap_dance.expect_on_double_hold != MO(1)) { +        // Do nothing - the DoubleHoldFastKeycode test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double hold of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    key_td->press(); +    run_one_scan_loop(); + +    // Pressing the other key produces the reports for the layer 1 mapping of +    // that key. +    EXPECT_REPORT(driver, (other_key.l1.expect_on_press)); +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key does not produce a report. +    EXPECT_NO_REPORT(driver); +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the reports for the layer 1 mapping of +    // that key. +    if (other_key.l1.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l1.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} + +// Test double hold of the tap dance key without tapping term delay after the hold +// (test variant for tap dances which send a keycode on double hold). +TEST_P(TapDanceLayers, DoubleHoldFastKeycode) { +    if (tap_dance.expect_on_double_hold == MO(1)) { +        // Do nothing - the DoubleHoldFastLayer test would run instead. +        return; +    } + +    TestDriver driver; +    InSequence s; + +    // No report gets sent immediately after the double hold of the tap dance +    // key. +    EXPECT_NO_REPORT(driver); +    tap_key(*key_td); +    key_td->press(); +    run_one_scan_loop(); + +    // Pressing the other key produces first the report for the tap dance +    // double hold keycode, and then the reports for the layer 0 mapping of the +    // other key. +    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold)); +    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold, other_key.l0.expect_on_press)); +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold)); +    } +    key_other->press(); +    run_one_scan_loop(); + +    // Releasing the tap dance key sends a release report for the corresponding +    // double hold keycode. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_EMPTY_REPORT(driver); +    } else { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_press)); +    } +    key_td->release(); +    run_one_scan_loop(); + +    // Releasing the other key produces the report for the layer 0 mapping of +    // that key. +    if (other_key.l0.expect_on_release != KC_NO) { +        EXPECT_REPORT(driver, (other_key.l0.expect_on_release)); +    } +    EXPECT_EMPTY_REPORT(driver); +    key_other->release(); +    run_one_scan_loop(); +} diff --git a/tests/tap_hold_configurations/hold_on_other_key_press/config.h b/tests/tap_hold_configurations/hold_on_other_key_press/config.h new file mode 100644 index 0000000000..98a72ec81f --- /dev/null +++ b/tests/tap_hold_configurations/hold_on_other_key_press/config.h @@ -0,0 +1,20 @@ +/* Copyright 2022 Vladislav Kucheriavykh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "test_common.h" +#define HOLD_ON_OTHER_KEY_PRESS diff --git a/tests/tap_hold_configurations/hold_on_other_key_press/test.mk b/tests/tap_hold_configurations/hold_on_other_key_press/test.mk new file mode 100644 index 0000000000..6b5968df16 --- /dev/null +++ b/tests/tap_hold_configurations/hold_on_other_key_press/test.mk @@ -0,0 +1,18 @@ +# Copyright 2022 Vladislav Kucheriavykh +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- diff --git a/tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp b/tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp new file mode 100644 index 0000000000..e77c756624 --- /dev/null +++ b/tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp @@ -0,0 +1,423 @@ +/* Copyright 2022 Vladislav Kucheriavykh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class HoldOnOtherKeyPress : public TestFixture {}; + +TEST_F(HoldOnOtherKeyPress, short_distinct_taps_of_mod_tap_key_and_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto       regular_key      = KeymapKey(0, 2, 0, KC_A); + +    set_keymap({mod_tap_hold_key, regular_key}); + +    /* Press mod-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    mod_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release mod-tap-hold key. */ +    EXPECT_REPORT(driver, (KC_P)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key. */ +    EXPECT_REPORT(driver, (KC_A)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key. */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, long_distinct_taps_of_mod_tap_key_and_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto       regular_key      = KeymapKey(0, 2, 0, KC_A); + +    set_keymap({mod_tap_hold_key, regular_key}); + +    /* Press mod-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    mod_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Idle for tapping term of mod tap hold key. */ +    EXPECT_REPORT(driver, (KC_LSFT)); +    idle_for(TAPPING_TERM + 1); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release mod-tap-hold key. */ +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key. */ +    EXPECT_REPORT(driver, (KC_A)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key. */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, short_distinct_taps_of_layer_tap_key_and_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto       regular_key        = KeymapKey(0, 2, 0, KC_A); +    auto       layer_key          = KeymapKey(0, 2, 0, KC_B); + +    set_keymap({layer_tap_hold_key, regular_key}); + +    /* Press layer-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release layer-tap-hold key. */ +    EXPECT_REPORT(driver, (KC_P)); +    EXPECT_EMPTY_REPORT(driver); +    layer_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key. */ +    EXPECT_REPORT(driver, (KC_A)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key. */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, long_distinct_taps_of_layer_tap_key_and_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); +    auto       regular_key        = KeymapKey(0, 2, 0, KC_A); +    auto       layer_key          = KeymapKey(0, 2, 0, KC_B); + +    set_keymap({layer_tap_hold_key, regular_key}); + +    /* Press layer-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Idle for tapping term of layer tap hold key. */ +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release layer-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key. */ +    EXPECT_REPORT(driver, (KC_A)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key. */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto       regular_key      = KeymapKey(0, 2, 0, KC_A); + +    set_keymap({mod_tap_hold_key, regular_key}); + +    /* Press mod-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    mod_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key. */ +    EXPECT_REPORT(driver, (KC_LSFT)); +    EXPECT_REPORT(driver, (KC_A, KC_LSFT)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key. */ +    EXPECT_REPORT(driver, (KC_LSFT)); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release mod-tap-hold key. */ +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Idle for tapping term of mod tap hold key. */ +    idle_for(TAPPING_TERM - 3); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) { +    TestDriver driver; +    InSequence s; +    auto       first_mod_tap_hold_key  = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto       second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); + +    set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key}); + +    /* Press first mod-tap-hold key */ +    EXPECT_NO_REPORT(driver); +    first_mod_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press second tap-hold key */ +    EXPECT_REPORT(driver, (KC_LSFT)); +    second_mod_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release second tap-hold key */ +    EXPECT_REPORT(driver, (KC_A, KC_LSFT)); +    EXPECT_REPORT(driver, (KC_LSFT)); +    second_mod_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release first mod-tap-hold key */ +    EXPECT_EMPTY_REPORT(driver); +    first_mod_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, tap_regular_key_while_layer_tap_key_is_held) { +    TestDriver driver; +    InSequence s; +    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); +    auto       regular_key        = KeymapKey(0, 2, 0, KC_A); +    auto       layer_key          = KeymapKey(1, 2, 0, KC_B); + +    set_keymap({layer_tap_hold_key, regular_key, layer_key}); + +    /* Press layer-tap-hold key */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key */ +    EXPECT_REPORT(driver, (KC_B)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release layer-tap-hold key */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, nested_tap_of_layer_0_layer_tap_keys) { +    TestDriver driver; +    InSequence s; +    /* The keys are layer-taps on layer 0 but regular keys on layer 1 */ +    auto first_layer_tap_key  = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto second_layer_tap_key = KeymapKey(0, 2, 0, LT(1, KC_P)); +    auto first_key_on_layer   = KeymapKey(1, 1, 0, KC_B); +    auto second_key_on_layer  = KeymapKey(1, 2, 0, KC_Q); + +    set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer}); + +    /* Press first layer-tap key */ +    EXPECT_NO_REPORT(driver); +    first_layer_tap_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press second layer-tap key */ +    EXPECT_REPORT(driver, (KC_Q)); +    second_layer_tap_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release second layer-tap key */ +    EXPECT_EMPTY_REPORT(driver); +    second_layer_tap_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release first layer-tap key */ +    EXPECT_NO_REPORT(driver); +    first_layer_tap_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, nested_tap_of_layer_tap_keys) { +    TestDriver driver; +    InSequence s; +    /* The keys are layer-taps on all layers */ +    auto first_key_layer_0  = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto second_key_layer_0 = KeymapKey(0, 2, 0, LT(1, KC_P)); +    auto first_key_layer_1  = KeymapKey(1, 1, 0, LT(2, KC_B)); +    auto second_key_layer_1 = KeymapKey(1, 2, 0, LT(2, KC_Q)); +    auto first_key_layer_2  = KeymapKey(2, 1, 0, KC_TRNS); +    auto second_key_layer_2 = KeymapKey(2, 2, 0, KC_TRNS); + +    set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2}); + +    /* Press first layer-tap key */ +    EXPECT_NO_REPORT(driver); +    first_key_layer_0.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press second layer-tap key */ +    EXPECT_NO_REPORT(driver); +    second_key_layer_0.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release second layer-tap key */ +    EXPECT_REPORT(driver, (KC_Q)); +    EXPECT_EMPTY_REPORT(driver); +    second_key_layer_0.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release first layer-tap key */ +    EXPECT_NO_REPORT(driver); +    first_key_layer_0.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, roll_mod_tap_key_with_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto       regular_key      = KeymapKey(0, 2, 0, KC_A); + +    set_keymap({mod_tap_hold_key, regular_key}); + +    /* Press mod-tap-hold key. */ +    EXPECT_NO_REPORT(driver); +    mod_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key. */ +    EXPECT_REPORT(driver, (KC_LSFT)); +    EXPECT_REPORT(driver, (KC_A, KC_LSFT)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release mod-tap-hold key. */ +    EXPECT_REPORT(driver, (KC_A)); +    mod_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key. */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +TEST_F(HoldOnOtherKeyPress, roll_layer_tap_key_with_regular_key) { +    TestDriver driver; +    InSequence s; + +    auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); +    auto regular_key        = KeymapKey(0, 2, 0, KC_A); +    auto layer_key          = KeymapKey(1, 2, 0, KC_B); + +    set_keymap({layer_tap_hold_key, regular_key, layer_key}); + +    /* Press layer-tap-hold key */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Press regular key */ +    EXPECT_REPORT(driver, (KC_B)); +    regular_key.press(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release layer-tap-hold key */ +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); + +    /* Release regular key */ +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} diff --git a/tests/test_common/main.cpp b/tests/test_common/main.cpp index 3e4b4c0f95..0f4e320b07 100644 --- a/tests/test_common/main.cpp +++ b/tests/test_common/main.cpp @@ -5,7 +5,7 @@ extern "C" {  #include "debug.h"  int8_t sendchar(uint8_t c) { -    fprintf(stderr, "%c", c); +    fprintf(stdout, "%c", c);      return 0;  } diff --git a/tests/test_common/test_driver.cpp b/tests/test_common/test_driver.cpp index 86644ab6bd..f1c52cb7b6 100644 --- a/tests/test_common/test_driver.cpp +++ b/tests/test_common/test_driver.cpp @@ -31,7 +31,7 @@ uint8_t hex_digit_to_keycode(uint8_t digit) {  }  } // namespace -TestDriver::TestDriver() : m_driver{&TestDriver::keyboard_leds, &TestDriver::send_keyboard, &TestDriver::send_mouse, &TestDriver::send_system, &TestDriver::send_consumer} { +TestDriver::TestDriver() : m_driver{&TestDriver::keyboard_leds, &TestDriver::send_keyboard, &TestDriver::send_mouse, &TestDriver::send_extra} {      host_set_driver(&m_driver);      m_this = this;  } @@ -53,12 +53,8 @@ void TestDriver::send_mouse(report_mouse_t* report) {      m_this->send_mouse_mock(*report);  } -void TestDriver::send_system(uint16_t data) { -    m_this->send_system_mock(data); -} - -void TestDriver::send_consumer(uint16_t data) { -    m_this->send_consumer(data); +void TestDriver::send_extra(report_extra_t* report) { +    m_this->send_extra_mock(*report);  }  namespace internal { diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp index b58cfd1ebc..982aec6c83 100644 --- a/tests/test_common/test_driver.hpp +++ b/tests/test_common/test_driver.hpp @@ -32,15 +32,13 @@ class TestDriver {      MOCK_METHOD1(send_keyboard_mock, void(report_keyboard_t&));      MOCK_METHOD1(send_mouse_mock, void(report_mouse_t&)); -    MOCK_METHOD1(send_system_mock, void(uint16_t)); -    MOCK_METHOD1(send_consumer_mock, void(uint16_t)); +    MOCK_METHOD1(send_extra_mock, void(report_extra_t&));     private:      static uint8_t     keyboard_leds(void);      static void        send_keyboard(report_keyboard_t* report);      static void        send_mouse(report_mouse_t* report); -    static void        send_system(uint16_t data); -    static void        send_consumer(uint16_t data); +    static void        send_extra(report_extra_t* report);      host_driver_t      m_driver;      uint8_t            m_leds = 0;      static TestDriver* m_this; @@ -66,7 +64,7 @@ class TestDriver {  #define EXPECT_REPORT(driver, report) EXPECT_CALL((driver), send_keyboard_mock(KeyboardReport report))  /** - * @brief Sets gmock expectation that Unicode `code_point` is sent with UC_LNX input + * @brief Sets gmock expectation that Unicode `code_point` is sent with UNICODE_MODE_LINUX input   * mode. For instance for U+2013,   *   *   EXPECT_UNICODE(driver, 0x2013); | 
