/ src / modules / keyboardmanager / KeyboardManagerEngineTest / SingleKeyRemappingTests.cpp
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  }