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

#    Copyright 2014 Mirantis, Inc.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

from __future__ import unicode_literals

import mock
import os

from os.path import join as join_path

from fuel_plugin_builder.actions.build import BaseBuildPlugin
from fuel_plugin_builder.actions.build import BuildPluginV1
from fuel_plugin_builder.actions.build import BuildPluginV2
from fuel_plugin_builder.actions.build import BuildPluginV3
from fuel_plugin_builder import errors
from fuel_plugin_builder.tests.base import BaseTestCase


class BaseBuild(BaseTestCase):

    # Prevent test runner to run tests in base
    __test__ = False
    # Redefine class
    builder_class = None

    releases = [
        {'os': 'ubuntu',
         'deployment_scripts_path': 'deployment_scripts_path',
         'repository_path': 'repository_path'}]

    def setUp(self):
        self.plugins_name = 'fuel_plugin'
        self.plugin_path = '/tmp/{0}'.format(self.plugins_name)
        self.builder = self.create_builder(self.plugin_path)

    def create_builder(self, plugin_path, meta=None):
        meta = meta or self.meta
        with mock.patch(
                'fuel_plugin_builder.actions.build.utils.parse_yaml',
                return_value=meta):
            return self.builder_class(plugin_path)

    def test_run(self):
        mocked_methods = [
            'clean',
            'run_pre_build_hook',
            'check',
            'build_repos',
            'add_checksums_file',
            'make_package']

        self.mock_methods(self.builder, mocked_methods)
        self.builder.run()

        self.builder.clean.assert_called_once_with()
        self.builder.run_pre_build_hook.assert_called_once_with()
        self.builder.check.assert_called_once_with()
        self.builder.add_checksums_file()
        self.builder.build_repos.assert_called_once_with()
        self.builder.make_package()

    @mock.patch('fuel_plugin_builder.actions.build.utils.which')
    @mock.patch('fuel_plugin_builder.actions.build.utils.exec_cmd',
                return_value=True)
    def test_run_pre_build_hook(self, exec_cmd_mock, which_mock):
        self.builder.run_pre_build_hook()
        exec_cmd_mock.assert_called_once_with(self.builder.pre_build_hook_cmd,
                                              self.builder.plugin_path)
        which_mock.assert_called_once_with(
            join_path(self.builder.plugin_path,
                      self.builder.pre_build_hook_cmd))

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def test_build_repos(self, utils_mock):
        with mock.patch.object(
                self.builder_class, 'build_ubuntu_repos') as build_ubuntu_mock:
            with mock.patch.object(
                    self.builder_class,
                    'build_centos_repos') as build_centos_mock:
                self.builder.build_repos()

        utils_mock.create_dir.assert_called_once_with(
            self.builder.build_src_dir)
        utils_mock.copy_files_in_dir.assert_called_once_with(
            '/tmp/fuel_plugin/*',
            self.builder.build_src_dir)
        build_centos_mock.assert_called_once_with([])
        build_ubuntu_mock.assert_called_once_with([
            '/tmp/fuel_plugin/.build/src/repository_path'])

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def test_build_ubuntu_repos(self, utils_mock):
        path = '/repo/path'
        self.builder.build_ubuntu_repos([path])
        utils_mock.exec_piped_cmds.assert_called_once_with(
            ['dpkg-scanpackages -m .', 'gzip -c9 > Packages.gz'],
            cwd=path)

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def test_build_centos_repos(self, utils_mock):
        path = '/repo/path'
        self.builder.build_centos_repos([path])
        utils_mock.create_dir.assert_called_once_with(
            '/repo/path/Packages')
        utils_mock.move_files_in_dir.assert_called_once_with(
            '/repo/path/*.rpm', '/repo/path/Packages')
        utils_mock.exec_cmd.assert_called_once_with(
            'createrepo -o /repo/path /repo/path')

    @mock.patch.object(BaseBuildPlugin, '_check_requirements')
    @mock.patch.object(BaseBuildPlugin, '_check_structure')
    def test_check(self, check_structure_mock, check_requirements_mock):
        self.builder.check()
        check_structure_mock.assert_called_once_with()
        check_requirements_mock.assert_called_once_with()

    @mock.patch('fuel_plugin_builder.actions.build.utils.which',
                return_value=True)
    def test_check_requirements(self, _):
        self.builder._check_requirements()

    @mock.patch('fuel_plugin_builder.actions.build.ValidatorManager')
    def test_check_structure(self, manager_class_mock):
        validator_manager_obj = mock.MagicMock()
        manager_class_mock.return_value = validator_manager_obj
        validator_mock = mock.MagicMock()
        validator_manager_obj.get_validator.return_value = validator_mock

        self.builder._check_structure()

        manager_class_mock.assert_called_once_with(self.plugin_path)
        validator_manager_obj.get_validator.assert_called_once_with()
        validator_mock.validate.assert_called_once_with()

    @mock.patch(
        'fuel_plugin_builder.actions.build.utils.create_checksums_file')
    def test_add_checksums_file(self, create_checksums_file_mock):
        self.builder.add_checksums_file()
        create_checksums_file_mock.assert_called_once_with(
            self.builder.build_src_dir, self.builder.checksums_path)

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def test_clean(self, utils_mock):
        self.builder.clean()
        utils_mock.assert_has_calls([
            mock.call.remove(self.builder.build_dir),
            mock.call.create_dir(self.builder.build_dir),
            mock.call.remove_by_mask(self.builder.result_package_mask)])


