codekingpro/portable-devtools
114k
1# Copyright 2008 Google Inc.2#3# Licensed under the Apache License, Version 2.0 (the 'License')4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an 'AS IS' BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15 16"""User Agent Parser Unit Tests.17Run:18# python -m user_agent_parser_test (runs all the tests, takes awhile)19or like:20# python -m user_agent_parser_test ParseTest.testBrowserscopeStrings21"""22 23 24from __future__ import unicode_literals, absolute_import25 26__author__ = "slamm@google.com (Stephen Lamm)"27 28import logging29import os30import platform31import re32import sys33import unittest34import warnings35import yaml36 37if platform.python_implementation() == "PyPy":38 from yaml import SafeLoader39else:40 try:41 from yaml import CSafeLoader as SafeLoader42 except ImportError:43 logging.getLogger(__name__).warning(44 "PyYaml C extension not available to run tests, this will result "45 "in dramatic tests slowdown."46 )47 from yaml import SafeLoader48 49 50from ua_parser import user_agent_parser51 52TEST_RESOURCES_DIR = os.path.join(53 os.path.abspath(os.path.dirname(__file__)), "../uap-core"54)55 56 57class ParseTest(unittest.TestCase):58 def testBrowserscopeStrings(self):59 self.runUserAgentTestsFromYAML(60 os.path.join(TEST_RESOURCES_DIR, "tests/test_ua.yaml")61 )62 63 def testBrowserscopeStringsOS(self):64 self.runOSTestsFromYAML(os.path.join(TEST_RESOURCES_DIR, "tests/test_os.yaml"))65 66 def testStringsOS(self):67 self.runOSTestsFromYAML(68 os.path.join(TEST_RESOURCES_DIR, "test_resources/additional_os_tests.yaml")69 )70 71 def testStringsDevice(self):72 self.runDeviceTestsFromYAML(73 os.path.join(TEST_RESOURCES_DIR, "tests/test_device.yaml")74 )75 76 def testMozillaStrings(self):77 self.runUserAgentTestsFromYAML(78 os.path.join(79 TEST_RESOURCES_DIR, "test_resources/firefox_user_agent_strings.yaml"80 )81 )82 83 # NOTE: The YAML file used here is one output by makePGTSComparisonYAML()84 # below, as opposed to the pgts_browser_list-orig.yaml file. The -orig85 # file is by no means perfect, but identifies many browsers that we86 # classify as "Other". This test itself is mostly useful to know when87 # somthing in UA parsing changes. An effort should be made to try and88 # reconcile the differences between the two YAML files.89 def testPGTSStrings(self):90 self.runUserAgentTestsFromYAML(91 os.path.join(TEST_RESOURCES_DIR, "test_resources/pgts_browser_list.yaml")92 )93 94 def testParseAll(self):95 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)"96 expected = {97 "device": {"family": "Mac", "brand": "Apple", "model": "Mac"},98 "os": {99 "family": "Mac OS X",100 "major": "10",101 "minor": "4",102 "patch": None,103 "patch_minor": None,104 },105 "user_agent": {106 "family": "Firefox",107 "major": "3",108 "minor": "5",109 "patch": "5",110 },111 "string": user_agent_string,112 }113 114 result = user_agent_parser.Parse(user_agent_string)115 self.assertEqual(116 result,117 expected,118 "UA: {0}\n expected<{1}> != actual<{2}>".format(119 user_agent_string, expected, result120 ),121 )122 123 # Run a set of test cases from a YAML file124 def runUserAgentTestsFromYAML(self, file_name):125 yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))126 yamlContents = yaml.load(yamlFile, Loader=SafeLoader)127 yamlFile.close()128 129 for test_case in yamlContents["test_cases"]:130 # Inputs to Parse()131 user_agent_string = test_case["user_agent_string"]132 133 # The expected results134 expected = {135 "family": test_case["family"],136 "major": test_case["major"],137 "minor": test_case["minor"],138 "patch": test_case["patch"],139 }140 141 result = {}142 result = user_agent_parser.ParseUserAgent(user_agent_string)143 self.assertEqual(144 result,145 expected,146 "UA: {0}\n expected<{1}, {2}, {3}, {4}> != actual<{5}, {6}, {7}, {8}>".format(147 user_agent_string,148 expected["family"],149 expected["major"],150 expected["minor"],151 expected["patch"],152 result["family"],153 result["major"],154 result["minor"],155 result["patch"],156 ),157 )158 self.assertLessEqual(159 len(user_agent_parser._PARSE_CACHE),160 user_agent_parser.MAX_CACHE_SIZE,161 "verify that the cache size never exceeds the configured setting",162 )163 164 def runOSTestsFromYAML(self, file_name):165 yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))166 yamlContents = yaml.load(yamlFile, Loader=SafeLoader)167 yamlFile.close()168 169 for test_case in yamlContents["test_cases"]:170 # Inputs to Parse()171 user_agent_string = test_case["user_agent_string"]172 173 # The expected results174 expected = {175 "family": test_case["family"],176 "major": test_case["major"],177 "minor": test_case["minor"],178 "patch": test_case["patch"],179 "patch_minor": test_case["patch_minor"],180 }181 182 result = user_agent_parser.ParseOS(user_agent_string)183 self.assertEqual(184 result,185 expected,186 "UA: {0}\n expected<{1} {2} {3} {4} {5}> != actual<{6} {7} {8} {9} {10}>".format(187 user_agent_string,188 expected["family"],189 expected["major"],190 expected["minor"],191 expected["patch"],192 expected["patch_minor"],193 result["family"],194 result["major"],195 result["minor"],196 result["patch"],197 result["patch_minor"],198 ),199 )200 201 def runDeviceTestsFromYAML(self, file_name):202 yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))203 yamlContents = yaml.load(yamlFile, Loader=SafeLoader)204 yamlFile.close()205 206 for test_case in yamlContents["test_cases"]:207 # Inputs to Parse()208 user_agent_string = test_case["user_agent_string"]209 210 # The expected results211 expected = {212 "family": test_case["family"],213 "brand": test_case["brand"],214 "model": test_case["model"],215 }216 217 result = user_agent_parser.ParseDevice(user_agent_string)218 self.assertEqual(219 result,220 expected,221 "UA: {0}\n expected<{1} {2} {3}> != actual<{4} {5} {6}>".format(222 user_agent_string,223 expected["family"],224 expected["brand"],225 expected["model"],226 result["family"],227 result["brand"],228 result["model"],229 ),230 )231 232 233class GetFiltersTest(unittest.TestCase):234 def testGetFiltersNoMatchesGiveEmptyDict(self):235 user_agent_string = "foo"236 filters = user_agent_parser.GetFilters(237 user_agent_string, js_user_agent_string=None238 )239 self.assertEqual({}, filters)240 241 def testGetFiltersJsUaPassedThrough(self):242 user_agent_string = "foo"243 filters = user_agent_parser.GetFilters(244 user_agent_string, js_user_agent_string="bar"245 )246 self.assertEqual({"js_user_agent_string": "bar"}, filters)247 248 def testGetFiltersJsUserAgentFamilyAndVersions(self):249 user_agent_string = (250 "Mozilla/4.0 (compatible; MSIE 8.0; "251 "Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; "252 ".NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"253 )254 filters = user_agent_parser.GetFilters(255 user_agent_string, js_user_agent_string="bar", js_user_agent_family="foo"256 )257 self.assertEqual(258 {"js_user_agent_string": "bar", "js_user_agent_family": "foo"}, filters259 )260 261 262class TestDeprecationWarnings(unittest.TestCase):263 def setUp(self):264 """In Python 2.7, catch_warnings apparently does not do anything if265 the warning category is not active, whereas in 3(.6 and up) it266 seems to work out of the box.267 """268 super(TestDeprecationWarnings, self).setUp()269 warnings.simplefilter("always", DeprecationWarning)270 271 def tearDown(self):272 # not ideal as it discards all other warnings updates from the273 # process, should really copy the contents of274 # `warnings.filters`, then reset-it.275 warnings.resetwarnings()276 super(TestDeprecationWarnings, self).tearDown()277 278 def test_parser_deprecation(self):279 with warnings.catch_warnings(record=True) as ws:280 user_agent_parser.ParseWithJSOverrides("")281 self.assertEqual(len(ws), 1)282 self.assertEqual(ws[0].category, DeprecationWarning)283 284 def test_printer_deprecation(self):285 with warnings.catch_warnings(record=True) as ws:286 user_agent_parser.Pretty("")287 self.assertEqual(len(ws), 1)288 self.assertEqual(ws[0].category, DeprecationWarning)289 290 def test_js_bits_deprecation(self):291 for parser, count in [292 (user_agent_parser.Parse, 3),293 (user_agent_parser.ParseUserAgent, 1),294 (user_agent_parser.ParseOS, 1),295 (user_agent_parser.ParseDevice, 1),296 ]:297 user_agent_parser._PARSE_CACHE.clear()298 with warnings.catch_warnings(record=True) as ws:299 parser("some random thing", js_attribute=True)300 self.assertEqual(len(ws), count)301 for w in ws:302 self.assertEqual(w.category, DeprecationWarning)303 304 305class ErrTest(unittest.TestCase):306 @unittest.skipIf(307 sys.version_info < (3,), "bytes and str are not differentiated in P2"308 )309 def test_bytes(self):310 with self.assertRaises(TypeError):311 user_agent_parser.Parse(b"")312 313 def test_int(self):314 with self.assertRaises(TypeError):315 user_agent_parser.Parse(0)316 317 def test_list(self):318 with self.assertRaises(TypeError):319 user_agent_parser.Parse([])320 321 def test_tuple(self):322 with self.assertRaises(TypeError):323 user_agent_parser.Parse(())324 325 326if __name__ == "__main__":327 unittest.main()328 