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

"""
Tests for the CancellableQueue
"""


from twisted.trial.unittest import TestCase

from elisa.core.utils.cancellable_queue import CancellableQueue
from elisa.core.utils import defer


class MockFunction(object):

    def __init__(self):
        self.cancelled = False
        self.called = False
        self.args = None
        self.kwargs = None
        self.dfr = None

    def _canceller(self, dfr):
        self.cancelled = True
        dfr.callback(None)

    def __call__(self, *args, **kwargs):
        self.called = True
        self.args = args
        self.kwargs = kwargs
        self.dfr = defer.Deferred(canceller=self._canceller)
        return self.dfr

class TestCancellableQueue(TestCase):

    def setUp(self):
        self.queue = CancellableQueue()


class TestSingleEnqueue(TestCancellableQueue):

    def test_without_params(self):
        call = MockFunction()
        dfr = self.queue.enqueue(call)
        self.failUnlessEqual(call.called, True)
        call.dfr.callback(None)
        return dfr

    def test_with_params(self):
        args = (3, True,)
        kwargs = {"arg1": 6, "arg2": "test"}

        call = MockFunction()
        dfr = self.queue.enqueue(call, *args, **kwargs)
        self.failUnlessEqual(call.args, args)
        self.failUnlessEqual(call.kwargs, kwargs)
        call.dfr.callback(None)
        return dfr

    def test_deferred_success(self):
        call = MockFunction()

        def callback(result):
            self.failUnlessEqual(result, "success")

        def errback(failure):
            self.fail("Errback should not be called")

        dfr = self.queue.enqueue(call)
        dfr.addCallbacks(callback, errback)
        call.dfr.callback("success")
        return dfr

    def test_deferred_failure(self):
        call = MockFunction()

        def callback(result):
            self.fail("Callback should not be called")

        def errback(failure):
            self.failUnlessEqual(failure.type, Exception)

        dfr = self.queue.enqueue(call)
        dfr.addCallbacks(callback, errback)
        call.dfr.errback(Exception())
        return dfr

    def test_cancellation(self):
        call = MockFunction()
        dfr = self.queue.enqueue(call)
        dfr.cancel()
        self.failUnlessEqual(call.called, True)
        self.failUnlessEqual(call.cancelled, True)
        return dfr

    def test_empty(self):
        call = MockFunction()
        dfr = self.queue.enqueue(call)
        self.queue.empty()
        self.failUnlessEqual(call.called, True)
        self.failUnlessEqual(call.cancelled, True)
        return dfr

class TestMultipleEnqueue(TestCancellableQueue):

    def test_chaining(self):
        call1 = MockFunction()
        call2 = MockFunction()

        dfr1 = self.queue.enqueue(call1)
        dfr2 = self.queue.enqueue(call2)
        self.failUnlessEqual(call1.called, True)
        call1.dfr.callback(None)
        self.failUnlessEqual(call2.called, True)
        call2.dfr.callback(None)
        return dfr2

    def test_cancellation(self):
        call1 = MockFunction()
        call2 = MockFunction()

        def on_cancel(failure):
            failure.trap(defer.CancelledError)

        dfr1 = self.queue.enqueue(call1)
        dfr2 = self.queue.enqueue(call2)
        dfr2.addErrback(on_cancel)
        dfr2.cancel()

        self.failUnlessEqual(call1.called, True)
        call1.dfr.callback(None)
        self.failUnlessEqual(call2.called, False)
        return dfr2

    def test_empty(self):
        call1 = MockFunction()
        call2 = MockFunction()

        def on_cancel(failure):
            failure.trap(defer.CancelledError)

        dfr1 = self.queue.enqueue(call1)
        dfr2 = self.queue.enqueue(call2)
        dfr2.addErrback(on_cancel)
        self.queue.empty()

        self.failUnlessEqual(call1.called, True)
        self.failUnlessEqual(call1.cancelled, True)
        self.failUnlessEqual(call2.called, False)
        return dfr1
