// 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 TapDanceLayersParams; class TapDanceLayers : public ::testing::WithParamInterface, public TestFixture { protected: TapDanceKeyParams tap_dance; OtherKeyParams other_key; std::unique_ptr 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(0, 1, 0, tap_dance.keycode); key_td_l1 = std::make_unique(1, 1, 0, KC_TRNS); key_other = std::make_unique(0, 2, 0, other_key.l0.keycode); key_other_l1 = std::make_unique(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& 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(); }