コード例 #1
0
class Win32HooksWithMocksTests(unittest.TestCase):
    """Unit tests for the Win32Hook class with mocks for low level dependencies"""
    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        self.hook = Hook()

        # Save pointers to original system calls before mocking
        self.CallNextHookEx = windll.user32.CallNextHookEx
        self.PeekMessageW = windll.user32.PeekMessageW
        self.TranslateMessage = windll.user32.TranslateMessage
        self.DispatchMessageW = windll.user32.DispatchMessageW
        self.atexit_register = atexit.register
        self.sys_exit = sys.exit
        self.fake_kbhook_id = 22
        self.fake_mousehook_id = 33

    def tearDown(self):
        """Cleanups after finishing a test"""
        self.hook.stop()

        # Restore the pointers to original system calls after mocking
        windll.user32.CallNextHookEx = self.CallNextHookEx
        windll.user32.PeekMessageW = self.PeekMessageW
        windll.user32.TranslateMessage = self.TranslateMessage
        windll.user32.DispatchMessageW = self.DispatchMessageW
        atexit.register = self.atexit_register
        sys.exit = self.sys_exit

    def test_none_hook_handler(self):
        """Test running a hook without a handler

        The next hook in chain still should be called
        Simulate an odd situation when we got a hook ID (a hook is inserted)
        but a handler for the hook processing wasn't supplied by a user
        """
        self.hook.keyboard_id = self.fake_kbhook_id
        self.hook.mouse_id = self.fake_mousehook_id
        self.hook.handler = None

        # replace the system API with a mock object
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        # prepare arguments for _keyboard_ll_hdl call
        kbd = win32structures.KBDLLHOOKSTRUCT(0, 0, 0, 0, 0)
        res = self.hook._keyboard_ll_hdl(-1, 3, id(kbd))
        windll.user32.CallNextHookEx.assert_called_with(
            self.fake_kbhook_id, -1, 3, id(kbd))
        self.assertEqual(res, 0)

        # Setup a fresh mock object and arguments for _mouse_ll_hdl call
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        mouse = win32structures.MSLLHOOKSTRUCT((11, 12), 0, 0, 0, 0)
        res = self.hook._mouse_ll_hdl(-1, 3, id(mouse))
        self.assertEqual(res, 0)
        windll.user32.CallNextHookEx.assert_called_with(
            self.fake_mousehook_id, -1, 3, id(mouse))

    def test_keyboard_hook_exception(self):
        """Test handling an exception in a keyboard hook"""
        self.hook.handler = _on_hook_event_with_exception
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        kbd = win32structures.KBDLLHOOKSTRUCT(0, 0, 0, 0, 0)
        self.hook.keyboard_id = self.fake_kbhook_id

        # Verify CallNextHookEx is called even if there is an exception is raised
        self.assertRaises(ValueError, self.hook._keyboard_ll_hdl, -1, 3,
                          id(kbd))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(
            self.fake_kbhook_id, -1, 3, id(kbd))
        self.assertRaises(ValueError, self.hook._keyboard_ll_hdl, 0, 3,
                          id(kbd))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(
            self.fake_kbhook_id, 0, 3, id(kbd))

    def test_mouse_hook_exception(self):
        """Test handling an exception in a mouse hook"""
        self.hook.handler = _on_hook_event_with_exception
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        mouse = win32structures.MSLLHOOKSTRUCT((11, 12), 0, 0, 0, 0)
        self.hook.mouse_id = self.fake_mousehook_id

        # Verify CallNextHookEx is called even if there is an exception is raised
        self.assertRaises(ValueError, self.hook._mouse_ll_hdl, -1, 3,
                          id(mouse))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(
            self.fake_mousehook_id, -1, 3, id(mouse))
        self.assertRaises(ValueError, self.hook._mouse_ll_hdl, 0, 3, id(mouse))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(
            self.fake_mousehook_id, 0, 3, id(mouse))

    @mock.patch.object(Hook, '_process_win_msgs')
    @mock.patch.object(Hook, 'is_hooked')
    def test_listen_loop(self, mock_is_hooked, mock_process_msgs):
        """Test running the main events loop"""
        atexit.register = mock.Mock(return_value=0)
        mock_is_hooked.side_effect = [1, 0]  # exit at a second loop
        mock_process_msgs.return_value = 0

        # mock hook IDs
        self.hook.keyboard_id = self.fake_kbhook_id
        self.hook.mouse_id = self.fake_mousehook_id

        # run the events loop
        self.hook.listen()

        # verify atexit.register calls
        atexit.register.assert_called()
        self.assertEqual(len(atexit.register.mock_calls), 2)
        name, args, kwargs = atexit.register.mock_calls[0]
        self.assertEqual(args[1], self.fake_kbhook_id)
        name, args, kwargs = atexit.register.mock_calls[1]
        self.assertEqual(args[1], self.fake_mousehook_id)

        # verify is_hooked method calls
        mock_is_hooked.assert_called()
        self.assertEqual(len(mock_is_hooked.mock_calls), 2)

        # verify _process_win_msgs method has been called
        mock_process_msgs.assert_called()
        self.assertEqual(len(mock_process_msgs.mock_calls), 1)

    @mock.patch.object(Hook, 'stop')
    def test_process_win_msg(self, mock_stop):
        """Test Hook._process_win_msgs"""
        # Mock external API
        windll.user32.PeekMessageW = mock.Mock(side_effect=[1, 0])
        windll.user32.TranslateMessage = mock.Mock()
        windll.user32.DispatchMessageW = mock.Mock()

        # Test processing the normal messages
        self.hook._process_win_msgs()
        windll.user32.PeekMessageW.assert_called()
        windll.user32.TranslateMessage.assert_called()
        windll.user32.DispatchMessageW.assert_called()

        # Test processing WM_QUIT
        def side_effect(*args):
            """Emulate reception of WM_QUIT"""
            args[0].contents.message = win32con.WM_QUIT
            return 1

        windll.user32.PeekMessageW = mock.Mock(side_effect=side_effect)
        self.assertRaises(SystemExit, self.hook._process_win_msgs)
        mock_stop.assert_called_once()

    def test_is_hooked(self):
        """Verify Hook.is_hooked method"""
        self.assertEqual(self.hook.is_hooked(), False)
        self.hook.mouse_is_hook = True
        self.assertEqual(self.hook.is_hooked(), True)
        self.hook.mouse_is_hook = False
        self.assertEqual(self.hook.is_hooked(), False)
        self.hook.keyboard_is_hook = True
        self.assertEqual(self.hook.is_hooked(), True)
        self.hook.keyboard_is_hook = False
        self.assertEqual(self.hook.is_hooked(), False)
        self.hook.hook(mouse=False, keyboard=False)
        self.assertEqual(self.hook.is_hooked(), False)
