"""Test Home Assistant package util methods."""
import os
import pkg_resources
import subprocess
import unittest

from distutils.sysconfig import get_python_lib
from unittest.mock import call, patch

import homeassistant.util.package as package

RESOURCE_DIR = os.path.abspath(
    os.path.join(os.path.dirname(__file__), '..', 'resources'))

TEST_EXIST_REQ = 'pip>=7.0.0'
TEST_NEW_REQ = 'pyhelloworld3==1.0.0'
TEST_ZIP_REQ = 'file://{}#{}' \
    .format(os.path.join(RESOURCE_DIR, 'pyhelloworld3.zip'), TEST_NEW_REQ)


@patch('homeassistant.util.package.subprocess.call')
@patch('homeassistant.util.package.check_package_exists')
class TestPackageUtilInstallPackage(unittest.TestCase):
    """Test for homeassistant.util.package module."""

    def test_install_existing_package(self, mock_exists, mock_subprocess):
        """Test an install attempt on an existing package."""
        mock_exists.return_value = True

        self.assertTrue(package.install_package(TEST_EXIST_REQ))

        self.assertEqual(mock_exists.call_count, 1)
        self.assertEqual(mock_exists.call_args, call(TEST_EXIST_REQ, None))

        self.assertEqual(mock_subprocess.call_count, 0)

    @patch('homeassistant.util.package.sys')
    def test_install(self, mock_sys, mock_exists, mock_subprocess):
        """Test an install attempt on a package that doesn't exist."""
        mock_exists.return_value = False
        mock_subprocess.return_value = 0

        self.assertTrue(package.install_package(TEST_NEW_REQ, False))

        self.assertEqual(mock_exists.call_count, 1)

        self.assertEqual(mock_subprocess.call_count, 1)
        self.assertEqual(
            mock_subprocess.call_args,
            call([
                mock_sys.executable, '-m', 'pip', 'install', '--quiet',
                TEST_NEW_REQ
            ])
        )

    @patch('homeassistant.util.package.sys')
    def test_install_upgrade(self, mock_sys, mock_exists, mock_subprocess):
        """Test an upgrade attempt on a package."""
        mock_exists.return_value = False
        mock_subprocess.return_value = 0

        self.assertTrue(package.install_package(TEST_NEW_REQ))

        self.assertEqual(mock_exists.call_count, 1)

        self.assertEqual(mock_subprocess.call_count, 1)
        self.assertEqual(
            mock_subprocess.call_args,
            call([
                mock_sys.executable, '-m', 'pip', 'install', '--quiet',
                TEST_NEW_REQ, '--upgrade'
            ])
        )

    @patch('homeassistant.util.package.sys')
    def test_install_target(self, mock_sys, mock_exists, mock_subprocess):
        """Test an install with a target."""
        target = 'target_folder'
        mock_exists.return_value = False
        mock_subprocess.return_value = 0

        self.assertTrue(
            package.install_package(TEST_NEW_REQ, False, target=target)
        )

        self.assertEqual(mock_exists.call_count, 1)

        self.assertEqual(mock_subprocess.call_count, 1)
        self.assertEqual(
            mock_subprocess.call_args,
            call([
                mock_sys.executable, '-m', 'pip', 'install', '--quiet',
                TEST_NEW_REQ, '--target', os.path.abspath(target)
            ])
        )

    @patch('homeassistant.util.package._LOGGER')
    @patch('homeassistant.util.package.sys')
    def test_install_error(self, mock_sys, mock_logger, mock_exists,
                           mock_subprocess):
        """Test an install with a target."""
        mock_exists.return_value = False
        mock_subprocess.side_effect = [subprocess.SubprocessError]

        self.assertFalse(package.install_package(TEST_NEW_REQ))

        self.assertEqual(mock_logger.exception.call_count, 1)


class TestPackageUtilCheckPackageExists(unittest.TestCase):
    """Test for homeassistant.util.package module."""

    def test_check_package_global(self):
        """Test for a globally-installed package."""
        installed_package = list(pkg_resources.working_set)[0].project_name

        self.assertTrue(package.check_package_exists(installed_package, None))

    def test_check_package_local(self):
        """Test for a locally-installed package."""
        lib_dir = get_python_lib()
        installed_package = list(pkg_resources.working_set)[0].project_name

        self.assertTrue(
            package.check_package_exists(installed_package, lib_dir)
        )

    def test_check_package_zip(self):
        """Test for an installed zip package."""
        self.assertFalse(package.check_package_exists(TEST_ZIP_REQ, None))
