File: test_notification.py

package info (click to toggle)
python-plyer 2.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,808 kB
  • sloc: python: 13,395; sh: 217; makefile: 177
file content (209 lines) | stat: -rw-r--r-- 5,918 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
'''
TestNotification
================

Tested platforms:

* Windows
* Linux - notify-send, dbus
'''

import unittest
import sys

from time import sleep
from os.path import dirname, abspath, join

from unittest.mock import Mock, patch
from plyer.tests.common import PlatformTest, platform_import


class MockedNotifySend:
    '''
    Mocked object used instead of the console-like calling
    of notify-send binary with parameters.
    '''
    @staticmethod
    def whereis_exe(binary):
        '''
        Mock whereis_exe, so that it looks like
        Linux notify-send binary is present on the system.
        '''
        return binary == 'notify-send'

    @staticmethod
    def call(args):
        '''
        Mocked subprocess.call to check console parameters.
        '''
        assert len(args) >= 3
        assert TestNotification.data['title'] in args
        assert TestNotification.data['message'] in args

    @staticmethod
    def warn(msg):
        '''
        Mocked warnings.warn, so that we check the custom ImportError message.
        '''
        assert 'dbus package is not installed' in msg


class TestNotification(unittest.TestCase):
    '''
    TestCase for plyer.notification.
    '''

    data = {
        'title': 'title',
        'message': 'My Message\nis multiline',
        'app_name': 'Plyer Test',
        'app_icon': join(
            dirname(abspath(__file__)),
            'images', 'kivy32.ico'
        ),
        'timeout': 0.7
    }

    def show_notification(self, instance):
        '''
        Call notify() from platform specific instance with sample data.
        '''
        instance.notify(**self.data)

    @PlatformTest('win')
    def test_notification_windows(self):
        '''
        Test Windows API for plyer.notification.
        '''
        import ctypes
        from ctypes import (
            WINFUNCTYPE, POINTER,
            create_unicode_buffer,
            c_bool, c_int
        )
        notif = platform_import(
            platform='win',
            module_name='notification'
        ).instance()
        enum_windows = ctypes.windll.user32.EnumWindows
        get_class_name = ctypes.windll.user32.GetClassNameW

        # loop over windows and get refs to
        # the opened plyer notifications
        clsnames = []

        def fetch_class(hwnd, *args):
            '''
            EnumWindowsProc callback for EnumWindows.
            '''
            buff = create_unicode_buffer(50)
            get_class_name(hwnd, buff, 50)

            if 'Plyer' in buff.value:
                clsnames.append(buff.value)

        # ensure it's not an empty facade
        self.assertIn('WindowsNotification', str(notif))

        # create enum function for EnumWindows
        enum_windows_proc = WINFUNCTYPE(
            # returns
            c_bool,

            # input params: hwnd, lParam
            POINTER(c_int), POINTER(c_int)
        )

        for i in range(3):
            self.show_notification(notif)

            # the balloon needs some time to became visible in WinAPI
            sleep(0.2)

            # fetch window class names
            enum_windows(
                # enum & params
                enum_windows_proc(fetch_class), None
            )

            # 3 active balloons at the same time,
            # class_name is incremented - see WindowsBalloonTip
            self.assertEqual(len(clsnames), i + 1)
            self.assertIn('PlyerTaskbar' + str(i), clsnames)
            clsnames = []

    @PlatformTest('linux')
    def test_notification_dbus(self):
        '''
        Test mocked Linux DBus for plyer.notification.
        '''
        notif = platform_import(
            platform='linux',
            module_name='notification'
        )
        self.assertIn('NotifyDbus', dir(notif))

        # (3) mocked Interface called from dbus
        interface = Mock()
        interface.side_effect = (interface, )

        # (2) mocked SessionBus called from dbus
        session_bus = Mock()
        session_bus.side_effect = (session_bus, )

        # (1) mocked dbus for import
        dbus = Mock(SessionBus=session_bus, Interface=interface)

        # inject the mocked module
        self.assertNotIn('dbus', sys.modules)
        sys.modules['dbus'] = dbus

        try:
            notif = notif.instance()
            self.assertIn('NotifyDbus', str(notif))

            # call notify()
            self.show_notification(notif)

            # check whether Mocks were called
            dbus.SessionBus.assert_called_once()

            session_bus.get_object.assert_called_once_with(
                'org.freedesktop.Notifications',
                '/org/freedesktop/Notifications'
            )

            interface.Notify.assert_called_once_with(
                TestNotification.data['app_name'],
                0,
                TestNotification.data['app_icon'],
                TestNotification.data['title'],
                TestNotification.data['message'],
                [], {},
                TestNotification.data['timeout'] * 1000
            )
        finally:
            del sys.modules['dbus']
        self.assertNotIn('dbus', sys.modules)

    @PlatformTest('linux')
    def test_notification_notifysend(self):
        '''
        Test mocked Linux notify-send for plyer.notification.
        '''
        notif = platform_import(
            platform='linux',
            module_name='notification',
            whereis_exe=MockedNotifySend.whereis_exe
        )
        self.assertIn('NotifySendNotification', dir(notif))
        with patch(target='warnings.warn', new=MockedNotifySend.warn):
            notif = notif.instance()
        self.assertIn('NotifySendNotification', str(notif))

        with patch(target='subprocess.call', new=MockedNotifySend.call):
            self.assertIsNone(self.show_notification(notif))


if __name__ == '__main__':
    unittest.main()