コード例 #2
0
class Win32HooksWithMocksTests(unittest.TestCase):

    """Unit tests for the Win32Hook class with mocks for low level dependencies"""

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        self.hook = Hook()

        # Save pointers to original system calls before mocking
        self.CallNextHookEx = windll.user32.CallNextHookEx
        self.PeekMessageW = windll.user32.PeekMessageW
        self.TranslateMessage = windll.user32.TranslateMessage
        self.DispatchMessageW = windll.user32.DispatchMessageW
        self.atexit_register = atexit.register
        self.sys_exit = sys.exit
        self.fake_kbhook_id = 22
        self.fake_mousehook_id = 33

    def tearDown(self):
        """Cleanups after finishing a test"""
        self.hook.stop()

        # Restore the pointers to original system calls after mocking
        windll.user32.CallNextHookEx = self.CallNextHookEx
        windll.user32.PeekMessageW = self.PeekMessageW
        windll.user32.TranslateMessage = self.TranslateMessage
        windll.user32.DispatchMessageW = self.DispatchMessageW
        atexit.register = self.atexit_register
        sys.exit = self.sys_exit

    def test_none_hook_handler(self):
        """Test running a hook without a handler

        The next hook in chain still should be called
        Simulate an odd situation when we got a hook ID (a hook is inserted)
        but a handler for the hook processing wasn't supplied by a user
        """
        self.hook.keyboard_id = self.fake_kbhook_id
        self.hook.mouse_id = self.fake_mousehook_id
        self.hook.handler = None

        # replace the system API with a mock object
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        # prepare arguments for _keyboard_ll_hdl call
        kbd = win32structures.KBDLLHOOKSTRUCT(0, 0, 0, 0, 0)
        res = self.hook._keyboard_ll_hdl(-1, 3, id(kbd))
        windll.user32.CallNextHookEx.assert_called_with(self.fake_kbhook_id, -1, 3, id(kbd))
        self.assertEqual(res, 0)

        # Setup a fresh mock object and arguments for _mouse_ll_hdl call
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        mouse = win32structures.MSLLHOOKSTRUCT((11, 12), 0, 0, 0, 0)
        res = self.hook._mouse_ll_hdl(-1, 3, id(mouse))
        self.assertEqual(res, 0)
        windll.user32.CallNextHookEx.assert_called_with(self.fake_mousehook_id, -1, 3, id(mouse))

    def test_keyboard_hook_exception(self):
        """Test handling an exception in a keyboard hook"""
        self.hook.handler = _on_hook_event_with_exception
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        kbd = win32structures.KBDLLHOOKSTRUCT(0, 0, 0, 0, 0)
        self.hook.keyboard_id = self.fake_kbhook_id

        # Verify CallNextHookEx is called even if there is an exception is raised
        self.assertRaises(ValueError, self.hook._keyboard_ll_hdl, -1, 3, id(kbd))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(self.fake_kbhook_id, -1, 3, id(kbd))
        self.assertRaises(ValueError, self.hook._keyboard_ll_hdl, 0, 3, id(kbd))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(self.fake_kbhook_id, 0, 3, id(kbd))

    def test_mouse_hook_exception(self):
        """Test handling an exception in a mouse hook"""
        self.hook.handler = _on_hook_event_with_exception
        windll.user32.CallNextHookEx = mock.Mock(return_value=0)
        mouse = win32structures.MSLLHOOKSTRUCT((11, 12), 0, 0, 0, 0)
        self.hook.mouse_id = self.fake_mousehook_id

        # Verify CallNextHookEx is called even if there is an exception is raised
        self.assertRaises(ValueError, self.hook._mouse_ll_hdl, -1, 3, id(mouse))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(self.fake_mousehook_id, -1, 3, id(mouse))
        self.assertRaises(ValueError, self.hook._mouse_ll_hdl, 0, 3, id(mouse))
        windll.user32.CallNextHookEx.assert_called()
        windll.user32.CallNextHookEx.assert_called_with(self.fake_mousehook_id, 0, 3, id(mouse))

    @mock.patch.object(Hook, '_process_win_msgs')
    @mock.patch.object(Hook, 'is_hooked')
    def test_listen_loop(self, mock_is_hooked, mock_process_msgs):
        """Test running the main events loop"""
        atexit.register = mock.Mock(return_value=0)
        mock_is_hooked.side_effect = [1, 0]  # exit at a second loop
        mock_process_msgs.return_value = 0

        # mock hook IDs
        self.hook.keyboard_id = self.fake_kbhook_id
        self.hook.mouse_id = self.fake_mousehook_id

        # run the events loop
        self.hook.listen()

        # verify atexit.register calls
        atexit.register.assert_called()
        self.assertEqual(len(atexit.register.mock_calls), 2)
        name, args, kwargs = atexit.register.mock_calls[0]
        self.assertEqual(args[1], self.fake_kbhook_id)
        name, args, kwargs = atexit.register.mock_calls[1]
        self.assertEqual(args[1], self.fake_mousehook_id)

        # verify is_hooked method calls
        mock_is_hooked.assert_called()
        self.assertEqual(len(mock_is_hooked.mock_calls), 2)

        # verify _process_win_msgs method has been called
        mock_process_msgs.assert_called()
        self.assertEqual(len(mock_process_msgs.mock_calls), 1)

    @mock.patch.object(Hook, 'stop')
    def test_process_win_msg(self, mock_stop):
        """Test Hook._process_win_msgs"""
        # Mock external API
        windll.user32.PeekMessageW = mock.Mock(side_effect=[1, 0])
        windll.user32.TranslateMessage = mock.Mock()
        windll.user32.DispatchMessageW = mock.Mock()

        # Test processing the normal messages
        self.hook._process_win_msgs()
        windll.user32.PeekMessageW.assert_called()
        windll.user32.TranslateMessage.assert_called()
        windll.user32.DispatchMessageW.assert_called()

        # Test processing WM_QUIT
        def side_effect(*args):
            """Emulate reception of WM_QUIT"""
            args[0].contents.message = win32con.WM_QUIT
            return 1
        windll.user32.PeekMessageW = mock.Mock(side_effect=side_effect)
        self.assertRaises(SystemExit, self.hook._process_win_msgs)
        mock_stop.assert_called_once()

    def test_is_hooked(self):
        """Verify Hook.is_hooked method"""
        self.assertEqual(self.hook.is_hooked(), False)
        self.hook.mouse_is_hook = True
        self.assertEqual(self.hook.is_hooked(), True)
        self.hook.mouse_is_hook = False
        self.assertEqual(self.hook.is_hooked(), False)
        self.hook.keyboard_is_hook = True
        self.assertEqual(self.hook.is_hooked(), True)
        self.hook.keyboard_is_hook = False
        self.assertEqual(self.hook.is_hooked(), False)
        self.hook.hook(mouse=False, keyboard=False)
        self.assertEqual(self.hook.is_hooked(), False)