# 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.

from elisa.core.manager import Manager, AlreadyRegistered, CannotUnregister
from elisa.core import common

from twisted.trial.unittest import TestCase
from twisted.internet import defer


class PluginRegistryMock(object):
    def create_component(self, name):
        if name == 'fail':
            return defer.fail(Exception(name))
        dfr = defer.succeed(name)
        return dfr


class BusMock(object):
    def register(self, callback, *message_types):
        pass

    def unregister(self, callback):
        pass


class ApplicationMock(object):
    bus = BusMock()
    plugin_registry = PluginRegistryMock()

    def log_failure(self, failure):
        pass


class SimpleProvider(object):
    pass


class SimpleProvider1(SimpleProvider): pass
class SimpleProvider2(SimpleProvider): pass
class SimpleProvider3(SimpleProvider): pass


class TestManager(TestCase):

    def setUp(self):
        self._patch_application()
        self._manager = Manager()
        self._provider = SimpleProvider()

    def tearDown(self):
        self._unpatch_application()

    def _patch_application(self):
        self._old_application = common.application
        common.application = ApplicationMock()

    def _unpatch_application(self):
        common.application = self._old_application

    def _check_provider_registered(self, result):
        self.failUnlessIn(self._provider, self._manager.components)

    def _check_provider_not_registered(self, result):
        self.failIfIn(self._provider, self._manager.components)

    def test_register_unregister_provider(self):
        dfr = self._manager.register_component(self._provider)
        dfr.addCallback(self._check_provider_registered)
        dfr.addCallback(lambda result: \
                            self._manager.unregister_component(self._provider))
        dfr.addCallback(self._check_provider_not_registered)
        return dfr

    def test_register_twice(self):
        dfr = self._manager.register_component(self._provider)

        def register_second_time(result):
            second_dfr = self._manager.register_component(self._provider)
            return self.failUnlessFailure(second_dfr, AlreadyRegistered)

        dfr.addCallback(register_second_time)
        dfr.addCallback(lambda result: \
                            self._manager.unregister_component(self._provider))
        return dfr

    def test_unregister_twice(self):
        dfr = self._manager.register_component(self._provider)
        dfr.addCallback(lambda result: \
                            self._manager.unregister_component(self._provider))

        def unregister_second_time(result):
            second_dfr = self._manager.unregister_component(self._provider)
            return self.failUnlessFailure(second_dfr, CannotUnregister)

        dfr.addCallback(unregister_second_time)
        return dfr

    def test_unregister_not_registered(self):
        dfr = self._manager.unregister_component(self._provider)
        return self.failUnlessFailure(dfr, CannotUnregister)

    def test_load_components_empty_list(self):
        # Even if the list of components is empty, we should get a deferred.
        dfr = self._manager._load_components([])
        self.failUnless(isinstance(dfr, defer.Deferred))
        dfr.addCallback(self.failUnlessEquals, [])
        return dfr

    def test_load_components(self):
        dfr = self._manager._load_components(['one', 'two', 'three'])

        def order_check(result):
            self.failUnlessEquals(self._manager.components,
                                  ['one', 'two', 'three'])

        dfr.addCallback(order_check)
        return dfr

    def test_load_one_component_fails(self):
        """
        Check that loading providers goes on even if one fails.
        """
        dfr = self._manager._load_components(['one', 'two', 'fail', 'three'])

        def check_failed(result):
            self.failUnlessEquals(self._manager.components,
                                  ['one', 'two', 'three'])

        dfr.addCallback(check_failed)
        return dfr

    def test_unload_components(self):
        one = SimpleProvider1()
        two = SimpleProvider2()
        three = SimpleProvider3()
        name = 'elisa.core.tests.test_manager:SimpleProvider'

        dfr = self._manager._load_components([one, two, three])

        def unload_component(result, number):
            return self._manager._unload_components(['%s%d' % (name, number)])

        def check_registered_components(result, expected_components):
            self.failUnlessEquals(self._manager.components,
                                  expected_components)

        dfr.addCallback(unload_component, 2)
        dfr.addCallback(check_registered_components, [one, three])
        dfr.addCallback(unload_component, 3)
        dfr.addCallback(unload_component, 1)
        dfr.addCallback(check_registered_components, [])
        return dfr

    def test_unload_one_component_fails(self):
        # Check that unloading providers goes on even if one fails.
        one = SimpleProvider1()
        two = SimpleProvider2()
        three = SimpleProvider3()
        name = 'elisa.core.tests.test_manager:SimpleProvider'

        dfr = self._manager._load_components([one, two, three])

        def unload_components(result):
            to_unload = ['%s1' % name, '%s2' % name, 'fail', '%s3' % name]
            return self._manager._unload_components(to_unload)

        def test_unloaded(result):
            expected = ['%s1' % name, '%s2' % name, '%s3' % name]
            self.failUnlessEquals(result, expected)

        dfr.addCallback(unload_components)
        dfr.addCallback(test_unloaded)
        return dfr
