# -*- coding: utf-8 -*-
# Author: Diego Sarmentero <diego.sarmentero@canonical.com>
#
# 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 ANY 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.
"""Test the common functions."""

from __future__ import unicode_literals

from PyQt4 import QtGui
from twisted.internet import defer
from twisted.trial.unittest import TestCase

from ubuntu_kylin_sso.utils import compat
from ubuntu_kylin_sso.qt import (
    build_general_error_message,
    maybe_elide_text,
    GENERIC_BACKEND_ERROR,
)
from ubuntu_kylin_sso.qt.common import (
    check_as_invalid,
    check_as_valid,
    password_assistance,
    password_check_match,
    BAD,
    GOOD,
    NORMAL,
    PASSWORD_DIGIT,
    PASSWORD_LENGTH,
    PASSWORD_MATCH,
    PASSWORD_UPPER,
)
from ubuntu_kylin_sso.qt.tests import build_string_for_pixels


class PasswordTestCase(TestCase):
    """Test passwords conditions."""

    @defer.inlineCallbacks
    def setUp(self):
        """Setup tests."""
        yield super(PasswordTestCase, self).setUp()
        self.line_edit = QtGui.QLineEdit()
        self.line_edit_confirm = QtGui.QLineEdit()
        self.label_assistance = QtGui.QLabel()

    def test_short_password(self):
        """Status with short password.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("foobar")
        password_assistance(self.line_edit, self.label_assistance)
        self.assertIn(
            BAD % PASSWORD_LENGTH,
            compat.text_type(self.label_assistance.text()),
        )

    def test_long_password(self):
        """Status with long password.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("foobarbaz")
        password_assistance(self.line_edit, self.label_assistance)
        self.assertIn(
            GOOD % PASSWORD_LENGTH,
            compat.text_type(self.label_assistance.text()),
        )

    def test_no_number_password(self):
        """Status with password without a number.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("foobarbaz")
        password_assistance(self.line_edit, self.label_assistance)
        self.assertIn(
            BAD % PASSWORD_DIGIT,
            compat.text_type(self.label_assistance.text()),
        )

    def test_number_password(self):
        """Status with password with a number.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("foobarba7")
        password_assistance(self.line_edit, self.label_assistance)
        self.assertIn(
            GOOD % PASSWORD_DIGIT,
            compat.text_type(self.label_assistance.text()),
        )

    def test_no_uppercase_password(self):
        """Status with password without uppercase letters.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("foobarbaz")
        password_assistance(self.line_edit, self.label_assistance)
        self.assertIn(
            BAD % PASSWORD_UPPER,
            compat.text_type(self.label_assistance.text()),
        )

    def test_upper_password(self):
        """Status with password with uppercase letters.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("Foobarba7")
        password_assistance(self.line_edit, self.label_assistance)
        self.assertIn(
            GOOD % PASSWORD_UPPER,
            compat.text_type(self.label_assistance.text()),
        )

    def test_matching_passwords(self):
        """Status when the passwords match.

        * Password assistance doesn't contain the message.
        """
        self.line_edit.setText("Foobarba7")
        self.line_edit_confirm.setText("Foobarba7")
        password_check_match(self.line_edit,
            self.line_edit_confirm, self.label_assistance)
        self.assertNotIn(
            BAD % PASSWORD_MATCH,
            compat.text_type(self.label_assistance.text()))

    def test_not_matching_passwords(self):
        """Status when the passwords not match.

        * Password assistance contains the right message.
        """
        self.line_edit.setText("Foobarba7")
        self.line_edit_confirm.setText("BazBarFo0")
        password_check_match(self.line_edit,
            self.line_edit_confirm, self.label_assistance)
        self.assertIn(
            BAD % PASSWORD_MATCH,
            compat.text_type(self.label_assistance.text()),
        )

    def test_password_assistance_in_focus_length_correct(self):
        """Check the QLineEdit for password when the length is correct."""
        self.line_edit.setText("aaaaaaaaa")
        self.line_edit_confirm.setText("")
        password_assistance(self.line_edit, self.label_assistance, NORMAL)
        self.assertIn(
            GOOD % PASSWORD_LENGTH,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            NORMAL % PASSWORD_UPPER,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            NORMAL % PASSWORD_DIGIT,
            compat.text_type(self.label_assistance.text()),
        )

    def test_password_assistance_in_focus_with_upper(self):
        """Check the QLineEdit for password when it has an upper case char."""
        self.line_edit.setText("AAa")
        self.line_edit_confirm.setText("")
        password_assistance(self.line_edit, self.label_assistance, NORMAL)
        self.assertIn(
            NORMAL % PASSWORD_LENGTH,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            GOOD % PASSWORD_UPPER,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            NORMAL % PASSWORD_DIGIT,
            compat.text_type(self.label_assistance.text()),
        )

    def test_password_assistance_in_focus_with_number(self):
        """Check the QLineEdit for password when it contains numbers."""
        self.line_edit.setText("a123")
        self.line_edit_confirm.setText("")
        password_assistance(self.line_edit, self.label_assistance, NORMAL)
        self.assertIn(
            NORMAL % PASSWORD_LENGTH,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            NORMAL % PASSWORD_UPPER,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            GOOD % PASSWORD_DIGIT,
            compat.text_type(self.label_assistance.text()),
        )

    def test_password_assistance_in_focus_all_ok(self):
        """Check the QLineEdit for password when all the condition are ok."""
        self.line_edit.setText("T3st3rqw")
        self.line_edit_confirm.setText("T3st3rqw")
        password_assistance(self.line_edit, self.label_assistance, NORMAL)
        self.assertIn(
            GOOD % PASSWORD_LENGTH,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            GOOD % PASSWORD_UPPER,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertIn(
            GOOD % PASSWORD_DIGIT,
            compat.text_type(self.label_assistance.text()),
        )
        self.assertNotIn(
            NORMAL % PASSWORD_MATCH,
            compat.text_type(self.label_assistance.text()),
        )


class LineEditStyleTestCase(TestCase):
    """Test QLineEdit styles for errors."""

    def test_check_valid(self):
        """Check the propery value of a QLineEdit marked as valid."""
        line_edit = QtGui.QLineEdit()
        check_as_valid(line_edit)
        self.assertFalse(line_edit.property("formError").toBool())

    def test_check_invalid(self):
        """Check the propery value of a QLineEdit marked as invalid."""
        line_edit = QtGui.QLineEdit()
        check_as_invalid(line_edit)
        self.assertTrue(line_edit.property("formError").toBool())


class ElidedTextTestCase(TestCase):
    """The test case for the maybe_elide_text function."""

    max_width = 100

    @defer.inlineCallbacks
    def setUp(self):
        """Setup tests."""
        yield super(ElidedTextTestCase, self).setUp()
        self.ui = QtGui.QLabel()

    def test_text_not_elided_if_too_short(self):
        """If text is shorter than max_width, do not elide."""
        text = build_string_for_pixels(self.ui, self.max_width - 10)

        maybe_elide_text(self.ui, text, self.max_width)

        self.assertEqual(self.ui.toolTip(), '')
        self.assertEqual(self.ui.text(), text)
        self.assertNotIn('\u2026', self.ui.text())

    def test_text_not_elided_if_equals_max_width(self):
        """If text is equal than max_width, do not elide."""
        text = build_string_for_pixels(self.ui, self.max_width)

        maybe_elide_text(self.ui, text, self.max_width)

        self.assertEqual(self.ui.toolTip(), '')
        self.assertEqual(self.ui.text(), text)
        self.assertNotIn('\u2026', self.ui.text())

    def test_text_elided_if_bigger_than_max_width(self):
        """If text is equal than max_width, do not elide."""
        text = build_string_for_pixels(self.ui, self.max_width * 2)

        maybe_elide_text(self.ui, text, self.max_width)

        self.assertEqual(self.ui.toolTip(), text)
        expected = compat.text_type(self.ui.text())
        self.assertTrue(expected.endswith('\u2026'))
        self.assertTrue(text.startswith(expected[:-1]))


class BuildGeneralErrorMessageTestCase(TestCase):
    """Test passwords conditions."""

    def test_with_message(self):
        """Test build_general_error_message with 'message' key."""
        error = "error message"
        err_dict = {'message': error}

        result = build_general_error_message(err_dict)

        self.assertEqual(result, error)

    def test_with_all(self):
        """Test build_general_error_message with 'all' key."""
        error = "error message"
        err_dict = {'__all__': error}

        result = build_general_error_message(err_dict)

        self.assertEqual(result, error)

    def test_with_message_and_all(self):
        """Test build_general_error_message with 'all' and 'message' key."""
        error = "error message"
        error2 = "error message2"
        err_dict = {'__all__': error, 'message': error2}

        result = build_general_error_message(err_dict)

        expected = '\n'.join((error, error2))
        self.assertEqual(result, expected)

    def test_with_all_and_error_message(self):
        """Test for 'all' and 'error_message' key."""
        error = "error message"
        error2 = "error message2"
        err_dict = {'__all__': error, 'error_message': error2}
        result = build_general_error_message(err_dict)
        expected = '\n'.join((error, error2))
        self.assertEqual(result, expected)

    def test_with_random_keys(self):
        """Test build_general_error_message with random keys."""
        error = "error message"
        error2 = "error message2"
        err_dict = {'my_bad': error, 'odd_error': error2}

        result = build_general_error_message(err_dict)

        expected = '\n'.join(
            [('%s: %s' % (k, v)) for k, v in err_dict.items()])
        self.assertEqual(result, expected)

    def test_with_random_keys_with_errtype(self):
        """Test build_general_error_message with random keys and errtype."""
        error = "error message"
        error2 = "error message2"
        err_dict = {'my_bad': error, 'odd_error': error2, 'errtype': 'Danger'}

        result = build_general_error_message(err_dict)

        expected = '\n'.join(
            [('%s: %s' % (k, v))
            for k, v in {'my_bad': error, 'odd_error': error2}.items()])
        self.assertEqual(result, expected)

    def test_with_not_dict(self):
        """Test build_general_error_message with argument not dict."""
        error = "error message"
        err_dict = Exception(error)

        result = build_general_error_message(err_dict)

        expected = GENERIC_BACKEND_ERROR
        self.assertEqual(result, expected)
