/ src / modules / keyboardmanager / KeyboardManagerEngineTest / AppSpecificShortcutRemappingTests.cpp
AppSpecificShortcutRemappingTests.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 TEST_CLASS (AppSpecificShortcutRemappingTests) 20 21 { 22 private: 23 KeyboardManagerInput::MockedInput mockedInputHandler; 24 State testState; 25 std::wstring testApp1 = L"testprocess1.exe"; 26 std::wstring testApp2 = L"testprocess2.exe"; 27 28 public: 29 TEST_METHOD_INITIALIZE(InitializeTestEnv) 30 { 31 // Reset test environment 32 TestHelpers::ResetTestEnv(mockedInputHandler, testState); 33 34 // Set HandleOSLevelShortcutRemapEvent as the hook procedure 35 std::function<intptr_t(LowlevelKeyboardEvent*)> currentHookProc = std::bind(&KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); 36 mockedInputHandler.SetHookProc(currentHookProc); 37 } 38 39 // Test if the app specific remap takes place when the target app is in foreground 40 TEST_METHOD (AppSpecificShortcut_ShouldGetRemapped_WhenAppIsInForeground) 41 { 42 // Remap Ctrl+A to Alt+V 43 Shortcut src; 44 src.SetKey(VK_CONTROL); 45 src.SetKey(0x41); 46 Shortcut dest; 47 dest.SetKey(VK_MENU); 48 dest.SetKey(0x56); 49 testState.AddAppSpecificShortcut(testApp1, src, dest); 50 51 // Set the testApp as the foreground process 52 mockedInputHandler.SetForegroundProcess(testApp1); 53 54 std::vector<INPUT> inputs{ 55 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 56 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 57 }; 58 59 // Send Ctrl+A keydown 60 mockedInputHandler.SendVirtualInput(inputs); 61 62 // Ctrl and A key states should be unchanged, Alt and V key states should be true 63 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 64 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 65 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true); 66 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); 67 } 68 69 // Test if the app specific remap takes place when the target app is not in foreground 70 TEST_METHOD (AppSpecificShortcut_ShouldNotGetRemapped_WhenAppIsNotInForeground) 71 { 72 // Remap Ctrl+A to Alt+V 73 Shortcut src; 74 src.SetKey(VK_CONTROL); 75 src.SetKey(0x41); 76 Shortcut dest; 77 dest.SetKey(VK_MENU); 78 dest.SetKey(0x56); 79 testState.AddAppSpecificShortcut(testApp1, src, dest); 80 81 // Set the testApp as the foreground process 82 mockedInputHandler.SetForegroundProcess(testApp2); 83 84 std::vector<INPUT> inputs{ 85 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 86 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 87 }; 88 89 // Send Ctrl+A keydown 90 mockedInputHandler.SendVirtualInput(inputs); 91 92 // Ctrl and A key states should be true, Alt and V key states should be false 93 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); 94 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true); 95 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false); 96 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false); 97 } 98 99 // Test if the keyboard manager state's activated app is correctly set after an app specific remap takes place 100 TEST_METHOD (AppSpecificShortcut_ShouldSetCorrectActivatedApp_WhenRemapOccurs) 101 { 102 // Remap Ctrl+A to Alt+V 103 Shortcut src; 104 src.SetKey(VK_CONTROL); 105 src.SetKey(0x41); 106 Shortcut dest; 107 dest.SetKey(VK_MENU); 108 dest.SetKey(0x56); 109 testState.AddAppSpecificShortcut(testApp1, src, dest); 110 111 // Set the testApp as the foreground process 112 mockedInputHandler.SetForegroundProcess(testApp1); 113 114 std::vector<INPUT> inputs1{ 115 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 116 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 117 }; 118 119 // Send Ctrl+A keydown 120 mockedInputHandler.SendVirtualInput(inputs1); 121 122 // Activated app should be testApp1 123 Assert::AreEqual(testApp1, testState.GetActivatedApp()); 124 125 std::vector<INPUT> inputs2{ 126 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 127 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } } 128 }; 129 130 // Release A then Ctrl 131 mockedInputHandler.SendVirtualInput(inputs2); 132 133 // Activated app should be empty string 134 Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp()); 135 } 136 // Test if the key states get cleared if foreground app changes after app-specific shortcut is invoked and then released 137 TEST_METHOD (AppSpecificShortcut_ShouldClearKeyStates_WhenForegroundAppChangesAfterShortcutIsPressedOnRelease) 138 { 139 // Remap Ctrl+A to Alt+Tab 140 Shortcut src; 141 src.SetKey(VK_CONTROL); 142 src.SetKey(0x41); 143 Shortcut dest; 144 dest.SetKey(VK_MENU); 145 dest.SetKey(VK_TAB); 146 testState.AddAppSpecificShortcut(testApp1, src, dest); 147 148 // Set the testApp as the foreground process 149 mockedInputHandler.SetForegroundProcess(testApp1); 150 151 std::vector<INPUT> inputs1{ 152 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 153 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 154 }; 155 156 // Send Ctrl+A keydown 157 mockedInputHandler.SendVirtualInput(inputs1); 158 159 // Set the testApp as the foreground process 160 mockedInputHandler.SetForegroundProcess(testApp2); 161 162 std::vector<INPUT> inputs2{ 163 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 164 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } } 165 }; 166 167 // Release A then Ctrl 168 mockedInputHandler.SendVirtualInput(inputs2); 169 170 // Ctrl, A, Alt and Tab should all be false 171 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 172 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 173 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false); 174 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_TAB), false); 175 } 176 177 // Test if the app specific shortcut to key remap takes place when the target app is in foreground 178 TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldGetRemapped_WhenAppIsInForeground) 179 { 180 // Remap Ctrl+A to V 181 Shortcut src; 182 src.SetKey(VK_CONTROL); 183 src.SetKey(0x41); 184 testState.AddAppSpecificShortcut(testApp1, src, (DWORD)0x56); 185 186 // Set the testApp as the foreground process 187 mockedInputHandler.SetForegroundProcess(testApp1); 188 189 std::vector<INPUT> inputs{ 190 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 191 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 192 }; 193 194 // Send Ctrl+A keydown 195 mockedInputHandler.SendVirtualInput(inputs); 196 197 // Ctrl and A key states should be unchanged, V key states should be true 198 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 199 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 200 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); 201 } 202 203 // Test if the app specific shortcut to key remap takes place when the target app is not in foreground 204 TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldNotGetRemapped_WhenAppIsNotInForeground) 205 { 206 // Remap Ctrl+A to V 207 Shortcut src; 208 src.SetKey(VK_CONTROL); 209 src.SetKey(0x41); 210 testState.AddAppSpecificShortcut(testApp1, src, (DWORD)0x56); 211 212 // Set the testApp as the foreground process 213 mockedInputHandler.SetForegroundProcess(testApp2); 214 215 std::vector<INPUT> inputs{ 216 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 217 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 218 }; 219 220 // Send Ctrl+A keydown 221 mockedInputHandler.SendVirtualInput(inputs); 222 223 // Ctrl and A key states should be true, V key state should be false 224 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); 225 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true); 226 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false); 227 } 228 229 // Test if the keyboard manager state's activated app is correctly set after an app specific shortcut to key remap takes place 230 TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldSetCorrectActivatedApp_WhenRemapOccurs) 231 { 232 // Remap Ctrl+A to V 233 Shortcut src; 234 src.SetKey(VK_CONTROL); 235 src.SetKey(0x41); 236 testState.AddAppSpecificShortcut(testApp1, src, (DWORD)0x56); 237 238 // Set the testApp as the foreground process 239 mockedInputHandler.SetForegroundProcess(testApp1); 240 241 std::vector<INPUT> inputs1{ 242 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 243 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 244 }; 245 246 // Send Ctrl+A keydown 247 mockedInputHandler.SendVirtualInput(inputs1); 248 249 // Activated app should be testApp1 250 Assert::AreEqual(testApp1, testState.GetActivatedApp()); 251 252 std::vector<INPUT> inputs2{ 253 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 254 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } } 255 }; 256 257 // Release A then Ctrl 258 mockedInputHandler.SendVirtualInput(inputs2); 259 260 // Activated app should be empty string 261 Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp()); 262 } 263 // Test if the key states get cleared if foreground app changes after app-specific shortcut to key shortcut is invoked and then released 264 TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldClearKeyStates_WhenForegroundAppChangesAfterShortcutIsPressedOnRelease) 265 { 266 // Remap Ctrl+A to V 267 Shortcut src; 268 src.SetKey(VK_CONTROL); 269 src.SetKey(0x41); 270 testState.AddAppSpecificShortcut(testApp1, src, (DWORD)0x56); 271 272 // Set the testApp as the foreground process 273 mockedInputHandler.SetForegroundProcess(testApp1); 274 275 std::vector<INPUT> inputs1{ 276 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 277 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } } 278 }; 279 280 // Send Ctrl+A keydown 281 mockedInputHandler.SendVirtualInput(inputs1); 282 283 // Set the testApp as the foreground process 284 mockedInputHandler.SetForegroundProcess(testApp2); 285 286 std::vector<INPUT> inputs2{ 287 { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } }, 288 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } } 289 }; 290 291 // Release A then Ctrl 292 mockedInputHandler.SendVirtualInput(inputs2); 293 294 // Ctrl, A, V should all be false 295 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 296 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); 297 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false); 298 } 299 300 // Disable app specific shortcut 301 TEST_METHOD (AppSpecificShortcutToDisable_ShouldDisable_WhenAppIsOnForeground) 302 { 303 Shortcut src; 304 src.SetKey(VK_CONTROL); 305 WORD actionKey = 0x41; 306 src.SetKey(actionKey); 307 WORD disableKey = CommonSharedConstants::VK_DISABLED; 308 testState.AddAppSpecificShortcut(testApp1, src, disableKey); 309 310 // Set the testApp as the foreground process 311 mockedInputHandler.SetForegroundProcess(testApp1); 312 313 std::vector<INPUT> inputs{ 314 { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } }, 315 { .type = INPUT_KEYBOARD, .ki = { .wVk = actionKey } } 316 }; 317 318 // Send Ctrl+A keydown 319 mockedInputHandler.SendVirtualInput(inputs); 320 321 // Check if Ctrl+A is released and disable key was not send 322 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); 323 Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false); 324 } 325 }; 326 }