class TestBaseBuildV1(BaseBuild):

    __test__ = True
    builder_class = BuildPluginV1

    meta = {
        'releases': BaseBuild.releases,
        'version': '1.2.3',
        'name': 'plugin_name'
    }

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def test_make_package(self, utils_mock):
        self.builder.make_package()
        tar_path = '/tmp/fuel_plugin/plugin_name-1.2.3.fp'

        utils_mock.make_tar_gz.assert_called_once_with(
            self.builder.build_src_dir,
            tar_path,
            'plugin_name-1.2.3')

    @mock.patch('fuel_plugin_builder.actions.build.utils.which',
                return_value=False)
    def test_check_requirements_raises_error(self, _):
        self.assertRaisesRegexp(
            errors.FuelCannotFindCommandError,
            'Cannot find commands "rpm, createrepo, dpkg-scanpackages", '
            'install required commands and try again',
            self.builder._check_requirements)


class TestBaseBuildV2(BaseBuild):

    __test__ = True
    builder_class = BuildPluginV2
    meta = {
        'releases': BaseBuild.releases,
        'version': '1.2.3',
        'name': 'plugin_name',
        'title': 'Plugin title',
        'description': 'Description',
        'licenses': ['Apache', 'BSD'],
        'authors': ['author1', 'author2'],
        'homepage': 'url'
    }

    def path_from_plugin(self, plugin_path, path):
        return join_path(plugin_path, path)

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def check_make_package(self, builder, plugin_path, utils_mock):
        plugin_path = plugin_path

        utils_mock.get_current_year.return_value = '2014'
        builder.make_package()
        rpm_src_path = self.path_from_plugin(plugin_path,
                                             '.build/rpm/SOURCES')
        utils_mock.create_dir.assert_called_once_with(rpm_src_path)

        fp_dst = self.path_from_plugin(
            plugin_path, '.build/rpm/SOURCES/plugin_name-1.2.fp')

        utils_mock.make_tar_gz.assert_called_once_with(
            self.path_from_plugin(plugin_path, '.build/src'),
            fp_dst,
            'plugin_name-1.2')

        spec_src = os.path.abspath(join_path(
            os.path.dirname(__file__), '..',
            self.builder.rpm_spec_src_path))
        utils_mock.render_to_file.assert_called_once_with(
            spec_src,
            join_path(plugin_path, '.build/rpm/plugin_rpm.spec'),
            {'vendor': 'author1, author2',
             'description': 'Description',
             'license': 'Apache and BSD',
             'summary': 'Plugin title',
             'version': '1.2.3',
             'homepage': 'url',
             'name': 'plugin_name-1.2',
             'year': '2014'})

        utils_mock.exec_cmd.assert_called_once_with(
            'rpmbuild -vv --nodeps --define "_topdir {0}" -bb '
            '{1}'.format(
                self.path_from_plugin(plugin_path, '.build/rpm'),
                self.path_from_plugin(plugin_path,
                                      '.build/rpm/plugin_rpm.spec')))

        utils_mock.copy_files_in_dir.assert_called_once_with(
            self.path_from_plugin(plugin_path,
                                  '.build/rpm/RPMS/noarch/*.rpm'),
            plugin_path
        )

    def test_make_package(self):
        self.check_make_package(self.builder, self.plugin_path)

    def test_make_package_with_non_ascii_chars_in_path(self):
        plugin_path = '/tmp/тест/' + self.plugins_name

        builder = self.create_builder(plugin_path)

        self.check_make_package(builder, plugin_path)

    @mock.patch('fuel_plugin_builder.actions.build.utils.which',
                return_value=False)
    def test_check_requirements_raises_error(self, _):
        self.assertRaisesRegexp(
            errors.FuelCannotFindCommandError,
            'Cannot find commands "rpmbuild, rpm, createrepo, '
            'dpkg-scanpackages", install required commands and try again',
            self.builder._check_requirements)

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def test_build_ubuntu_repos(self, utils_mock):
        path = '/repo/path'
        self.builder.build_ubuntu_repos([path])
        utils_mock.exec_piped_cmds.assert_called_once_with(
            ['dpkg-scanpackages -m .', 'gzip -c9 > Packages.gz'],
            cwd=path)
        release_src = os.path.abspath(join_path(
            os.path.dirname(__file__), '..',
            self.builder.release_tmpl_src_path))
        utils_mock.render_to_file.assert_called_once_with(
            release_src,
            '/repo/path/Release',
            {'major_version': '1.2',
             'plugin_name': 'plugin_name'})


