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()
|