mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 02:06:36 +02:00
[KBM] Migrate Engine and Editor into separate processes (#10774)
* Move KBM engine into separate process (#10672) * [KBM] Migrate KBM UI out of the runner (#10709) * Clean up keyboard hook handles (#10817) * [C++ common] Unhandled exception handler (#10821) * [KBM] Use icon in the KeyboardManagerEditor (#10845) * [KBM] Move resources from the Common project to the Editor. (#10844) * KBM Editor tests (#10858) * Rename engine executable (#10868) * clean up (#10870) * [KBM] Changed Editor and libraries output folders (#10871) * [KBM] New logs structure (#10872) * Add unhandled exception handling to the editor (#10874) * [KBM] Trace for edit keyboard window * Logging for XamlBridge message loop * [KBM] Added Editor and Engine to the installer (#10876) * Fix spelling * Interprocess communication logs, remove unnecessary windows message logs * [KBM] Separated telemetry for the engine and editor. (#10889) * [KBM] Editor test project (#10891) * Versions for the engine and the editor (#10897) * Add the editor's and the engine's executables to signing process (#10900) * [KBM editor] Run only one instance, exit when parent process exits (#10890) * [KBM] Force kill editor process to avoid XAML crash (#10907) * [KBM] Force kill editor process to avoid XAML crash * Fix event releasing Co-authored-by: mykhailopylyp <17161067+mykhailopylyp@users.noreply.github.com> * Make the editor dpi aware (#10908) * [KBM] KeyboardManagerCommon refactoring (#10909) * Do not start the process if it is already started (#10910) * logs * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * [KBM] Rename InitUnhandledExceptionHandler to make it explicit that is for x64 only. We will fix it properly when adding support for ARM64 and add a header with the proper conditional building. * [KBM] rename file/class/variables using camel case * [KBM] Rename "event_locker" -> "EventLocker" * [KBM] rename process_waiter Add a TODO comment * [KBM] rename methods Add TODO comment * [KBM] use uppercase for function names * [KBM] use uppercase for methos, lowercase for properties * [KBM] rename method, make methods private, formatting * [KBM] rename private variables * [KBM] use uppercase for function names * [KBM] Added support to run the editor stand-alone when built in debug mode * Update src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp * Check success of event creation, comment (#10947) * [KBM] code formatting (#10951) * [KBM] code formatting * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp * [KBM] tracing * [KBM] Remappings not showing fix. (#10954) * removed mutex * retry loop for reading * retry on reading config once * log error Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Seraphima Zykova <zykovas91@gmail.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include "MockedInput.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace RemappingLogicTests
|
||||
{
|
||||
TEST_CLASS (AppSpecificShortcutRemappingTests)
|
||||
|
||||
{
|
||||
private:
|
||||
KeyboardManagerInput::MockedInput mockedInputHandler;
|
||||
KeyboardManagerState testState;
|
||||
std::wstring testApp1 = L"testtrocess1.exe";
|
||||
std::wstring testApp2 = L"testprocess2.exe";
|
||||
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
// Reset test environment
|
||||
TestHelpers::ResetTestEnv(mockedInputHandler, testState);
|
||||
|
||||
// Set HandleOSLevelShortcutRemapEvent as the hook procedure
|
||||
std::function<intptr_t(LowlevelKeyboardEvent*)> currentHookProc = std::bind(&KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState));
|
||||
mockedInputHandler.SetHookProc(currentHookProc);
|
||||
}
|
||||
|
||||
// Test if the app specific remap takes place when the target app is in foreground
|
||||
TEST_METHOD (AppSpecificShortcut_ShouldGetRemapped_WhenAppIsInForeground)
|
||||
{
|
||||
// Remap Ctrl+A to Alt+V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_MENU);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, dest);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be unchanged, Alt and V key states should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
}
|
||||
|
||||
// Test if the app specific remap takes place when the target app is not in foreground
|
||||
TEST_METHOD (AppSpecificShortcut_ShouldNotGetRemapped_WhenAppIsNotInForeground)
|
||||
{
|
||||
// Remap Ctrl+A to Alt+V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_MENU);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, dest);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp2);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be true, Alt and V key states should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if the the keyboard manager state's activated app is correctly set after an app specific remap takes place
|
||||
TEST_METHOD (AppSpecificShortcut_ShouldSetCorrectActivatedApp_WhenRemapOccurs)
|
||||
{
|
||||
// Remap Ctrl+A to Alt+V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_MENU);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, dest);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Activated app should be testApp1
|
||||
Assert::AreEqual(testApp1, testState.GetActivatedApp());
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Activated app should be empty string
|
||||
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
|
||||
}
|
||||
// Test if the key states get cleared if foreground app changes after app-specific shortcut is invoked and then released
|
||||
TEST_METHOD (AppSpecificShortcut_ShouldClearKeyStates_WhenForegroundAppChangesAfterShortcutIsPressedOnRelease)
|
||||
{
|
||||
// Remap Ctrl+A to Alt+Tab
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_MENU);
|
||||
dest.SetKey(VK_TAB);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, dest);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp2);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A, Alt and Tab should all be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_TAB), false);
|
||||
}
|
||||
|
||||
// Test if the app specific shortcut to key remap takes place when the target app is in foreground
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldGetRemapped_WhenAppIsInForeground)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be unchanged, V key states should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
}
|
||||
|
||||
// Test if the app specific shortcut to key remap takes place when the target app is not in foreground
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldNotGetRemapped_WhenAppIsNotInForeground)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp2);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be true, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if the the keyboard manager state's activated app is correctly set after an app specific shortcut to key remap takes place
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldSetCorrectActivatedApp_WhenRemapOccurs)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Activated app should be testApp1
|
||||
Assert::AreEqual(testApp1, testState.GetActivatedApp());
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Activated app should be empty string
|
||||
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
|
||||
}
|
||||
// Test if the key states get cleared if foreground app changes after app-specific shortcut to key shortcut is invoked and then released
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldClearKeyStates_WhenForegroundAppChangesAfterShortcutIsPressedOnRelease)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp2);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A, V should all be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Disable app specific shortcut
|
||||
TEST_METHOD (AppSpecificShortcutToDisable_ShouldDisable_WhenAppIsOnForeground)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
testState.AddAppSpecificShortcut(testApp1, src, disableKey);
|
||||
|
||||
// Set the testApp as the foreground process
|
||||
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Check if Ctrl+A is released and disable key was not send
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/ErrorTypes.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/keyboard_layout.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace KeyboardManagerCommonTests
|
||||
{
|
||||
// Tests for methods in the KeyboardManagerHelper namespace
|
||||
TEST_CLASS (KeyboardManagerHelperTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns SameKeyPreviouslyMapped on passing the same key for both arguments
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnSameKeyPreviouslyMapped_OnPassingSameKeyForBothArguments)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = 0x41;
|
||||
DWORD key2 = key1;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::SameKeyPreviouslyMapped);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing left modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingLeftModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_LCONTROL;
|
||||
DWORD key2 = VK_CONTROL;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierKey);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing right modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingRightModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_RCONTROL;
|
||||
DWORD key2 = VK_CONTROL;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierKey);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing left modifier and right modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingLeftModifierAndRightModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_LCONTROL;
|
||||
DWORD key2 = VK_RCONTROL;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing keys of different types
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingKeysOfDifferentTypes)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = VK_CONTROL;
|
||||
DWORD key2 = VK_SHIFT;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing different action keys
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingDifferentActionKeys)
|
||||
{
|
||||
// Arrange
|
||||
DWORD key1 = 0x41;
|
||||
DWORD key2 = 0x42;
|
||||
|
||||
// Act
|
||||
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the CheckRepeatedModifier method returns true on passing vector with same modifier repeated
|
||||
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingSameModifierRepeated)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<int32_t> keys = { VK_CONTROL, VK_CONTROL, 0x41 };
|
||||
|
||||
// Act
|
||||
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, VK_CONTROL);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result);
|
||||
}
|
||||
|
||||
// Test if the CheckRepeatedModifier method returns true on passing vector with conflicting modifier repeated
|
||||
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingConflictingModifierRepeated)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<int32_t> keys = { VK_CONTROL, VK_LCONTROL, 0x41 };
|
||||
|
||||
// Act
|
||||
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, VK_LCONTROL);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result);
|
||||
}
|
||||
|
||||
// Test if the CheckRepeatedModifier method returns false on passing vector with different modifiers
|
||||
TEST_METHOD (CheckRepeatedModifier_ShouldReturnFalse_OnPassingDifferentModifiers)
|
||||
{
|
||||
// Arrange
|
||||
std::vector<int32_t> keys = { VK_CONTROL, VK_SHIFT, 0x41 };
|
||||
|
||||
// Act
|
||||
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, VK_SHIFT);
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{7f4b3a60-bc27-45a7-8000-68b0b6ea7466}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>KeyboardManagerEngineTest</RootNamespace>
|
||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
<ProjectName>KeyboardManagerEngineTest</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\KeyboardManagerEngine\</OutDir>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<UseFullPaths>true</UseFullPaths>
|
||||
<DisableSpecificWarnings>4002</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp" />
|
||||
<ClCompile Include="MockedInputSanityTests.cpp" />
|
||||
<ClCompile Include="SetKeyEventTests.cpp" />
|
||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
||||
<ClCompile Include="MockedInput.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutTests.cpp" />
|
||||
<ClCompile Include="SingleKeyRemappingTests.cpp" />
|
||||
<ClCompile Include="HelperTests.cpp" />
|
||||
<ClCompile Include="TestHelpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="MockedInput.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="TestHelpers.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj">
|
||||
<Project>{e496b7fc-1e99-4bab-849b-0e8367040b02}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MockedInput.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TestHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SingleKeyRemappingTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MockedInputSanityTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SetKeyEventTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HelperTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MockedInput.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TestHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,163 @@
|
||||
#include "pch.h"
|
||||
#include "MockedInput.h"
|
||||
|
||||
using namespace KeyboardManagerInput;
|
||||
|
||||
// Set the keyboard hook procedure to be tested
|
||||
void MockedInput::SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure)
|
||||
{
|
||||
hookProc = hookProcedure;
|
||||
}
|
||||
|
||||
// Function to simulate keyboard input - arguments and return value based on SendInput function (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)
|
||||
UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
|
||||
{
|
||||
// Iterate over inputs
|
||||
for (UINT i = 0; i < cInputs; i++)
|
||||
{
|
||||
LowlevelKeyboardEvent keyEvent;
|
||||
|
||||
// Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-syskeydown
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
if (keyboardState[VK_MENU] == true)
|
||||
{
|
||||
keyEvent.wParam = WM_SYSKEYUP;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyEvent.wParam = WM_KEYUP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pInputs[i].ki.wVk == VK_F10 || keyboardState[VK_MENU] == true)
|
||||
{
|
||||
keyEvent.wParam = WM_SYSKEYDOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyEvent.wParam = WM_KEYDOWN;
|
||||
}
|
||||
}
|
||||
KBDLLHOOKSTRUCT lParam = {};
|
||||
|
||||
// Set only vkCode and dwExtraInfo since other values are unused
|
||||
lParam.vkCode = pInputs[i].ki.wVk;
|
||||
lParam.dwExtraInfo = pInputs[i].ki.dwExtraInfo;
|
||||
keyEvent.lParam = &lParam;
|
||||
|
||||
// If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
|
||||
if (sendVirtualInputCallCondition == nullptr || sendVirtualInputCallCondition(&keyEvent))
|
||||
{
|
||||
sendVirtualInputCallCount++;
|
||||
}
|
||||
|
||||
// Call low level hook handler
|
||||
intptr_t result = MockedKeyboardHook(&keyEvent);
|
||||
|
||||
// Set keyboard state if the hook does not suppress the input
|
||||
if (result == 0)
|
||||
{
|
||||
// If key up flag is set, then set keyboard state to false
|
||||
keyboardState[pInputs[i].ki.wVk] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
|
||||
// Handling modifier key codes
|
||||
switch (pInputs[i].ki.wVk)
|
||||
{
|
||||
case VK_CONTROL:
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
keyboardState[VK_LCONTROL] = false;
|
||||
keyboardState[VK_RCONTROL] = false;
|
||||
}
|
||||
break;
|
||||
case VK_LCONTROL:
|
||||
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_RCONTROL:
|
||||
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_MENU:
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
keyboardState[VK_LMENU] = false;
|
||||
keyboardState[VK_RMENU] = false;
|
||||
}
|
||||
break;
|
||||
case VK_LMENU:
|
||||
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_RMENU:
|
||||
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_SHIFT:
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
keyboardState[VK_LSHIFT] = false;
|
||||
keyboardState[VK_RSHIFT] = false;
|
||||
}
|
||||
break;
|
||||
case VK_LSHIFT:
|
||||
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_RSHIFT:
|
||||
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cInputs;
|
||||
}
|
||||
|
||||
// Function to simulate keyboard hook behavior
|
||||
intptr_t MockedInput::MockedKeyboardHook(LowlevelKeyboardEvent* data)
|
||||
{
|
||||
// If the hookProc is set to null, then skip the hook
|
||||
if (hookProc != nullptr)
|
||||
{
|
||||
return hookProc(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get the state of a particular key
|
||||
bool MockedInput::GetVirtualKeyState(int key)
|
||||
{
|
||||
return keyboardState[key];
|
||||
}
|
||||
|
||||
// Function to reset the mocked keyboard state
|
||||
void MockedInput::ResetKeyboardState()
|
||||
{
|
||||
std::fill(keyboardState.begin(), keyboardState.end(), false);
|
||||
}
|
||||
|
||||
// Function to set SendVirtualInput call count condition
|
||||
void MockedInput::SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition)
|
||||
{
|
||||
sendVirtualInputCallCount = 0;
|
||||
sendVirtualInputCallCondition = condition;
|
||||
}
|
||||
|
||||
// Function to get SendVirtualInput call count
|
||||
int MockedInput::GetSendVirtualInputCallCount()
|
||||
{
|
||||
return sendVirtualInputCallCount;
|
||||
}
|
||||
|
||||
// Function to get the foreground process name
|
||||
void MockedInput::SetForegroundProcess(std::wstring process)
|
||||
{
|
||||
currentProcess = process;
|
||||
}
|
||||
|
||||
// Function to get the foreground process name
|
||||
void MockedInput::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
|
||||
{
|
||||
foregroundProcess = currentProcess;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include <keyboardmanager/common/InputInterface.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <common/hooks/LowlevelKeyboardEvent.h>
|
||||
|
||||
namespace KeyboardManagerInput
|
||||
{
|
||||
// Class for mocked keyboard input
|
||||
class MockedInput :
|
||||
public InputInterface
|
||||
{
|
||||
private:
|
||||
// Stores the states for all the keys - false for key up, and true for key down
|
||||
std::vector<bool> keyboardState;
|
||||
|
||||
// Function to be executed as a low level hook. By default it is nullptr so the hook is skipped
|
||||
std::function<intptr_t(LowlevelKeyboardEvent*)> hookProc;
|
||||
|
||||
// Stores the count of sendVirtualInput calls given if the condition sendVirtualInputCallCondition is satisfied
|
||||
int sendVirtualInputCallCount = 0;
|
||||
std::function<bool(LowlevelKeyboardEvent*)> sendVirtualInputCallCondition;
|
||||
|
||||
std::wstring currentProcess;
|
||||
|
||||
public:
|
||||
MockedInput()
|
||||
{
|
||||
keyboardState.resize(256, false);
|
||||
}
|
||||
|
||||
// Set the keyboard hook procedure to be tested
|
||||
void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure);
|
||||
|
||||
// Function to simulate keyboard input
|
||||
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize);
|
||||
|
||||
// Function to simulate keyboard hook behavior
|
||||
intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data);
|
||||
|
||||
// Function to get the state of a particular key
|
||||
bool GetVirtualKeyState(int key);
|
||||
|
||||
// Function to reset the mocked keyboard state
|
||||
void ResetKeyboardState();
|
||||
|
||||
// Function to set SendVirtualInput call count condition
|
||||
void SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition);
|
||||
|
||||
// Function to get SendVirtualInput call count
|
||||
int GetSendVirtualInputCallCount();
|
||||
|
||||
// Function to get the foreground process name
|
||||
void SetForegroundProcess(std::wstring process);
|
||||
|
||||
// Function to get the foreground process name
|
||||
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include "MockedInput.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/KeyboardEventHandlers.h>
|
||||
#include "TestHelpers.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace RemappingLogicTests
|
||||
{
|
||||
// Tests for MockedInput test helper - to ensure simulated keyboard input behaves as expected
|
||||
TEST_CLASS (MockedInputSanityTests)
|
||||
{
|
||||
private:
|
||||
KeyboardManagerInput::MockedInput mockedInputHandler;
|
||||
KeyboardManagerState testState;
|
||||
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
// Reset test environment
|
||||
TestHelpers::ResetTestEnv(mockedInputHandler, testState);
|
||||
}
|
||||
|
||||
// Test if mocked input is working
|
||||
TEST_METHOD (MockedInput_ShouldSetKeyboardState_OnKeyEvent)
|
||||
{
|
||||
// Send key down and key up for A key (0x41) and check keyboard state both times
|
||||
const int nInputs = 1;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include "MockedInput.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/KeyboardEventHandlers.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace RemappingLogicTests
|
||||
{
|
||||
// Tests for the SetKeyEvent method
|
||||
TEST_CLASS (SetKeyEventTests)
|
||||
{
|
||||
private:
|
||||
KeyboardManagerInput::MockedInput mockedInputHandler;
|
||||
KeyboardManagerState testState;
|
||||
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
// Reset test environment
|
||||
TestHelpers::ResetTestEnv(mockedInputHandler, testState);
|
||||
}
|
||||
|
||||
// Test if SetKeyEvent sets the extended key flag for all the extended keys
|
||||
TEST_METHOD (SetKeyEvent_ShouldUseExtendedKeyFlag_WhenArgumentIsExtendedKey)
|
||||
{
|
||||
const int nInputs = 15;
|
||||
INPUT input[nInputs] = {};
|
||||
|
||||
// List of extended keys
|
||||
WORD keyCodes[nInputs] = { VK_RCONTROL, VK_RMENU, VK_NUMLOCK, VK_SNAPSHOT, VK_CANCEL, VK_INSERT, VK_HOME, VK_PRIOR, VK_DELETE, VK_END, VK_NEXT, VK_LEFT, VK_DOWN, VK_RIGHT, VK_UP };
|
||||
|
||||
for (int i = 0; i < nInputs; i++)
|
||||
{
|
||||
// Set key events for all the extended keys
|
||||
KeyboardManagerHelper::SetKeyEvent(input, i, INPUT_KEYBOARD, keyCodes[i], 0, 0);
|
||||
// Extended key flag should be set
|
||||
Assert::AreEqual(true, bool(input[i].ki.dwFlags & KEYEVENTF_EXTENDEDKEY));
|
||||
}
|
||||
}
|
||||
|
||||
// Test if SetKeyEvent sets the scan code field to 0 for dummy key
|
||||
TEST_METHOD (SetKeyEvent_ShouldSetScanCodeFieldTo0_WhenArgumentIsDummyKey)
|
||||
{
|
||||
const int nInputs = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
INPUT input[nInputs] = {};
|
||||
|
||||
int index = 0;
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(input, index, 0);
|
||||
|
||||
// Assert that wScan for both inputs is 0
|
||||
Assert::AreEqual<unsigned int>(0, input[0].ki.wScan);
|
||||
Assert::AreEqual<unsigned int>(0, input[1].ki.wScan);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/ErrorTypes.h>
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/keyboard_layout.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace KeyboardManagerCommonTests
|
||||
{
|
||||
// Tests for methods in the Shortcut class
|
||||
TEST_CLASS (KeyboardManagerHelperTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns false on passing shortcut with null action key
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithNullActionKey)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(NULL);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns false on passing shortcut with only action key
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyActionKey)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(0x41);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns false on passing shortcut with only modifier keys
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyModifierKeys)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(VK_CONTROL);
|
||||
s.SetKey(VK_SHIFT);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsFalse(result);
|
||||
}
|
||||
|
||||
// Test if the IsValidShortcut method returns true on passing shortcut with modifier and action key
|
||||
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithModifierAndActionKey)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s;
|
||||
s.SetKey(VK_CONTROL);
|
||||
s.SetKey(0x41);
|
||||
|
||||
// Act
|
||||
bool result = s.IsValidShortcut();
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing invalid shortcut for one of the arguments
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingInvalidShortcutForOneOfTheArguments)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ NULL });
|
||||
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x41 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns SameShortcutPreviouslyMapped on passing same shortcut for both arguments
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnSameShortcutPreviouslyMapped_OnPassingSameShortcutForBothArguments)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x41 });
|
||||
Shortcut s2 = s1;
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different action keys
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentActionKeys)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x41 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different modifiers
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentModifiers)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<int32_t>{ VK_SHIFT, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ VK_LCONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with right modifier and common modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithRightModifierAndCommonModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ VK_RCONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut);
|
||||
}
|
||||
|
||||
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and right modifier
|
||||
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndRightModifierOfSameType)
|
||||
{
|
||||
// Arrange
|
||||
Shortcut s1(std::vector<int32_t>{ VK_LCONTROL, 0x42 });
|
||||
Shortcut s2(std::vector<int32_t>{ VK_RCONTROL, 0x42 });
|
||||
|
||||
// Act
|
||||
auto result = Shortcut::DoKeysOverlap(s1, s2);
|
||||
|
||||
// Assert
|
||||
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include "MockedInput.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace RemappingLogicTests
|
||||
{
|
||||
// Tests for single key remapping logic
|
||||
TEST_CLASS (SingleKeyRemappingTests)
|
||||
{
|
||||
private:
|
||||
KeyboardManagerInput::MockedInput mockedInputHandler;
|
||||
KeyboardManagerState testState;
|
||||
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
// Reset test environment
|
||||
TestHelpers::ResetTestEnv(mockedInputHandler, testState);
|
||||
|
||||
// Set HandleSingleKeyRemapEvent as the hook procedure
|
||||
std::function<intptr_t(LowlevelKeyboardEvent*)> currentHookProc = std::bind(&KeyboardEventHandlers::HandleSingleKeyRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState));
|
||||
mockedInputHandler.SetHookProc(currentHookProc);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a single key remap
|
||||
TEST_METHOD (RemappedKey_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to B
|
||||
testState.AddSingleKeyRemap(0x41, 0x42);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and B key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and B key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), false);
|
||||
}
|
||||
|
||||
// Test if key is suppressed if a key is disabled by single key remap
|
||||
TEST_METHOD (RemappedKeyDisabled_ShouldNotChangeKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to VK_DISABLE (disabled)
|
||||
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_DISABLED);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a remap to Win (Both) key
|
||||
TEST_METHOD (RemappedKeyToWinBoth_ShouldSetWinLeftKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to Common Win key
|
||||
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_WIN_BOTH);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and common Win key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), true);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and common Win key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), false);
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Caps Lock is remapped to Ctrl
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCapsLockIsMappedToCtrlAltShift)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Caps Lock to Ctrl
|
||||
testState.AddSingleKeyRemap(VK_CAPITAL, VK_CONTROL);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CAPITAL;
|
||||
|
||||
// Send Caps Lock keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to Caps Lock
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToCapsLock)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Ctrl to Caps Lock
|
||||
testState.AddSingleKeyRemap(VK_CONTROL, VK_CAPITAL);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
|
||||
// Send Ctrl keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly twice with the suppress flag when Caps Lock is remapped to shortcut with Ctrl and Shift
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyTwice_WhenCapsLockIsMappedToShortcutWithCtrlAltShift)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Caps Lock to Ctrl+Shift+V
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(VK_SHIFT);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddSingleKeyRemap(VK_CAPITAL, dest);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CAPITAL;
|
||||
|
||||
// Send Caps Lock keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to a shortcut with Caps Lock
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToShortcutWithCapsLock)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Ctrl to Ctrl+Caps Lock
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(VK_CAPITAL);
|
||||
testState.AddSingleKeyRemap(VK_CONTROL, dest);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
|
||||
// Send Ctrl keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a single key to two key shortcut remap
|
||||
TEST_METHOD (RemappedKeyToTwoKeyShortcut_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to Ctrl+V
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddSingleKeyRemap(0x41, dest);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and Ctrl, V key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and Ctrl, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a single key to three key shortcut remap
|
||||
TEST_METHOD (RemappedKeyToThreeKeyShortcut_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to Ctrl+Shift+V
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(VK_SHIFT);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddSingleKeyRemap(0x41, dest);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and Ctrl, Shift, V key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and Ctrl, Shift, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a remap from a single key to a shortcut containing the source key
|
||||
TEST_METHOD (RemappedKeyToShortcutContainingSourceKey_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap LCtrl to LCtrl+V
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_LCONTROL);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddSingleKeyRemap(VK_LCONTROL, dest);
|
||||
const int nInputs = 1;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LCONTROL;
|
||||
|
||||
// Send LCtrl keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// LCtrl, V key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send LCtrl keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// LCtrl, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "pch.h"
|
||||
#include "TestHelpers.h"
|
||||
#include "MockedInput.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
|
||||
namespace TestHelpers
|
||||
{
|
||||
// Function to reset the environment variables for tests
|
||||
void ResetTestEnv(KeyboardManagerInput::MockedInput& input, KeyboardManagerState& state)
|
||||
{
|
||||
input.ResetKeyboardState();
|
||||
input.SetHookProc(nullptr);
|
||||
input.SetSendVirtualInputTestHandler(nullptr);
|
||||
input.SetForegroundProcess(L"");
|
||||
state.ClearSingleKeyRemaps();
|
||||
state.ClearOSLevelShortcuts();
|
||||
state.ClearAppSpecificShortcuts();
|
||||
|
||||
// Allocate memory for the keyboardManagerState activatedApp member to avoid CRT assert errors
|
||||
std::wstring maxLengthString;
|
||||
maxLengthString.resize(MAX_PATH);
|
||||
state.SetActivatedApp(maxLengthString);
|
||||
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace KeyboardManagerInput
|
||||
{
|
||||
class MockedInput;
|
||||
}
|
||||
class KeyboardManagerState;
|
||||
|
||||
namespace TestHelpers
|
||||
{
|
||||
// Function to reset the environment variables for tests
|
||||
void ResetTestEnv(KeyboardManagerInput::MockedInput& input, KeyboardManagerState& state);
|
||||
|
||||
// Function to return the index of the given key code from the drop down key list
|
||||
int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <shlwapi.h>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include "winrt/Windows.Foundation.h"
|
||||
@@ -0,0 +1,13 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by KeyboardManagerTest.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys KeyboardManagerTest"
|
||||
#define INTERNAL_NAME "KeyboardManagerTest"
|
||||
#define ORIGINAL_FILENAME "KeyboardManagerTest.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
Reference in New Issue
Block a user