~ruther/qmk_firmware

dd94877ec6d2ee5c4cdb0e71287abd76585b0268 — Isaac Elenbaas 1 year, 7 months ago 1fb02d5
Improve test invocation, fix Retro Shift bugs, and add Auto+Retro Shift test cases (#15889)

48 files changed, 2584 insertions(+), 89 deletions(-)

M Makefile
M builddefs/build_full_test.mk
M builddefs/build_test.mk
M docs/feature_auto_shift.md
M docs/unit_testing.md
M quantum/action.c
M quantum/action_tapping.c
M quantum/process_keycode/process_auto_shift.c
M quantum/process_keycode/process_auto_shift.h
A tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/config.h
A tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test.mk
A tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test_auto_shift.cpp
A tests/auto_shift/auto_shift_repeat/config.h
A tests/auto_shift/auto_shift_repeat/test.mk
A tests/auto_shift/auto_shift_repeat/test_auto_shift.cpp
M tests/auto_shift/config.h
A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/config.h
A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/config.h
A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test.mk
A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test_retro_shift.cpp
A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test.mk
A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test_retro_shift.cpp
A tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/config.h
A tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test.mk
A tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test_retro_shift.cpp
A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/config.h
A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test.mk
A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test_retro_shift.cpp
A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/config.h
A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test.mk
A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test_retro_shift.cpp
M tests/auto_shift/test.mk
M tests/basic/config.h
M tests/basic/test.mk
A tests/caps_word/auto_shift/config.h
R tests/caps_word/{caps_word_autoshift/config.h => auto_shift/retro_shift/config.h}
R tests/caps_word/{caps_word_autoshift/test.mk => auto_shift/retro_shift/test.mk}
R tests/caps_word/{caps_word_autoshift/test_caps_word_autoshift.cpp => auto_shift/retro_shift/test_caps_word_retroshift.cpp}
A tests/caps_word/auto_shift/test.mk
A tests/caps_word/auto_shift/test_caps_word_autoshift.cpp
R tests/caps_word/{caps_word_unicodemap/config.h => unicodemap/config.h}
R tests/caps_word/{caps_word_unicodemap/test.mk => unicodemap/test.mk}
R tests/caps_word/{caps_word_unicodemap/test_caps_word_unicodemap.cpp => unicodemap/test_caps_word_unicodemap.cpp}
M tests/tap_hold_configurations/default_mod_tap/test.mk
M tests/tap_hold_configurations/permissive_hold/config.h
M tests/test_common/build.mk
M tests/test_common/main.cpp
M tests/test_common/test_common.h
M Makefile => Makefile +17 -9
@@ 300,17 300,18 @@ endef
define BUILD_TEST
    TEST_PATH := $1
    TEST_NAME := $$(notdir $$(TEST_PATH))
    TEST_FULL_NAME := $$(subst /,_,$$(patsubst $$(ROOT_DIR)tests/%,%,$$(TEST_PATH)))
    MAKE_TARGET := $2
    COMMAND := $1
    MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f $(BUILDDEFS_PATH)/build_test.mk $$(MAKE_TARGET)
    MAKE_VARS := TEST=$$(TEST_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS="$$(FULL_TESTS)"
    MAKE_VARS := TEST=$$(TEST_NAME) TEST_OUTPUT=$$(TEST_FULL_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS="$$(FULL_TESTS)"
    MAKE_MSG := $$(MSG_MAKE_TEST)
    $$(eval $$(call BUILD))
    ifneq ($$(MAKE_TARGET),clean)
        TEST_EXECUTABLE := $$(TEST_OUTPUT_DIR)/$$(TEST_NAME).elf
        TESTS += $$(TEST_NAME)
        TEST_EXECUTABLE := $$(TEST_OUTPUT_DIR)/$$(TEST_FULL_NAME).elf
        TESTS += $$(TEST_FULL_NAME)
        TEST_MSG := $$(MSG_TEST)
        $$(TEST_NAME)_COMMAND := \
        $$(TEST_FULL_NAME)_COMMAND := \
            printf "$$(TEST_MSG)\n"; \
            $$(TEST_EXECUTABLE); \
            if [ $$$$? -gt 0 ]; \


@@ 322,15 323,22 @@ endef

define PARSE_TEST
    TESTS :=
    TEST_NAME := $$(firstword $$(subst :, ,$$(RULE)))
    TEST_TARGET := $$(subst $$(TEST_NAME),,$$(subst $$(TEST_NAME):,,$$(RULE)))
    # list of possible targets, colon-delimited, to reassign to MAKE_TARGET and remove
    TARGETS := :clean:
    ifneq (,$$(findstring :$$(lastword $$(subst :, ,$$(RULE))):, $$(TARGETS)))
        MAKE_TARGET := $$(lastword $$(subst :, ,$$(RULE)))
        TEST_SUBPATH := $$(subst $$(eval) ,/,$$(wordlist 2, $$(words $$(subst :, ,$$(RULE))), _ $$(subst :, ,$$(RULE))))
    else
        MAKE_TARGET :=
        TEST_SUBPATH := $$(subst :,/,$$(RULE))
    endif
    include $(BUILDDEFS_PATH)/testlist.mk
    ifeq ($$(TEST_NAME),all)
    ifeq ($$(RULE),all)
        MATCHED_TESTS := $$(TEST_LIST)
    else
        MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring x$$(TEST_NAME)x, x$$(notdir $$(TEST))x), $$(TEST),))
        MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring /$$(TEST_SUBPATH)/, $$(patsubst %,%/,$$(TEST))), $$(TEST),))
    endif
    $$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))
    $$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(MAKE_TARGET))))
