# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.

"""
Unit tests for the notifying dictionary.
"""

from twisted.trial.unittest import TestCase

from elisa.core.utils import defer
from elisa.core.utils.notifying_dict import NotifyingDictionary


class TestNotifyingDict(TestCase):

    def setUp(self):
        self.dict = NotifyingDictionary({'one': 1, 'two': 2, 'three': 3})
        self.timeout = 2

    def signal_connect_after(self, emitter, signal, callback, *args, **kwargs):
        """
        Connect L{callback} to L{signal} emitted by L{emitter} object and
        return a deferred that will fail if the callback raises an exception
        and succeed otherwise.
        L{callback} is added to the signal handler list after the default class
        signal handler; see gobject.GObject.connect_object_after.
        Disconnection is performed automatically when the signal has been
        emitted once.

        # FIXME: this generic code is copied/pasted from
                 pigment.tests.widgets.test_widget
        """
        dfr = defer.Deferred()
        id = None
        def callback_wrapper(*args, **kwargs):
            emitter.disconnect(id)
            try:
                callback(*args, **kwargs)
            except Exception, e:
                dfr.errback(e)
            else:
                dfr.callback(None)

        id = emitter.connect_after(signal, callback_wrapper, *args, **kwargs)
        return dfr

    def assertItemsChangedEmitted(self, notifying_dict, expected):
        """
        Make sure that 'items-changed' is emitted by L{notifying_dict} and that
        the argument passed is equal to L{expected}.

        If the signal is not emitted the test will hang. To prevent that make
        sure that the test case has a timeout set.

        @rtype L{elisa.core.utils.defer.CancellableDeferred}
        """
        def on_change_notification(notifier, orig, new):
            self.assertEquals(new, expected)

        dfr = self.signal_connect_after(notifying_dict.notifier, 'items-changed',
                                        on_change_notification)
        return dfr

    def test_setitem(self):
        dfrs = []

        dfrs.append(self.assertItemsChangedEmitted(self.dict, {'two': 5}))
        self.dict['two'] = 5

        dfrs.append(self.assertItemsChangedEmitted(self.dict, {'two': 6}))
        self.dict['two'] = 6

        dfrs.append(self.assertItemsChangedEmitted(self.dict, {'five': 2}))
        self.dict['five'] = 2

        return defer.DeferredList(dfrs)

    def test_update(self):
        dfr = self.assertItemsChangedEmitted(self.dict,
                                             {'six': 6, 'one': 3, 'three': 1})
        self.dict.update({'one': 3, 'six': 6, 'three': 1})
        return dfr
