# -*- coding: utf-8 -*-

# Import Python libs
from __future__ import absolute_import

# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
    MagicMock,
    patch,
    NO_MOCK,
    NO_MOCK_REASON,
    call
)

# Import Salt Libs
import salt.modules.gentoo_service as gentoo_service


@skipIf(NO_MOCK, NO_MOCK_REASON)
class GentooServicesTestCase(TestCase, LoaderModuleMockMixin):
    '''
    Test cases for salt.modules.gentoo_service
    '''

    def setup_loader_modules(self):
        return {gentoo_service: {}}

    def test_service_list_parser(self):
        '''
        Test for parser of rc-status results
        '''
        # no services is enabled
        mock = MagicMock(return_value='')
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            self.assertFalse(gentoo_service.get_enabled())
        mock.assert_called_once_with('rc-update -v show')

    def test_get_enabled_single_runlevel(self):
        '''
        Test for Return a list of service that are enabled on boot
        '''
        service_name = 'name'
        runlevels = ['default']
        mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            enabled_services = gentoo_service.get_enabled()
            self.assertTrue(service_name in enabled_services)
            self.assertEqual(enabled_services[service_name], runlevels)

    def test_get_enabled_filters_out_disabled_services(self):
        '''
        Test for Return a list of service that are enabled on boot
        '''
        service_name = 'name'
        runlevels = ['default']
        disabled_service = 'disabled'
        service_list = self.__services({service_name: runlevels, disabled_service: []})

        mock = MagicMock(return_value=service_list)
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            enabled_services = gentoo_service.get_enabled()
            self.assertEqual(len(enabled_services), 1)
            self.assertTrue(service_name in enabled_services)
            self.assertEqual(enabled_services[service_name], runlevels)

    def test_get_enabled_with_multiple_runlevels(self):
        '''
        Test for Return a list of service that are enabled on boot at more than one runlevel
        '''
        service_name = 'name'
        runlevels = ['non-default', 'default']
        mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            enabled_services = gentoo_service.get_enabled()
            self.assertTrue(service_name in enabled_services)
            self.assertEqual(enabled_services[service_name][0], runlevels[1])
            self.assertEqual(enabled_services[service_name][1], runlevels[0])

    def test_get_disabled(self):
        '''
        Test for Return a list of service that are installed but disabled
        '''
        disabled_service = 'disabled'
        enabled_service = 'enabled'
        service_list = self.__services({disabled_service: [],
                                        enabled_service: ['default']})
        mock = MagicMock(return_value=service_list)
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            disabled_services = gentoo_service.get_disabled()
            self.assertTrue(len(disabled_services), 1)
            self.assertTrue(disabled_service in disabled_services)

    def test_available(self):
        '''
        Test for Returns ``True`` if the specified service is
        available, otherwise returns
        ``False``.
        '''
        disabled_service = 'disabled'
        enabled_service = 'enabled'
        multilevel_service = 'multilevel'
        missing_service = 'missing'
        shutdown_service = 'shutdown'
        service_list = self.__services({disabled_service: [],
                                        enabled_service: ['default'],
                                        multilevel_service: ['default', 'shutdown'],
                                        shutdown_service: ['shutdown']})
        mock = MagicMock(return_value=service_list)
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            self.assertTrue(gentoo_service.available(enabled_service))
            self.assertTrue(gentoo_service.available(multilevel_service))
            self.assertTrue(gentoo_service.available(disabled_service))
            self.assertTrue(gentoo_service.available(shutdown_service))
            self.assertFalse(gentoo_service.available(missing_service))

    def test_missing(self):
        '''
        Test for The inverse of service.available.
        '''
        disabled_service = 'disabled'
        enabled_service = 'enabled'
        service_list = self.__services({disabled_service: [],
                                        enabled_service: ['default']})
        mock = MagicMock(return_value=service_list)
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            self.assertFalse(gentoo_service.missing(enabled_service))
            self.assertFalse(gentoo_service.missing(disabled_service))
            self.assertTrue(gentoo_service.missing('missing'))

    def test_getall(self):
        '''
        Test for Return all available boot services
        '''
        disabled_service = 'disabled'
        enabled_service = 'enabled'
        service_list = self.__services({disabled_service: [],
                                        enabled_service: ['default']})
        mock = MagicMock(return_value=service_list)
        with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}):
            all_services = gentoo_service.get_all()
            self.assertEqual(len(all_services), 2)
            self.assertTrue(disabled_service in all_services)
            self.assertTrue(enabled_service in all_services)

    def test_start(self):
        '''
        Test for Start the specified service
        '''
        mock = MagicMock(return_value=True)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.start('name'))
        mock.assert_called_once_with('/etc/init.d/name start', python_shell=False)

    def test_stop(self):
        '''
        Test for Stop the specified service
        '''
        mock = MagicMock(return_value=True)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.stop('name'))
        mock.assert_called_once_with('/etc/init.d/name stop', python_shell=False)

    def test_restart(self):
        '''
        Test for Restart the named service
        '''
        mock = MagicMock(return_value=True)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.restart('name'))
        mock.assert_called_once_with('/etc/init.d/name restart', python_shell=False)

    def test_reload_(self):
        '''
        Test for Reload the named service
        '''
        mock = MagicMock(return_value=True)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.reload_('name'))
        mock.assert_called_once_with('/etc/init.d/name reload', python_shell=False)

    def test_zap(self):
        '''
        Test for Reload the named service
        '''
        mock = MagicMock(return_value=True)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.zap('name'))
        mock.assert_called_once_with('/etc/init.d/name zap', python_shell=False)

    def test_status(self):
        '''
        Test for Return the status for a service
        '''
        mock = MagicMock(return_value=True)
        with patch.dict(gentoo_service.__salt__, {'status.pid': mock}):
            self.assertTrue(gentoo_service.status('name', 1))

        # service is running
        mock = MagicMock(return_value=0)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertTrue(gentoo_service.status('name'))
        mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)

        # service is not running
        mock = MagicMock(return_value=1)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.status('name'))
        mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)

        # service is stopped
        mock = MagicMock(return_value=3)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.status('name'))
        mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)

        # service has crashed
        mock = MagicMock(return_value=32)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}):
            self.assertFalse(gentoo_service.status('name'))
        mock.assert_called_once_with('/etc/init.d/name status', python_shell=False)

    def test_enable(self):
        '''
        Test for Enable the named service to start at boot
        '''
        rc_update_mock = MagicMock(return_value=0)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
            self.assertTrue(gentoo_service.enable('name'))
        rc_update_mock.assert_called_once_with('rc-update add name', python_shell=False)
        rc_update_mock.reset_mock()

        # move service from 'l1' to 'l2' runlevel
        service_name = 'name'
        runlevels = ['l1']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels='l2'))
        rc_update_mock.assert_has_calls([call('rc-update delete name l1', python_shell=False),
                                         call('rc-update add name l2', python_shell=False)])
        rc_update_mock.reset_mock()

        # requested levels are the same as the current ones
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels='l1'))
        self.assertTrue(rc_update_mock.call_count == 0)
        rc_update_mock.reset_mock()

        # same as above with the list instead of the string
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels=['l1']))
        self.assertTrue(rc_update_mock.call_count == 0)
        rc_update_mock.reset_mock()

        # add service to 'l2' runlevel
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels=['l2', 'l1']))
        rc_update_mock.assert_called_once_with('rc-update add name l2', python_shell=False)
        rc_update_mock.reset_mock()

        # remove service from 'l1' runlevel
        runlevels = ['l1', 'l2']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels=['l2']))
        rc_update_mock.assert_called_once_with('rc-update delete name l1', python_shell=False)
        rc_update_mock.reset_mock()

        # move service from 'l2' add to 'l3', leaving at l1
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels=['l1', 'l3']))
        rc_update_mock.assert_has_calls([call('rc-update delete name l2', python_shell=False),
                                         call('rc-update add name l3', python_shell=False)])
        rc_update_mock.reset_mock()

        # remove from l1, l3, and add to l2, l4, and leave at l5
        runlevels = ['l1', 'l3', 'l5']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.enable('name', runlevels=['l2', 'l4', 'l5']))
        rc_update_mock.assert_has_calls([call('rc-update delete name l1 l3', python_shell=False),
                                         call('rc-update add name l2 l4', python_shell=False)])
        rc_update_mock.reset_mock()

        # rc-update failed
        rc_update_mock = MagicMock(return_value=1)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
            self.assertFalse(gentoo_service.enable('name'))
        rc_update_mock.assert_called_once_with('rc-update add name', python_shell=False)
        rc_update_mock.reset_mock()

        # move service delete failed
        runlevels = ['l1']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertFalse(gentoo_service.enable('name', runlevels='l2'))
        rc_update_mock.assert_called_once_with('rc-update delete name l1', python_shell=False)
        rc_update_mock.reset_mock()

        # move service delete succeeds. add fails
        rc_update_mock = MagicMock()
        rc_update_mock.side_effect = [0, 1]
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertFalse(gentoo_service.enable('name', runlevels='l2'))
        rc_update_mock.assert_has_calls([call('rc-update delete name l1', python_shell=False),
                                         call('rc-update add name l2', python_shell=False)])
        rc_update_mock.reset_mock()

    def test_disable(self):
        '''
        Test for Disable the named service to start at boot
        '''
        rc_update_mock = MagicMock(return_value=0)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
            self.assertTrue(gentoo_service.disable('name'))
        rc_update_mock.assert_called_once_with('rc-update delete name', python_shell=False)
        rc_update_mock.reset_mock()

        # disable service
        service_name = 'name'
        runlevels = ['l1']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.disable('name', runlevels='l1'))
        rc_update_mock.assert_called_once_with('rc-update delete name l1',
                                               python_shell=False)
        rc_update_mock.reset_mock()

        # same as above with list
        runlevels = ['l1']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.disable('name', runlevels=['l1']))
        rc_update_mock.assert_called_once_with('rc-update delete name l1',
                                               python_shell=False)
        rc_update_mock.reset_mock()

        # remove from 'l1', and leave at 'l2'
        runlevels = ['l1', 'l2']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.disable('name', runlevels=['l1']))
        rc_update_mock.assert_called_once_with('rc-update delete name l1',
                                               python_shell=False)
        rc_update_mock.reset_mock()

        # remove from non-enabled level
        runlevels = ['l2']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.disable('name', runlevels=['l1']))
        self.assertTrue(rc_update_mock.call_count == 0)
        rc_update_mock.reset_mock()

        # remove from 'l1' and 'l3', leave at 'l2'
        runlevels = ['l1', 'l2', 'l3']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertTrue(gentoo_service.disable('name', runlevels=['l1', 'l3']))
        rc_update_mock.assert_called_once_with('rc-update delete name l1 l3',
                                               python_shell=False)
        rc_update_mock.reset_mock()

        # rc-update failed
        rc_update_mock = MagicMock(return_value=1)
        with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
            self.assertFalse(gentoo_service.disable('name'))
        rc_update_mock.assert_called_once_with('rc-update delete name', python_shell=False)
        rc_update_mock.reset_mock()

        # move service delete failed
        runlevels = ['l1']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertFalse(gentoo_service.disable('name', runlevels='l1'))
        rc_update_mock.assert_called_once_with('rc-update delete name l1', python_shell=False)
        rc_update_mock.reset_mock()

        # move service delete succeeds. add fails
        runlevels = ['l1', 'l2', 'l3']
        level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels}))
        with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}):
            with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}):
                self.assertFalse(gentoo_service.disable('name', runlevels=['l1', 'l3']))
        rc_update_mock.assert_called_once_with('rc-update delete name l1 l3',
                                               python_shell=False)
        rc_update_mock.reset_mock()

    def test_enabled(self):
        '''
        Test for Return True if the named service is enabled, false otherwise
        '''
        mock = MagicMock(return_value={'name': ['default']})
        with patch.object(gentoo_service, 'get_enabled', mock):
            # service is enabled at any level
            self.assertTrue(gentoo_service.enabled('name'))
            # service is enabled at the requested runlevels
            self.assertTrue(gentoo_service.enabled('name', runlevels='default'))
            # service is enabled at a different runlevels
            self.assertFalse(gentoo_service.enabled('name', runlevels='boot'))

        mock = MagicMock(return_value={'name': ['boot', 'default']})
        with patch.object(gentoo_service, 'get_enabled', mock):
            # service is enabled at any level
            self.assertTrue(gentoo_service.enabled('name'))
            # service is enabled at the requested runlevels
            self.assertTrue(gentoo_service.enabled('name', runlevels='default'))
            # service is enabled at all levels
            self.assertTrue(gentoo_service.enabled('name', runlevels=['boot', 'default']))
            # service is enabled at a different runlevels
            self.assertFalse(gentoo_service.enabled('name', runlevels='some-other-level'))
            # service is enabled at a different runlevels
            self.assertFalse(gentoo_service.enabled('name', runlevels=['boot', 'some-other-level']))

    def test_disabled(self):
        '''
        Test for Return True if the named service is disabled, false otherwise
        '''
        mock = MagicMock(return_value=['name'])
        with patch.object(gentoo_service, 'get_disabled', mock):
            self.assertTrue(gentoo_service.disabled('name'))

    def __services(self, services):
        return '\n'.join([' | '.join([svc, ' '.join(services[svc])]) for svc in services])
