# 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 twisted.trial.unittest import TestCase
from twisted.internet import defer

from elisa.core.interface_controller import InterfaceController, \
                                            FrontendOptionMissing

from elisa.core.config import Config
from elisa.core import common

import os, pkg_resources

class TestInterfaceController(TestCase):
    
    def setUp(self):
        # create all the files under _trial_temp
        self.test_dir = os.path.abspath('test_interface_controller')
        pkg_resources.ensure_directory(self.test_dir)

        self.interface_controller = InterfaceController()

        self.old_application = None

        # patch the application to fit our cases
        self.patch_application()

    def tearDown(self):
        # unpatch common.application
        self.unpatch_application()

    def patch_application(self):
        """
        Setup common.application, saving the old application object.

        Make common.application a generic object so we can set
        common.application.config and common.application.plugin_registry
        """
        assert self.old_application is None

        self.old_application = common.application

        class PluginRegistry(object):
            def create_component(self, name, config):
                if name.startswith('fail'):
                    return defer.fail(Exception(name))
                return defer.succeed(name)

        class Application(object):
            config = None
            plugin_registry = None

            def log_failure(self, failure):
                pass

        common.application = Application()
        common.application.plugin_registry = PluginRegistry()

    def unpatch_application(self):
        """
        Restore the application object saved in patch_application().
        """
        common.application = self.old_application
        self.old_application = None

    def _setup_config(self, filename, default_config):
        """
        setup a local config and set it to common.application.config
        """
        file_name = os.path.join(self.test_dir, filename)
        simple_config = Config(filename, default_config)
        common.application.config = simple_config

    def test_simple_initialize(self):
        """
        test if the initialize method works as expected when everything is
        fine
        """
        config_string = """
        [general]
        frontends = ['frontenda', 'frontendb']

        [frontenda]
        frontend = 'pgm.frontend:Frontend'

        [frontendb]
        frontend = 'gtk.frontend:Frontend'
        """
        self._setup_config('simple_initialize_test.conf',
                            config_string)

        def check_frontends(result):
            frontends = self.interface_controller.frontends
            self.assertEquals(len(frontends.keys()), 2)
            self.assertEquals(frontends['frontenda'], 'pgm.frontend:Frontend')
            self.assertEquals(frontends['frontendb'], 'gtk.frontend:Frontend')

            res = map(lambda x: x[0], result)
            self.assertEquals(res, [True, True])

        dfr = self.interface_controller.initialize()
        dfr.addCallback(check_frontends)
        return dfr

    def test_missing_section_initialize(self):
        """
        test, that it still works when the section in the configuration file
        is missing
        """
        config_string = """
        [general]
        frontends = ['frontend1', 'frontend2', 'frontend3']

        [frontend1]
        frontend = 'raval.frontend:Frontend'

        [frontend3]
        frontend = 'lcd.frontend:Frontend'
        """

        self._setup_config('missing_section_init_test.conf',
                            config_string)

        def check_frontends(result):
            frontends = self.interface_controller.frontends
            self.assertEquals(len(frontends.keys()), 2)
            self.assertEquals(frontends['frontend1'], 'raval.frontend:Frontend')
            self.assertEquals(frontends['frontend3'], 'lcd.frontend:Frontend')

            res = map(lambda x: x[0], result)
            self.assertEquals(res, [True, False, True])

        dfr = self.interface_controller.initialize()
        dfr.addCallback(check_frontends)
        return dfr

    def test_missing_frontend_option_initialize(self):
        """
        test if creating a frontend without the necessary frontend-option
        fails as expected
        """
        # create a stimple config for testing
        config_string = """
        [general]
        frontends = ['frontend12', 'frontend3']

        [frontend12]

        [frontend3]
        frontend = 'wxm.window:Window'
        """
        self._setup_config('missing_option_test.conf',
                            config_string)

        def check_frontends(result):
            frontends = self.interface_controller.frontends
            self.assertEquals(len(frontends.keys()), 1)
            self.assertEquals(frontends['frontend3'], 'wxm.window:Window')

            res = filter(lambda x: not x[0], result)[0]
            self.assertEquals(res[1], 'frontend12')
            self.assertEquals(res[2].type, FrontendOptionMissing)

        dfr = self.interface_controller.initialize()
        dfr.addCallback(check_frontends)
        return dfr

    def test_one_failed_initialize(self):
        """
        test that all other frontends get created even if one fails
        """

        config_string = """
        [general]
        frontends = ['frontend1', 'frontend2', 'frontend3']

        [frontend1]
        frontend = 'braille.frt:Frontend'

        [frontend2]
        frontend = 'failed'

        [frontend3]
        frontend = 'hildon.frontend:Frontend'
        """
        self._setup_config('failed_frontend_option.conf',
                            config_string)

        def check_frontends(result):
            frontends = self.interface_controller.frontends
            self.assertEquals(len(frontends.keys()), 2)
            self.assertEquals(frontends['frontend1'], 'braille.frt:Frontend')
            self.assertEquals(frontends['frontend3'],
                                                    'hildon.frontend:Frontend')

            res = map(lambda x: x[0], result)
            self.assertEquals(res, [True, False, True])

        dfr = self.interface_controller.initialize()
        dfr.addCallback(check_frontends)
        return dfr

    def test_all_failed_initialize(self):
        """
        test that we get an errback, when creation of all frontends fails
        """
         # create a stimple config for testing
        config_string = """
        [general]
        frontends = ['frontend1', 'frontend2', 'frontend3']

        [frontend1]
        frontend = 'failure'

        [frontend2]
        frontend = 'failed'

        [frontend3]
        """

        self._setup_config('all_failed_test.conf',
                            config_string)

        dfr = self.interface_controller.initialize()

        def check_empty(result_list):
            res = map(lambda x: x[0], result_list)
            self.assertEquals(res, [False, False, False])

        dfr.addCallback(check_empty)

        return dfr

    def test_clean_got_called(self):

        frontends = {}

        class SimpleObject(object):

            def __init__(self):
                self.called = False

            def clean(self):
                self.called = True
                return defer.succeed(self)


        obja = SimpleObject()
        frontends['objecta'] = obja

        objb = SimpleObject()
        frontends['objectb'] = objb
        
        # overwrite the frontends
        self.interface_controller.frontends = frontends

        def check_got_called(result):
            self.assertTrue(obja.called)
            self.assertTrue(objb.called)

        dfr = self.interface_controller.stop()
        dfr.addCallback(check_got_called)
        return dfr

