~ruther/qmk_firmware

962e4c0e1854b10612bab547c3d842c5f967dd23 — Stefan Kerkmann 2 years ago e2ab98f
[Test] Reset timer for every unit test and provide timestamps for log messages (#17028)

M builddefs/build_full_test.mk => builddefs/build_full_test.mk +2 -0
@@ 24,6 24,8 @@ $(TEST)_SRC := \
	tests/test_common/matrix.c \
	tests/test_common/test_driver.cpp \
	tests/test_common/keyboard_report_util.cpp \
	tests/test_common/keycode_util.cpp \
	tests/test_common/keycode_table.cpp \
	tests/test_common/test_fixture.cpp \
	tests/test_common/test_keymap_key.cpp \
	tests/test_common/test_logger.cpp \

M lib/python/qmk/cli/__init__.py => lib/python/qmk/cli/__init__.py +1 -0
@@ 57,6 57,7 @@ subcommands = [
    'qmk.cli.generate.keyboard_c',
    'qmk.cli.generate.keyboard_h',
    'qmk.cli.generate.keycodes',
    'qmk.cli.generate.keycodes_tests',
    'qmk.cli.generate.rgb_breathe_table',
    'qmk.cli.generate.rules_mk',
    'qmk.cli.generate.version_h',

A lib/python/qmk/cli/generate/keycodes_tests.py => lib/python/qmk/cli/generate/keycodes_tests.py +39 -0
@@ 0,0 1,39 @@
"""Used by the make system to generate a keycode lookup table from keycodes_{version}.json
"""
from milc import cli

from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.commands import dump_lines
from qmk.path import normpath
from qmk.keycodes import load_spec


def _generate_defines(lines, keycodes):
    lines.append('')
    lines.append('std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {')
    for key, value in keycodes["keycodes"].items():
        lines.append(f'    {{{value.get("key")}, "{value.get("key")}"}},')
    lines.append('};')


@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.subcommand('Used by the make system to generate a keycode lookup table from keycodes_{version}.json', hidden=True)
def generate_keycodes_tests(cli):
    """Generates a keycode to identifier lookup table for unit test output.
    """

    # Build the keycodes.h file.
    keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '// clang-format off']
    keycodes_h_lines.append('extern "C" {\n#include <keycode.h>\n}')
    keycodes_h_lines.append('#include <map>')
    keycodes_h_lines.append('#include <string>')
    keycodes_h_lines.append('#include <cstdint>')

    keycodes = load_spec(cli.args.version)

    _generate_defines(keycodes_h_lines, keycodes)

    # Show the results
    dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)

M platforms/test/timer.c => platforms/test/timer.c +2 -1
@@ 15,8 15,9 @@
 */

#include "timer.h"
#include <stdatomic.h>

static uint32_t current_time = 0;
static atomic_uint_least32_t current_time = 0;