endef



M builddefs/build_full_test.mk => builddefs/build_full_test.mk +4 -4
@@ 13,10 13,10 @@
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

$(TEST)_INC := \
$(TEST_OUTPUT)_INC := \
	tests/test_common/common_config.h

$(TEST)_SRC := \
$(TEST_OUTPUT)_SRC := \
	$(QUANTUM_SRC) \
	$(SRC) \
	$(QUANTUM_PATH)/keymap_introspection.c \


@@ 30,8 30,8 @@ $(TEST)_SRC := \
	tests/test_common/test_logger.cpp \
	$(patsubst $(ROOTDIR)/%,%,$(wildcard $(TEST_PATH)/*.cpp))

$(TEST)_DEFS := $(OPT_DEFS) "-DKEYMAP_C=\"keymap.c\""
$(TEST_OUTPUT)_DEFS := $(OPT_DEFS) "-DKEYMAP_C=\"keymap.c\""

$(TEST)_CONFIG := $(TEST_PATH)/config.h
$(TEST_OUTPUT)_CONFIG := $(TEST_PATH)/config.h

VPATH += $(TOP_DIR)/tests/test_common

M builddefs/build_test.mk => builddefs/build_test.mk +8 -8
@@ 9,13 9,13 @@ OPT = g
include paths.mk
include $(BUILDDEFS_PATH)/message.mk

TARGET=test/$(TEST)
TARGET=test/$(TEST_OUTPUT)

GTEST_OUTPUT = $(BUILD_DIR)/gtest

TEST_OBJ = $(BUILD_DIR)/test_obj

OUTPUTS := $(TEST_OBJ)/$(TEST) $(GTEST_OUTPUT)
OUTPUTS := $(TEST_OBJ)/$(TEST_OUTPUT) $(GTEST_OUTPUT)

GTEST_INC := \
	$(LIB_PATH)/googletest/googletest/include \


@@ 71,18 71,18 @@ ifneq ($(filter $(FULL_TESTS),$(TEST)),)
include $(BUILDDEFS_PATH)/build_full_test.mk
endif

$(TEST)_SRC += \
$(TEST_OUTPUT)_SRC += \
	tests/test_common/main.cpp \
	$(QUANTUM_PATH)/logging/print.c

ifneq ($(strip $(INTROSPECTION_KEYMAP_C)),)
$(TEST)_DEFS += -DINTROSPECTION_KEYMAP_C=\"$(strip $(INTROSPECTION_KEYMAP_C))\"
$(TEST_OUTPUT)_DEFS += -DINTROSPECTION_KEYMAP_C=\"$(strip $(INTROSPECTION_KEYMAP_C))\"
endif

$(TEST_OBJ)/$(TEST)_SRC := $($(TEST)_SRC)
$(TEST_OBJ)/$(TEST)_INC := $($(TEST)_INC) $(VPATH) $(GTEST_INC)
$(TEST_OBJ)/$(TEST)_DEFS := $($(TEST)_DEFS)
$(TEST_OBJ)/$(TEST)_CONFIG := $($(TEST)_CONFIG)
$(TEST_OBJ)/$(TEST_OUTPUT)_SRC := $($(TEST_OUTPUT)_SRC)
$(TEST_OBJ)/$(TEST_OUTPUT)_INC := $($(TEST_OUTPUT)_INC) $(VPATH) $(GTEST_INC)
$(TEST_OBJ)/$(TEST_OUTPUT)_DEFS := $($(TEST_OUTPUT)_DEFS)
$(TEST_OBJ)/$(TEST_OUTPUT)_CONFIG := $($(TEST_OUTPUT)_CONFIG)

include $(PLATFORM_PATH)/$(PLATFORM_KEY)/platform.mk
include $(BUILDDEFS_PATH)/common_rules.mk

M docs/feature_auto_shift.md => docs/feature_auto_shift.md +12 -6
@@ 180,18 180,18 @@ For more granular control, there is `get_auto_shifted_key`. The default function
bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    switch (keycode) {
#    ifndef NO_AUTO_SHIFT_ALPHA
        case KC_A ... KC_Z:
        case AUTO_SHIFT_ALPHA:
#    endif
#    ifndef NO_AUTO_SHIFT_NUMERIC
        case KC_1 ... KC_0:
        case AUTO_SHIFT_NUMERIC:
#    endif
#    ifndef NO_AUTO_SHIFT_SPECIAL
#    ifndef NO_AUTO_SHIFT_TAB
#        ifndef NO_AUTO_SHIFT_TAB
        case KC_TAB:
#    endif
#    ifndef NO_AUTO_SHIFT_SYMBOLS
#        endif
#        ifndef NO_AUTO_SHIFT_SYMBOLS
        case AUTO_SHIFT_SYMBOLS:
#    endif
#        endif
#    endif
#    ifdef AUTO_SHIFT_ENTER
        case KC_ENT:


@@ 310,10 310,16 @@ generating taps on release. For example:
#define RETRO_SHIFT 500
```

Without a value set, holds of any length without an interrupting key will produce the shifted value.

This value (if set) must be greater than one's `TAPPING_TERM`, as the key press
must be designated as a 'hold' by `process_tapping` before we send the modifier.
[Per-key tapping terms](tap_hold.md#tapping-term) can be used as a workaround.
There is no such limitation in regards to `AUTO_SHIFT_TIMEOUT` for normal keys.

**Note:** Tap Holds must be added to Auto Shift, see [here.](feature_auto_shift.md#auto-shift-per-key)
`IS_RETRO` may be helpful if one wants all Tap Holds retro shifted.

### Retro Shift and Tap Hold Configurations

Tap Hold Configurations work a little differently when using Retro Shift.

M docs/unit_testing.md => docs/unit_testing.md +3 -1
@@ 36,7 36,9 @@ Note how there's several different tests, each mocking out a separate part. Also

## Running the Tests

To run all the tests in the codebase, type `make test:all`. You can also run test matching a substring by typing `make test:matchingsubstring` Note that the tests are always compiled with the native compiler of your platform, so they are also run like any other program on your computer.
To run all the tests in the codebase, type `make test:all`. You can also run test matching a substring by typing `make test:matchingsubstring`. `matchingsubstring` can contain colons to be more specific; `make test:tap_hold_configurations` will run the `tap_hold_configurations` tests for all features while `make test:retro_shift:tap_hold_configurations` will run the `tap_hold_configurations` tests for only the Retro Shift feature.

Note that the tests are always compiled with the native compiler of your platform, so they are also run like any other program on your computer.

## Debugging the Tests


M quantum/action.c => quantum/action.c +1 -1
@@ 497,7 497,7 @@ void process_action(keyrecord_t *record, action_t action) {
                default:
                    if (event.pressed) {
                        if (tap_count > 0) {
#    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
#    ifdef HOLD_ON_OTHER_KEY_PRESS
                            if (
#        ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
                                get_hold_on_other_key_press(get_event_keycode(record->event, false), record) &&

M quantum/action_tapping.c => quantum/action_tapping.c +39 -39
@@ 116,25 116,26 @@ void action_tapping_process(keyrecord_t record) {
 * readable. The conditional definition of tapping_keycode and all the
 * conditional uses of it are hidden inside macros named TAP_...
 */
#    if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
#        define TAP_DEFINE_KEYCODE const uint16_t tapping_keycode = get_record_keycode(&tapping_key, false)
#    else
#        define TAP_DEFINE_KEYCODE
#    endif
#    define TAP_DEFINE_KEYCODE const uint16_t tapping_keycode = get_record_keycode(&tapping_key, false)

#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
#        ifdef RETRO_TAPPING_PER_KEY
#            define TAP_GET_RETRO_TAPPING get_retro_tapping(tapping_keycode, &tapping_key)
#            define TAP_GET_RETRO_TAPPING(keyp) get_auto_shifted_key(tapping_keycode, keyp) && get_retro_tapping(tapping_keycode, &tapping_key)
#        else
#            define TAP_GET_RETRO_TAPPING true
#            define TAP_GET_RETRO_TAPPING(keyp) get_auto_shifted_key(tapping_keycode, keyp)
#        endif
#        define MAYBE_RETRO_SHIFTING(ev) (TAP_GET_RETRO_TAPPING && (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16((ev).time, tapping_key.event.time) < (RETRO_SHIFT + 0))
/* Used to extend TAPPING_TERM:
 *     indefinitely if RETRO_SHIFT does not have a value
 *     to RETRO_SHIFT if RETRO_SHIFT is set
 * for possibly retro shifted keys.
 */
#        define MAYBE_RETRO_SHIFTING(ev, keyp) (get_auto_shifted_key(tapping_keycode, keyp) && TAP_GET_RETRO_TAPPING(keyp) && ((RETRO_SHIFT + 0) == 0 || TIMER_DIFF_16((ev).time, tapping_key.event.time) < (RETRO_SHIFT + 0)))
#        define TAP_IS_LT IS_QK_LAYER_TAP(tapping_keycode)
#        define TAP_IS_MT IS_QK_MOD_TAP(tapping_keycode)
#        define TAP_IS_RETRO IS_RETRO(tapping_keycode)
#    else
#        define TAP_GET_RETRO_TAPPING false
#        define MAYBE_RETRO_SHIFTING(ev) false
#        define TAP_GET_RETRO_TAPPING(keyp) false
#        define MAYBE_RETRO_SHIFTING(ev, kp) false
#        define TAP_IS_LT false
#        define TAP_IS_MT false
#        define TAP_IS_RETRO false


@@ 187,20 188,19 @@ bool process_tapping(keyrecord_t *keyp) {
        return true;
    }

#    if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
    TAP_DEFINE_KEYCODE;
#    endif

    // process "pressed" tapping key state
    if (tapping_key.event.pressed) {
        if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event)) {
        if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event, keyp)) {
            if (IS_NOEVENT(event)) {
                // early return for tick events
                return true;
            }
            if (tapping_key.tap.count == 0) {
                if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
                    retroshift_swap_times();
#    endif
                    // first tap!
                    ac_dprintf("Tapping: First tap(0->1).\n");
                    tapping_key.tap.count = 1;


@@ 218,28 218,12 @@ bool process_tapping(keyrecord_t *keyp) {
                 */
                // clang-format off
                else if (
                    !event.pressed && waiting_buffer_typed(event) &&
                    (
                        !event.pressed && waiting_buffer_typed(event) &&
                        TAP_GET_PERMISSIVE_HOLD
                    )
                    // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
                    // unnecessarily and fixes them for Layer Taps.
                    || (TAP_GET_RETRO_TAPPING &&
                        (
                            // Rolled over the two keys.
                            (tapping_key.tap.interrupted == true && (
                                (TAP_IS_LT && TAP_GET_HOLD_ON_OTHER_KEY_PRESS) ||
                                (TAP_IS_MT && TAP_GET_HOLD_ON_OTHER_KEY_PRESS)
                                )
                            )
                            // Makes Retro Shift ignore the default behavior of
                            // MTs and LTs on nested taps below TAPPING_TERM or RETRO_SHIFT
                            || (
                                TAP_IS_RETRO
                                && (event.key.col != tapping_key.event.key.col || event.key.row != tapping_key.event.key.row)
                                && !event.pressed && waiting_buffer_typed(event)
                            )
                        )
                        TAP_GET_PERMISSIVE_HOLD ||
                        // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
                        // unnecessarily and fixes them for Layer Taps.
                        TAP_GET_RETRO_TAPPING(keyp)
                    )
                ) {
                    // clang-format on


@@ 284,10 268,16 @@ bool process_tapping(keyrecord_t *keyp) {
                    process_record(keyp);
                    return true;
                } else {
                    // set interrupted flag when other key preesed during tapping
                    // set interrupted flag when other key pressed during tapping
                    if (event.pressed) {
                        tapping_key.tap.interrupted = true;
                        if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS) {
                        if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
                            // Auto Shift cannot evaluate this early
                            // Retro Shift uses the hold action for all nested taps even without HOLD_ON_OTHER_KEY_PRESS, so this is fine to skip
                            && !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp))
#    endif
                        ) {
                            ac_dprintf("Tapping: End. No tap. Interfered by pressed key\n");
                            process_record(&tapping_key);
                            tapping_key = (keyrecord_t){0};


@@ 332,6 322,9 @@ bool process_tapping(keyrecord_t *keyp) {
                    return true;
                } else {
                    ac_dprintf("Tapping: key event while last tap(>0).\n");
#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
                    retroshift_swap_times();
#    endif
                    process_record(keyp);
                    return true;
                }


@@ 388,7 381,7 @@ bool process_tapping(keyrecord_t *keyp) {
    }
    // process "released" tapping key state
    else {
        if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event)) {
        if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event, keyp)) {
            if (IS_NOEVENT(event)) {
                // early return for tick events
                return true;


@@ 506,9 499,16 @@ void waiting_buffer_scan_tap(void) {
        return;
    }

#    if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
    TAP_DEFINE_KEYCODE;
#    endif
    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
        keyrecord_t *candidate = &waiting_buffer[i];
        if (IS_EVENT(candidate->event) && KEYEQ(candidate->event.key, tapping_key.event.key) && !candidate->event.pressed && WITHIN_TAPPING_TERM(candidate->event)) {
        // clang-format off
        if (IS_EVENT(candidate->event) && KEYEQ(candidate->event.key, tapping_key.event.key) && !candidate->event.pressed && (
            WITHIN_TAPPING_TERM(waiting_buffer[i].event) || MAYBE_RETRO_SHIFTING(waiting_buffer[i].event, &tapping_key)
        )) {
            // clang-format on
            tapping_key.tap.count = 1;
            candidate->tap.count  = 1;
            process_record(&tapping_key);

M quantum/process_keycode/process_auto_shift.c => quantum/process_keycode/process_auto_shift.c +17 -12
@@ 66,7 66,7 @@ __attribute__((weak)) bool get_custom_auto_shifted_key(uint16_t keycode, keyreco
    return false;
}

/** \brief Called on physical press, returns whether is Auto Shift key */
/** \brief Called on physical press, returns whether key is an Auto Shift key */
__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    switch (keycode) {
#ifndef NO_AUTO_SHIFT_ALPHA


@@ 178,9 178,8 @@ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record)
    }

    // Store record to be sent to user functions if there's no release record then.
    autoshift_lastrecord               = *record;
    autoshift_lastrecord.event.pressed = false;
    autoshift_lastrecord.event.time    = 0;
    autoshift_lastrecord            = *record;
    autoshift_lastrecord.event.time = 0;
    // clang-format off
#if defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)
    if (keycode == autoshift_lastkey &&


@@ 409,8 408,12 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
            // If Retro Shift is disabled, possible custom actions shouldn't happen.
            // clang-format off
#if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
#    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
            const bool is_hold_on_interrupt = get_hold_on_other_key_press(keycode, record);
#    ifdef HOLD_ON_OTHER_KEY_PRESS
            const bool is_hold_on_interrupt = (IS_QK_MOD_TAP(keycode)
#        ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
                && get_hold_on_other_key_press(keycode, record)
#        endif
            );
#    else
            const bool is_hold_on_interrupt = false;
#    endif


@@ 450,8 453,12 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
#endif
        ) {
            // Fixes modifiers not being applied to rolls with AUTO_SHIFT_MODIFIERS set.
#ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
            if (autoshift_flags.in_progress && get_hold_on_other_key_press(keycode, record)) {
#ifdef HOLD_ON_OTHER_KEY_PRESS
            if (autoshift_flags.in_progress
#    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
                && get_hold_on_other_key_press(keycode, record)
#    endif
            ) {
                autoshift_end(KC_NO, now, false, &autoshift_lastrecord);
            }
#endif


@@ 488,10 495,8 @@ void retroshift_poll_time(keyevent_t *event) {
}
// Used to swap the times of Retro Shifted key and Auto Shift key that interrupted it.
void retroshift_swap_times(void) {
    if (last_retroshift_time != 0 && autoshift_flags.in_progress) {
        uint16_t temp        = retroshift_time;
        retroshift_time      = last_retroshift_time;
        last_retroshift_time = temp;
    if (autoshift_flags.in_progress) {
        autoshift_time = last_retroshift_time;
    }
}
#endif

M quantum/process_keycode/process_auto_shift.h => quantum/process_keycode/process_auto_shift.h +1 -0
@@ 56,4 56,5 @@ uint16_t (get_autoshift_timeout)(uint16_t keycode, keyrecord_t *record);
void     set_autoshift_timeout(uint16_t timeout);
void     autoshift_matrix_scan(void);
bool     get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record);
bool     get_auto_shifted_key(uint16_t keycode, keyrecord_t *record);
// clang-format on

A tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/config.h => tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/config.h +22 -0
@@ 0,0 1,22 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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 AUTO_SHIFT_REPEAT
#define AUTO_SHIFT_NO_AUTO_REPEAT

A tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test.mk => tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test_auto_shift.cpp => tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test_auto_shift.cpp +105 -0
@@ 0,0 1,105 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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::AnyNumber;
using testing::AnyOf;
using testing::InSequence;

class AutoShiftNoAutoRepeat : public TestFixture {};

TEST_F(AutoShiftNoAutoRepeat, no_auto_repeat) {
    TestDriver driver;
    InSequence s;
    auto       repeat_key = KeymapKey(0, 1, 0, KC_A);

    set_keymap({repeat_key});

    /* Press repeat key. */
    EXPECT_NO_REPORT(driver);
    repeat_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Idle for auto-repeat to (not) kick in. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release repeat key. */
    EXPECT_NO_REPORT(driver);
    repeat_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(AutoShiftNoAutoRepeat, tap_regular_key_while_another_key_repeats) {
    TestDriver driver;
    InSequence s;
    auto       repeat_key  = KeymapKey(0, 1, 0, KC_P);
    auto       regular_key = KeymapKey(0, 2, 0, KC_A);

    set_keymap({repeat_key, regular_key});

    /* Press repeat key. */
    EXPECT_NO_REPORT(driver);
    repeat_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release repeat key. */
    EXPECT_REPORT(driver, (KC_P));
    EXPECT_EMPTY_REPORT(driver);
    repeat_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Press repeat key. */
    EXPECT_REPORT(driver, (KC_P));
    repeat_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Press regular key. */
    EXPECT_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_REPORT(driver, (KC_P, KC_A));
    EXPECT_REPORT(driver, (KC_P));
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release repeat key. */
    EXPECT_EMPTY_REPORT(driver);
    repeat_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

A tests/auto_shift/auto_shift_repeat/config.h => tests/auto_shift/auto_shift_repeat/config.h +21 -0
@@ 0,0 1,21 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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 AUTO_SHIFT_REPEAT

A tests/auto_shift/auto_shift_repeat/test.mk => tests/auto_shift/auto_shift_repeat/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/auto_shift_repeat/test_auto_shift.cpp => tests/auto_shift/auto_shift_repeat/test_auto_shift.cpp +107 -0
@@ 0,0 1,107 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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::AnyNumber;
using testing::InSequence;

class AutoShiftRepeat : public TestFixture {};

TEST_F(AutoShiftRepeat, tap_regular_key_cancelling_another_key_hold) {
    TestDriver driver;
    InSequence s;
    auto       repeat_key  = KeymapKey(0, 1, 0, KC_P);
    auto       regular_key = KeymapKey(0, 2, 0, KC_A);

    set_keymap({repeat_key, regular_key});

    /* Press repeat key. */
    EXPECT_NO_REPORT(driver);
    repeat_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Press regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());
    EXPECT_REPORT(driver, (KC_P));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());
    EXPECT_REPORT(driver, (KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release repeat key. */
    EXPECT_NO_REPORT(driver);
    repeat_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(AutoShiftRepeat, tap_regular_key_while_another_key_is_held) {
    TestDriver driver;
    InSequence s;
    auto       repeat_key  = KeymapKey(0, 1, 0, KC_P);
    auto       regular_key = KeymapKey(0, 2, 0, KC_A);

    set_keymap({repeat_key, regular_key});

    /* Press repeat key. */
    EXPECT_NO_REPORT(driver);
    repeat_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Idle for auto-repeat to kick in. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_P));
    idle_for(AUTO_SHIFT_TIMEOUT);
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Press regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_REPORT(driver, (KC_P, KC_A));
    EXPECT_REPORT(driver, (KC_P));
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release repeat key. */
    EXPECT_EMPTY_REPORT(driver);
    repeat_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

M tests/auto_shift/config.h => tests/auto_shift/config.h +1 -1
@@ 16,4 16,4 @@

#pragma once

#include "test_common.h"
\ No newline at end of file
#include "test_common.h"

A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/config.h => tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/config.h +24 -0
@@ 0,0 1,24 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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 RETRO_SHIFT 2 * TAPPING_TERM
// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested
#define AUTO_SHIFT_TIMEOUT TAPPING_TERM
#define AUTO_SHIFT_MODIFIERS

A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/config.h => tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/config.h +21 -0
@@ 0,0 1,21 @@
/* Copyright 2021 Stefan Kerkmann
 *
 * 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 RETRO_SHIFT

A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test.mk => tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test_retro_shift.cpp => tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test_retro_shift.cpp +57 -0
@@ 0,0 1,57 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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"

bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    return true;
}

using testing::_;
using testing::AnyNumber;
using testing::AnyOf;
using testing::InSequence;

class RetroShiftDefaultTapHold : public TestFixture {};

TEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_for_long) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));

    set_keymap({mod_tap_hold_key});

    /* Press mod-tap-hold key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_hold_key.press();
    run_one_scan_loop();
    idle_for(4 * TAPPING_TERM);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test.mk => tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test_retro_shift.cpp => tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test_retro_shift.cpp +485 -0
@@ 0,0 1,485 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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"

bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    return true;
}

using testing::_;
using testing::AnyNumber;
using testing::AnyOf;
using testing::InSequence;

class RetroShiftDefaultTapHold : public TestFixture {};

TEST_F(RetroShiftDefaultTapHold, tap_mod_tap_key) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));

    set_keymap({mod_tap_hold_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_A));
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_under_retro_shift) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));

    set_keymap({mod_tap_hold_key});

    /* Press mod-tap-hold key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_hold_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_over_retro_shift) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));

    set_keymap({mod_tap_hold_key});

    /* Press mod-tap-hold key. */
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_hold_key.press();
    run_one_scan_loop();
    idle_for(RETRO_SHIFT);
    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);
}

TEST_F(RetroShiftDefaultTapHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftDefaultTapHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_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);
}

TEST_F(RetroShiftDefaultTapHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftDefaultTapHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftDefaultTapHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftDefaultTapHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_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);

    /* Release regular key. */
    EXPECT_REPORT(driver, (KC_A));
    EXPECT_EMPTY_REPORT(driver);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftDefaultTapHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_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);

    /* Release mod-tap-regular key. */
    EXPECT_REPORT(driver, (KC_A));
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftDefaultTapHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_REPORT(driver, (KC_P));
    EXPECT_EMPTY_REPORT(driver);
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_hold_key.release();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_NO_REPORT(driver);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftDefaultTapHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_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);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

A tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/config.h => tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/config.h +26 -0
@@ 0,0 1,26 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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

#define RETRO_SHIFT 2 * TAPPING_TERM
// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested
#define AUTO_SHIFT_TIMEOUT TAPPING_TERM
#define AUTO_SHIFT_MODIFIERS

A tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test.mk => tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test_retro_shift.cpp => tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test_retro_shift.cpp +442 -0
@@ 0,0 1,442 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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"

bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    return true;
}

using testing::_;
using testing::AnyNumber;
using testing::AnyOf;
using testing::InSequence;

class RetroShiftHoldOnOtherKeyPress : public TestFixture {};

TEST_F(RetroShiftHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_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);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    EXPECT_EMPTY_REPORT(driver);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftHoldOnOtherKeyPress, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/config.h => tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/config.h +26 -0
@@ 0,0 1,26 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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 PERMISSIVE_HOLD

#define RETRO_SHIFT 2 * TAPPING_TERM
// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested
#define AUTO_SHIFT_TIMEOUT TAPPING_TERM
#define AUTO_SHIFT_MODIFIERS

A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test.mk => tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test_retro_shift.cpp => tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test_retro_shift.cpp +419 -0
@@ 0,0 1,419 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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"

bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    return true;
}

using testing::_;
using testing::AnyNumber;
using testing::AnyOf;
using testing::InSequence;

class RetroShiftPermissiveHold : public TestFixture {};

TEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_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);
}

TEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftPermissiveHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftPermissiveHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftPermissiveHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_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);

    /* Release regular key. */
    EXPECT_REPORT(driver, (KC_A));
    EXPECT_EMPTY_REPORT(driver);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftPermissiveHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_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);

    /* Release mod-tap-regular key. */
    EXPECT_REPORT(driver, (KC_A));
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftPermissiveHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_REPORT(driver, (KC_P));
    EXPECT_EMPTY_REPORT(driver);
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_hold_key.release();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_NO_REPORT(driver);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftPermissiveHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_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);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LSFT, KC_A));
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/config.h => tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/config.h +27 -0
@@ 0,0 1,27 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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
#define PERMISSIVE_HOLD

#define RETRO_SHIFT 2 * TAPPING_TERM
// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested
#define AUTO_SHIFT_TIMEOUT TAPPING_TERM
#define AUTO_SHIFT_MODIFIERS

A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test.mk => tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test.mk +20 -0
@@ 0,0 1,20 @@
# Copyright 2022 Isaac Elenbaas
#
# 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
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes

A tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test_retro_shift.cpp => tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test_retro_shift.cpp +442 -0
@@ 0,0 1,442 @@
/* Copyright 2022 Isaac Elenbaas
 *
 * 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"

bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    return true;
}

using testing::_;
using testing::AnyNumber;
using testing::AnyOf;
using testing::InSequence;

class RetroShiftPermissiveHoldHoldOnOtherKeyPress : public TestFixture {};

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_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);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    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);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    idle_for(AUTO_SHIFT_TIMEOUT);
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL));
    mod_tap_regular_key.release();
    run_one_scan_loop();
    idle_for(TAPPING_TERM);
    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);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    EXPECT_EMPTY_REPORT(driver);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    EXPECT_REPORT(driver, (KC_LCTL, KC_A));
    EXPECT_REPORT(driver, (KC_LCTL));
    EXPECT_EMPTY_REPORT(driver);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       regular_key      = KeymapKey(0, 1, 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_NO_REPORT(driver);
    regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

TEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
    TestDriver driver;
    InSequence s;
    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));
    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));

    set_keymap({mod_tap_hold_key, mod_tap_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 mod-tap-regular key. */
    EXPECT_NO_REPORT(driver);
    mod_tap_regular_key.press();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-hold key. */
    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
    mod_tap_hold_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);

    /* Release mod-tap-regular key. */
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
                KeyboardReport(KC_LCTL, KC_LSFT),
                KeyboardReport(KC_LSFT),
                KeyboardReport(KC_LCTL))))
        .Times(AnyNumber());
    // clang-format on
    EXPECT_EMPTY_REPORT(driver);
    idle_for(AUTO_SHIFT_TIMEOUT);
    mod_tap_regular_key.release();
    run_one_scan_loop();
    testing::Mock::VerifyAndClearExpectations(&driver);
}

