public void TestKeyboardInteractEventProducer_PressedKeyTest()
        {
            /* PRECONDITIONS */
            Debug.Assert(keyboardModule != null);
            Debug.Assert(keyboardInteractEventProducer != null);
            Debug.Assert(hookNativeMethodsMock != null);
            Debug.Assert(hookNativeMethodsMock.Mock != null);
            Debug.Assert(nativeKeyboardMock != null);

            /* GIVEN */
            //get the callback
            GlobalHook.CppGetMessageCallback callback = GetCallback();

            //setting up fake nativeKeyboardMock behaviors, messages and corresponding expected Events
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x41)).Returns(Key.A);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x48)).Returns(Key.H);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x36)).Returns(Key.D6);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x30)).Returns(Key.D0);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0xBB)).Returns(Key.OemPlus);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0xBF)).Returns(Key.OemQuestion);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0xA0)).Returns(Key.LeftShift);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x24)).Returns(Key.Home);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x25)).Returns(Key.Left);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x12)).Returns(Key.LeftAlt);

            GlobalHook.HookMessage[] hookMessages =
            {
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x41
                },                                                                                               //A
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_SYSKEYDOWN, wParam = (IntPtr)0x48
                },                                                                                                  //H
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x36
                },                                                                                               //6
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x30
                },                                                                                               //0
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_SYSKEYDOWN, wParam = (IntPtr)0xBB
                },                                                                                                  //+
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0xBF
                },                                                                                               //"/" or ?
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_SYSKEYDOWN, wParam = (IntPtr)0xA0
                },                                                                                                 //Left SHIFT
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_SYSKEYDOWN, wParam = (IntPtr)0x24
                },                                                                                                 //HOME
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x25
                },                                                                                               //LeftArrow
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x12
                }                                                                                               //Alt
            };

            KeyboardInteractEvent[] expectedEvents =
            {
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.A
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.H
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.D6
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.D0
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.OemPlus
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.OemQuestion
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.LeftShift
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.Home
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.Left
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.LeftAlt
                }
            };

            Assert.IsTrue(hookMessages.Length == expectedEvents.Length);

            /* WHEN */
            // consumedEvent.Signal() will be called gdw one event has been found and meet expectation
            using var consumedEvent = new CountdownEvent(hookMessages.Length);
            Assert.IsTrue(consumedEvent.CurrentCount == hookMessages.Length);

            //didStartConsumingEvent.Set() will be called in Await method gdw the consumer is attached
            using var didStartConsumingEvent = new ManualResetEvent(false);

            //Running the task in another thread
            var thread = new Thread(async() =>
            {
                await foreach (var @event in Await <IAsyncEnumerable <KeyboardInteractEvent> >(keyboardInteractEventProducer.GetEvents(), didStartConsumingEvent))
                {
                    if (!IsKeyboardInteractEventFound(@event, expectedEvents))
                    {
                        continue;
                    }
                    consumedEvent.Signal();
                }
            });

            thread.Start();
            // true if the consumer is attached!
            Assert.IsTrue(didStartConsumingEvent.WaitOne(maxWaitTime));

            // We must call the callback after the consumer is attached!
            // otherwise the message is automatically dismissed.
            foreach (GlobalHook.HookMessage message in hookMessages)
            {
                callback(message);
            }

            /* THEN */
            //true if all events generated by the fake messages have meet expectation.
            Assert.IsTrue(consumedEvent.Wait(maxWaitTime), "Did not find all matching keyboard interact events in time.");

            //total shut down and resources release
            keyboardInteractEventProducer.StopCapture();
            keyboardModule.Initialize(false);
        }
        public void TestKeyboardInteractEventProducer_ModifierKeysTest()
        {
            /* PRECONDITIONS */
            Debug.Assert(keyboardModule != null);
            Debug.Assert(keyboardInteractEventProducer != null);
            Debug.Assert(hookNativeMethodsMock != null);
            Debug.Assert(hookNativeMethodsMock.Mock != null);
            Debug.Assert(nativeKeyboardMock != null);

            /* GIVEN */
            //get the callback
            GlobalHook.CppGetMessageCallback callback = GetCallback();

            //setting up fake nativeKeyboardMock behaviors, messages and corresponding expected Events
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x41)).Returns(Key.A);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x42)).Returns(Key.B);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x43)).Returns(Key.C);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x44)).Returns(Key.D);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x46)).Returns(Key.F);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x47)).Returns(Key.G);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x48)).Returns(Key.H);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x49)).Returns(Key.I);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x4A)).Returns(Key.J);

            // Setting up the modifierkeys sequence which should work together with the fake messages to generate expected events
            nativeKeyboardMock.SetupSequence(nM => nM.IsKeyPressed(INativeKeyboard.VirtualKeyCode.VK_MENU))
            .Returns(true).Returns(false).Returns(false).Returns(false).Returns(false).Returns(true).Returns(true).Returns(false).Returns(true);


            nativeKeyboardMock.SetupSequence(nM => nM.IsKeyPressed(INativeKeyboard.VirtualKeyCode.VK_CONTROL))
            .Returns(false).Returns(true).Returns(false).Returns(false).Returns(false).Returns(false).Returns(true).Returns(true).Returns(true);


            nativeKeyboardMock.SetupSequence(nM => nM.IsKeyPressed(INativeKeyboard.VirtualKeyCode.VK_SHIFT))
            .Returns(false).Returns(false).Returns(true).Returns(false).Returns(false).Returns(true).Returns(false).Returns(true).Returns(true);

            nativeKeyboardMock.SetupSequence(nM => nM.IsKeyPressed(INativeKeyboard.VirtualKeyCode.VK_LWIN))
            .Returns(false).Returns(false).Returns(false).Returns(true).Returns(false).Returns(false).Returns(false).Returns(false).Returns(false);

            nativeKeyboardMock.Setup(nM => nM.IsKeyPressed(INativeKeyboard.VirtualKeyCode.VK_RWIN))
            .Returns(false);

            //setting up fake messages and corresponding expected Events
            GlobalHook.HookMessage[] hookMessages =
            {
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x41
                },                                                                                               //A with Alt down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x42
                },                                                                                               //B with Control down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x43
                },                                                                                               //C with Shift down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x44
                },                                                                                               //D with Left Windows down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x46
                },                                                                                               //F with None
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x47
                },                                                                                               //G with Alt + Shift down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x48
                },                                                                                               //H with Alt + Control down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x49
                },                                                                                               //I with Control + Shift down
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x4A
                }                                                                                               //J with Control + Alt + Shift Windows down
            };

            KeyboardInteractEvent[] expectedEvents =
            {
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.A, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Alt
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.B, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Control
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.C, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Shift
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.D, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Windows
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.F, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.None
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.G, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Alt | ModifierKeys.Shift
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.H, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Alt | ModifierKeys.Control
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.I, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Shift | ModifierKeys.Control
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.J, ModifierKeys_System_Windows_Input_ModifierKeys = ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift
                }
            };

            Assert.IsTrue(hookMessages.Length == expectedEvents.Length);

            /* WHEN */
            // consumedEvent.Signal() will be called gdw one event has been found and meet expectation
            using var consumedEvent = new CountdownEvent(hookMessages.Length);
            Assert.IsTrue(consumedEvent.CurrentCount == hookMessages.Length);

            //didStartConsumingEvent.Set() will be called in Await method gdw the consumer is attached
            using var didStartConsumingEvent = new ManualResetEvent(false);

            //Running the task in another thread
            var thread = new Thread(async() =>
            {
                await foreach (var @event in Await <IAsyncEnumerable <KeyboardInteractEvent> >(keyboardInteractEventProducer.GetEvents(), didStartConsumingEvent))
                {
                    if (!IsKeyboardInteractEventFound(@event, expectedEvents))
                    {
                        continue;
                    }
                    consumedEvent.Signal();
                }
            });

            thread.Start();
            // true if the consumer is attached!
            Assert.IsTrue(didStartConsumingEvent.WaitOne(maxWaitTime));

            // We must call the callback after the consumer is attached!
            // otherwise the message is automatically dismissed.
            foreach (GlobalHook.HookMessage message in hookMessages)
            {
                callback(message);
            }

            /* THEN */
            //true if all events generated by the fake messages have meet expectation.
            Assert.IsTrue(consumedEvent.Wait(maxWaitTime), "Did not find all matching keyboard interact events in time.");

            //total shut down and resources release
            keyboardInteractEventProducer.StopCapture();
            keyboardModule.Initialize(false);
        }
        public void TestKeyboardInteractEventProducer_UnicodeTest()
        {
            /* PRECONDITIONS */
            Debug.Assert(keyboardModule != null);
            Debug.Assert(keyboardInteractEventProducer != null);
            Debug.Assert(hookNativeMethodsMock != null);
            Debug.Assert(hookNativeMethodsMock.Mock != null);

            //setting up fake nativeKeyboardMock behaviors, messages and corresponding expected Events
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x41)).Returns(Key.A);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x5A)).Returns(Key.Z);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x30)).Returns(Key.D0);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x39)).Returns(Key.D9);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0xBD)).Returns(Key.OemMinus);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0xBF)).Returns(Key.OemQuestion);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x12)).Returns(Key.LeftAlt);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0xA2)).Returns(Key.LeftCtrl);
            nativeKeyboardMock.Setup(nativeK => nativeK.KeyFromVirtualKey(0x20)).Returns(Key.Space);

            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0x41)).Returns('a');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0x5A)).Returns('z');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0x30)).Returns('0');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0x39)).Returns('9');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0xBD)).Returns('-');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0xBF)).Returns('/');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0x12)).Returns('\u0000');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0xA2)).Returns('\u0000');
            nativeKeyboardMock.Setup(nativeK => nativeK.ToUnicode(0x20)).Returns(' ');

            /* GIVEN */
            GlobalHook.CppGetMessageCallback callback = GetCallback();
            //setting up fake messages and corresponding expected Events
            GlobalHook.HookMessage[] hookMessages =
            {
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x41
                },                                                                                               //a
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x5A
                },                                                                                               //z
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x30
                },                                                                                               //0
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x39
                },                                                                                               //9
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_SYSKEYDOWN, wParam = (IntPtr)0xBD
                },                                                                                                 //-
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0xBF
                },                                                                                               // '/'
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x12
                },                                                                                               //alt
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0xA2
                },                                                                                               //control
                new GlobalHook.HookMessage {
                    Type = (uint)GlobalHook.MessageType.WM_KEYDOWN, wParam = (IntPtr)0x20
                }                                                                                               //space
            };
            KeyboardInteractEvent[] expectedEvents =
            {
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.A, MappedCharacter_Unicode = 'a'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.Z, MappedCharacter_Unicode = 'z'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.D0, MappedCharacter_Unicode = '0'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.D9, MappedCharacter_Unicode = '9'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.OemMinus, MappedCharacter_Unicode = '-'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.OemQuestion, MappedCharacter_Unicode = '/'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.LeftAlt, MappedCharacter_Unicode = '\u0000'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.LeftCtrl, MappedCharacter_Unicode = '\u0000'
                },
                new KeyboardInteractEvent {
                    PressedKey_System_Windows_Input_Key = Key.Space, MappedCharacter_Unicode = ' '
                }
            };

            Assert.IsTrue(hookMessages.Length == expectedEvents.Length);

            /* WHEN */
            /* WHEN */
            // consumedEvent.Signal() will be called gdw one event has been found and meet expectation
            using var consumedEvent = new CountdownEvent(hookMessages.Length);
            Assert.IsTrue(consumedEvent.CurrentCount == hookMessages.Length);

            //didStartConsumingEvent.Set() will be called in Await method gdw the consumer is attached
            using var didStartConsumingEvent = new ManualResetEvent(false);

            //Running the task in another thread
            var thread = new Thread(async() =>
            {
                await foreach (var @event in Await <IAsyncEnumerable <KeyboardInteractEvent> >(keyboardInteractEventProducer.GetEvents(), didStartConsumingEvent))
                {
                    if (!IsKeyboardInteractEventFound(@event, expectedEvents))
                    {
                        continue;
                    }
                    consumedEvent.Signal();
                }
            });

            thread.Start();
            // true if the consumer is attached!
            Assert.IsTrue(didStartConsumingEvent.WaitOne(maxWaitTime));

            // We must call the callback after the consumer is attached!
            // otherwise the message is automatically dismissed.
            foreach (GlobalHook.HookMessage message in hookMessages)
            {
                callback(message);
            }

            /* THEN */
            Assert.IsTrue(consumedEvent.Wait(maxWaitTime), "Did not find all matching keyboard interact events in time.");

            //total shut down and resources release
            keyboardInteractEventProducer.StopCapture();
            keyboardModule.Initialize(false);
        }