void timer_init(void) {
    current_time = 0;

M quantum/action_tapping.c => quantum/action_tapping.c +2 -2
@@ 143,8 143,8 @@ void action_tapping_process(keyrecord_t record) {
#            define TAP_GET_RETRO_TAPPING true
#        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))
#        define TAP_IS_LT IS_LT(tapping_keycode)
#        define TAP_IS_MT IS_MT(tapping_keycode)
#        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

M quantum/process_keycode/process_auto_shift.c => quantum/process_keycode/process_auto_shift.c +1 -1
@@ 405,7 405,7 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
#       elif defined(IGNORE_MOD_TAP_INTERRUPT)
            const bool is_hold_on_interrupt = false;
#       else
            const bool is_hold_on_interrupt = IS_MT(keycode);
            const bool is_hold_on_interrupt = IS_QK_MOD_TAP(keycode);
#       endif
#   endif
        if (IS_RETRO(keycode)

M quantum/process_keycode/process_auto_shift.h => quantum/process_keycode/process_auto_shift.h +2 -3
@@ 22,9 22,8 @@
#    define AUTO_SHIFT_TIMEOUT 175
#endif

#define IS_LT(kc) ((kc) >= QK_LAYER_TAP && (kc) <= QK_LAYER_TAP_MAX)
#define IS_MT(kc) ((kc) >= QK_MOD_TAP && (kc) <= QK_MOD_TAP_MAX)
#define IS_RETRO(kc) (IS_MT(kc) || IS_LT(kc))
#define IS_RETRO(kc) (IS_QK_MOD_TAP(kc) || IS_QK_LAYER_TAP(kc))

#define DO_GET_AUTOSHIFT_TIMEOUT(keycode, record, ...) record
// clang-format off
#define AUTO_SHIFT_ALPHA KC_A ... KC_Z

A tests/basic/test_keycode_util.cpp => tests/basic/test_keycode_util.cpp +52 -0
@@ 0,0 1,52 @@
// Copyright 2022 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later

#include "test_common.hpp"

class KeycodeToIdentifierSuite : public ::testing::TestWithParam<std::pair<std::uint16_t, std::string>> {};

TEST_P(KeycodeToIdentifierSuite, ConversionTests) {
    ASSERT_EQ(get_keycode_identifier_or_default(GetParam().first), GetParam().second);
}

INSTANTIATE_TEST_CASE_P(ConversionTestsP, KeycodeToIdentifierSuite,
                        // clang-format off
::testing::Values(
    // Goto layer
    std::make_pair(TO(0), "TO(0)"),
    std::make_pair(TO(0x1F), "TO(31)"),
    // Momentary switch layer
    std::make_pair(MO(0), "MO(0)"),
    std::make_pair(MO(0x1F), "MO(31)"),
    // Set default layer
    std::make_pair(DF(0), "DF(0)"),
    std::make_pair(DF(0x1F), "DF(31)"),
    // Toggle layer
    std::make_pair(TG(0), "TG(0)"),
    std::make_pair(TG(0x1F), "TG(31)"),
    // One-shot layer
    std::make_pair(OSL(0), "OSL(0)"),
    std::make_pair(OSL(0x1F), "OSL(31)"),
    // One-shot mod
    std::make_pair(OSM(MOD_LSFT), "OSM(MOD_LSFT)"),
    std::make_pair(OSM(MOD_LSFT | MOD_LCTL), "OSM(MOD_LCTL | MOD_LSFT)"),
    // Layer Mod
    std::make_pair(LM(0, MOD_LSFT), "LM(0, MOD_LSFT)"),
    std::make_pair(LM(0xF, MOD_LSFT), "LM(15, MOD_LSFT)"),
    std::make_pair(LM(0xF, MOD_LSFT | MOD_LCTL), "LM(15, MOD_LCTL | MOD_LSFT)"),
    // Layer tap toggle
    std::make_pair(TT(0), "TT(0)"),
    std::make_pair(TT(0x1F), "TT(31)"),
    // Layer tap
    std::make_pair(LT(0, KC_A), "LT(0, KC_A)"),
    std::make_pair(LT(0xF, KC_SPACE), "LT(15, KC_SPACE)"),
    std::make_pair(LT(1, KC_SPC), "LT(1, KC_SPACE)"),
    // Mod tap
    std::make_pair(MT(MOD_LCTL, KC_A), "MT(MOD_LCTL, KC_A)"),
    std::make_pair(MT(MOD_LCTL | MOD_LSFT, KC_A), "MT(MOD_LCTL | MOD_LSFT, KC_A)"),
    std::make_pair(ALT_T(KC_TAB), "MT(MOD_LALT, KC_TAB)"),
    // Mods
    std::make_pair(LCTL(KC_A), "QK_MODS(KC_A, QK_LCTL)"),
    std::make_pair(HYPR(KC_SPACE), "QK_MODS(KC_SPACE, QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI)")
));
// clang-format on

M tests/caps_word/test_caps_word.cpp => tests/caps_word/test_caps_word.cpp +8 -1
@@ 505,7 505,8 @@ class CapsWordDoubleTapShift : public ::testing::WithParamInterface<CapsWordDoub
TEST_P(CapsWordDoubleTapShift, Activation) {
    TestDriver driver;
    KeymapKey  left_shift(0, 0, 0, GetParam().left_shift_keycode);
    set_keymap({left_shift});
    KeymapKey  esc(0, 0, 1, KC_ESCAPE);
    set_keymap({left_shift, esc});

    // clang-format off
    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(


@@ 524,6 525,12 @@ TEST_P(CapsWordDoubleTapShift, Activation) {
    EXPECT_EQ(is_caps_word_on(), true);

    testing::Mock::VerifyAndClearExpectations(&driver);

    // We have to manually reset the internal state of the caps word state
    // machine at this point. This due to imperfect test isolation which can't
    // reset the caps word double shift timer on test case setup.
    idle_for(CAPS_WORD_IDLE_TIMEOUT);
    tap_key(esc);
}

// Double tap doesn't count if another key is pressed between the taps.

M tests/test_common/keyboard_report_util.cpp => tests/test_common/keyboard_report_util.cpp +39 -6
@@ 15,11 15,16 @@
 */

#include "keyboard_report_util.hpp"
#include <cstdint>
#include <vector>
#include <algorithm>

using namespace testing;

extern std::map<uint16_t, std::string> KEYCODE_ID_TABLE;

namespace {

std::vector<uint8_t> get_keys(const report_keyboard_t& report) {
    std::vector<uint8_t> result;
#if defined(NKRO_ENABLE)


@@ 36,6 41,19 @@ std::vector<uint8_t> get_keys(const report_keyboard_t& report) {
    std::sort(result.begin(), result.end());
    return result;
}

std::vector<uint8_t> get_mods(const report_keyboard_t& report) {
    std::vector<uint8_t> result;
    for (size_t i = 0; i < 8; i++) {
        if (report.mods & (1 << i)) {
            uint8_t code = KC_LEFT_CTRL + i;
            result.emplace_back(code);
        }
    }
    std::sort(result.begin(), result.end());
    return result;
}

} // namespace

bool operator==(const report_keyboard_t& lhs, const report_keyboard_t& rhs) {


@@ 44,21 62,36 @@ bool operator==(const report_keyboard_t& lhs, const report_keyboard_t& rhs) {
    return lhs.mods == rhs.mods && lhskeys == rhskeys;
}

std::ostream& operator<<(std::ostream& stream, const report_keyboard_t& report) {
std::ostream& operator<<(std::ostream& os, const report_keyboard_t& report) {
    auto keys = get_keys(report);
    auto mods = get_mods(report);

    // TODO: This should probably print friendly names for the keys
    stream << "Keyboard Report: Mods (" << (uint32_t)report.mods << ") Keys (";
    os << std::setw(10) << std::left << "report: ";

    if (!keys.size() && !mods.size()) {
        return os << "empty" << std::endl;
    }

    os << "(";
    for (auto key = keys.cbegin(); key != keys.cend();) {
        stream << +(*key);
        os << KEYCODE_ID_TABLE.at(*key);
        key++;
        if (key != keys.cend()) {
            stream << ",";
            os << ", ";
        }
    }

    os << ") [";

    for (auto mod = mods.cbegin(); mod != mods.cend();) {
        os << KEYCODE_ID_TABLE.at(*mod);
        mod++;
        if (mod != mods.cend()) {
            os << ", ";
        }
    }

    return stream << ")" << std::endl;
    return os << "]" << std::endl;
}

KeyboardReportMatcher::KeyboardReportMatcher(const std::vector<uint8_t>& keys) {

A tests/test_common/keycode_table.cpp => tests/test_common/keycode_table.cpp +663 -0
@@ 0,0 1,663 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

/*******************************************************************************
  88888888888 888      d8b                .d888 d8b 888               d8b
      888     888      Y8P               d88P"  Y8P 888               Y8P
      888     888                        888        888
      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b
      888     888 "88b 888 88K           888    888 888 d8P  Y8b      888 88K
      888     888  888 888 "Y8888b.      888    888 888 88888888      888 "Y8888b.
      888     888  888 888      X88      888    888 888 Y8b.          888      X88
      888     888  888 888  88888P'      888    888 888  "Y8888       888  88888P'
                                                        888                 888
                                                        888                 888
                                                        888                 888
     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888
    d88P"88b d8P  Y8b 888 "88b d8P  Y8b 888P"      "88b 888   d8P  Y8b d88" 888
    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888
    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888
     "Y88888  "Y8888  888  888  "Y8888  888    "Y888888  "Y888 "Y8888   "Y88888
         888
    Y8b d88P
     "Y88P"
*******************************************************************************/

// clang-format off
extern "C" {
#include <keycode.h>
}
#include <map>
#include <string>
#include <cstdint>

std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {
    {KC_NO, "KC_NO"},
    {KC_TRANSPARENT, "KC_TRANSPARENT"},
    {KC_A, "KC_A"},
    {KC_B, "KC_B"},
    {KC_C, "KC_C"},
    {KC_D, "KC_D"},
    {KC_E, "KC_E"},
    {KC_F, "KC_F"},
    {KC_G, "KC_G"},
    {KC_H, "KC_H"},
    {KC_I, "KC_I"},
    {KC_J, "KC_J"},
    {KC_K, "KC_K"},
    {KC_L, "KC_L"},
    {KC_M, "KC_M"},
    {KC_N, "KC_N"},
    {KC_O, "KC_O"},
    {KC_P, "KC_P"},
    {KC_Q, "KC_Q"},
    {KC_R, "KC_R"},
    {KC_S, "KC_S"},
    {KC_T, "KC_T"},
    {KC_U, "KC_U"},
    {KC_V, "KC_V"},
    {KC_W, "KC_W"},
    {KC_X, "KC_X"},
    {KC_Y, "KC_Y"},
    {KC_Z, "KC_Z"},
    {KC_1, "KC_1"},
    {KC_2, "KC_2"},
    {KC_3, "KC_3"},
    {KC_4, "KC_4"},
    {KC_5, "KC_5"},
    {KC_6, "KC_6"},
    {KC_7, "KC_7"},
    {KC_8, "KC_8"},
    {KC_9, "KC_9"},
    {KC_0, "KC_0"},
    {KC_ENTER, "KC_ENTER"},
    {KC_ESCAPE, "KC_ESCAPE"},
    {KC_BACKSPACE, "KC_BACKSPACE"},
    {KC_TAB, "KC_TAB"},
    {KC_SPACE, "KC_SPACE"},
    {KC_MINUS, "KC_MINUS"},
    {KC_EQUAL, "KC_EQUAL"},
    {KC_LEFT_BRACKET, "KC_LEFT_BRACKET"},
    {KC_RIGHT_BRACKET, "KC_RIGHT_BRACKET"},
    {KC_BACKSLASH, "KC_BACKSLASH"},
    {KC_NONUS_HASH, "KC_NONUS_HASH"},
    {KC_SEMICOLON, "KC_SEMICOLON"},
    {KC_QUOTE, "KC_QUOTE"},
    {KC_GRAVE, "KC_GRAVE"},
    {KC_COMMA, "KC_COMMA"},
    {KC_DOT, "KC_DOT"},
    {KC_SLASH, "KC_SLASH"},
    {KC_CAPS_LOCK, "KC_CAPS_LOCK"},
    {KC_F1, "KC_F1"},
    {KC_F2, "KC_F2"},
    {KC_F3, "KC_F3"},
    {KC_F4, "KC_F4"},
    {KC_F5, "KC_F5"},
    {KC_F6, "KC_F6"},
    {KC_F7, "KC_F7"},
    {KC_F8, "KC_F8"},
    {KC_F9, "KC_F9"},
    {KC_F10, "KC_F10"},
    {KC_F11, "KC_F11"},
    {KC_F12, "KC_F12"},
    {KC_PRINT_SCREEN, "KC_PRINT_SCREEN"},
    {KC_SCROLL_LOCK, "KC_SCROLL_LOCK"},
    {KC_PAUSE, "KC_PAUSE"},
    {KC_INSERT, "KC_INSERT"},
    {KC_HOME, "KC_HOME"},
    {KC_PAGE_UP, "KC_PAGE_UP"},
    {KC_DELETE, "KC_DELETE"},
    {KC_END, "KC_END"},
    {KC_PAGE_DOWN, "KC_PAGE_DOWN"},
    {KC_RIGHT, "KC_RIGHT"},
    {KC_LEFT, "KC_LEFT"},
    {KC_DOWN, "KC_DOWN"},
    {KC_UP, "KC_UP"},
    {KC_NUM_LOCK, "KC_NUM_LOCK"},
    {KC_KP_SLASH, "KC_KP_SLASH"},
    {KC_KP_ASTERISK, "KC_KP_ASTERISK"},
    {KC_KP_MINUS, "KC_KP_MINUS"},
    {KC_KP_PLUS, "KC_KP_PLUS"},
    {KC_KP_ENTER, "KC_KP_ENTER"},
    {KC_KP_1, "KC_KP_1"},
    {KC_KP_2, "KC_KP_2"},
    {KC_KP_3, "KC_KP_3"},
    {KC_KP_4, "KC_KP_4"},
    {KC_KP_5, "KC_KP_5"},
    {KC_KP_6, "KC_KP_6"},
    {KC_KP_7, "KC_KP_7"},
    {KC_KP_8, "KC_KP_8"},
    {KC_KP_9, "KC_KP_9"},
    {KC_KP_0, "KC_KP_0"},
    {KC_KP_DOT, "KC_KP_DOT"},
    {KC_NONUS_BACKSLASH, "KC_NONUS_BACKSLASH"},
    {KC_APPLICATION, "KC_APPLICATION"},
    {KC_KB_POWER, "KC_KB_POWER"},
    {KC_KP_EQUAL, "KC_KP_EQUAL"},
    {KC_F13, "KC_F13"},
    {KC_F14, "KC_F14"},
    {KC_F15, "KC_F15"},
    {KC_F16, "KC_F16"},
    {KC_F17, "KC_F17"},
    {KC_F18, "KC_F18"},
    {KC_F19, "KC_F19"},
    {KC_F20, "KC_F20"},
    {KC_F21, "KC_F21"},
    {KC_F22, "KC_F22"},
    {KC_F23, "KC_F23"},
    {KC_F24, "KC_F24"},
    {KC_EXECUTE, "KC_EXECUTE"},
    {KC_HELP, "KC_HELP"},
    {KC_MENU, "KC_MENU"},
    {KC_SELECT, "KC_SELECT"},
    {KC_STOP, "KC_STOP"},
    {KC_AGAIN, "KC_AGAIN"},
    {KC_UNDO, "KC_UNDO"},
    {KC_CUT, "KC_CUT"},
    {KC_COPY, "KC_COPY"},
    {KC_PASTE, "KC_PASTE"},
    {KC_FIND, "KC_FIND"},
    {KC_KB_MUTE, "KC_KB_MUTE"},
    {KC_KB_VOLUME_UP, "KC_KB_VOLUME_UP"},
    {KC_KB_VOLUME_DOWN, "KC_KB_VOLUME_DOWN"},
    {KC_LOCKING_CAPS_LOCK, "KC_LOCKING_CAPS_LOCK"},
    {KC_LOCKING_NUM_LOCK, "KC_LOCKING_NUM_LOCK"},
    {KC_LOCKING_SCROLL_LOCK, "KC_LOCKING_SCROLL_LOCK"},
    {KC_KP_COMMA, "KC_KP_COMMA"},
    {KC_KP_EQUAL_AS400, "KC_KP_EQUAL_AS400"},
    {KC_INTERNATIONAL_1, "KC_INTERNATIONAL_1"},
    {KC_INTERNATIONAL_2, "KC_INTERNATIONAL_2"},
    {KC_INTERNATIONAL_3, "KC_INTERNATIONAL_3"},
    {KC_INTERNATIONAL_4, "KC_INTERNATIONAL_4"},
    {KC_INTERNATIONAL_5, "KC_INTERNATIONAL_5"},
    {KC_INTERNATIONAL_6, "KC_INTERNATIONAL_6"},
    {KC_INTERNATIONAL_7, "KC_INTERNATIONAL_7"},
    {KC_INTERNATIONAL_8, "KC_INTERNATIONAL_8"},
    {KC_INTERNATIONAL_9, "KC_INTERNATIONAL_9"},
    {KC_LANGUAGE_1, "KC_LANGUAGE_1"},
    {KC_LANGUAGE_2, "KC_LANGUAGE_2"},
    {KC_LANGUAGE_3, "KC_LANGUAGE_3"},
    {KC_LANGUAGE_4, "KC_LANGUAGE_4"},
    {KC_LANGUAGE_5, "KC_LANGUAGE_5"},
    {KC_LANGUAGE_6, "KC_LANGUAGE_6"},
    {KC_LANGUAGE_7, "KC_LANGUAGE_7"},
    {KC_LANGUAGE_8, "KC_LANGUAGE_8"},
    {KC_LANGUAGE_9, "KC_LANGUAGE_9"},
    {KC_ALTERNATE_ERASE, "KC_ALTERNATE_ERASE"},
    {KC_SYSTEM_REQUEST, "KC_SYSTEM_REQUEST"},
    {KC_CANCEL, "KC_CANCEL"},
    {KC_CLEAR, "KC_CLEAR"},
    {KC_PRIOR, "KC_PRIOR"},
    {KC_RETURN, "KC_RETURN"},
    {KC_SEPARATOR, "KC_SEPARATOR"},
    {KC_OUT, "KC_OUT"},
    {KC_OPER, "KC_OPER"},
    {KC_CLEAR_AGAIN, "KC_CLEAR_AGAIN"},
    {KC_CRSEL, "KC_CRSEL"},
    {KC_EXSEL, "KC_EXSEL"},
    {KC_SYSTEM_POWER, "KC_SYSTEM_POWER"},
    {KC_SYSTEM_SLEEP, "KC_SYSTEM_SLEEP"},
    {KC_SYSTEM_WAKE, "KC_SYSTEM_WAKE"},
    {KC_AUDIO_MUTE, "KC_AUDIO_MUTE"},
    {KC_AUDIO_VOL_UP, "KC_AUDIO_VOL_UP"},
    {KC_AUDIO_VOL_DOWN, "KC_AUDIO_VOL_DOWN"},
    {KC_MEDIA_NEXT_TRACK, "KC_MEDIA_NEXT_TRACK"},
    {KC_MEDIA_PREV_TRACK, "KC_MEDIA_PREV_TRACK"},
    {KC_MEDIA_STOP, "KC_MEDIA_STOP"},
    {KC_MEDIA_PLAY_PAUSE, "KC_MEDIA_PLAY_PAUSE"},
    {KC_MEDIA_SELECT, "KC_MEDIA_SELECT"},
    {KC_MEDIA_EJECT, "KC_MEDIA_EJECT"},
    {KC_MAIL, "KC_MAIL"},
    {KC_CALCULATOR, "KC_CALCULATOR"},
    {KC_MY_COMPUTER, "KC_MY_COMPUTER"},
    {KC_WWW_SEARCH, "KC_WWW_SEARCH"},
    {KC_WWW_HOME, "KC_WWW_HOME"},
    {KC_WWW_BACK, "KC_WWW_BACK"},
    {KC_WWW_FORWARD, "KC_WWW_FORWARD"},
    {KC_WWW_STOP, "KC_WWW_STOP"},
    {KC_WWW_REFRESH, "KC_WWW_REFRESH"},
    {KC_WWW_FAVORITES, "KC_WWW_FAVORITES"},
    {KC_MEDIA_FAST_FORWARD, "KC_MEDIA_FAST_FORWARD"},
    {KC_MEDIA_REWIND, "KC_MEDIA_REWIND"},
    {KC_BRIGHTNESS_UP, "KC_BRIGHTNESS_UP"},
    {KC_BRIGHTNESS_DOWN, "KC_BRIGHTNESS_DOWN"},
    {KC_CONTROL_PANEL, "KC_CONTROL_PANEL"},
    {KC_ASSISTANT, "KC_ASSISTANT"},
    {KC_MS_UP, "KC_MS_UP"},
    {KC_MS_DOWN, "KC_MS_DOWN"},
    {KC_MS_LEFT, "KC_MS_LEFT"},
    {KC_MS_RIGHT, "KC_MS_RIGHT"},
    {KC_MS_BTN1, "KC_MS_BTN1"},
    {KC_MS_BTN2, "KC_MS_BTN2"},
    {KC_MS_BTN3, "KC_MS_BTN3"},
    {KC_MS_BTN4, "KC_MS_BTN4"},
    {KC_MS_BTN5, "KC_MS_BTN5"},
    {KC_MS_BTN6, "KC_MS_BTN6"},
    {KC_MS_BTN7, "KC_MS_BTN7"},
    {KC_MS_BTN8, "KC_MS_BTN8"},
    {KC_MS_WH_UP, "KC_MS_WH_UP"},
    {KC_MS_WH_DOWN, "KC_MS_WH_DOWN"},
    {KC_MS_WH_LEFT, "KC_MS_WH_LEFT"},
    {KC_MS_WH_RIGHT, "KC_MS_WH_RIGHT"},
    {KC_MS_ACCEL0, "KC_MS_ACCEL0"},
    {KC_MS_ACCEL1, "KC_MS_ACCEL1"},
    {KC_MS_ACCEL2, "KC_MS_ACCEL2"},
    {KC_LEFT_CTRL, "KC_LEFT_CTRL"},
    {KC_LEFT_SHIFT, "KC_LEFT_SHIFT"},
    {KC_LEFT_ALT, "KC_LEFT_ALT"},
    {KC_LEFT_GUI, "KC_LEFT_GUI"},
    {KC_RIGHT_CTRL, "KC_RIGHT_CTRL"},
    {KC_RIGHT_SHIFT, "KC_RIGHT_SHIFT"},
    {KC_RIGHT_ALT, "KC_RIGHT_ALT"},
    {KC_RIGHT_GUI, "KC_RIGHT_GUI"},
    {SH_TG, "SH_TG"},
    {SH_TT, "SH_TT"},
    {SH_MON, "SH_MON"},
    {SH_MOFF, "SH_MOFF"},
    {SH_OFF, "SH_OFF"},
    {SH_ON, "SH_ON"},
    {SH_OS, "SH_OS"},
    {MAGIC_SWAP_CONTROL_CAPSLOCK, "MAGIC_SWAP_CONTROL_CAPSLOCK"},
    {MAGIC_UNSWAP_CONTROL_CAPSLOCK, "MAGIC_UNSWAP_CONTROL_CAPSLOCK"},
    {MAGIC_TOGGLE_CONTROL_CAPSLOCK, "MAGIC_TOGGLE_CONTROL_CAPSLOCK"},
    {MAGIC_UNCAPSLOCK_TO_CONTROL, "MAGIC_UNCAPSLOCK_TO_CONTROL"},
    {MAGIC_CAPSLOCK_TO_CONTROL, "MAGIC_CAPSLOCK_TO_CONTROL"},
    {MAGIC_SWAP_LALT_LGUI, "MAGIC_SWAP_LALT_LGUI"},
    {MAGIC_UNSWAP_LALT_LGUI, "MAGIC_UNSWAP_LALT_LGUI"},
    {MAGIC_SWAP_RALT_RGUI, "MAGIC_SWAP_RALT_RGUI"},
    {MAGIC_UNSWAP_RALT_RGUI, "MAGIC_UNSWAP_RALT_RGUI"},
    {MAGIC_UNNO_GUI, "MAGIC_UNNO_GUI"},
    {MAGIC_NO_GUI, "MAGIC_NO_GUI"},
    {MAGIC_TOGGLE_GUI, "MAGIC_TOGGLE_GUI"},
    {MAGIC_SWAP_GRAVE_ESC, "MAGIC_SWAP_GRAVE_ESC"},
    {MAGIC_UNSWAP_GRAVE_ESC, "MAGIC_UNSWAP_GRAVE_ESC"},
    {MAGIC_SWAP_BACKSLASH_BACKSPACE, "MAGIC_SWAP_BACKSLASH_BACKSPACE"},
    {MAGIC_UNSWAP_BACKSLASH_BACKSPACE, "MAGIC_UNSWAP_BACKSLASH_BACKSPACE"},
    {MAGIC_TOGGLE_BACKSLASH_BACKSPACE, "MAGIC_TOGGLE_BACKSLASH_BACKSPACE"},
    {MAGIC_HOST_NKRO, "MAGIC_HOST_NKRO"},
    {MAGIC_UNHOST_NKRO, "MAGIC_UNHOST_NKRO"},
    {MAGIC_TOGGLE_NKRO, "MAGIC_TOGGLE_NKRO"},
    {MAGIC_SWAP_ALT_GUI, "MAGIC_SWAP_ALT_GUI"},
    {MAGIC_UNSWAP_ALT_GUI, "MAGIC_UNSWAP_ALT_GUI"},
    {MAGIC_TOGGLE_ALT_GUI, "MAGIC_TOGGLE_ALT_GUI"},
    {MAGIC_SWAP_LCTL_LGUI, "MAGIC_SWAP_LCTL_LGUI"},
    {MAGIC_UNSWAP_LCTL_LGUI, "MAGIC_UNSWAP_LCTL_LGUI"},
    {MAGIC_SWAP_RCTL_RGUI, "MAGIC_SWAP_RCTL_RGUI"},
    {MAGIC_UNSWAP_RCTL_RGUI, "MAGIC_UNSWAP_RCTL_RGUI"},
    {MAGIC_SWAP_CTL_GUI, "MAGIC_SWAP_CTL_GUI"},
    {MAGIC_UNSWAP_CTL_GUI, "MAGIC_UNSWAP_CTL_GUI"},
    {MAGIC_TOGGLE_CTL_GUI, "MAGIC_TOGGLE_CTL_GUI"},
    {MAGIC_EE_HANDS_LEFT, "MAGIC_EE_HANDS_LEFT"},
    {MAGIC_EE_HANDS_RIGHT, "MAGIC_EE_HANDS_RIGHT"},
    {MAGIC_SWAP_ESCAPE_CAPSLOCK, "MAGIC_SWAP_ESCAPE_CAPSLOCK"},
    {MAGIC_UNSWAP_ESCAPE_CAPSLOCK, "MAGIC_UNSWAP_ESCAPE_CAPSLOCK"},
    {MAGIC_TOGGLE_ESCAPE_CAPSLOCK, "MAGIC_TOGGLE_ESCAPE_CAPSLOCK"},
    {QK_MIDI_ON, "QK_MIDI_ON"},
    {QK_MIDI_OFF, "QK_MIDI_OFF"},
    {QK_MIDI_TOGGLE, "QK_MIDI_TOGGLE"},
    {QK_MIDI_NOTE_C_0, "QK_MIDI_NOTE_C_0"},
    {QK_MIDI_NOTE_C_SHARP_0, "QK_MIDI_NOTE_C_SHARP_0"},
    {QK_MIDI_NOTE_D_0, "QK_MIDI_NOTE_D_0"},
    {QK_MIDI_NOTE_D_SHARP_0, "QK_MIDI_NOTE_D_SHARP_0"},
    {QK_MIDI_NOTE_E_0, "QK_MIDI_NOTE_E_0"},
    {QK_MIDI_NOTE_F_0, "QK_MIDI_NOTE_F_0"},
    {QK_MIDI_NOTE_F_SHARP_0, "QK_MIDI_NOTE_F_SHARP_0"},
    {QK_MIDI_NOTE_G_0, "QK_MIDI_NOTE_G_0"},
    {QK_MIDI_NOTE_G_SHARP_0, "QK_MIDI_NOTE_G_SHARP_0"},
    {QK_MIDI_NOTE_A_0, "QK_MIDI_NOTE_A_0"},
    {QK_MIDI_NOTE_A_SHARP_0, "QK_MIDI_NOTE_A_SHARP_0"},
    {QK_MIDI_NOTE_B_0, "QK_MIDI_NOTE_B_0"},
    {QK_MIDI_NOTE_C_1, "QK_MIDI_NOTE_C_1"},
    {QK_MIDI_NOTE_C_SHARP_1, "QK_MIDI_NOTE_C_SHARP_1"},
    {QK_MIDI_NOTE_D_1, "QK_MIDI_NOTE_D_1"},
    {QK_MIDI_NOTE_D_SHARP_1, "QK_MIDI_NOTE_D_SHARP_1"},
    {QK_MIDI_NOTE_E_1, "QK_MIDI_NOTE_E_1"},
    {QK_MIDI_NOTE_F_1, "QK_MIDI_NOTE_F_1"},
    {QK_MIDI_NOTE_F_SHARP_1, "QK_MIDI_NOTE_F_SHARP_1"},
    {QK_MIDI_NOTE_G_1, "QK_MIDI_NOTE_G_1"},
    {QK_MIDI_NOTE_G_SHARP_1, "QK_MIDI_NOTE_G_SHARP_1"},
    {QK_MIDI_NOTE_A_1, "QK_MIDI_NOTE_A_1"},
    {QK_MIDI_NOTE_A_SHARP_1, "QK_MIDI_NOTE_A_SHARP_1"},
    {QK_MIDI_NOTE_B_1, "QK_MIDI_NOTE_B_1"},
    {QK_MIDI_NOTE_C_2, "QK_MIDI_NOTE_C_2"},
    {QK_MIDI_NOTE_C_SHARP_2, "QK_MIDI_NOTE_C_SHARP_2"},
    {QK_MIDI_NOTE_D_2, "QK_MIDI_NOTE_D_2"},
    {QK_MIDI_NOTE_D_SHARP_2, "QK_MIDI_NOTE_D_SHARP_2"},
    {QK_MIDI_NOTE_E_2, "QK_MIDI_NOTE_E_2"},
    {QK_MIDI_NOTE_F_2, "QK_MIDI_NOTE_F_2"},
    {QK_MIDI_NOTE_F_SHARP_2, "QK_MIDI_NOTE_F_SHARP_2"},
    {QK_MIDI_NOTE_G_2, "QK_MIDI_NOTE_G_2"},
    {QK_MIDI_NOTE_G_SHARP_2, "QK_MIDI_NOTE_G_SHARP_2"},
    {QK_MIDI_NOTE_A_2, "QK_MIDI_NOTE_A_2"},
    {QK_MIDI_NOTE_A_SHARP_2, "QK_MIDI_NOTE_A_SHARP_2"},
    {QK_MIDI_NOTE_B_2, "QK_MIDI_NOTE_B_2"},
    {QK_MIDI_NOTE_C_3, "QK_MIDI_NOTE_C_3"},
    {QK_MIDI_NOTE_C_SHARP_3, "QK_MIDI_NOTE_C_SHARP_3"},
    {QK_MIDI_NOTE_D_3, "QK_MIDI_NOTE_D_3"},
    {QK_MIDI_NOTE_D_SHARP_3, "QK_MIDI_NOTE_D_SHARP_3"},
    {QK_MIDI_NOTE_E_3, "QK_MIDI_NOTE_E_3"},
    {QK_MIDI_NOTE_F_3, "QK_MIDI_NOTE_F_3"},
    {QK_MIDI_NOTE_F_SHARP_3, "QK_MIDI_NOTE_F_SHARP_3"},
    {QK_MIDI_NOTE_G_3, "QK_MIDI_NOTE_G_3"},
    {QK_MIDI_NOTE_G_SHARP_3, "QK_MIDI_NOTE_G_SHARP_3"},
    {QK_MIDI_NOTE_A_3, "QK_MIDI_NOTE_A_3"},
    {QK_MIDI_NOTE_A_SHARP_3, "QK_MIDI_NOTE_A_SHARP_3"},
    {QK_MIDI_NOTE_B_3, "QK_MIDI_NOTE_B_3"},
    {QK_MIDI_NOTE_C_4, "QK_MIDI_NOTE_C_4"},
    {QK_MIDI_NOTE_C_SHARP_4, "QK_MIDI_NOTE_C_SHARP_4"},
    {QK_MIDI_NOTE_D_4, "QK_MIDI_NOTE_D_4"},
    {QK_MIDI_NOTE_D_SHARP_4, "QK_MIDI_NOTE_D_SHARP_4"},
    {QK_MIDI_NOTE_E_4, "QK_MIDI_NOTE_E_4"},
    {QK_MIDI_NOTE_F_4, "QK_MIDI_NOTE_F_4"},
    {QK_MIDI_NOTE_F_SHARP_4, "QK_MIDI_NOTE_F_SHARP_4"},
    {QK_MIDI_NOTE_G_4, "QK_MIDI_NOTE_G_4"},
    {QK_MIDI_NOTE_G_SHARP_4, "QK_MIDI_NOTE_G_SHARP_4"},
    {QK_MIDI_NOTE_A_4, "QK_MIDI_NOTE_A_4"},
    {QK_MIDI_NOTE_A_SHARP_4, "QK_MIDI_NOTE_A_SHARP_4"},
    {QK_MIDI_NOTE_B_4, "QK_MIDI_NOTE_B_4"},
    {QK_MIDI_NOTE_C_5, "QK_MIDI_NOTE_C_5"},
    {QK_MIDI_NOTE_C_SHARP_5, "QK_MIDI_NOTE_C_SHARP_5"},
    {QK_MIDI_NOTE_D_5, "QK_MIDI_NOTE_D_5"},
    {QK_MIDI_NOTE_D_SHARP_5, "QK_MIDI_NOTE_D_SHARP_5"},
    {QK_MIDI_NOTE_E_5, "QK_MIDI_NOTE_E_5"},
    {QK_MIDI_NOTE_F_5, "QK_MIDI_NOTE_F_5"},
    {QK_MIDI_NOTE_F_SHARP_5, "QK_MIDI_NOTE_F_SHARP_5"},
    {QK_MIDI_NOTE_G_5, "QK_MIDI_NOTE_G_5"},
    {QK_MIDI_NOTE_G_SHARP_5, "QK_MIDI_NOTE_G_SHARP_5"},
    {QK_MIDI_NOTE_A_5, "QK_MIDI_NOTE_A_5"},
    {QK_MIDI_NOTE_A_SHARP_5, "QK_MIDI_NOTE_A_SHARP_5"},
    {QK_MIDI_NOTE_B_5, "QK_MIDI_NOTE_B_5"},
    {QK_MIDI_OCTAVE_N2, "QK_MIDI_OCTAVE_N2"},
    {QK_MIDI_OCTAVE_N1, "QK_MIDI_OCTAVE_N1"},
    {QK_MIDI_OCTAVE_0, "QK_MIDI_OCTAVE_0"},
    {QK_MIDI_OCTAVE_1, "QK_MIDI_OCTAVE_1"},
    {QK_MIDI_OCTAVE_2, "QK_MIDI_OCTAVE_2"},
    {QK_MIDI_OCTAVE_3, "QK_MIDI_OCTAVE_3"},
    {QK_MIDI_OCTAVE_4, "QK_MIDI_OCTAVE_4"},
    {QK_MIDI_OCTAVE_5, "QK_MIDI_OCTAVE_5"},
    {QK_MIDI_OCTAVE_6, "QK_MIDI_OCTAVE_6"},
    {QK_MIDI_OCTAVE_7, "QK_MIDI_OCTAVE_7"},
    {QK_MIDI_OCTAVE_DOWN, "QK_MIDI_OCTAVE_DOWN"},
    {QK_MIDI_OCTAVE_UP, "QK_MIDI_OCTAVE_UP"},
    {QK_MIDI_TRANSPOSE_N6, "QK_MIDI_TRANSPOSE_N6"},
    {QK_MIDI_TRANSPOSE_N5, "QK_MIDI_TRANSPOSE_N5"},
    {QK_MIDI_TRANSPOSE_N4, "QK_MIDI_TRANSPOSE_N4"},
    {QK_MIDI_TRANSPOSE_N3, "QK_MIDI_TRANSPOSE_N3"},
    {QK_MIDI_TRANSPOSE_N2, "QK_MIDI_TRANSPOSE_N2"},
    {QK_MIDI_TRANSPOSE_N1, "QK_MIDI_TRANSPOSE_N1"},
    {QK_MIDI_TRANSPOSE_0, "QK_MIDI_TRANSPOSE_0"},
    {QK_MIDI_TRANSPOSE_1, "QK_MIDI_TRANSPOSE_1"},
    {QK_MIDI_TRANSPOSE_2, "QK_MIDI_TRANSPOSE_2"},
    {QK_MIDI_TRANSPOSE_3, "QK_MIDI_TRANSPOSE_3"},
    {QK_MIDI_TRANSPOSE_4, "QK_MIDI_TRANSPOSE_4"},
    {QK_MIDI_TRANSPOSE_5, "QK_MIDI_TRANSPOSE_5"},
    {QK_MIDI_TRANSPOSE_6, "QK_MIDI_TRANSPOSE_6"},
    {QK_MIDI_TRANSPOSE_DOWN, "QK_MIDI_TRANSPOSE_DOWN"},
    {QK_MIDI_TRANSPOSE_UP, "QK_MIDI_TRANSPOSE_UP"},
    {QK_MIDI_VELOCITY_0, "QK_MIDI_VELOCITY_0"},
    {QK_MIDI_VELOCITY_1, "QK_MIDI_VELOCITY_1"},
    {QK_MIDI_VELOCITY_2, "QK_MIDI_VELOCITY_2"},
    {QK_MIDI_VELOCITY_3, "QK_MIDI_VELOCITY_3"},
    {QK_MIDI_VELOCITY_4, "QK_MIDI_VELOCITY_4"},
    {QK_MIDI_VELOCITY_5, "QK_MIDI_VELOCITY_5"},
    {QK_MIDI_VELOCITY_6, "QK_MIDI_VELOCITY_6"},
    {QK_MIDI_VELOCITY_7, "QK_MIDI_VELOCITY_7"},
    {QK_MIDI_VELOCITY_8, "QK_MIDI_VELOCITY_8"},
    {QK_MIDI_VELOCITY_9, "QK_MIDI_VELOCITY_9"},
    {QK_MIDI_VELOCITY_10, "QK_MIDI_VELOCITY_10"},
    {QK_MIDI_VELOCITY_DOWN, "QK_MIDI_VELOCITY_DOWN"},
    {QK_MIDI_VELOCITY_UP, "QK_MIDI_VELOCITY_UP"},
    {QK_MIDI_CHANNEL_1, "QK_MIDI_CHANNEL_1"},
    {QK_MIDI_CHANNEL_2, "QK_MIDI_CHANNEL_2"},
    {QK_MIDI_CHANNEL_3, "QK_MIDI_CHANNEL_3"},
    {QK_MIDI_CHANNEL_4, "QK_MIDI_CHANNEL_4"},
    {QK_MIDI_CHANNEL_5, "QK_MIDI_CHANNEL_5"},
    {QK_MIDI_CHANNEL_6, "QK_MIDI_CHANNEL_6"},
    {QK_MIDI_CHANNEL_7, "QK_MIDI_CHANNEL_7"},
    {QK_MIDI_CHANNEL_8, "QK_MIDI_CHANNEL_8"},
    {QK_MIDI_CHANNEL_9, "QK_MIDI_CHANNEL_9"},
    {QK_MIDI_CHANNEL_10, "QK_MIDI_CHANNEL_10"},
    {QK_MIDI_CHANNEL_11, "QK_MIDI_CHANNEL_11"},
    {QK_MIDI_CHANNEL_12, "QK_MIDI_CHANNEL_12"},
    {QK_MIDI_CHANNEL_13, "QK_MIDI_CHANNEL_13"},
    {QK_MIDI_CHANNEL_14, "QK_MIDI_CHANNEL_14"},
    {QK_MIDI_CHANNEL_15, "QK_MIDI_CHANNEL_15"},
    {QK_MIDI_CHANNEL_16, "QK_MIDI_CHANNEL_16"},
    {QK_MIDI_CHANNEL_DOWN, "QK_MIDI_CHANNEL_DOWN"},
    {QK_MIDI_CHANNEL_UP, "QK_MIDI_CHANNEL_UP"},
    {QK_MIDI_ALL_NOTES_OFF, "QK_MIDI_ALL_NOTES_OFF"},
    {QK_MIDI_SUSTAIN, "QK_MIDI_SUSTAIN"},
    {QK_MIDI_PORTAMENTO, "QK_MIDI_PORTAMENTO"},
    {QK_MIDI_SOSTENUTO, "QK_MIDI_SOSTENUTO"},
    {QK_MIDI_SOFT, "QK_MIDI_SOFT"},
    {QK_MIDI_LEGATO, "QK_MIDI_LEGATO"},
    {QK_MIDI_MODULATION, "QK_MIDI_MODULATION"},
    {QK_MIDI_MODULATION_SPEED_DOWN, "QK_MIDI_MODULATION_SPEED_DOWN"},
    {QK_MIDI_MODULATION_SPEED_UP, "QK_MIDI_MODULATION_SPEED_UP"},
    {QK_MIDI_PITCH_BEND_DOWN, "QK_MIDI_PITCH_BEND_DOWN"},
    {QK_MIDI_PITCH_BEND_UP, "QK_MIDI_PITCH_BEND_UP"},
    {SQ_ON, "SQ_ON"},
    {SQ_OFF, "SQ_OFF"},
    {SQ_TOG, "SQ_TOG"},
    {SQ_TMPD, "SQ_TMPD"},
    {SQ_TMPU, "SQ_TMPU"},
    {SQ_RESD, "SQ_RESD"},
    {SQ_RESU, "SQ_RESU"},
    {SQ_SALL, "SQ_SALL"},
    {SQ_SCLR, "SQ_SCLR"},
    {QK_JOYSTICK_BUTTON_0, "QK_JOYSTICK_BUTTON_0"},
    {QK_JOYSTICK_BUTTON_1, "QK_JOYSTICK_BUTTON_1"},
    {QK_JOYSTICK_BUTTON_2, "QK_JOYSTICK_BUTTON_2"},
    {QK_JOYSTICK_BUTTON_3, "QK_JOYSTICK_BUTTON_3"},
    {QK_JOYSTICK_BUTTON_4, "QK_JOYSTICK_BUTTON_4"},
    {QK_JOYSTICK_BUTTON_5, "QK_JOYSTICK_BUTTON_5"},
    {QK_JOYSTICK_BUTTON_6, "QK_JOYSTICK_BUTTON_6"},
    {QK_JOYSTICK_BUTTON_7, "QK_JOYSTICK_BUTTON_7"},
    {QK_JOYSTICK_BUTTON_8, "QK_JOYSTICK_BUTTON_8"},
    {QK_JOYSTICK_BUTTON_9, "QK_JOYSTICK_BUTTON_9"},
    {QK_JOYSTICK_BUTTON_10, "QK_JOYSTICK_BUTTON_10"},
    {QK_JOYSTICK_BUTTON_11, "QK_JOYSTICK_BUTTON_11"},
    {QK_JOYSTICK_BUTTON_12, "QK_JOYSTICK_BUTTON_12"},
    {QK_JOYSTICK_BUTTON_13, "QK_JOYSTICK_BUTTON_13"},
    {QK_JOYSTICK_BUTTON_14, "QK_JOYSTICK_BUTTON_14"},
    {QK_JOYSTICK_BUTTON_15, "QK_JOYSTICK_BUTTON_15"},
    {QK_JOYSTICK_BUTTON_16, "QK_JOYSTICK_BUTTON_16"},
    {QK_JOYSTICK_BUTTON_17, "QK_JOYSTICK_BUTTON_17"},
    {QK_JOYSTICK_BUTTON_18, "QK_JOYSTICK_BUTTON_18"},
    {QK_JOYSTICK_BUTTON_19, "QK_JOYSTICK_BUTTON_19"},
    {QK_JOYSTICK_BUTTON_20, "QK_JOYSTICK_BUTTON_20"},
    {QK_JOYSTICK_BUTTON_21, "QK_JOYSTICK_BUTTON_21"},
    {QK_JOYSTICK_BUTTON_22, "QK_JOYSTICK_BUTTON_22"},
    {QK_JOYSTICK_BUTTON_23, "QK_JOYSTICK_BUTTON_23"},
    {QK_JOYSTICK_BUTTON_24, "QK_JOYSTICK_BUTTON_24"},
    {QK_JOYSTICK_BUTTON_25, "QK_JOYSTICK_BUTTON_25"},
    {QK_JOYSTICK_BUTTON_26, "QK_JOYSTICK_BUTTON_26"},
    {QK_JOYSTICK_BUTTON_27, "QK_JOYSTICK_BUTTON_27"},
    {QK_JOYSTICK_BUTTON_28, "QK_JOYSTICK_BUTTON_28"},
    {QK_JOYSTICK_BUTTON_29, "QK_JOYSTICK_BUTTON_29"},
    {QK_JOYSTICK_BUTTON_30, "QK_JOYSTICK_BUTTON_30"},
    {QK_JOYSTICK_BUTTON_31, "QK_JOYSTICK_BUTTON_31"},
    {QK_PROGRAMMABLE_BUTTON_1, "QK_PROGRAMMABLE_BUTTON_1"},
    {QK_PROGRAMMABLE_BUTTON_2, "QK_PROGRAMMABLE_BUTTON_2"},
    {QK_PROGRAMMABLE_BUTTON_3, "QK_PROGRAMMABLE_BUTTON_3"},
    {QK_PROGRAMMABLE_BUTTON_4, "QK_PROGRAMMABLE_BUTTON_4"},
    {QK_PROGRAMMABLE_BUTTON_5, "QK_PROGRAMMABLE_BUTTON_5"},
    {QK_PROGRAMMABLE_BUTTON_6, "QK_PROGRAMMABLE_BUTTON_6"},
    {QK_PROGRAMMABLE_BUTTON_7, "QK_PROGRAMMABLE_BUTTON_7"},
    {QK_PROGRAMMABLE_BUTTON_8, "QK_PROGRAMMABLE_BUTTON_8"},
    {QK_PROGRAMMABLE_BUTTON_9, "QK_PROGRAMMABLE_BUTTON_9"},
    {QK_PROGRAMMABLE_BUTTON_10, "QK_PROGRAMMABLE_BUTTON_10"},
    {QK_PROGRAMMABLE_BUTTON_11, "QK_PROGRAMMABLE_BUTTON_11"},
    {QK_PROGRAMMABLE_BUTTON_12, "QK_PROGRAMMABLE_BUTTON_12"},
    {QK_PROGRAMMABLE_BUTTON_13, "QK_PROGRAMMABLE_BUTTON_13"},
    {QK_PROGRAMMABLE_BUTTON_14, "QK_PROGRAMMABLE_BUTTON_14"},
    {QK_PROGRAMMABLE_BUTTON_15, "QK_PROGRAMMABLE_BUTTON_15"},
    {QK_PROGRAMMABLE_BUTTON_16, "QK_PROGRAMMABLE_BUTTON_16"},
    {QK_PROGRAMMABLE_BUTTON_17, "QK_PROGRAMMABLE_BUTTON_17"},
    {QK_PROGRAMMABLE_BUTTON_18, "QK_PROGRAMMABLE_BUTTON_18"},
    {QK_PROGRAMMABLE_BUTTON_19, "QK_PROGRAMMABLE_BUTTON_19"},
    {QK_PROGRAMMABLE_BUTTON_20, "QK_PROGRAMMABLE_BUTTON_20"},
    {QK_PROGRAMMABLE_BUTTON_21, "QK_PROGRAMMABLE_BUTTON_21"},
    {QK_PROGRAMMABLE_BUTTON_22, "QK_PROGRAMMABLE_BUTTON_22"},
    {QK_PROGRAMMABLE_BUTTON_23, "QK_PROGRAMMABLE_BUTTON_23"},
    {QK_PROGRAMMABLE_BUTTON_24, "QK_PROGRAMMABLE_BUTTON_24"},
    {QK_PROGRAMMABLE_BUTTON_25, "QK_PROGRAMMABLE_BUTTON_25"},
    {QK_PROGRAMMABLE_BUTTON_26, "QK_PROGRAMMABLE_BUTTON_26"},
    {QK_PROGRAMMABLE_BUTTON_27, "QK_PROGRAMMABLE_BUTTON_27"},
    {QK_PROGRAMMABLE_BUTTON_28, "QK_PROGRAMMABLE_BUTTON_28"},
    {QK_PROGRAMMABLE_BUTTON_29, "QK_PROGRAMMABLE_BUTTON_29"},
    {QK_PROGRAMMABLE_BUTTON_30, "QK_PROGRAMMABLE_BUTTON_30"},
    {QK_PROGRAMMABLE_BUTTON_31, "QK_PROGRAMMABLE_BUTTON_31"},
    {QK_PROGRAMMABLE_BUTTON_32, "QK_PROGRAMMABLE_BUTTON_32"},
    {QK_AUDIO_ON, "QK_AUDIO_ON"},
    {QK_AUDIO_OFF, "QK_AUDIO_OFF"},
    {QK_AUDIO_TOGGLE, "QK_AUDIO_TOGGLE"},
    {QK_AUDIO_CLICKY_TOGGLE, "QK_AUDIO_CLICKY_TOGGLE"},
    {QK_AUDIO_CLICKY_ON, "QK_AUDIO_CLICKY_ON"},
    {QK_AUDIO_CLICKY_OFF, "QK_AUDIO_CLICKY_OFF"},
    {QK_AUDIO_CLICKY_UP, "QK_AUDIO_CLICKY_UP"},
    {QK_AUDIO_CLICKY_DOWN, "QK_AUDIO_CLICKY_DOWN"},
    {QK_AUDIO_CLICKY_RESET, "QK_AUDIO_CLICKY_RESET"},
    {QK_MUSIC_ON, "QK_MUSIC_ON"},
    {QK_MUSIC_OFF, "QK_MUSIC_OFF"},
    {QK_MUSIC_TOGGLE, "QK_MUSIC_TOGGLE"},
    {QK_MUSIC_MODE_NEXT, "QK_MUSIC_MODE_NEXT"},
    {QK_AUDIO_VOICE_NEXT, "QK_AUDIO_VOICE_NEXT"},
    {QK_AUDIO_VOICE_PREVIOUS, "QK_AUDIO_VOICE_PREVIOUS"},
    {QK_STENO_BOLT, "QK_STENO_BOLT"},
    {QK_STENO_GEMINI, "QK_STENO_GEMINI"},
    {QK_STENO_COMB, "QK_STENO_COMB"},
    {QK_STENO_COMB_MAX, "QK_STENO_COMB_MAX"},
    {QK_MACRO_0, "QK_MACRO_0"},
    {QK_MACRO_1, "QK_MACRO_1"},
    {QK_MACRO_2, "QK_MACRO_2"},
    {QK_MACRO_3, "QK_MACRO_3"},
    {QK_MACRO_4, "QK_MACRO_4"},
    {QK_MACRO_5, "QK_MACRO_5"},
    {QK_MACRO_6, "QK_MACRO_6"},
    {QK_MACRO_7, "QK_MACRO_7"},
    {QK_MACRO_8, "QK_MACRO_8"},
    {QK_MACRO_9, "QK_MACRO_9"},
    {QK_MACRO_10, "QK_MACRO_10"},
    {QK_MACRO_11, "QK_MACRO_11"},
    {QK_MACRO_12, "QK_MACRO_12"},
    {QK_MACRO_13, "QK_MACRO_13"},
    {QK_MACRO_14, "QK_MACRO_14"},
    {QK_MACRO_15, "QK_MACRO_15"},
    {QK_MACRO_16, "QK_MACRO_16"},
    {QK_MACRO_17, "QK_MACRO_17"},
    {QK_MACRO_18, "QK_MACRO_18"},
    {QK_MACRO_19, "QK_MACRO_19"},
    {QK_MACRO_20, "QK_MACRO_20"},
    {QK_MACRO_21, "QK_MACRO_21"},
    {QK_MACRO_22, "QK_MACRO_22"},
    {QK_MACRO_23, "QK_MACRO_23"},
    {QK_MACRO_24, "QK_MACRO_24"},
    {QK_MACRO_25, "QK_MACRO_25"},
    {QK_MACRO_26, "QK_MACRO_26"},
    {QK_MACRO_27, "QK_MACRO_27"},
    {QK_MACRO_28, "QK_MACRO_28"},
    {QK_MACRO_29, "QK_MACRO_29"},
    {QK_MACRO_30, "QK_MACRO_30"},
    {QK_MACRO_31, "QK_MACRO_31"},
    {QK_BACKLIGHT_ON, "QK_BACKLIGHT_ON"},
    {QK_BACKLIGHT_OFF, "QK_BACKLIGHT_OFF"},
    {QK_BACKLIGHT_TOGGLE, "QK_BACKLIGHT_TOGGLE"},
    {QK_BACKLIGHT_DOWN, "QK_BACKLIGHT_DOWN"},
    {QK_BACKLIGHT_UP, "QK_BACKLIGHT_UP"},
    {QK_BACKLIGHT_STEP, "QK_BACKLIGHT_STEP"},
    {QK_BACKLIGHT_TOGGLE_BREATHING, "QK_BACKLIGHT_TOGGLE_BREATHING"},
    {RGB_TOG, "RGB_TOG"},
    {RGB_MODE_FORWARD, "RGB_MODE_FORWARD"},
    {RGB_MODE_REVERSE, "RGB_MODE_REVERSE"},
    {RGB_HUI, "RGB_HUI"},
    {RGB_HUD, "RGB_HUD"},
    {RGB_SAI, "RGB_SAI"},
    {RGB_SAD, "RGB_SAD"},
    {RGB_VAI, "RGB_VAI"},
    {RGB_VAD, "RGB_VAD"},
    {RGB_SPI, "RGB_SPI"},
    {RGB_SPD, "RGB_SPD"},
    {RGB_MODE_PLAIN, "RGB_MODE_PLAIN"},
    {RGB_MODE_BREATHE, "RGB_MODE_BREATHE"},
    {RGB_MODE_RAINBOW, "RGB_MODE_RAINBOW"},
    {RGB_MODE_SWIRL, "RGB_MODE_SWIRL"},
    {RGB_MODE_SNAKE, "RGB_MODE_SNAKE"},
    {RGB_MODE_KNIGHT, "RGB_MODE_KNIGHT"},
    {RGB_MODE_XMAS, "RGB_MODE_XMAS"},
    {RGB_MODE_GRADIENT, "RGB_MODE_GRADIENT"},
    {RGB_MODE_RGBTEST, "RGB_MODE_RGBTEST"},
    {RGB_MODE_TWINKLE, "RGB_MODE_TWINKLE"},
    {QK_BOOTLOADER, "QK_BOOTLOADER"},
    {QK_REBOOT, "QK_REBOOT"},
    {QK_DEBUG_TOGGLE, "QK_DEBUG_TOGGLE"},
    {QK_CLEAR_EEPROM, "QK_CLEAR_EEPROM"},
    {QK_MAKE, "QK_MAKE"},
    {QK_AUTO_SHIFT_DOWN, "QK_AUTO_SHIFT_DOWN"},
    {QK_AUTO_SHIFT_UP, "QK_AUTO_SHIFT_UP"},
    {QK_AUTO_SHIFT_REPORT, "QK_AUTO_SHIFT_REPORT"},
    {QK_AUTO_SHIFT_ON, "QK_AUTO_SHIFT_ON"},
    {QK_AUTO_SHIFT_OFF, "QK_AUTO_SHIFT_OFF"},
    {QK_AUTO_SHIFT_TOGGLE, "QK_AUTO_SHIFT_TOGGLE"},
    {QK_GRAVE_ESCAPE, "QK_GRAVE_ESCAPE"},
    {QK_VELOCIKEY_TOGGLE, "QK_VELOCIKEY_TOGGLE"},
    {QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN, "QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN"},
    {QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE, "QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE"},
    {QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN, "QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN"},
    {QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE, "QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE"},
    {QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN, "QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN"},
    {QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE, "QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE"},
    {QK_SPACE_CADET_RIGHT_SHIFT_ENTER, "QK_SPACE_CADET_RIGHT_SHIFT_ENTER"},
    {QK_OUTPUT_AUTO, "QK_OUTPUT_AUTO"},
    {QK_OUTPUT_USB, "QK_OUTPUT_USB"},
    {QK_OUTPUT_BLUETOOTH, "QK_OUTPUT_BLUETOOTH"},
    {QK_UNICODE_MODE_NEXT, "QK_UNICODE_MODE_NEXT"},
    {QK_UNICODE_MODE_PREVIOUS, "QK_UNICODE_MODE_PREVIOUS"},
    {QK_UNICODE_MODE_MACOS, "QK_UNICODE_MODE_MACOS"},
    {QK_UNICODE_MODE_LINUX, "QK_UNICODE_MODE_LINUX"},
    {QK_UNICODE_MODE_WINDOWS, "QK_UNICODE_MODE_WINDOWS"},
    {QK_UNICODE_MODE_BSD, "QK_UNICODE_MODE_BSD"},
    {QK_UNICODE_MODE_WINCOMPOSE, "QK_UNICODE_MODE_WINCOMPOSE"},
    {QK_UNICODE_MODE_EMACS, "QK_UNICODE_MODE_EMACS"},
    {QK_HAPTIC_ON, "QK_HAPTIC_ON"},
    {QK_HAPTIC_OFF, "QK_HAPTIC_OFF"},
    {QK_HAPTIC_TOGGLE, "QK_HAPTIC_TOGGLE"},
    {QK_HAPTIC_RESET, "QK_HAPTIC_RESET"},
    {QK_HAPTIC_FEEDBACK_TOGGLE, "QK_HAPTIC_FEEDBACK_TOGGLE"},
    {QK_HAPTIC_BUZZ_TOGGLE, "QK_HAPTIC_BUZZ_TOGGLE"},
    {QK_HAPTIC_MODE_NEXT, "QK_HAPTIC_MODE_NEXT"},
    {QK_HAPTIC_MODE_PREVIOUS, "QK_HAPTIC_MODE_PREVIOUS"},
    {QK_HAPTIC_CONTINUOUS_TOGGLE, "QK_HAPTIC_CONTINUOUS_TOGGLE"},
    {QK_HAPTIC_CONTINUOUS_UP, "QK_HAPTIC_CONTINUOUS_UP"},
    {QK_HAPTIC_CONTINUOUS_DOWN, "QK_HAPTIC_CONTINUOUS_DOWN"},
    {QK_HAPTIC_DWELL_UP, "QK_HAPTIC_DWELL_UP"},
    {QK_HAPTIC_DWELL_DOWN, "QK_HAPTIC_DWELL_DOWN"},
    {QK_COMBO_ON, "QK_COMBO_ON"},
    {QK_COMBO_OFF, "QK_COMBO_OFF"},
    {QK_COMBO_TOGGLE, "QK_COMBO_TOGGLE"},
    {QK_DYNAMIC_MACRO_RECORD_START_1, "QK_DYNAMIC_MACRO_RECORD_START_1"},
    {QK_DYNAMIC_MACRO_RECORD_START_2, "QK_DYNAMIC_MACRO_RECORD_START_2"},
    {QK_DYNAMIC_MACRO_RECORD_STOP, "QK_DYNAMIC_MACRO_RECORD_STOP"},
    {QK_DYNAMIC_MACRO_PLAY_1, "QK_DYNAMIC_MACRO_PLAY_1"},
    {QK_DYNAMIC_MACRO_PLAY_2, "QK_DYNAMIC_MACRO_PLAY_2"},
    {QK_LEADER, "QK_LEADER"},
    {QK_LOCK, "QK_LOCK"},
    {QK_ONE_SHOT_ON, "QK_ONE_SHOT_ON"},
    {QK_ONE_SHOT_OFF, "QK_ONE_SHOT_OFF"},
    {QK_ONE_SHOT_TOGGLE, "QK_ONE_SHOT_TOGGLE"},
    {QK_KEY_OVERRIDE_TOGGLE, "QK_KEY_OVERRIDE_TOGGLE"},
    {QK_KEY_OVERRIDE_ON, "QK_KEY_OVERRIDE_ON"},
    {QK_KEY_OVERRIDE_OFF, "QK_KEY_OVERRIDE_OFF"},
    {QK_SECURE_LOCK, "QK_SECURE_LOCK"},
    {QK_SECURE_UNLOCK, "QK_SECURE_UNLOCK"},
    {QK_SECURE_TOGGLE, "QK_SECURE_TOGGLE"},
    {QK_SECURE_REQUEST, "QK_SECURE_REQUEST"},
    {QK_DYNAMIC_TAPPING_TERM_PRINT, "QK_DYNAMIC_TAPPING_TERM_PRINT"},
    {QK_DYNAMIC_TAPPING_TERM_UP, "QK_DYNAMIC_TAPPING_TERM_UP"},
    {QK_DYNAMIC_TAPPING_TERM_DOWN, "QK_DYNAMIC_TAPPING_TERM_DOWN"},
    {QK_CAPS_WORD_TOGGLE, "QK_CAPS_WORD_TOGGLE"},
    {QK_AUTOCORRECT_ON, "QK_AUTOCORRECT_ON"},
    {QK_AUTOCORRECT_OFF, "QK_AUTOCORRECT_OFF"},
    {QK_AUTOCORRECT_TOGGLE, "QK_AUTOCORRECT_TOGGLE"},
    {SAFE_RANGE, "SAFE_RANGE"},
};

A tests/test_common/keycode_util.cpp => tests/test_common/keycode_util.cpp +128 -0
@@ 0,0 1,128 @@
#include "keycode_util.hpp"
#include <cstdint>
extern "C" {
#include "action_code.h"
#include "keycode.h"
#include "quantum_keycodes.h"
#include "util.h"
}
#include <string>
#include <iomanip>
#include <map>

extern std::map<uint16_t, std::string> KEYCODE_ID_TABLE;

std::string get_mods(uint8_t mods) {
    std::stringstream s;
    if ((mods & MOD_RCTL) == MOD_RCTL) {
        s << XSTR(MOD_RCTL) << " | ";
    } else if ((mods & MOD_LCTL) == MOD_LCTL) {
        s << XSTR(MOD_LCTL) << " | ";
    }

    if ((mods & MOD_RSFT) == MOD_RSFT) {
        s << XSTR(MOD_RSFT) << " | ";
    } else if ((mods & MOD_LSFT) == MOD_LSFT) {
        s << XSTR(MOD_LSFT) << " | ";
    }

    if ((mods & MOD_RALT) == MOD_RALT) {
        s << XSTR(MOD_RALT) << " | ";
    } else if ((mods & MOD_LALT) == MOD_LALT) {
        s << XSTR(MOD_LALT) << " | ";
    }

    if ((mods & MOD_RGUI) == MOD_RGUI) {
        s << XSTR(MOD_RGUI) << " | ";
    } else if ((mods & MOD_LGUI) == MOD_LGUI) {
        s << XSTR(MOD_LGUI) << " | ";
    }

    auto _mods = s.str();

    if (_mods.size()) {
        _mods.resize(_mods.size() - 3);
    }

    return std::string(_mods);
}

std::string get_qk_mods(uint16_t keycode) {
    std::stringstream s;
    if ((keycode & QK_RCTL) == QK_RCTL) {
        s << XSTR(QK_RCTL) << " | ";
    } else if ((keycode & QK_LCTL) == QK_LCTL) {
        s << XSTR(QK_LCTL) << " | ";
    }

    if ((keycode & QK_RSFT) == QK_RSFT) {
        s << XSTR(QK_RSFT) << " | ";
    } else if ((keycode & QK_LSFT) == QK_LSFT) {
        s << XSTR(QK_LSFT) << " | ";
    }

    if ((keycode & QK_RALT) == QK_RALT) {
        s << XSTR(QK_RALT) << " | ";
    } else if ((keycode & QK_LALT) == QK_LALT) {
        s << XSTR(QK_LALT) << " | ";
    }

    if ((keycode & QK_RGUI) == QK_RGUI) {
        s << XSTR(QK_RGUI) << " | ";
    } else if ((keycode & QK_LGUI) == QK_LGUI) {
        s << XSTR(QK_LGUI) << " | ";
    }

    auto _mods = s.str();

    if (_mods.size()) {
        _mods.resize(_mods.size() - 3);
    }

    return std::string(_mods);
}

std::string generate_identifier(uint16_t kc) {
    std::stringstream s;
    if (IS_QK_MOD_TAP(kc)) {
        s << "MT(" << get_mods(QK_MOD_TAP_GET_MODS(kc)) << ", " << KEYCODE_ID_TABLE.at(kc & 0xFF) << ")";
    } else if (IS_QK_LAYER_TAP(kc)) {
        s << "LT(" << +QK_LAYER_TAP_GET_LAYER(kc) << ", " << KEYCODE_ID_TABLE.at(kc & 0xFF) << ")";
    } else if (IS_QK_TO(kc)) {
        s << "TO(" << +QK_TO_GET_LAYER(kc) << ")";
    } else if (IS_QK_MOMENTARY(kc)) {
        s << "MO(" << +QK_MOMENTARY_GET_LAYER(kc) << ")";
    } else if (IS_QK_DEF_LAYER(kc)) {
        s << "DF(" << +QK_DEF_LAYER_GET_LAYER(kc) << ")";
    } else if (IS_QK_TOGGLE_LAYER(kc)) {
        s << "TG(" << +QK_TOGGLE_LAYER_GET_LAYER(kc) << ")";
    } else if (IS_QK_LAYER_TAP_TOGGLE(kc)) {
        s << "TT(" << +QK_LAYER_TAP_TOGGLE_GET_LAYER(kc) << ")";
    } else if (IS_QK_ONE_SHOT_LAYER(kc)) {
        s << "OSL(" << +QK_ONE_SHOT_LAYER_GET_LAYER(kc) << ")";
    } else if (IS_QK_LAYER_MOD(kc)) {
        s << "LM(" << +QK_LAYER_MOD_GET_LAYER(kc) << ", " << get_mods(QK_LAYER_MOD_GET_MODS(kc)) << ")";
    } else if (IS_QK_ONE_SHOT_MOD(kc)) {
        s << "OSM(" << get_mods(QK_ONE_SHOT_MOD_GET_MODS(kc)) << ")";
    } else if (IS_QK_MODS(kc)) {
        s << "QK_MODS(" << KEYCODE_ID_TABLE.at(QK_MODS_GET_BASIC_KEYCODE(kc)) << ", " << get_qk_mods(kc) << ")";
    } else if (IS_QK_TAP_DANCE(kc)) {
        s << "TD(" << +(kc & 0xFF) << ")";
    } else {
        // Fallback - we didn't found any matching keycode, generate the hex representation.
        s << "unknown keycode: 0x" << std::hex << kc << ". Add conversion to " << XSTR(generate_identifier);
    }

    return std::string(s.str());
}

std::string get_keycode_identifier_or_default(uint16_t keycode) {
    auto identifier = KEYCODE_ID_TABLE.find(keycode);
    if (identifier != KEYCODE_ID_TABLE.end()) {
        return identifier->second;
    }

    KEYCODE_ID_TABLE[keycode] = generate_identifier(keycode);

    return KEYCODE_ID_TABLE[keycode];
}

A tests/test_common/keycode_util.hpp => tests/test_common/keycode_util.hpp +5 -0
@@ 0,0 1,5 @@
#pragma once

#include <string>

std::string get_keycode_identifier_or_default(uint16_t keycode);

M tests/test_common/matrix.c => tests/test_common/matrix.c +6 -2
@@ 41,11 41,15 @@ void matrix_init_kb(void) {}
void matrix_scan_kb(void) {}

void press_key(uint8_t col, uint8_t row) {
    matrix[row] |= 1 << col;
    matrix[row] |= (matrix_row_t)1 << col;
}

void release_key(uint8_t col, uint8_t row) {
    matrix[row] &= ~(1 << col);
    matrix[row] &= ~((matrix_row_t)1 << col);
}

bool matrix_is_on(uint8_t row, uint8_t col) {
    return (matrix[row] & ((matrix_row_t)1 << col));
}

void clear_all_keys(void) {

M tests/test_common/test_common.hpp => tests/test_common/test_common.hpp +1 -0
@@ 22,5 22,6 @@ extern "C" {
}
#include "test_driver.hpp"
#include "test_matrix.h"
#include "test_keymap_key.hpp"
#include "keyboard_report_util.hpp"
#include "test_fixture.hpp"

M tests/test_common/test_fixture.cpp => tests/test_common/test_fixture.cpp +16 -13
@@ 12,6 12,7 @@
#include "test_logger.hpp"
#include "test_matrix.h"
#include "test_keymap_key.hpp"
#include "timer.h"

extern "C" {
#include "action.h"


@@ 41,7 42,7 @@ extern "C" uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t position) {
}

void TestFixture::SetUpTestCase() {
    test_logger.info() << "TestFixture setup-up start." << std::endl;
    test_logger.info() << "test fixture setup-up start." << std::endl;

    // The following is enough to bootstrap the values set in main
    eeconfig_init_quantum();


@@ 50,17 51,19 @@ void TestFixture::SetUpTestCase() {
    TestDriver driver;
    keyboard_init();

    test_logger.info() << "TestFixture setup-up end." << std::endl;
    test_logger.info() << "test fixture setup-up end." << std::endl;
}

void TestFixture::TearDownTestCase() {}

TestFixture::TestFixture() {
    m_this = this;
    timer_clear();
    test_logger.info() << "tapping term is " << +GET_TAPPING_TERM(KC_TRANSPARENT, &(keyrecord_t){}) << "ms" << std::endl;
}

TestFixture::~TestFixture() {
    test_logger.info() << "TestFixture clean-up start." << std::endl;
    test_logger.info() << "test fixture clean-up start." << std::endl;
    TestDriver driver;

    /* Reset keyboard state. */


@@ 85,17 88,15 @@ TestFixture::~TestFixture() {
    EXPECT_NO_REPORT(driver);
    idle_for(TAPPING_TERM * 10);
    testing::Mock::VerifyAndClearExpectations(&driver);

    m_this = nullptr;

    test_logger.info() << "TestFixture clean-up end." << std::endl;

    test_logger.info() << "test fixture clean-up end." << std::endl;
    print_test_log();
}

void TestFixture::add_key(KeymapKey key) {
    if (this->find_key(key.layer, key.position)) {
        FAIL() << "Key is already mapped for layer " << +key.layer << " and (column,row) (" << +key.position.col << "," << +key.position.row << ")";
        FAIL() << "key is already mapped for layer " << +key.layer << " and (column,row) (" << +key.position.col << "," << +key.position.row << ")";
    }

    this->keymap.push_back(key);


@@ 149,7 150,7 @@ void TestFixture::get_keycode(const layer_t layer, const keypos_t position, uint
        /* See if this is done in hardware as well, because this is 100% out of bounds reads on all QMK keebs out there. */
        auto msg = [&]() {
            std::stringstream msg;
            msg << "Keycode for position (" << +position.col << "," << +position.row << ") requested! This is out of bounds." << std::endl;
            msg << "keycode for position (" << +position.col << "," << +position.row << ") requested! This is out of bounds." << std::endl;
            return msg.str();
        }();



@@ 164,17 165,18 @@ void TestFixture::get_keycode(const layer_t layer, const keypos_t position, uint
        return;
    }

    FAIL() << "No key is mapped for layer " << +layer << " and (column,row) " << +position.col << "," << +position.row << ")";
    FAIL() << "no key is mapped for layer " << +layer << " and (column,row) " << +position.col << "," << +position.row << ")";
}

void TestFixture::run_one_scan_loop() {
    keyboard_task();
    advance_time(1);
    this->idle_for(1);
}

void TestFixture::idle_for(unsigned time) {
    test_logger.trace() << +time << " keyboard task " << (time > 1 ? "loops" : "loop") << std::endl;
    for (unsigned i = 0; i < time; i++) {
        run_one_scan_loop();
        keyboard_task();
        advance_time(1);
    }
}



@@ 182,12 184,13 @@ void TestFixture::print_test_log() const {
    const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
    if (HasFailure()) {
        std::cerr << test_info->test_case_name() << "." << test_info->name() << " failed!" << std::endl;
        test_logger.print_header();
        test_logger.print_log();
    }
    test_logger.reset();
}

void TestFixture::expect_layer_state(layer_t layer_state) const {
    test_logger.trace() << "Layer state: (" << +layer_state << ") Highest layer bit: (" << +get_highest_layer(layer_state) << ")" << std::endl;
    test_logger.trace() << "layer state: (" << +layer_state << ") highest layer bit: (" << +get_highest_layer(layer_state) << ")" << std::endl;
    EXPECT_TRUE(layer_state_is(layer_state));
}

M tests/test_common/test_keymap_key.cpp => tests/test_common/test_keymap_key.cpp +13 -3
@@ 15,16 15,26 @@
 */

#include "test_keymap_key.hpp"
#include <cstdint>
#include <ios>
#include "matrix.h"
#include "test_logger.hpp"
#include "gtest/gtest-message.h"
#include "gtest/gtest.h"
#include "timer.h"

void KeymapKey::press() {
    test_logger.trace() << "Key pressed:  (" << +this->position.col << "," << +this->position.row << ")" << std::endl;
    EXPECT_FALSE(matrix_is_on(position.row, position.col)) << "tried to press key " << this->name << " that was already pressed! Check the test code." << std::endl;

    press_key(this->position.col, this->position.row);
    this->timestamp_pressed = timer_read32();
    test_logger.trace() << std::setw(10) << std::left << "pressed: " << this->name << std::endl;
}

void KeymapKey::release() {
    test_logger.trace() << "Key released: (" << +this->position.col << "," << +this->position.row << ")" << std::endl;
    EXPECT_TRUE(matrix_is_on(this->position.row, this->position.col)) << "tried to release key " << this->name << " that wasn't pressed before! Check the test code." << std::endl;

    release_key(this->position.col, this->position.row);
}
\ No newline at end of file
    uint32_t now = timer_read32();
    test_logger.trace() << std::setw(10) << std::left << "released: " << this->name << " was pressed for " << now - this->timestamp_pressed << "ms" << std::endl;
}

M tests/test_common/test_keymap_key.hpp => tests/test_common/test_keymap_key.hpp +13 -3
@@ 16,6 16,9 @@

#pragma once

#include <cstddef>
#include <string>
#include "keycode_util.hpp"
extern "C" {
#include "keyboard.h"
#include "test_matrix.h"


@@ 26,8 29,13 @@ extern "C" {
typedef uint8_t layer_t;

struct KeymapKey {
    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(keycode) { validate(); }
    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode, uint16_t report_code) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(report_code) { validate(); }
    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(keycode), name(get_keycode_identifier_or_default(keycode)) {
        validate();
    }

    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode, uint16_t report_code) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(report_code), name{get_keycode_identifier_or_default(keycode)} {
        validate();
    }

    void press();
    void release();


@@ 35,6 43,7 @@ struct KeymapKey {
    const layer_t  layer;
    const keypos_t position;
    const uint16_t code;
    std::string    name;
    /* Sometimes the keycode does not match the code that is send in the usb report, so we provide it here. */
    const uint16_t report_code;



@@ 43,4 52,5 @@ struct KeymapKey {
        assert(position.col <= MATRIX_COLS);
        assert(position.row <= MATRIX_ROWS);
    }
};
\ No newline at end of file
    uint32_t timestamp_pressed;
};

M tests/test_common/test_logger.cpp => tests/test_common/test_logger.cpp +13 -3
@@ 14,30 14,40 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iomanip>
#include <iostream>
#include "test_logger.hpp"
#include "timer.h"

TestLogger test_logger;

TestLogger& TestLogger::info() {
    *this << "[ INFO     ] ";
    return *this;
    return this->timestamp();
}

TestLogger& TestLogger::trace() {
    *this << "[ TRACE    ] ";
    return *this;
    return this->timestamp();
}

TestLogger& TestLogger::error() {
    *this << "[ ERROR    ] ";
    return *this;
    return this->timestamp();
}

TestLogger& TestLogger::timestamp() {
    *this << std::setw(6) << timer_read32() << " ";
    return *this;
}
void TestLogger::reset() {
    this->m_log.str("");
};

void TestLogger::print_header() {
    std::cerr << "[ LEVEL    ] [TIME] [EVENT]" << std::endl;
}

void TestLogger::print_log() {
    std::cerr << this->m_log.str();
}

M tests/test_common/test_logger.hpp => tests/test_common/test_logger.hpp +5 -3
@@ 25,11 25,13 @@ class TestLogger : public std::ostream {
    TestLogger& info();
    TestLogger& trace();
    TestLogger& error();
    void print_log();
    void reset();
    void        print_log();
    void        print_header();
    void        reset();

   private:
    TestLogger&    timestamp();
    std::stringbuf m_log;
};

extern TestLogger test_logger;
\ No newline at end of file
extern TestLogger test_logger;