M tests/auto_shift/test.mk => tests/auto_shift/test.mk +1 -1
@@ 17,4 17,4 @@
# Keep this file, even if it is empty, as a marker that this folder contains tests
# --------------------------------------------------------------------------------

AUTO_SHIFT_ENABLE = yes
\ No newline at end of file
AUTO_SHIFT_ENABLE = yes

M tests/basic/config.h => tests/basic/config.h +1 -1
@@ 16,4 16,4 @@

#pragma once

#include "test_common.h"
\ No newline at end of file
#include "test_common.h"

M tests/basic/test.mk => tests/basic/test.mk +1 -1
@@ 15,4 15,4 @@

# --------------------------------------------------------------------------------
# Keep this file, even if it is empty, as a marker that this folder contains tests
# --------------------------------------------------------------------------------
\ No newline at end of file
# --------------------------------------------------------------------------------

A tests/caps_word/auto_shift/config.h => tests/caps_word/auto_shift/config.h +21 -0
@@ 0,0 1,21 @@
// 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 <http://www.gnu.org/licenses/>.

#pragma once

#include "test_common.h"

#define TAPPING_TERM 200
#define AUTO_SHIFT_TIMEOUT 150

R tests/caps_word/caps_word_autoshift/config.h => tests/caps_word/auto_shift/retro_shift/config.h +0 -0
R tests/caps_word/caps_word_autoshift/test.mk => tests/caps_word/auto_shift/retro_shift/test.mk +0 -0
R tests/caps_word/caps_word_autoshift/test_caps_word_autoshift.cpp => tests/caps_word/auto_shift/retro_shift/test_caps_word_retroshift.cpp +4 -0
@@ 27,6 27,10 @@
            KeyboardReport(KC_LSFT))))
