def test_apprise_details(): """ API: Apprise() Details """ # Caling load matix a second time which is an internal function causes it # to skip over content already loaded into our matrix and thefore accesses # other if/else parts of the code that aren't otherwise called __load_matrix() a = Apprise() # Details object details = a.details() # Dictionary response assert isinstance(details, dict) # Apprise version assert 'version' in details assert details.get('version') == __version__ # Defined schemas identify each plugin assert 'schemas' in details assert isinstance(details.get('schemas'), list) # We have an entry per defined plugin assert 'asset' in details assert isinstance(details.get('asset'), dict) assert 'app_id' in details['asset'] assert 'app_desc' in details['asset'] assert 'default_extension' in details['asset'] assert 'theme' in details['asset'] assert 'image_path_mask' in details['asset'] assert 'image_url_mask' in details['asset'] assert 'image_url_logo' in details['asset'] # All plugins must have a name defined; the below generates # a list of entrys that do not have a string defined. assert (not len([ x['service_name'] for x in details['schemas'] if not compat_is_basestring(x['service_name']) ]))
def test_gnome_plugin(): """ API: NotifyGnome Plugin() """ # Our module base gi_name = 'gi' # First we do an import without the gi library available to ensure # we can handle cases when the library simply isn't available if gi_name in sys.modules: # Test cases where the gi library exists; we want to remove it # for the purpose of testing and capture the handling of the # library when it is missing del sys.modules[gi_name] reload(sys.modules['apprise.plugins.NotifyGnome']) # We need to fake our gnome environment for testing purposes since # the gi library isn't available in Travis CI gi = types.ModuleType(gi_name) gi.repository = types.ModuleType(gi_name + '.repository') gi.module = types.ModuleType(gi_name + '.module') mock_pixbuf = mock.Mock() mock_notify = mock.Mock() gi.repository.GdkPixbuf = \ types.ModuleType(gi_name + '.repository.GdkPixbuf') gi.repository.GdkPixbuf.Pixbuf = mock_pixbuf gi.repository.Notify = mock.Mock() gi.repository.Notify.init.return_value = True gi.repository.Notify.Notification = mock_notify # Emulate require_version function: gi.require_version = mock.Mock( name=gi_name + '.require_version') # Force the fake module to exist sys.modules[gi_name] = gi sys.modules[gi_name + '.repository'] = gi.repository sys.modules[gi_name + '.repository.Notify'] = gi.repository.Notify # Notify Object notify_obj = mock.Mock() notify_obj.set_urgency.return_value = True notify_obj.set_icon_from_pixbuf.return_value = True notify_obj.set_image_from_pixbuf.return_value = True notify_obj.show.return_value = True mock_notify.new.return_value = notify_obj mock_pixbuf.new_from_file.return_value = True # The following libraries need to be reloaded to prevent # TypeError: super(type, obj): obj must be an instance or subtype of type # This is better explained in this StackOverflow post: # https://stackoverflow.com/questions/31363311/\ # any-way-to-manually-fix-operation-of-\ # super-after-ipython-reload-avoiding-ty # reload(sys.modules['apprise.plugins.NotifyGnome']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise']) # Create our instance obj = apprise.Apprise.instantiate('gnome://', suppress_exceptions=False) obj.duration = 0 # Check that it found our mocked environments assert(obj._enabled is True) # Test url() call assert(compat_is_basestring(obj.url()) is True) # test notifications assert(obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # test notification without a title assert(obj.notify(title='', body='body', notify_type=apprise.NotifyType.INFO) is True) # Test our loading of our icon exception; it will still allow the # notification to be sent mock_pixbuf.new_from_file.side_effect = AttributeError() assert(obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # Undo our change mock_pixbuf.new_from_file.side_effect = None # Test our exception handling during initialization sys.modules['gi.repository.Notify']\ .Notification.new.return_value = None sys.modules['gi.repository.Notify']\ .Notification.new.side_effect = AttributeError() assert(obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False) # Undo our change sys.modules['gi.repository.Notify']\ .Notification.new.side_effect = None # Toggle our testing for when we can't send notifications because the # package has been made unavailable to us obj._enabled = False assert(obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False) # Test the setting of a the urgency apprise.plugins.NotifyGnome(urgency=0) # Verify this all works in the event a ValueError is also thronw # out of the call to gi.require_version() # Emulate require_version function: gi.require_version.side_effect = ValueError() # The following libraries need to be reloaded to prevent # TypeError: super(type, obj): obj must be an instance or subtype of type # This is better explained in this StackOverflow post: # https://stackoverflow.com/questions/31363311/\ # any-way-to-manually-fix-operation-of-\ # super-after-ipython-reload-avoiding-ty # reload(sys.modules['apprise.plugins.NotifyGnome']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise']) # Create our instance obj = apprise.Apprise.instantiate('gnome://', suppress_exceptions=False) assert(isinstance(obj, apprise.plugins.NotifyGnome) is True) obj.duration = 0 # Our notifications can not work without our gi library having been # loaded. assert(obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False)
def test_plugin(mock_refresh, mock_send): """ API: NotifyPushjet Plugin() (pt1) """ # iterate over our dictionary and test it out for (url, meta) in TEST_URLS: # Our expected instance instance = meta.get('instance', None) # Our expected server objects self = meta.get('self', None) # Our expected Query response (True, False, or exception type) response = meta.get('response', True) # Allow us to force the server response code to be something other then # the defaults response = meta.get('response', True if response else False) test_notify_exceptions = meta.get('test_notify_exceptions', False) test_exceptions = ( plugins.pushjet.errors.AccessError( 0, 'pushjet.AccessError() not handled'), plugins.pushjet.errors.NonexistentError( 0, 'pushjet.NonexistentError() not handled'), plugins.pushjet.errors.SubscriptionError( 0, 'gntp.SubscriptionError() not handled'), plugins.pushjet.errors.RequestError( 'pushjet.RequestError() not handled'), ) try: obj = Apprise.instantiate(url, suppress_exceptions=False) if obj is None: # We're done (assuming this is what we were expecting) assert instance is None continue if instance is None: # Expected None but didn't get it print('%s instantiated %s (but expected None)' % (url, str(obj))) assert (False) assert (isinstance(obj, instance)) if isinstance(obj, plugins.NotifyBase.NotifyBase): # We loaded okay; now lets make sure we can reverse this url assert (compat_is_basestring(obj.url()) is True) # Instantiate the exact same object again using the URL from # the one that was already created properly obj_cmp = Apprise.instantiate(obj.url()) # Our object should be the same instance as what we had # originally expected above. if not isinstance(obj_cmp, plugins.NotifyBase.NotifyBase): # Assert messages are hard to trace back with the way # these tests work. Just printing before throwing our # assertion failure makes things easier to debug later on print('TEST FAIL: {} regenerated as {}'.format( url, obj.url())) assert (False) if self: # Iterate over our expected entries inside of our object for key, val in self.items(): # Test that our object has the desired key assert (hasattr(key, obj)) assert (getattr(key, obj) == val) try: if test_notify_exceptions is False: # Store our response mock_send.return_value = response mock_send.side_effect = None # check that we're as expected assert obj.notify(title='test', body='body', notify_type=NotifyType.INFO) == response else: for exception in test_exceptions: mock_send.side_effect = exception mock_send.return_value = None try: assert obj.notify( title='test', body='body', notify_type=NotifyType.INFO) is False except AssertionError: # Don't mess with these entries raise except Exception: # We can't handle this exception type raise except AssertionError: # Don't mess with these entries print('%s AssertionError' % url) raise except Exception as e: # Check that we were expecting this exception to happen if not isinstance(e, response): raise except AssertionError: # Don't mess with these entries print('%s AssertionError' % url) raise except Exception as e: # Handle our exception if (instance is None): raise if not isinstance(e, instance): raise
def test_notify_base(): """ API: NotifyBase() object """ # invalid types throw exceptions try: nb = NotifyBase(**{'format': 'invalid'}) # We should never reach here as an exception should be thrown assert(False) except TypeError: assert(True) # Bad port information nb = NotifyBase(port='invalid') assert nb.port is None nb = NotifyBase(port=10) assert nb.port == 10 # Throttle overrides.. nb = NotifyBase() nb.throttle_attempt = 0.0 start_time = default_timer() nb.throttle() elapsed = default_timer() - start_time # Should be a very fast response time since we set it to zero but we'll # check for less then 500 to be fair as some testing systems may be slower # then other assert elapsed < 0.5 start_time = default_timer() nb.throttle(1.0) elapsed = default_timer() - start_time # Should be a very fast response time since we set it to zero but we'll # check for less then 500 to be fair as some testing systems may be slower # then other assert elapsed < 1.5 # our NotifyBase wasn't initialized with an ImageSize so this will fail assert nb.image_url(notify_type=NotifyType.INFO) is None assert nb.image_path(notify_type=NotifyType.INFO) is None assert nb.image_raw(notify_type=NotifyType.INFO) is None # Color handling assert nb.color(notify_type='invalid') is None assert compat_is_basestring( nb.color(notify_type=NotifyType.INFO, color_type=None)) assert isinstance( nb.color(notify_type=NotifyType.INFO, color_type=int), int) assert isinstance( nb.color(notify_type=NotifyType.INFO, color_type=tuple), tuple) # Create an object nb = NotifyBase() # Force an image size since the default doesn't have one nb.image_size = NotifyImageSize.XY_256 # We'll get an object this time around assert nb.image_url(notify_type=NotifyType.INFO) is not None assert nb.image_path(notify_type=NotifyType.INFO) is not None assert nb.image_raw(notify_type=NotifyType.INFO) is not None # But we will not get a response with an invalid notification type assert nb.image_url(notify_type='invalid') is None assert nb.image_path(notify_type='invalid') is None assert nb.image_raw(notify_type='invalid') is None # Static function testing assert NotifyBase.escape_html("<content>'\t \n</content>") == \ '<content>'  \n</content>' assert NotifyBase.escape_html( "<content>'\t \n</content>", convert_new_lines=True) == \ '<content>'  <br/></content>' assert NotifyBase.split_path( '/path/?name=Dr%20Disrespect', unquote=False) == \ ['path', '?name=Dr%20Disrespect'] assert NotifyBase.split_path( '/path/?name=Dr%20Disrespect', unquote=True) == \ ['path', '?name=Dr', 'Disrespect'] # Test is_email assert NotifyBase.is_email('*****@*****.**') is True assert NotifyBase.is_email('invalid.com') is False # Test is_hostname assert NotifyBase.is_hostname('example.com') is True # Test quote assert NotifyBase.unquote('%20') == ' ' assert NotifyBase.quote(' ') == '%20' assert NotifyBase.unquote(None) == '' assert NotifyBase.quote(None) == ''
def test_notify_base(): """ API: NotifyBase() object """ # invalid types throw exceptions try: nb = NotifyBase(**{'format': 'invalid'}) # We should never reach here as an exception should be thrown assert (False) except TypeError: assert (True) # invalid types throw exceptions try: nb = NotifyBase(**{'overflow': 'invalid'}) # We should never reach here as an exception should be thrown assert (False) except TypeError: assert (True) # Bad port information nb = NotifyBase(port='invalid') assert nb.port is None nb = NotifyBase(port=10) assert nb.port == 10 try: nb.url() assert False except NotImplementedError: # Each sub-module is that inherits this as a parent is required to # over-ride this function. So direct calls to this throws a not # implemented error intentionally assert True try: nb.send('test message') assert False except NotImplementedError: # Each sub-module is that inherits this as a parent is required to # over-ride this function. So direct calls to this throws a not # implemented error intentionally assert True # Throttle overrides.. nb = NotifyBase() nb.request_rate_per_sec = 0.0 start_time = default_timer() nb.throttle() elapsed = default_timer() - start_time # Should be a very fast response time since we set it to zero but we'll # check for less then 500 to be fair as some testing systems may be slower # then other assert elapsed < 0.5 # Concurrent calls should achieve the same response start_time = default_timer() nb.throttle() elapsed = default_timer() - start_time assert elapsed < 0.5 nb = NotifyBase() nb.request_rate_per_sec = 1.0 # Set our time to now start_time = default_timer() nb.throttle() elapsed = default_timer() - start_time # A first call to throttle (Without telling it a time previously ran) does # not block for any length of time; it just merely sets us up for # concurrent calls to block assert elapsed < 0.5 # Concurrent calls could take up to the rate_per_sec though... start_time = default_timer() nb.throttle(last_io=datetime.now()) elapsed = default_timer() - start_time assert elapsed > 0.5 and elapsed < 1.5 nb = NotifyBase() nb.request_rate_per_sec = 1.0 # Set our time to now start_time = default_timer() nb.throttle(last_io=datetime.now()) elapsed = default_timer() - start_time # because we told it that we had already done a previous action (now) # the throttle holds out until the right time has passed assert elapsed > 0.5 and elapsed < 1.5 # Concurrent calls could take up to the rate_per_sec though... start_time = default_timer() nb.throttle(last_io=datetime.now()) elapsed = default_timer() - start_time assert elapsed > 0.5 and elapsed < 1.5 nb = NotifyBase() start_time = default_timer() nb.request_rate_per_sec = 1.0 # Force a time in the past nb.throttle(last_io=(datetime.now() - timedelta(seconds=20))) elapsed = default_timer() - start_time # Should be a very fast response time since we set it to zero but we'll # check for less then 500 to be fair as some testing systems may be slower # then other assert elapsed < 0.5 # our NotifyBase wasn't initialized with an ImageSize so this will fail assert nb.image_url(notify_type=NotifyType.INFO) is None assert nb.image_path(notify_type=NotifyType.INFO) is None assert nb.image_raw(notify_type=NotifyType.INFO) is None # Color handling assert nb.color(notify_type='invalid') is None assert compat_is_basestring( nb.color(notify_type=NotifyType.INFO, color_type=None)) assert isinstance(nb.color(notify_type=NotifyType.INFO, color_type=int), int) assert isinstance(nb.color(notify_type=NotifyType.INFO, color_type=tuple), tuple) # Create an object nb = NotifyBase() # Force an image size since the default doesn't have one nb.image_size = NotifyImageSize.XY_256 # We'll get an object this time around assert nb.image_url(notify_type=NotifyType.INFO) is not None assert nb.image_path(notify_type=NotifyType.INFO) is not None assert nb.image_raw(notify_type=NotifyType.INFO) is not None # But we will not get a response with an invalid notification type assert nb.image_url(notify_type='invalid') is None assert nb.image_path(notify_type='invalid') is None assert nb.image_raw(notify_type='invalid') is None # Static function testing assert NotifyBase.escape_html("<content>'\t \n</content>") == \ '<content>'  \n</content>' assert NotifyBase.escape_html( "<content>'\t \n</content>", convert_new_lines=True) == \ '<content>'  <br/></content>' assert NotifyBase.split_path( '/path/?name=Dr%20Disrespect', unquote=False) == \ ['path', '?name=Dr%20Disrespect'] assert NotifyBase.split_path( '/path/?name=Dr%20Disrespect', unquote=True) == \ ['path', '?name=Dr', 'Disrespect'] # Test is_email assert NotifyBase.is_email('*****@*****.**') is True assert NotifyBase.is_email('invalid.com') is False # Test is_hostname assert NotifyBase.is_hostname('example.com') is True # Test quote assert NotifyBase.unquote('%20') == ' ' assert NotifyBase.quote(' ') == '%20' assert NotifyBase.unquote(None) == '' assert NotifyBase.quote(None) == ''
def test_apprise(): """ API: Apprise() object """ # Caling load matix a second time which is an internal function causes it # to skip over content already loaded into our matrix and thefore accesses # other if/else parts of the code that aren't otherwise called __load_matrix() a = Apprise() # no items assert (len(a) == 0) # Create an Asset object asset = AppriseAsset(theme='default') # We can load the device using our asset a = Apprise(asset=asset) # We can load our servers up front as well servers = [ 'faast://abcdefghijklmnop-abcdefg', 'kodi://kodi.server.local', ] a = Apprise(servers=servers) # 2 servers loaded assert (len(a) == 2) # We can retrieve our URLs this way: assert (len(a.urls()) == 2) # We can add another server assert (a.add('mmosts://mattermost.server.local/' '3ccdd113474722377935511fc85d3dd4') is True) assert (len(a) == 3) # We can pop an object off of our stack by it's indexed value: obj = a.pop(0) assert (isinstance(obj, NotifyBase) is True) assert (len(a) == 2) # We can retrieve elements from our list too by reference: assert (compat_is_basestring(a[0].url()) is True) # We can iterate over our list too: count = 0 for o in a: assert (compat_is_basestring(o.url()) is True) count += 1 # verify that we did indeed iterate over each element assert (len(a) == count) # We can empty our set a.clear() assert (len(a) == 0) # An invalid schema assert (a.add('this is not a parseable url at all') is False) assert (len(a) == 0) # An unsupported schema assert (a.add('invalid://we.just.do.not.support.this.plugin.type') is False) assert (len(a) == 0) # A poorly formatted URL assert (a.add('json://user:@@@:bad?no.good') is False) assert (len(a) == 0) # Add a server with our asset we created earlier assert (a.add( 'mmosts://mattermost.server.local/' '3ccdd113474722377935511fc85d3dd4', asset=asset) is True) # Clear our server listings again a.clear() # No servers to notify assert (a.notify(title="my title", body="my body") is False) class BadNotification(NotifyBase): def __init__(self, **kwargs): super(BadNotification, self).__init__(**kwargs) # We fail whenever we're initialized raise TypeError() class GoodNotification(NotifyBase): def __init__(self, **kwargs): super(GoodNotification, self).__init__(notify_format=NotifyFormat.HTML, **kwargs) def notify(self, **kwargs): # Pretend everything is okay return True # Store our bad notification in our schema map SCHEMA_MAP['bad'] = BadNotification # Store our good notification in our schema map SCHEMA_MAP['good'] = GoodNotification # Just to explain what is happening here, we would have parsed the # url properly but failed when we went to go and create an instance # of it. assert (a.add('bad://localhost') is False) assert (len(a) == 0) assert (a.add('good://localhost') is True) assert (len(a) == 1) # Bad Notification Type is still allowed as it is presumed the user # know's what their doing assert (a.notify(title="my title", body="my body", notify_type='bad') is True) # No Title/Body combo's assert (a.notify(title=None, body=None) is False) assert (a.notify(title='', body=None) is False) assert (a.notify(title=None, body='') is False) # As long as one is present, we're good assert (a.notify(title=None, body='present') is True) assert (a.notify(title='present', body=None) is True) assert (a.notify(title="present", body="present") is True) # Clear our server listings again a.clear() class ThrowNotification(NotifyBase): def notify(self, **kwargs): # Pretend everything is okay raise TypeError() class RuntimeNotification(NotifyBase): def notify(self, **kwargs): # Pretend everything is okay raise RuntimeError() class FailNotification(NotifyBase): def notify(self, **kwargs): # Pretend everything is okay return False # Store our bad notification in our schema map SCHEMA_MAP['throw'] = ThrowNotification # Store our good notification in our schema map SCHEMA_MAP['fail'] = FailNotification # Store our good notification in our schema map SCHEMA_MAP['runtime'] = RuntimeNotification assert (a.add('runtime://localhost') is True) assert (a.add('throw://localhost') is True) assert (a.add('fail://localhost') is True) assert (len(a) == 3) # Test when our notify both throws an exception and or just # simply returns False assert (a.notify(title="present", body="present") is False) # Create a Notification that throws an unexected exception class ThrowInstantiateNotification(NotifyBase): def __init__(self, **kwargs): # Pretend everything is okay raise TypeError() SCHEMA_MAP['throw'] = ThrowInstantiateNotification # Reset our object a.clear() assert (len(a) == 0) # Instantiate a good object plugin = a.instantiate('good://localhost', tag="good") assert (isinstance(plugin, NotifyBase)) # Test simple tagging inside of the object assert ("good" in plugin) assert ("bad" not in plugin) # the in (__contains__ override) is based on or'ed content; so although # 'bad' isn't tagged as being in the plugin, 'good' is, so the return # value of this is True assert (["bad", "good"] in plugin) assert (set(["bad", "good"]) in plugin) assert (("bad", "good") in plugin) # We an add already substatiated instances into our Apprise object a.add(plugin) assert (len(a) == 1) # Reset our object again a.clear() try: a.instantiate('throw://localhost', suppress_exceptions=False) assert (False) except TypeError: assert (True) assert (len(a) == 0) assert (a.instantiate('throw://localhost', suppress_exceptions=True) is None) assert (len(a) == 0)
def test_dbus_plugin(mock_mainloop, mock_byte, mock_bytearray, mock_interface, mock_sessionbus): """ API: NotifyDBus Plugin() """ # Our module base gi_name = 'gi' # First we do an import without the gi library available to ensure # we can handle cases when the library simply isn't available if gi_name in sys.modules: # Test cases where the gi library exists; we want to remove it # for the purpose of testing and capture the handling of the # library when it is missing del sys.modules[gi_name] reload(sys.modules['apprise.plugins.NotifyDBus']) # We need to fake our dbus environment for testing purposes since # the gi library isn't available in Travis CI gi = types.ModuleType(gi_name) gi.repository = types.ModuleType(gi_name + '.repository') mock_pixbuf = mock.Mock() mock_image = mock.Mock() mock_pixbuf.new_from_file.return_value = mock_image mock_image.get_width.return_value = 100 mock_image.get_height.return_value = 100 mock_image.get_rowstride.return_value = 1 mock_image.get_has_alpha.return_value = 0 mock_image.get_bits_per_sample.return_value = 8 mock_image.get_n_channels.return_value = 1 mock_image.get_pixels.return_value = '' gi.repository.GdkPixbuf = \ types.ModuleType(gi_name + '.repository.GdkPixbuf') gi.repository.GdkPixbuf.Pixbuf = mock_pixbuf # Emulate require_version function: gi.require_version = mock.Mock(name=gi_name + '.require_version') # Force the fake module to exist sys.modules[gi_name] = gi sys.modules[gi_name + '.repository'] = gi.repository # Exception Handling mock_mainloop.qt.DBusQtMainLoop.return_value = True mock_mainloop.qt.DBusQtMainLoop.side_effect = ImportError sys.modules['dbus.mainloop.qt'] = mock_mainloop.qt reload(sys.modules['apprise.plugins.NotifyDBus']) mock_mainloop.qt.DBusQtMainLoop.side_effect = None # Python v2.x mock_mainloop.glib.DBusGMainLoop.return_value = True mock_mainloop.glib.DBusGMainLoop.side_effect = ImportError() # Python 3.x mock_mainloop.glib.NativeMainLoop.return_value = True mock_mainloop.glib.NativeMainLoop.side_effect = ImportError() sys.modules['dbus.mainloop.glib'] = mock_mainloop.glib reload(sys.modules['apprise.plugins.NotifyDBus']) mock_mainloop.glib.DBusGMainLoop.side_effect = None mock_mainloop.glib.NativeMainLoop.side_effect = None # The following libraries need to be reloaded to prevent # TypeError: super(type, obj): obj must be an instance or subtype of type # This is better explained in this StackOverflow post: # https://stackoverflow.com/questions/31363311/\ # any-way-to-manually-fix-operation-of-\ # super-after-ipython-reload-avoiding-ty # reload(sys.modules['apprise.plugins.NotifyDBus']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise']) # Create our instance (identify all supported types) obj = apprise.Apprise.instantiate('dbus://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj = apprise.Apprise.instantiate('kde://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj = apprise.Apprise.instantiate('qt://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj = apprise.Apprise.instantiate('glib://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj.duration = 0 # Check that it found our mocked environments assert (obj._enabled is True) # Test our class loading using a series of arguments try: apprise.plugins.NotifyDBus(**{'schema': 'invalid'}) # We should not reach here as the invalid schema # should force an exception assert (False) except TypeError: # Expected behaviour assert (True) # Invalid URLs assert apprise.plugins.NotifyDBus.parse_url('') is None # Set our X and Y coordinate and try the notification assert (apprise.plugins.NotifyDBus( x_axis=0, y_axis=0, **{ 'schema': 'dbus' }).notify(title='', body='body', notify_type=apprise.NotifyType.INFO) is True) # test notifications assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # test notification without a title assert (obj.notify( title='', body='body', notify_type=apprise.NotifyType.INFO) is True) # If our underlining object throws for whatever reason, we will # gracefully fail mock_notify = mock.Mock() mock_interface.return_value = mock_notify mock_notify.Notify.side_effect = AttributeError() assert (obj.notify( title='', body='body', notify_type=apprise.NotifyType.INFO) is False) mock_notify.Notify.side_effect = None # Test our loading of our icon exception; it will still allow the # notification to be sent mock_pixbuf.new_from_file.side_effect = AttributeError() assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # Undo our change mock_pixbuf.new_from_file.side_effect = None # Test our exception handling during initialization # Toggle our testing for when we can't send notifications because the # package has been made unavailable to us obj._enabled = False assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False) # Test the setting of a the urgency apprise.plugins.NotifyDBus(urgency=0) # # We can still notify if the gi library is the only inaccessible # compontent # # Emulate require_version function: gi.require_version.side_effect = ImportError() # The following libraries need to be reloaded to prevent # TypeError: super(type, obj): obj must be an instance or subtype of type # This is better explained in this StackOverflow post: # https://stackoverflow.com/questions/31363311/\ # any-way-to-manually-fix-operation-of-\ # super-after-ipython-reload-avoiding-ty # reload(sys.modules['apprise.plugins.NotifyDBus']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise']) # Create our instance obj = apprise.Apprise.instantiate('glib://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj.duration = 0 # Test url() call assert (compat_is_basestring(obj.url()) is True) # Our notification succeeds even though the gi library was not loaded assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # Verify this all works in the event a ValueError is also thronw # out of the call to gi.require_version() # Emulate require_version function: gi.require_version.side_effect = ValueError() # The following libraries need to be reloaded to prevent # TypeError: super(type, obj): obj must be an instance or subtype of type # This is better explained in this StackOverflow post: # https://stackoverflow.com/questions/31363311/\ # any-way-to-manually-fix-operation-of-\ # super-after-ipython-reload-avoiding-ty # reload(sys.modules['apprise.plugins.NotifyDBus']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise']) # Create our instance obj = apprise.Apprise.instantiate('glib://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj.duration = 0 # Test url() call assert (compat_is_basestring(obj.url()) is True) # Our notification succeeds even though the gi library was not loaded assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # Force a global import error _session_bus = sys.modules['dbus'] sys.modules['dbus'] = compile('raise ImportError()', 'dbus', 'exec') # Reload our modules reload(sys.modules['apprise.plugins.NotifyDBus']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise']) # Create our instance obj = apprise.Apprise.instantiate('glib://', suppress_exceptions=False) assert (isinstance(obj, apprise.plugins.NotifyDBus) is True) obj.duration = 0 # Test url() call assert (compat_is_basestring(obj.url()) is True) # Our notification fail because the dbus library wasn't present assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False) # Since playing with the sys.modules is not such a good idea, # let's just put it back now :) sys.modules['dbus'] = _session_bus # Reload our modules reload(sys.modules['apprise.plugins.NotifyDBus']) reload(sys.modules['apprise.plugins']) reload(sys.modules['apprise.Apprise']) reload(sys.modules['apprise'])
def test_growl_plugin(mock_gntp): """ API: NotifyGrowl Plugin() """ # iterate over our dictionary and test it out for (url, meta) in TEST_URLS: # Our expected instance instance = meta.get('instance', None) # Our expected exception exception = meta.get('exception', None) # Our expected server objects self = meta.get('self', None) # Our expected Query response (True, False, or exception type) response = meta.get('response', True) # Allow us to force the server response code to be something other then # the defaults growl_response = meta.get('growl_response', True if response else False) test_growl_notify_exceptions = meta.get('test_growl_notify_exceptions', False) test_growl_register_exceptions = meta.get( 'test_growl_register_exceptions', False) mock_notifier = mock.Mock() mock_gntp.return_value = mock_notifier test_growl_exceptions = ( plugins.gntp.errors.NetworkError(0, 'gntp.ParseError() not handled'), plugins.gntp.errors.AuthError(0, 'gntp.AuthError() not handled'), plugins.gntp.errors.UnsupportedError( 'gntp.UnsupportedError() not handled'), ) if test_growl_notify_exceptions is True: # Store oure exceptions test_growl_notify_exceptions = test_growl_exceptions elif test_growl_register_exceptions is True: # Store oure exceptions test_growl_register_exceptions = test_growl_exceptions for exception in test_growl_register_exceptions: mock_notifier.register.side_effect = exception try: obj = Apprise.instantiate(url, suppress_exceptions=False) except TypeError: # This is the response we expect assert True except Exception: # We can't handle this exception type assert False # We're done this part of the test continue else: # Store our response mock_notifier.notify.return_value = growl_response try: obj = Apprise.instantiate(url, suppress_exceptions=False) assert (exception is None) if obj is None: # We're done continue if instance is None: # Expected None but didn't get it assert (False) assert (isinstance(obj, instance) is True) if isinstance(obj, plugins.NotifyBase.NotifyBase): # We loaded okay; now lets make sure we can reverse this url assert (compat_is_basestring(obj.url()) is True) # Instantiate the exact same object again using the URL from # the one that was already created properly obj_cmp = Apprise.instantiate(obj.url()) # Our object should be the same instance as what we had # originally expected above. if not isinstance(obj_cmp, plugins.NotifyBase.NotifyBase): # Assert messages are hard to trace back with the way # these tests work. Just printing before throwing our # assertion failure makes things easier to debug later on print('TEST FAIL: {} regenerated as {}'.format( url, obj.url())) assert (False) if self: # Iterate over our expected entries inside of our object for key, val in self.items(): # Test that our object has the desired key assert (hasattr(key, obj)) assert (getattr(key, obj) == val) try: if test_growl_notify_exceptions is False: # check that we're as expected assert obj.notify(title='test', body='body', notify_type=NotifyType.INFO) == response else: for exception in test_growl_notify_exceptions: mock_notifier.notify.side_effect = exception try: assert obj.notify( title='test', body='body', notify_type=NotifyType.INFO) is False except AssertionError: # Don't mess with these entries raise except Exception as e: # We can't handle this exception type print('%s / %s' % (url, str(e))) assert False except AssertionError: # Don't mess with these entries raise except Exception as e: # Check that we were expecting this exception to happen assert isinstance(e, response) except AssertionError: # Don't mess with these entries print('%s AssertionError' % url) raise except Exception as e: # Handle our exception print('%s / %s' % (url, str(e))) assert (exception is not None) assert (isinstance(e, exception))
def test_windows_plugin(): """ API: NotifyWindows Plugin() """ # We need to fake our windows environment for testing purposes win32api_name = 'win32api' win32api = types.ModuleType(win32api_name) sys.modules[win32api_name] = win32api win32api.GetModuleHandle = mock.Mock(name=win32api_name + '.GetModuleHandle') win32api.PostQuitMessage = mock.Mock(name=win32api_name + '.PostQuitMessage') win32con_name = 'win32con' win32con = types.ModuleType(win32con_name) sys.modules[win32con_name] = win32con win32con.CW_USEDEFAULT = mock.Mock(name=win32con_name + '.CW_USEDEFAULT') win32con.IDI_APPLICATION = mock.Mock(name=win32con_name + '.IDI_APPLICATION') win32con.IMAGE_ICON = mock.Mock(name=win32con_name + '.IMAGE_ICON') win32con.LR_DEFAULTSIZE = 1 win32con.LR_LOADFROMFILE = 2 win32con.WM_DESTROY = mock.Mock(name=win32con_name + '.WM_DESTROY') win32con.WM_USER = 0 win32con.WS_OVERLAPPED = 1 win32con.WS_SYSMENU = 2 win32gui_name = 'win32gui' win32gui = types.ModuleType(win32gui_name) sys.modules[win32gui_name] = win32gui win32gui.CreateWindow = mock.Mock(name=win32gui_name + '.CreateWindow') win32gui.DestroyWindow = mock.Mock(name=win32gui_name + '.DestroyWindow') win32gui.LoadIcon = mock.Mock(name=win32gui_name + '.LoadIcon') win32gui.LoadImage = mock.Mock(name=win32gui_name + '.LoadImage') win32gui.NIF_ICON = 1 win32gui.NIF_INFO = mock.Mock(name=win32gui_name + '.NIF_INFO') win32gui.NIF_MESSAGE = 2 win32gui.NIF_TIP = 4 win32gui.NIM_ADD = mock.Mock(name=win32gui_name + '.NIM_ADD') win32gui.NIM_DELETE = mock.Mock(name=win32gui_name + '.NIM_DELETE') win32gui.NIM_MODIFY = mock.Mock(name=win32gui_name + '.NIM_MODIFY') win32gui.RegisterClass = mock.Mock(name=win32gui_name + '.RegisterClass') win32gui.UnregisterClass = mock.Mock(name=win32gui_name + '.UnregisterClass') win32gui.Shell_NotifyIcon = mock.Mock(name=win32gui_name + '.Shell_NotifyIcon') win32gui.UpdateWindow = mock.Mock(name=win32gui_name + '.UpdateWindow') win32gui.WNDCLASS = mock.Mock(name=win32gui_name + '.WNDCLASS') # The following allows our mocked content to kick in. In python 3.x keys() # returns an iterator, therefore we need to convert the keys() back into # a list object to prevent from getting the error: # "RuntimeError: dictionary changed size during iteration" # for mod in list(sys.modules.keys()): if mod.startswith('apprise.'): del (sys.modules[mod]) reload(apprise) # Create our instance obj = apprise.Apprise.instantiate('windows://', suppress_exceptions=False) obj.duration = 0 # Test URL functionality assert (compat_is_basestring(obj.url()) is True) # Check that it found our mocked environments assert (obj._enabled is True) # _on_destroy check obj._on_destroy(0, '', 0, 0) # test notifications assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # Test our loading of our icon exception; it will still allow the # notification to be sent win32gui.LoadImage.side_effect = AttributeError assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is True) # Undo our change win32gui.LoadImage.side_effect = None # Test our global exception handling win32gui.UpdateWindow.side_effect = AttributeError assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False) # Undo our change win32gui.UpdateWindow.side_effect = None # Toggle our testing for when we can't send notifications because the # package has been made unavailable to us obj._enabled = False assert (obj.notify(title='title', body='body', notify_type=apprise.NotifyType.INFO) is False)