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

"""
Testing of L{elisa.plugins.pigment.graph.image.Image} class.
"""

import pgm
import glib
from pkg_resources import resource_filename

from twisted.trial.unittest import TestCase

from elisa.core.utils import defer
from elisa.plugins.pigment.graph.image import Image


class LoadingImageTestCase(TestCase):
    """
    Facilities for test cases involving image loading.
    """

    def setUp(self):
        self._flush_glib_main_context_sources()
        self.timeout = 2

        # necessary for in texture memory image caching (cloning)
        canvas = pgm.Canvas()
        Image.canvas = canvas

    def _flush_glib_main_context_sources(self):
        context = glib.main_context_default()
        while context.pending():
            context.iteration(True)

    def run_pending_image_loading(self):
        """
        Iterate the main loop in order to allow pending Pigment image loading
        to be completed.

        Warning: if no image loading was initiated that function will block
                 indefinitely.
        """
        context = glib.main_context_default()
        context.iteration(True)

    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 assertSignalEmitted(self, emitter, signal, expected_parameters=()):
        """
        Return a deferred that will succeed if L{signal} is emitted by
        L{emitted} and the parameters sent are equal to L{expected_parameters}.

        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 callback(e, *parameters):
            self.assertEquals(parameters, expected_parameters)

        dfr = self.signal_connect_after(emitter, signal, callback)
        return dfr


class TestSetFromFileDeferred(LoadingImageTestCase):
    """
    Tests for method 'set_from_file_deferred'. 
    """

    def test_non_existant_image_file(self):
        raise NotImplementedError()

    test_non_existant_image_file.todo = "Since Pigment does not report missing "\
                                        "files, the way to test it is to wait "\
                                        "enough time and make sure that the "\
                                        "file-loaded signal was not emitted. "\
                                        "That should be done in "\
                                        "Image.set_from_file_deferred that "\
                                        "should call errback on the returned "\
                                        "deferred."
    def test_invalid_image_file(self):
        raise NotImplementedError()

    test_invalid_image_file.todo = test_non_existant_image_file.todo

    def test_valid_image_file(self):
        i = Image()
        image_path = resource_filename("elisa.plugins.pigment",
                                       "tests/data/little_star.png")
        dfr = self.assertSignalEmitted(i, "file-loaded")
        dfr_loading = i.set_from_file_deferred(image_path)
        self.run_pending_image_loading()

        return defer.DeferredList([dfr, dfr_loading])


class TestSetFromFileWithCaching(LoadingImageTestCase):
    """
    Tests for method 'set_from_file_with_caching'. 
    """

    def test_valid_image_file(self):
        i = Image()
        image_path = resource_filename("elisa.plugins.pigment",
                                       "tests/data/little_star.png")
        dfr = self.assertSignalEmitted(i, "file-loaded")
        dfr_loading = i.set_from_file_with_caching(image_path)
        self.run_pending_image_loading()

        return defer.DeferredList([dfr, dfr_loading])
