# Copyright (c) 2012, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the Willow Garage, Inc. nor the names of its
#       contributors may be used to endorse or promote products derived from
#       this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from contextlib import contextmanager
import os
import sys
try:
    from cStringIO import StringIO
except ImportError:
    from io import StringIO

import rospkg
import rospkg.os_detect

import unittest

from mock import patch
from mock import DEFAULT

from rosdep2 import main
from rosdep2.main import rosdep_main
from rosdep2.main import setup_proxy_opener


GITHUB_BASE_URL = 'https://github.com/ros/rosdistro/raw/master/rosdep/base.yaml'
GITHUB_PYTHON_URL = 'https://github.com/ros/rosdistro/raw/master/rosdep/python.yaml'


def get_test_dir():
    return os.path.abspath(os.path.dirname(__file__))


def get_test_tree_dir():
    return os.path.abspath(os.path.join(get_test_dir(), 'tree'))


def get_test_catkin_tree_dir():
    return os.path.abspath(os.path.join(get_test_tree_dir(), 'catkin'))


def get_cache_dir():
    p = os.path.join(get_test_dir(), 'sources_cache')
    assert os.path.isdir(p)
    return p


@contextmanager
def fakeout():
    realstdout = sys.stdout
    realstderr = sys.stderr
    fakestdout = StringIO()
    fakestderr = StringIO()
    sys.stdout = fakestdout
    sys.stderr = fakestderr
    yield fakestdout, fakestderr
    sys.stdout = realstdout
    sys.stderr = realstderr

# the goal of these tests is only to test that we are wired into the
# APIs.  More exhaustive tests are at the unit level.


class TestRosdepMain(unittest.TestCase):

    def setUp(self):
        if 'ROSDEP_DEBUG' in os.environ:
            del os.environ['ROSDEP_DEBUG']
        self.old_rr = rospkg.get_ros_root()
        self.old_rpp = rospkg.get_ros_package_path()
        if 'ROS_ROOT' in os.environ:
            del os.environ['ROS_ROOT']
        os.environ['ROS_PACKAGE_PATH'] = os.path.join(get_test_tree_dir())

    def tearDown(self):
        if self.old_rr is not None:
            os.environ['ROS_ROOT'] = self.old_rr
        if self.old_rpp is not None:
            os.environ['ROS_PACKAGE_PATH'] = self.old_rpp

    def test_bad_commands(self):
        sources_cache = get_cache_dir()
        cmd_extras = ['-c', sources_cache]
        for commands in [[], ['fake', 'something'], ['check'], ['install', '-a', 'rospack_fake'],
                         ['check', 'rospack_fake', '--os', 'ubuntulucid'],
                         ]:
            try:
                rosdep_main(commands + cmd_extras)
                assert False, 'system exit should have occurred'
            except SystemExit:
                pass

    def test_check(self):
        sources_cache = get_cache_dir()
        cmd_extras = ['-c', sources_cache]

        with fakeout() as b:
            try:
                rosdep_main(['check', 'python_dep'] + cmd_extras)
            except SystemExit:
                assert False, 'system exit occurred: %s\n%s' % (b[0].getvalue(), b[1].getvalue())

            stdout, stderr = b
            assert stdout.getvalue().strip() == 'All system dependencies have been satisified', stdout.getvalue()
            assert not stderr.getvalue(), stderr.getvalue()
        try:
            osd = rospkg.os_detect.OsDetect()
            override = '%s:%s' % (osd.get_name(), osd.get_codename())
            with fakeout() as b:
                rosdep_main(['check', 'python_dep', '--os', override] + cmd_extras)
                stdout, stderr = b
                assert stdout.getvalue().strip() == 'All system dependencies have been satisified'
                assert not stderr.getvalue(), stderr.getvalue()
        except SystemExit:
            assert False, 'system exit occurred'

        # this used to abort, but now rosdep assumes validity for even empty stack args
        try:
            with fakeout() as b:
                rosdep_main(['check', 'packageless'] + cmd_extras)
                stdout, stderr = b
                assert stdout.getvalue().strip() == 'All system dependencies have been satisified'
                assert not stderr.getvalue(), stderr.getvalue()
        except SystemExit:
            assert False, 'system exit occurred'

        try:
            rosdep_main(['check', 'nonexistent'] + cmd_extras)
            assert False, 'system exit should have occurred'
        except SystemExit:
            pass

    def test_install(self):
        sources_cache = get_cache_dir()
        cmd_extras = ['-c', sources_cache]
        catkin_tree = get_test_catkin_tree_dir()

        try:
            # python must have already been installed
            with fakeout() as b:
                rosdep_main(['install', 'python_dep'] + cmd_extras)
                stdout, stderr = b
                assert 'All required rosdeps installed' in stdout.getvalue(), stdout.getvalue()
                assert not stderr.getvalue(), stderr.getvalue()
            with fakeout() as b:
                rosdep_main(['install', 'python_dep', '-r'] + cmd_extras)
                stdout, stderr = b
                assert 'All required rosdeps installed' in stdout.getvalue(), stdout.getvalue()
                assert not stderr.getvalue(), stderr.getvalue()
            with fakeout() as b:
                rosdep_main([
                    'install', '-s', '-i',
                    '--os', 'ubuntu:lucid',
                    '--rosdistro', 'fuerte',
                    '--from-paths', catkin_tree
                ] + cmd_extras)
                stdout, stderr = b
                expected = [
                    '#[apt] Installation commands:',
                    '  sudo -H apt-get install ros-fuerte-catkin',
                    '  sudo -H apt-get install libboost1.40-all-dev'
                ]
                lines = stdout.getvalue().splitlines()
                assert set(lines) == set(expected), lines