예제 #1
0
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)
예제 #3
0
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
예제 #4
0
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>") == \
        '&lt;content&gt;&apos;&emsp;&nbsp;\n&lt;/content&gt;'

    assert NotifyBase.escape_html(
        "<content>'\t \n</content>", convert_new_lines=True) == \
        '&lt;content&gt;&apos;&emsp;&nbsp;&lt;br/&gt;&lt;/content&gt;'

    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) == ''
예제 #5
0
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>") == \
        '&lt;content&gt;&apos;&emsp;&nbsp;\n&lt;/content&gt;'

    assert NotifyBase.escape_html(
        "<content>'\t \n</content>", convert_new_lines=True) == \
        '&lt;content&gt;&apos;&emsp;&nbsp;&lt;br/&gt;&lt;/content&gt;'

    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) == ''
예제 #6
0
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)
예제 #7
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'])
예제 #8
0
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)