// clang-format on

bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
    return true;
}

using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AnyOf;

A tests/caps_word/auto_shift/test.mk => tests/caps_word/auto_shift/test.mk +18 -0
@@ 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 <http://www.gnu.org/licenses/>.

CAPS_WORD_ENABLE = yes
AUTO_SHIFT_ENABLE = yes


A tests/caps_word/auto_shift/test_caps_word_autoshift.cpp => tests/caps_word/auto_shift/test_caps_word_autoshift.cpp +66 -0
@@ 0,0 1,66 @@
// 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 <http://www.gnu.org/licenses/>.

#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);
}

R tests/caps_word/caps_word_unicodemap/config.h => tests/caps_word/unicodemap/config.h +0 -0
R tests/caps_word/caps_word_unicodemap/test.mk => tests/caps_word/unicodemap/test.mk +0 -0
R tests/caps_word/caps_word_unicodemap/test_caps_word_unicodemap.cpp => tests/caps_word/unicodemap/test_caps_word_unicodemap.cpp +0 -0
M tests/tap_hold_configurations/default_mod_tap/test.mk => tests/tap_hold_configurations/default_mod_tap/test.mk +1 -1
@@ 15,4 15,4 @@

# --------------------------------------------------------------------------------
# Keep this file, even if it is empty, as a marker that this folder contains tests
# --------------------------------------------------------------------------------
\ No newline at end of file
# --------------------------------------------------------------------------------

M tests/tap_hold_configurations/permissive_hold/config.h => tests/tap_hold_configurations/permissive_hold/config.h +1 -1
@@ 18,4 18,4 @@

#include "test_common.h"

#define PERMISSIVE_HOLD
\ No newline at end of file
#define PERMISSIVE_HOLD

M tests/test_common/build.mk => tests/test_common/build.mk +1 -1
@@ 13,4 13,4 @@
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

CUSTOM_MATRIX=yes
\ No newline at end of file
CUSTOM_MATRIX=yes

M tests/test_common/main.cpp => tests/test_common/main.cpp +1 -1
@@ 29,4 29,4 @@ int main(int argc, char **argv) {
    init_logging();

    return RUN_ALL_TESTS();
}
\ No newline at end of file
}

M tests/test_common/test_common.h => tests/test_common/test_common.h +1 -1
@@ 1,4 1,4 @@
#pragma once

#define MATRIX_ROWS 4
#define MATRIX_COLS 10
\ No newline at end of file
#define MATRIX_COLS 10

Do not follow this link