# 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.tests.elisa_test_case import ElisaTestCase

from elisa.core.components.message import Message

from elisa.core import bus
from twisted.internet import reactor
import platform

class DataMessage(Message):
    def __init__(self, data):
        Message.__init__(self)
        self.data = data

class FooMessage(Message):
    def __init__(self, foo):
        Message.__init__(self)
        self.foo = foo

class BarMessage(Message):
    pass

class TestMessageBus(ElisaTestCase):

    def setUp(self):
        self._bus = bus.Bus()
        self._bus.start()
        return ElisaTestCase.setUp(self)

    def tearDown(self):
        self._bus.stop()
        return ElisaTestCase.tearDown(self)

    def test_delayed_dispatch(self):
        """ Create a bus, send messages and start it. Check the
        messages sent before startup are correctly dispatched.
        """
        global received_messages
        received_messages = []
        mybus = bus.Bus()

        def on_message(message, sender):
            global received_messages
            received_messages.append(message)

        mybus.register(on_message)

        for i in range(5):
            data = '%s. Hello you' % i
            mybus.send_message(DataMessage(data), sender=self)

        def done(result):
            global received_messages
            self.assertEqual(len(received_messages), 5)
            mybus.unregister(on_message)

        dfr = mybus.deferred_dispatch()
        dfr.addCallback(done)
        return dfr

    def test_simple_emit(self):
        """ Check a message receiver callback correctly gets called on
        message bus dispatch.
        """
        global received_messages
        received_messages = []

        def on_message(message, sender):
            global received_messages
            received_messages.append(message)

        self._bus.register(on_message)

        def done(result):
            global received_messages
            self.assertEqual(len(received_messages), 1)

        dfr = self._bus.send_message(DataMessage("data"), sender=self)
        dfr.addCallback(done)
        return dfr

    def test_dispatch_with_callback_failure(self):
        """ Check a message receiver callback raising an exception
        doesn't break the call chain of the other callbacks.
        """
        global dispatched
        dispatched = False

        def first_callback(message, sender):
            raise Exception("I am a failed callback")

        def second_callback(message, sender):
            global dispatched
            dispatched = True

        self._bus.register(first_callback)
        self._bus.register(second_callback)

        def done(result):
            global dispatched
            self.assertEqual(dispatched, True)

        def on_failure(failure):
            self.fail("should not be reached")

        dfr = self._bus.send_message(DataMessage("data"), sender=self)
        dfr.addCallbacks(done, on_failure)
        return dfr

    def test_message_filter(self):
        """ Check the receiver callback is called only for some
        Message types defined at callback registration.
        """
        global received_messages
        received_messages = []

        def on_message(message, sender):
            global received_messages
            received_messages.append(message)

        self._bus.register(on_message, FooMessage)

        def data_sent(result):
            self.assertEqual(len(received_messages), 0)

        def send_foo_message(result):
            return self._bus.send_message(FooMessage("data"), sender=self)

        def foo_sent(result):
            self.assertEqual(len(received_messages), 1)

        dfr = self._bus.send_message(DataMessage("data"), sender=self)
        dfr.addCallback(data_sent)
        dfr.addCallback(send_foo_message)
        dfr.addCallback(foo_sent)
        return dfr

    def test_multiple_filters(self):
        """ Test messages dispatch to callbacks supporting multiple
        Message types.
        """
        global received_messages
        received_messages = []

        def on_message(message, sender):
            global received_messages
            received_messages.append(message)

        self._bus.register(on_message, DataMessage,FooMessage)

        def data_sent(result):
            self.assertEqual(len(received_messages), 1)

        def send_foo_message(result):
            msg = FooMessage("data")
            return self._bus.send_message(msg, sender=self)

        def foo_sent(result):
            self.assertEqual(len(received_messages), 2)

        def send_bar_message(result):
            return self._bus.send_message(BarMessage(), sender=self)

        def bar_sent(result):
            self.assertEqual(len(received_messages), 2)

        dfr = self._bus.send_message(DataMessage("data"), sender=self)
        dfr.addCallback(data_sent)
        dfr.addCallback(send_foo_message)
        dfr.addCallback(foo_sent)
        dfr.addCallback(send_bar_message)
        dfr.addCallback(bar_sent)
        return dfr

    def test_decorator(self):
        """ Check receiver callback registration and message dispatch
        using the bus_listener function decorator.
        """
        global received_messages
        received_messages = []

        @bus.bus_listener(self._bus, FooMessage)
        def on_message(message, sender):
            global received_messages
            received_messages.append(message)

        def done(result):
            self.assertEqual(len(received_messages), 1)

        dfr = self._bus.send_message(FooMessage("data"), sender=self)
        dfr.addCallback(done)
        return dfr

    def test_register_callback_during_dispatch(self):
        """ Check that registering a new callback during dispatch of one
        message doesn't affect current dispatching process.

        See also: https://bugs.launchpad.net/elisa/+bug/251732
        """
        global second_callback_called
        second_callback_called = False

        class DataMessage(Message):
            def __init__(self, register_new_cb):
                self.register_new_cb = register_new_cb

        def first_callback(message, sender):
            if message.register_new_cb:
                self._bus.register(second_callback)

        def second_callback(message, sender):
            global second_callback_called
            second_callback_called = True

        self._bus.register(first_callback)

        def second_dispatch_done(result):
            global second_callback_called
            self.assertEqual(second_callback_called, True)

        def first_dispatch_done(result):
            global second_callback_called
            self.assertEqual(second_callback_called, False)
            # send another message, this time not asking to register
            # another callback
            dfr = self._bus.send_message(DataMessage(False), sender=self)
            dfr.addCallback(second_dispatch_done)
            return dfr

        # send a message asking to register a new callback
        dfr = self._bus.send_message(DataMessage(True), sender=self)
        dfr.addCallback(first_dispatch_done)
        return dfr