class TestBaseBuildV3(BaseBuild):

    __test__ = True
    builder_class = BuildPluginV3
    meta = {
        'releases': BaseBuild.releases,
        'version': '1.2.3',
        'name': 'plugin_name',
        'title': 'Plugin title',
        'description': 'Description',
        'licenses': ['Apache', 'BSD'],
        'authors': ['author1', 'author2'],
        'homepage': 'url'
    }

    def path_from_plugin(self, path):
        return join_path(self.plugin_path, path)

    @mock.patch('fuel_plugin_builder.actions.build.utils')
    def _test_make_package(self, utils_mock):
        utils_mock.get_current_year.return_value = '2014'
        utils_mock.read_if_exist.side_effect = ['echo uninst', 'echo preinst',
                                                'echo postinst']
        self.builder.make_package()
        rpm_src_path = self.path_from_plugin('.build/rpm/SOURCES')
        utils_mock.create_dir.assert_called_once_with(rpm_src_path)

        fp_dst = self.path_from_plugin('.build/rpm/SOURCES/plugin_name-1.2.fp')
        utils_mock.make_tar_gz.assert_called_once_with(
            self.path_from_plugin('.build/src'),
            fp_dst,
            'plugin_name-1.2')

        utils_mock.exec_cmd.assert_called_once_with(
            'rpmbuild -vv --nodeps --define "_topdir {0}" -bb '
            '{1}'.format(
                self.path_from_plugin('.build/rpm'),
                self.path_from_plugin('.build/rpm/plugin_rpm.spec')))

        utils_mock.copy_files_in_dir.assert_called_once_with(
            self.path_from_plugin('.build/rpm/RPMS/noarch/*.rpm'),
            self.plugin_path)

        utils_mock.read_if_exist.assert_has_calls([
            mock.call(self.path_from_plugin('uninstall.sh')),
            mock.call(self.path_from_plugin('pre_install.sh')),
            mock.call(self.path_from_plugin('post_install.sh'))])
        return utils_mock

    def test_make_package(self):
        utils_mock = self._test_make_package()
        spec_src = os.path.abspath(join_path(
            os.path.dirname(__file__), '..',
            self.builder.rpm_spec_src_path))

        utils_mock.render_to_file.assert_called_once_with(
            spec_src,
            join_path(self.plugin_path, '.build/rpm/plugin_rpm.spec'),
            {'vendor': 'author1, author2',
             'description': 'Description',
             'license': 'Apache and BSD',
             'summary': 'Plugin title',
             'version': '1.2.3',
             'homepage': 'url',
             'name': 'plugin_name-1.2',
             'year': '2014',
             'preinstall_hook': 'echo preinst',
             'postinstall_hook': 'echo postinst',
             'uninstall_hook': 'echo uninst',
             'build_version': '1'})

    def test_make_package_with_build_version(self):

        meta = {
            'releases': BaseBuild.releases,
            'version': '1.2.3',
            'name': 'plugin_name',
            'title': 'Plugin title',
            'description': 'Description',
            'licenses': ['Apache', 'BSD'],
            'authors': ['author1', 'author2'],
            'homepage': 'url',
            'build_version': '34'
        }

        self.builder = self.create_builder(self.plugin_path, meta=meta)
        utils_mock = self._test_make_package()

        spec_src = os.path.abspath(join_path(
            os.path.dirname(__file__), '..',
            self.builder.rpm_spec_src_path))

        utils_mock.render_to_file.assert_called_once_with(
            spec_src,
            join_path(self.plugin_path, '.build/rpm/plugin_rpm.spec'),
            {'vendor': 'author1, author2',
             'description': 'Description',
             'license': 'Apache and BSD',
             'summary': 'Plugin title',
             'version': '1.2.3',
             'homepage': 'url',
             'name': 'plugin_name-1.2',
             'year': '2014',
             'preinstall_hook': 'echo preinst',
             'postinstall_hook': 'echo postinst',
             'uninstall_hook': 'echo uninst',
             'build_version': '34'})
