SingleKeyRemappingTests.cpp
1 #include "pch.h" 2 3 // Suppressing 26466 - Don't use static_cast downcasts - in CppUnitTest.h 4 #pragma warning(push) 5 #pragma warning(disable : 26466) 6 #include "CppUnitTest.h" 7 #pragma warning(pop) 8 9 #include "MockedInput.h" 10 #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h> 11 #include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h> 12 #include "TestHelpers.h" 13 #include <common/interop/shared_constants.h> 14 15 using namespace Microsoft::VisualStudio::CppUnitTestFramework; 16 17 namespace RemappingLogicTests 18 { 19 // Tests for single key remapping logic 20 TEST_CLASS (SingleKeyRemappingTests) 21 { 22 private: 23 KeyboardManagerInput::MockedInput mockedInputHandler; 24 State testState; 25 26 public: 27 TEST_METHOD_INITIALIZE(InitializeTestEnv) 28 { 29 // Reset test environment 30 TestHelpers::ResetTestEnv(mockedInputHandler, testState); 31 32 // Set HandleSingleKeyRemapEvent as the hook procedure 33 std::function<intptr_t(LowlevelKeyboardEvent*)> currentHookProc = std::bind(&KeyboardEventHandlers::HandleSingleKeyRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); 34 mockedInputHandler.SetHookProc(currentHookProc); 35 } 36 37 // Test if correct keyboard states are set for a single key remap 38 TEST_METHOD (RemappedKey_ShouldSetTargetKeyState_OnKeyEvent) 39 { 40 // Remap A to B 41 testState.AddSingleKeyRemap(0x41, (DWORD)0x42); 42 43 std::vector<INPUT> inputs1{ 44 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }, 45 }; 46 47 // Send A keydown 48 mockedInputHandler.SendVirtualInput(inputs1); 49 50 // A key state should be unchanged, and B key state should be true 51 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 52 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true); 53 54 std::vector<INPUT> inputs2{ 55 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 56 }; 57 58 // Send A keyup 59 mockedInputHandler.SendVirtualInput(inputs2); 60 61 // A key state should be unchanged, and B key state should be false 62 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 63 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), false); 64 } 65 66 // Test if key is suppressed if a key is disabled by single key remap 67 TEST_METHOD (RemappedKeyDisabled_ShouldNotChangeKeyState_OnKeyEvent) 68 { 69 // Remap A to VK_DISABLE (disabled) 70 testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_DISABLED); 71 72 std::vector<INPUT> inputs1{ 73 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }, 74 }; 75 76 // Send A keydown 77 mockedInputHandler.SendVirtualInput(inputs1); 78 79 // A key state should be unchanged 80 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 81 82 std::vector<INPUT> inputs2{ 83 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 84 }; 85 86 // Send A keyup 87 mockedInputHandler.SendVirtualInput(inputs2); 88 89 // A key state should be unchanged 90 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 91 } 92 93 // Test if correct keyboard states are set for a remap to Win (Both) key 94 TEST_METHOD (RemappedKeyToWinBoth_ShouldSetWinLeftKeyState_OnKeyEvent) 95 { 96 // Remap A to Common Win key 97 testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_WIN_BOTH); 98 99 std::vector<INPUT> inputs1{ 100 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }, 101 }; 102 103 // Send A keydown 104 mockedInputHandler.SendVirtualInput(inputs1); 105 106 // A key state should be unchanged, and common Win key state should be true 107 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 108 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), true); 109 110 std::vector<INPUT> inputs2{ 111 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 112 }; 113 114 // Send A keyup 115 mockedInputHandler.SendVirtualInput(inputs2); 116 117 // A key state should be unchanged, and common Win key state should be false 118 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 119 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), false); 120 } 121 122 // Test if SendVirtualInput is sent exactly once with the suppress flag when Caps Lock is remapped to Ctrl 123 TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCapsLockIsMappedToCtrlAltShift) 124 { 125 // Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag 126 mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) { 127 if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) 128 return true; 129 else 130 return false; 131 }); 132 133 // Remap Caps Lock to Ctrl 134 testState.AddSingleKeyRemap(VK_CAPITAL, (DWORD)VK_CONTROL); 135 136 std::vector<INPUT> inputs{ 137 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CAPITAL } }, 138 }; 139 140 // Send Caps Lock keydown 141 mockedInputHandler.SendVirtualInput(inputs); 142 143 // SendVirtualInput should be called exactly once with the above condition 144 Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); 145 } 146 147 // Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to Caps Lock 148 TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToCapsLock) 149 { 150 // Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag 151 mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) { 152 if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) 153 return true; 154 else 155 return false; 156 }); 157 158 // Remap Ctrl to Caps Lock 159 testState.AddSingleKeyRemap(VK_CONTROL, (DWORD)VK_CAPITAL); 160 161 std::vector<INPUT> inputs{ 162 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 163 }; 164 165 // Send Ctrl keydown 166 mockedInputHandler.SendVirtualInput(inputs); 167 168 // SendVirtualInput should be called exactly once with the above condition 169 Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); 170 } 171 172 // Test if SendVirtualInput is sent exactly twice with the suppress flag when Caps Lock is remapped to shortcut with Ctrl and Shift 173 TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyTwice_WhenCapsLockIsMappedToShortcutWithCtrlAltShift) 174 { 175 // Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag 176 mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) { 177 if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) 178 return true; 179 else 180 return false; 181 }); 182 183 // Remap Caps Lock to Ctrl+Shift+V 184 Shortcut dest; 185 dest.SetKey(VK_CONTROL); 186 dest.SetKey(VK_SHIFT); 187 dest.SetKey(0x56); 188 testState.AddSingleKeyRemap(VK_CAPITAL, dest); 189 190 std::vector<INPUT> inputs{ 191 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CAPITAL } }, 192 }; 193 194 // Send Caps Lock keydown 195 mockedInputHandler.SendVirtualInput(inputs); 196 197 // SendVirtualInput should be called exactly twice with the above condition 198 Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount()); 199 } 200 201 // Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to a shortcut with Caps Lock 202 TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToShortcutWithCapsLock) 203 { 204 // Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag 205 mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) { 206 if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) 207 return true; 208 else 209 return false; 210 }); 211 212 // Remap Ctrl to Ctrl+Caps Lock 213 Shortcut dest; 214 dest.SetKey(VK_CONTROL); 215 dest.SetKey(VK_CAPITAL); 216 testState.AddSingleKeyRemap(VK_CONTROL, dest); 217 218 std::vector<INPUT> inputs{ 219 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 220 }; 221 222 // Send Ctrl keydown 223 mockedInputHandler.SendVirtualInput(inputs); 224 225 // SendVirtualInput should be called exactly once with the above condition 226 Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); 227 } 228 229 // Test if correct keyboard states are set for a single key to two key shortcut remap 230 TEST_METHOD (RemappedKeyToTwoKeyShortcut_ShouldSetTargetKeyState_OnKeyEvent) 231 { 232 // Remap A to Ctrl+V 233 Shortcut dest; 234 dest.SetKey(VK_CONTROL); 235 dest.SetKey(0x56); 236 testState.AddSingleKeyRemap(0x41, dest); 237 238 std::vector<INPUT> inputs1{ 239 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }, 240 }; 241 242 // Send A keydown 243 mockedInputHandler.SendVirtualInput(inputs1); 244 245 // A key state should be unchanged, and Ctrl, V key state should be true 246 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 247 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); 248 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); 249 250 std::vector<INPUT> inputs2{ 251 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 252 }; 253 254 // Send A keyup 255 mockedInputHandler.SendVirtualInput(inputs2); 256 257 // A key state should be unchanged, and Ctrl, V key state should be false 258 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 259 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 260 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false); 261 } 262 263 // Test if correct keyboard states are set for a single key to three key shortcut remap 264 TEST_METHOD (RemappedKeyToThreeKeyShortcut_ShouldSetTargetKeyState_OnKeyEvent) 265 { 266 // Remap A to Ctrl+Shift+V 267 Shortcut dest; 268 dest.SetKey(VK_CONTROL); 269 dest.SetKey(VK_SHIFT); 270 dest.SetKey(0x56); 271 testState.AddSingleKeyRemap(0x41, dest); 272 273 std::vector<INPUT> inputs1{ 274 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }, 275 }; 276 277 // Send A keydown 278 mockedInputHandler.SendVirtualInput(inputs1); 279 280 // A key state should be unchanged, and Ctrl, Shift, V key state should be true 281 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 282 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); 283 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), true); 284 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); 285 286 std::vector<INPUT> inputs2{ 287 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 288 }; 289 290 // Send A keyup 291 mockedInputHandler.SendVirtualInput(inputs2); 292 293 // A key state should be unchanged, and Ctrl, Shift, V key state should be false 294 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 295 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 296 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false); 297 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false); 298 } 299 300 // Test if correct keyboard states are set for a remap from a single key to a shortcut containing the source key 301 TEST_METHOD (RemappedKeyToShortcutContainingSourceKey_ShouldSetTargetKeyState_OnKeyEvent) 302 { 303 // Remap LCtrl to LCtrl+V 304 Shortcut dest; 305 dest.SetKey(VK_LCONTROL); 306 dest.SetKey(0x56); 307 testState.AddSingleKeyRemap(VK_LCONTROL, dest); 308 309 std::vector<INPUT> inputs1{ 310 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_LCONTROL } }, 311 }; 312 313 // Send LCtrl keydown 314 mockedInputHandler.SendVirtualInput(inputs1); 315 316 // LCtrl, V key state should be true 317 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), true); 318 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); 319 320 std::vector<INPUT> inputs2{ 321 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_LCONTROL, .dwFlags = KEYEVENTF_KEYUP } }, 322 }; 323 324 // Send LCtrl keyup 325 mockedInputHandler.SendVirtualInput(inputs2); 326 327 // LCtrl, V key state should be false 328 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), false); 329 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false); 330 } 331 }; 332 }