# -*- coding: utf-8 -*-
#
# Copyright 2011-2012 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT AN WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations
# including the two.
# You must obey the GNU General Public License in all respects
# for all of the code used other than OpenSSL.  If you modify
# file(s) with this exception, you may extend this exception to your
# version of the file(s), but you are not obligated to do so.  If you
# do not wish to do so, delete this exception statement from your
# version.  If you delete this exception statement from all source
# files in the program, then also delete it here.
"""Specific tests for the qt implementation."""

from PyQt4.QtNetwork import (
    QSslConfiguration,
    QSslError,
)
from twisted.internet import defer
from ubuntuone.devtools.testcases import TestCase

from ubuntu_kylin_sso.utils.webclient import qtnetwork


class SetupProxyTestCase(TestCase):
    """Test the proxy setup."""

    @defer.inlineCallbacks
    def setUp(self):
        """Set the different tests."""
        yield super(SetupProxyTestCase, self).setUp()
        self.called = []

        self.settings = dict(https=dict(username='user', password='pasword'))
        self.patch(qtnetwork.gsettings, 'get_proxy_settings',
                lambda: self.settings)
        self.patch(qtnetwork, "get_cert_dir", lambda: "")
        self.patch(qtnetwork.glob, "glob", lambda dir: [])

        self.proxy = None

        self.real_build_proxy = qtnetwork.build_proxy

        def build_proxy(settings):
            """Log build_proxy calls."""
            self.called.append(('build_proxy', settings))
            self.proxy = self.real_build_proxy(settings)
            return self.proxy

        self.patch(qtnetwork, 'build_proxy', lambda s: build_proxy(s))

        self.addCleanup(self._clean_webclient_instance)

    def _set_old_platform(self, platform):
        """Set back the platform."""
        qtnetwork.sys.platform = platform

    def _clean_webclient_instance(self):
        """Set the webclient not to have a proxy."""
        qtnetwork.WebClient.proxy_instance = None

    def test_setup_no_certs(self):
        # Ensure WebClient can start even if it finds no certs.
        # It may or may not end up working depending on the certs actually
        # being in the system, but not having them locally shouldn't prevent
        # startup.
        qtnetwork.WebClient()

    def test_setup_unreadable_cert(self):
        # If for some reason a cert of our own is found but can't be read,
        # make sure we raise WebClientError.
        # glob only returns paths that actually exist, but we fake this by
        # passing something that can't be opened.
        self.patch(qtnetwork.glob, "glob", lambda dir: ["asdfasdfasdf"])
        self.assertRaises(qtnetwork.WebClientError, qtnetwork.WebClient)


class SetupLinuxProxyTestCase(SetupProxyTestCase):
    """Test setting up the proxy."""

    @defer.inlineCallbacks
    def setUp(self):
        """Set the different tests."""
        yield super(SetupLinuxProxyTestCase, self).setUp()
        old_platform = qtnetwork.sys.platform
        qtnetwork.sys.platform = 'linux'
        self.addCleanup(self._set_old_platform, old_platform)

    def test_setup_proxy(self):
        """Test setting the proxy."""
        qtnetwork.WebClient()
        self.assertEqual(self.proxy, qtnetwork.WebClient.proxy_instance)
        self.assertIn(('build_proxy', self.settings), self.called)

    def test_setup_instance_present(self):
        """Test when the instance is present."""
        # set the instance and assert we did not reset it
        qtnetwork.WebClient.proxy_instance = 'other_proxy'
        qtnetwork.WebClient()
        self.assertNotEqual(self.proxy, qtnetwork.WebClient.proxy_instance)
        self.assertNotIn(('build_proxy', self.settings), self.called)


class FakeQNetworkReply(object):
    """A fake QNetworkReply."""

    def __init__(self):
        """Initialize this fake."""
        url = qtnetwork.QUrl("http://fake_server")
        self._request = qtnetwork.QNetworkRequest(url)

    def request(self):
        """Return a test network request."""
        return self._request


class SSLErrorsTestCase(TestCase):
    """Tests for when SSL errors happen."""

    def test_handle_ssl_errors(self):
        """Test the _handle_ssl_errors method."""
        result = []
        self.patch(qtnetwork.logger, "error", result.append)
        client = qtnetwork.WebClient()
        reply = FakeQNetworkReply()
        errors = [QSslError()]
        client._handle_ssl_errors(reply, errors)
        self.assertTrue(type(result[0]), unicode)

    def test_nonempty_cert_list(self):
        """Test the Qt CA list has more than our included certs."""
        qtnetwork.WebClient()
        ssl_config = QSslConfiguration.defaultConfiguration()
        ca_certs = ssl_config.caCertificates()
        self.assertTrue(len(ca_certs) > 3)
