#!/usr/bin/python2
#
# Copyright 2008 Google 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.


"""User Agent Parser Unit Tests.
Run:
# python -m user_agent_parser_test  (runs all the tests, takes awhile)
or like:
# python -m user_agent_parser_test ParseTest.testBrowserscopeStrings
"""


from __future__ import unicode_literals, absolute_import

__author__ = 'slamm@google.com (Stephen Lamm)'

import os
import re
import unittest
import yaml

from ua_parser import user_agent_parser

TEST_RESOURCES_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                  '../uap-core')


class ParseTest(unittest.TestCase):
    def testBrowserscopeStrings(self):
        self.runUserAgentTestsFromYAML(os.path.join(
            TEST_RESOURCES_DIR, 'tests/test_ua.yaml'))

    def testBrowserscopeStringsOS(self):
        self.runOSTestsFromYAML(os.path.join(
            TEST_RESOURCES_DIR, 'tests/test_os.yaml'))

    def testStringsOS(self):
        self.runOSTestsFromYAML(os.path.join(
            TEST_RESOURCES_DIR, 'test_resources/additional_os_tests.yaml'))

    def testStringsDevice(self):
        self.runDeviceTestsFromYAML(os.path.join(
            TEST_RESOURCES_DIR, 'tests/test_device.yaml'))

    def testMozillaStrings(self):
        self.runUserAgentTestsFromYAML(os.path.join(
            TEST_RESOURCES_DIR, 'test_resources/firefox_user_agent_strings.yaml'))

    # NOTE: The YAML file used here is one output by makePGTSComparisonYAML()
    # below, as opposed to the pgts_browser_list-orig.yaml file.  The -orig
    # file is by no means perfect, but identifies many browsers that we
    # classify as "Other".  This test itself is mostly useful to know when
    # somthing in UA parsing changes.  An effort should be made to try and
    # reconcile the differences between the two YAML files.
    def testPGTSStrings(self):
        self.runUserAgentTestsFromYAML(os.path.join(
            TEST_RESOURCES_DIR, 'test_resources/pgts_browser_list.yaml'))

    def testParseAll(self):
        user_agent_string = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; fr; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5,gzip(gfe),gzip(gfe)'
        expected = {
            'device': {
                'family': 'Other',
                'brand': None,
                'model': None
            },
            'os': {
                'family': 'Mac OS X',
                'major': '10',
                'minor': '4',
                'patch': None,
                'patch_minor': None
            },
            'user_agent': {
                'family': 'Firefox',
                'major': '3',
                'minor': '5',
                'patch': '5'
            },
            'string': user_agent_string
        }

        result = user_agent_parser.Parse(user_agent_string)
        self.assertEqual(
            result, expected,
            "UA: {0}\n expected<{1}> != actual<{2}>".format(user_agent_string, expected, result))
    # Make a YAML file for manual comparsion with pgts_browser_list-orig.yaml
    def makePGTSComparisonYAML(self):
        import codecs
        outfile = codecs.open('outfile.yaml', 'w', 'utf-8')
        print >> outfile, "test_cases:"

        yamlFile = open(os.path.join(TEST_RESOURCES_DIR,
                                     'pgts_browser_list.yaml'))
        yamlContents = yaml.load(yamlFile)
        yamlFile.close()

        for test_case in yamlContents['test_cases']:
            user_agent_string = test_case['user_agent_string']
            kwds = {}
            if 'js_ua' in test_case:
                kwds = eval(test_case['js_ua'])

            (family, major, minor, patch) = user_agent_parser.ParseUserAgent(user_agent_string, **kwds)

            # Escape any double-quotes in the UA string
            user_agent_string = re.sub(r'"', '\\"', user_agent_string)
            print >> outfile, '    - user_agent_string: "' + user_agent_string + '"' + "\n" +\
                              '      family: "' + family + "\"\n" +\
                              "      major: " + ('' if (major is None) else "'" + major + "'") + "\n" +\
                              "      minor: " + ('' if (minor is None) else "'" + minor + "'") + "\n" +\
                              "      patch: " + ('' if (patch is None) else "'" + patch + "'")
        outfile.close()

    # Run a set of test cases from a YAML file
    def runUserAgentTestsFromYAML(self, file_name):
        yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
        yamlContents = yaml.load(yamlFile)
        yamlFile.close()

        for test_case in yamlContents['test_cases']:
            # Inputs to Parse()
            user_agent_string = test_case['user_agent_string']
            kwds = {}
            if 'js_ua' in test_case:
                kwds = eval(test_case['js_ua'])

            # The expected results
            expected = {'family': test_case['family'],
                        'major': test_case['major'],
                        'minor': test_case['minor'],
                        'patch': test_case['patch']}

            result = {}
            result = user_agent_parser.ParseUserAgent(user_agent_string, **kwds)
            self.assertEqual(
                result, expected,
                "UA: {0}\n expected<{1}, {2}, {3}, {4}> != actual<{5}, {6}, {7}, {8}>".format(
                    user_agent_string,
                    expected['family'], expected['major'], expected['minor'], expected['patch'],
                    result['family'], result['major'], result['minor'], result['patch']))

    def runOSTestsFromYAML(self, file_name):
        yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
        yamlContents = yaml.load(yamlFile)
        yamlFile.close()

        for test_case in yamlContents['test_cases']:
            # Inputs to Parse()
            user_agent_string = test_case['user_agent_string']
            kwds = {}
            if 'js_ua' in test_case:
                kwds = eval(test_case['js_ua'])

            # The expected results
            expected = {
                'family': test_case['family'],
                'major': test_case['major'],
                'minor': test_case['minor'],
                'patch': test_case['patch'],
                'patch_minor': test_case['patch_minor']
            }

            result = user_agent_parser.ParseOS(user_agent_string, **kwds)
            self.assertEqual(
                result, expected,
                "UA: {0}\n expected<{1} {2} {3} {4} {5}> != actual<{6} {7} {8} {9} {10}>".format(
                    user_agent_string,
                    expected['family'],
                    expected['major'],
                    expected['minor'],
                    expected['patch'],
                    expected['patch_minor'],
                    result['family'],
                    result['major'],
                    result['minor'],
                    result['patch'],
                    result['patch_minor']))

    def runDeviceTestsFromYAML(self, file_name):
        yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
        yamlContents = yaml.load(yamlFile)
        yamlFile.close()

        for test_case in yamlContents['test_cases']:
            # Inputs to Parse()
            user_agent_string = test_case['user_agent_string']
            kwds = {}
            if 'js_ua' in test_case:
                kwds = eval(test_case['js_ua'])

            # The expected results
            expected = {
                'family': test_case['family'],
                'brand': test_case['brand'],
                'model': test_case['model']
            }

            result = user_agent_parser.ParseDevice(user_agent_string, **kwds)
            self.assertEqual(
                result, expected,
                "UA: {0}\n expected<{1} {2} {3}> != actual<{4} {5} {6}>".format(
                    user_agent_string,
                    expected['family'],
                    expected['brand'],
                    expected['model'],
                    result['family'],
                    result['brand'],
                    result['model']))


class GetFiltersTest(unittest.TestCase):
    def testGetFiltersNoMatchesGiveEmptyDict(self):
        user_agent_string = 'foo'
        filters = user_agent_parser.GetFilters(
            user_agent_string, js_user_agent_string=None)
        self.assertEqual({}, filters)

    def testGetFiltersJsUaPassedThrough(self):
        user_agent_string = 'foo'
        filters = user_agent_parser.GetFilters(
            user_agent_string, js_user_agent_string='bar')
        self.assertEqual({'js_user_agent_string': 'bar'}, filters)

    def testGetFiltersJsUserAgentFamilyAndVersions(self):
        user_agent_string = (
            'Mozilla/4.0 (compatible; MSIE 8.0; '
            'Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; '
            '.NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)')
        filters = user_agent_parser.GetFilters(
            user_agent_string, js_user_agent_string='bar',
            js_user_agent_family='foo')
        self.assertEqual({
            'js_user_agent_string': 'bar',
            'js_user_agent_family': 'foo'}, filters)


if __name__ == '__main__':
    unittest.main()
