/* -*- mode: c++ -*-
* Copyright (C) 2020 Keyboard.io, Inc.
*
* 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, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
#include "testing/setup-googletest.h"
SETUP_GOOGLETEST();
namespace kaleidoscope {
namespace testing {
namespace {
using ::testing::IsEmpty;
class LayerActivationOrder : public VirtualDeviceTest {
public:
const KeyAddr KEYSWITCH_TOP_LEFT = KeyAddr{0, 0}; // layer-dependent key
const KeyAddr KEYSWITCH_TOP_RIGHT = KeyAddr{0, 15}; // layer-dependent key
const KeyAddr KEYSWITCH_LEFT_PALM = KeyAddr{3, 6}; // ShiftToLayer(1)
const KeyAddr KEYSWITCH_RIGHT_PALM = KeyAddr{3, 9}; // ShiftToLayer(2)
const KeyAddr KEYSWITCH_LEFT_THUMB_RIGHTMOST = KeyAddr{3, 7}; // L0 deactivate macro
const Key LAYER0_KEY = Key_0;
const Key LAYER1_KEY = Key_1;
const Key LAYER2_KEY = Key_2;
void pressKeyswitch(const KeyAddr& addr) {
sim_.Press(addr.row(), addr.col());
}
void releaseKeyswitch(const KeyAddr& addr) {
sim_.Release(addr.row(), addr.col());
}
auto pressKeyswitchAndRunCycle(const KeyAddr& addr) {
pressKeyswitch(addr);
return RunCycle();
}
auto releaseKeyswitchAndRunCycle(const KeyAddr& addr) {
releaseKeyswitch(addr);
return RunCycle();
}
void assertSingleKeyboardReportContaining(std::unique_ptr &state, Key k) {
ASSERT_EQ(state->HIDReports()->Keyboard().size(), 1);
EXPECT_THAT(
state->HIDReports()->Keyboard(0).ActiveKeycodes(),
Contains(k));
}
void assertSingleKeyboardReportNotContaining(std::unique_ptr &state, Key k) {
ASSERT_EQ(state->HIDReports()->Keyboard().size(), 1);
EXPECT_THAT(
state->HIDReports()->Keyboard(0).ActiveKeycodes(),
::testing::Not(Contains(k)));
}
void assertSingleEmptyReport(std::unique_ptr &state) {
ASSERT_EQ(state->HIDReports()->Keyboard().size(), 1);
EXPECT_THAT(
state->HIDReports()->Keyboard(0).ActiveKeycodes(),
IsEmpty());
}
void assertNoReport(std::unique_ptr &state) {
ASSERT_EQ(state->HIDReports()->Keyboard().size(), 0);
}
void assertNoReportAfterCycle() {
auto state = RunCycle();
assertNoReport(state);
}
void TestPressAndRelease(const KeyAddr& addr, Key k) {
auto state = pressKeyswitchAndRunCycle(addr);
assertSingleKeyboardReportContaining(state, k);
state = releaseKeyswitchAndRunCycle(addr);
assertSingleEmptyReport(state);
assertNoReportAfterCycle();
}
};
TEST_F(LayerActivationOrder, BaseLayerHasNotRegressed) {
TestPressAndRelease(KEYSWITCH_TOP_LEFT, LAYER0_KEY);
}
TEST_F(LayerActivationOrder, ShifToLayerOne) {
// Pressing (KEYSWITCH_LEFT_PALM) shifts to Layer 1, and we stay there until release.
auto state = pressKeyswitchAndRunCycle(KEYSWITCH_LEFT_PALM);
TestPressAndRelease(KEYSWITCH_TOP_LEFT, LAYER1_KEY);
// Releasing (KEYSWITCH_LEFT_PALM) gets us back to the base layer
state = releaseKeyswitchAndRunCycle(KEYSWITCH_LEFT_PALM);
TestPressAndRelease(KEYSWITCH_TOP_LEFT, LAYER0_KEY);
}
TEST_F(LayerActivationOrder, ShiftingWithCaching) {
// Pressing (KEYSWITCH_TOP_LEFT) will activate the key on layer 0
auto state = pressKeyswitchAndRunCycle(KEYSWITCH_TOP_LEFT);
assertSingleKeyboardReportContaining(state, LAYER0_KEY);
// Pressing (KEYSWITCH_LEFT_PALM) will switch to Layer 1
state = pressKeyswitchAndRunCycle(KEYSWITCH_LEFT_PALM);
// ...since we're still pressing (KEYSWITCH_TOP_LEFT), and there was no change
// in the HID states, we shouldn't emit a report.
assertNoReport(state);
// Pressing (KEYSWITCH_TOP_RIGHT), the report shall contain keys from both
// layer 0 and layer1, because we started holding the layer 0 key prior to
// switching layers, so it's code should remain cached.
state = pressKeyswitchAndRunCycle(KEYSWITCH_TOP_RIGHT);
assertSingleKeyboardReportContaining(state, LAYER0_KEY);
assertSingleKeyboardReportContaining(state, LAYER1_KEY);
// Releasing (KEYSWITCH_TOP_LEFT), the report should now contain the key from
// layer1 only, and should not contain the layer0 key anymore.
state = releaseKeyswitchAndRunCycle(KEYSWITCH_TOP_LEFT);
assertSingleKeyboardReportContaining(state, LAYER1_KEY);
assertSingleKeyboardReportNotContaining(state, LAYER0_KEY);
// Release (KEYSWITCH_TOP_RIGHT)
state = releaseKeyswitchAndRunCycle(KEYSWITCH_TOP_RIGHT);
// Test the layer 1 key in isolation again
TestPressAndRelease(KEYSWITCH_TOP_LEFT, LAYER1_KEY);
// Release the layer key as well.
state = releaseKeyswitchAndRunCycle(KEYSWITCH_LEFT_PALM);
// Since the layer key release is internal to us, we shouldn't send a report.
assertNoReport(state);
}
TEST_F(LayerActivationOrder, Ordering) {
// Pressing (KEYSWITCH_RIGHT_PALM) will switch to Layer 2
auto state = pressKeyswitchAndRunCycle(KEYSWITCH_RIGHT_PALM);
// Pressing (KEYSWITCH_TOP_LEFT) will activate a key on layer 2
state = pressKeyswitchAndRunCycle(KEYSWITCH_TOP_LEFT);
assertSingleKeyboardReportContaining(state, LAYER2_KEY);
// Pressing (KEYSWITCH_LEFT_PALM) will activate Layer 1
state = pressKeyswitchAndRunCycle(KEYSWITCH_LEFT_PALM);
// Pressing (KEYSWITCH_TOP_RIGHT) will activate the layer 1 key now, due to
// activation ordering.
state = pressKeyswitchAndRunCycle(KEYSWITCH_TOP_RIGHT);
// We should have both the layer 1 and the layer 2 key active, because we're
// holding both.
assertSingleKeyboardReportContaining(state, LAYER1_KEY);
assertSingleKeyboardReportContaining(state, LAYER2_KEY);
// Releaseing all held keys, we should get an empty report.
releaseKeyswitch(KEYSWITCH_TOP_LEFT);
releaseKeyswitch(KEYSWITCH_TOP_RIGHT);
releaseKeyswitch(KEYSWITCH_LEFT_PALM);
releaseKeyswitch(KEYSWITCH_RIGHT_PALM);
state = RunCycle();
assertSingleEmptyReport(state);
// One more cycle, and we should generate no report at all
state = RunCycle();
assertNoReport(state);
}
TEST_F(LayerActivationOrder, LayerZero) {
// Pressing the rightmost of the left thumb keys should deactivate layer 0
auto state = pressKeyswitchAndRunCycle(KEYSWITCH_LEFT_THUMB_RIGHTMOST);
// Pressing KEYSWITCH_TOP_LEFT should fall back to activating the key on layer 0
state = pressKeyswitchAndRunCycle(KEYSWITCH_TOP_LEFT);
assertSingleKeyboardReportContaining(state, LAYER0_KEY);
// Releasing all keys should generate a single empty report
releaseKeyswitch(KEYSWITCH_TOP_LEFT);
releaseKeyswitch(KEYSWITCH_LEFT_THUMB_RIGHTMOST);
state = RunCycle();
assertSingleEmptyReport(state);
// Afterwards, we should generate no more reports.
state = RunCycle();
assertNoReport(state);
}
} // namespace
} // namespace testing
} // namespace kaleidoscope