user_agent_parser_test.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/python2.5
  2. #
  3. # Copyright 2008 Google Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the 'License')
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an 'AS IS' BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """User Agent Parser Unit Tests.
  17. Run:
  18. # python -m user_agent_parser_test (runs all the tests, takes awhile)
  19. or like:
  20. # python -m user_agent_parser_test ParseTest.testBrowserscopeStrings
  21. """
  22. from __future__ import unicode_literals, absolute_import
  23. __author__ = 'slamm@google.com (Stephen Lamm)'
  24. import os
  25. import re
  26. import unittest
  27. import yaml
  28. from ua_parser import user_agent_parser
  29. TEST_RESOURCES_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)),
  30. '../uap-core')
  31. class ParseTest(unittest.TestCase):
  32. def testBrowserscopeStrings(self):
  33. self.runUserAgentTestsFromYAML(os.path.join(
  34. TEST_RESOURCES_DIR, 'tests/test_ua.yaml'))
  35. def testBrowserscopeStringsOS(self):
  36. self.runOSTestsFromYAML(os.path.join(
  37. TEST_RESOURCES_DIR, 'tests/test_os.yaml'))
  38. def testStringsOS(self):
  39. self.runOSTestsFromYAML(os.path.join(
  40. TEST_RESOURCES_DIR, 'test_resources/additional_os_tests.yaml'))
  41. def testStringsDevice(self):
  42. self.runDeviceTestsFromYAML(os.path.join(
  43. TEST_RESOURCES_DIR, 'tests/test_device.yaml'))
  44. def testMozillaStrings(self):
  45. self.runUserAgentTestsFromYAML(os.path.join(
  46. TEST_RESOURCES_DIR, 'test_resources/firefox_user_agent_strings.yaml'))
  47. # NOTE: The YAML file used here is one output by makePGTSComparisonYAML()
  48. # below, as opposed to the pgts_browser_list-orig.yaml file. The -orig
  49. # file is by no means perfect, but identifies many browsers that we
  50. # classify as "Other". This test itself is mostly useful to know when
  51. # somthing in UA parsing changes. An effort should be made to try and
  52. # reconcile the differences between the two YAML files.
  53. def testPGTSStrings(self):
  54. self.runUserAgentTestsFromYAML(os.path.join(
  55. TEST_RESOURCES_DIR, 'test_resources/pgts_browser_list.yaml'))
  56. def testParseAll(self):
  57. 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)'
  58. expected = {
  59. 'device': {
  60. 'family': 'Other',
  61. 'brand': None,
  62. 'model': None
  63. },
  64. 'os': {
  65. 'family': 'Mac OS X',
  66. 'major': '10',
  67. 'minor': '4',
  68. 'patch': None,
  69. 'patch_minor': None
  70. },
  71. 'user_agent': {
  72. 'family': 'Firefox',
  73. 'major': '3',
  74. 'minor': '5',
  75. 'patch': '5'
  76. },
  77. 'string': user_agent_string
  78. }
  79. result = user_agent_parser.Parse(user_agent_string)
  80. self.assertEqual(
  81. result, expected,
  82. "UA: {0}\n expected<{1}> != actual<{2}>".format(user_agent_string, expected, result))
  83. # Make a YAML file for manual comparsion with pgts_browser_list-orig.yaml
  84. def makePGTSComparisonYAML(self):
  85. import codecs
  86. outfile = codecs.open('outfile.yaml', 'w', 'utf-8')
  87. print >> outfile, "test_cases:"
  88. yamlFile = open(os.path.join(TEST_RESOURCES_DIR,
  89. 'pgts_browser_list.yaml'))
  90. yamlContents = yaml.load(yamlFile)
  91. yamlFile.close()
  92. for test_case in yamlContents['test_cases']:
  93. user_agent_string = test_case['user_agent_string']
  94. kwds = {}
  95. if 'js_ua' in test_case:
  96. kwds = eval(test_case['js_ua'])
  97. (family, major, minor, patch) = user_agent_parser.ParseUserAgent(user_agent_string, **kwds)
  98. # Escape any double-quotes in the UA string
  99. user_agent_string = re.sub(r'"', '\\"', user_agent_string)
  100. print >> outfile, ' - user_agent_string: "' + user_agent_string + '"' + "\n" +\
  101. ' family: "' + family + "\"\n" +\
  102. " major: " + ('' if (major is None) else "'" + major + "'") + "\n" +\
  103. " minor: " + ('' if (minor is None) else "'" + minor + "'") + "\n" +\
  104. " patch: " + ('' if (patch is None) else "'" + patch + "'")
  105. outfile.close()
  106. # Run a set of test cases from a YAML file
  107. def runUserAgentTestsFromYAML(self, file_name):
  108. yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
  109. yamlContents = yaml.load(yamlFile)
  110. yamlFile.close()
  111. for test_case in yamlContents['test_cases']:
  112. # Inputs to Parse()
  113. user_agent_string = test_case['user_agent_string']
  114. kwds = {}
  115. if 'js_ua' in test_case:
  116. kwds = eval(test_case['js_ua'])
  117. # The expected results
  118. expected = {'family': test_case['family'],
  119. 'major': test_case['major'],
  120. 'minor': test_case['minor'],
  121. 'patch': test_case['patch']}
  122. result = {}
  123. result = user_agent_parser.ParseUserAgent(user_agent_string, **kwds)
  124. self.assertEqual(
  125. result, expected,
  126. "UA: {0}\n expected<{1}, {2}, {3}, {4}> != actual<{5}, {6}, {7}, {8}>".format(
  127. user_agent_string,
  128. expected['family'], expected['major'], expected['minor'], expected['patch'],
  129. result['family'], result['major'], result['minor'], result['patch']))
  130. def runOSTestsFromYAML(self, file_name):
  131. yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
  132. yamlContents = yaml.load(yamlFile)
  133. yamlFile.close()
  134. for test_case in yamlContents['test_cases']:
  135. # Inputs to Parse()
  136. user_agent_string = test_case['user_agent_string']
  137. kwds = {}
  138. if 'js_ua' in test_case:
  139. kwds = eval(test_case['js_ua'])
  140. # The expected results
  141. expected = {
  142. 'family': test_case['family'],
  143. 'major': test_case['major'],
  144. 'minor': test_case['minor'],
  145. 'patch': test_case['patch'],
  146. 'patch_minor': test_case['patch_minor']
  147. }
  148. result = user_agent_parser.ParseOS(user_agent_string, **kwds)
  149. self.assertEqual(
  150. result, expected,
  151. "UA: {0}\n expected<{1} {2} {3} {4} {5}> != actual<{6} {7} {8} {9} {10}>".format(
  152. user_agent_string,
  153. expected['family'],
  154. expected['major'],
  155. expected['minor'],
  156. expected['patch'],
  157. expected['patch_minor'],
  158. result['family'],
  159. result['major'],
  160. result['minor'],
  161. result['patch'],
  162. result['patch_minor']))
  163. def runDeviceTestsFromYAML(self, file_name):
  164. yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
  165. yamlContents = yaml.load(yamlFile)
  166. yamlFile.close()
  167. for test_case in yamlContents['test_cases']:
  168. # Inputs to Parse()
  169. user_agent_string = test_case['user_agent_string']
  170. kwds = {}
  171. if 'js_ua' in test_case:
  172. kwds = eval(test_case['js_ua'])
  173. # The expected results
  174. expected = {
  175. 'family': test_case['family'],
  176. 'brand': test_case['brand'],
  177. 'model': test_case['model']
  178. }
  179. result = user_agent_parser.ParseDevice(user_agent_string, **kwds)
  180. self.assertEqual(
  181. result, expected,
  182. "UA: {0}\n expected<{1} {2} {3}> != actual<{4} {5} {6}>".format(
  183. user_agent_string,
  184. expected['family'],
  185. expected['brand'],
  186. expected['model'],
  187. result['family'],
  188. result['brand'],
  189. result['model']))
  190. class GetFiltersTest(unittest.TestCase):
  191. def testGetFiltersNoMatchesGiveEmptyDict(self):
  192. user_agent_string = 'foo'
  193. filters = user_agent_parser.GetFilters(
  194. user_agent_string, js_user_agent_string=None)
  195. self.assertEqual({}, filters)
  196. def testGetFiltersJsUaPassedThrough(self):
  197. user_agent_string = 'foo'
  198. filters = user_agent_parser.GetFilters(
  199. user_agent_string, js_user_agent_string='bar')
  200. self.assertEqual({'js_user_agent_string': 'bar'}, filters)
  201. def testGetFiltersJsUserAgentFamilyAndVersions(self):
  202. user_agent_string = (
  203. 'Mozilla/4.0 (compatible; MSIE 8.0; '
  204. 'Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; '
  205. '.NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)')
  206. filters = user_agent_parser.GetFilters(
  207. user_agent_string, js_user_agent_string='bar',
  208. js_user_agent_family='foo')
  209. self.assertEqual({
  210. 'js_user_agent_string': 'bar',
  211. 'js_user_agent_family': 'foo'}, filters)
  212. if __name__ == '__main__':
  213. unittest.main()