Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(486)

Side by Side Diff: tools/chrome_proxy/webdriver/common.py

Issue 2574923002: Add common IntegrationTest class with assertions and RunAllTests() func (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/chrome_proxy/webdriver/simple_smoke.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import argparse 5 import argparse
6 import json 6 import json
7 import os 7 import os
8 import re 8 import re
9 import socket 9 import socket
10 import shlex 10 import shlex
11 import sys 11 import sys
12 import time 12 import time
13 import traceback 13 import traceback
14 import unittest
14 15
15 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 16 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
16 os.pardir, 'third_party', 'webdriver', 'pylib')) 17 os.pardir, 'third_party', 'webdriver', 'pylib'))
17 from selenium import webdriver 18 from selenium import webdriver
18 from selenium.webdriver.chrome.options import Options 19 from selenium.webdriver.chrome.options import Options
19 20
20 # TODO(robertogden): Add logging. 21 # TODO(robertogden): Add logging.
21 22
22 23
23 def ParseFlags(): 24 def ParseFlags():
24 """Parses the given command line arguments. 25 """Parses the given command line arguments.
25 26
26 Returns: 27 Returns:
27 A new Namespace object with class properties for each argument added below. 28 A new Namespace object with class properties for each argument added below.
28 See pydoc for argparse. 29 See pydoc for argparse.
29 """ 30 """
30 parser = argparse.ArgumentParser() 31 parser = argparse.ArgumentParser()
31 parser.add_argument('--browser_args', nargs=1, type=str, help='Override ' 32 parser.add_argument('--browser_args', type=str, help='Override browser flags '
32 'browser flags in code with these flags') 33 'in code with these flags')
33 parser.add_argument('--via_header_value', metavar='via_header', nargs=1, 34 parser.add_argument('--via_header_value',
34 default='1.1 Chrome-Compression-Proxy', help='What the via should match to ' 35 default='1.1 Chrome-Compression-Proxy', help='What the via should match to '
35 'be considered valid') 36 'be considered valid')
36 parser.add_argument('--android', help='If given, attempts to run the test on ' 37 parser.add_argument('--android', help='If given, attempts to run the test on '
37 'Android via adb. Ignores usage of --chrome_exec', action='store_true') 38 'Android via adb. Ignores usage of --chrome_exec', action='store_true')
38 parser.add_argument('--android_package', nargs=1, 39 parser.add_argument('--android_package',
39 default='com.android.chrome', help='Set the android package for Chrome') 40 default='com.android.chrome', help='Set the android package for Chrome')
40 parser.add_argument('--chrome_exec', nargs=1, type=str, help='The path to ' 41 parser.add_argument('--chrome_exec', type=str, help='The path to '
41 'the Chrome or Chromium executable') 42 'the Chrome or Chromium executable')
42 parser.add_argument('chrome_driver', nargs=1, type=str, help='The path to ' 43 parser.add_argument('chrome_driver', type=str, help='The path to '
43 'the ChromeDriver executable. If not given, the default system chrome ' 44 'the ChromeDriver executable. If not given, the default system chrome '
44 'will be used.') 45 'will be used.')
45 parser.add_argument('--disable_buffer', help='Causes stdout and stderr from ' 46 parser.add_argument('--disable_buffer', help='Causes stdout and stderr from '
46 'tests to output normally. Otherwise, the standard output and standard ' 47 'tests to output normally. Otherwise, the standard output and standard '
47 'error streams are buffered during the test run, and output during a ' 48 'error streams are buffered during the test run, and output during a '
48 'passing test is discarded. Output will always be echoed normally on test ' 49 'passing test is discarded. Output will always be echoed normally on test '
49 'fail or error and is added to the failure messages.', action='store_true') 50 'fail or error and is added to the failure messages.', action='store_true')
50 parser.add_argument('-c', '--catch', help='Control-C during the test run ' 51 parser.add_argument('-c', '--catch', help='Control-C during the test run '
51 'waits for the current test to end and then reports all the results so ' 52 'waits for the current test to end and then reports all the results so '
52 'far. A second Control-C raises the normal KeyboardInterrupt exception.', 53 'far. A second Control-C raises the normal KeyboardInterrupt exception.',
53 action='store_true') 54 action='store_true')
54 parser.add_argument('-f', '--failfast', help='Stop the test run on the first ' 55 parser.add_argument('-f', '--failfast', help='Stop the test run on the first '
55 'error or failure.', action='store_true') 56 'error or failure.', action='store_true')
56 # TODO(robertogden): Log sys.argv here. 57 # TODO(robertogden): Log sys.argv here.
57 return parser.parse_args(sys.argv[1:]) 58 return parser.parse_args(sys.argv[1:])
58 59
59 def HandleException(test_name=None):
60 """Writes the exception being handled and a stack trace to stderr.
61
62 Args:
63 test_name: The string name of the test that led to this exception.
64 """
65 sys.stderr.write("**************************************\n")
66 sys.stderr.write("**************************************\n")
67 sys.stderr.write("** **\n")
68 sys.stderr.write("** UNCAUGHT EXCEPTION **\n")
69 sys.stderr.write("** **\n")
70 sys.stderr.write("**************************************\n")
71 sys.stderr.write("**************************************\n")
72 if test_name:
73 sys.stderr.write("Failed test: %s" % test_name)
74 traceback.print_exception(*sys.exc_info())
75 sys.exit(1)
76 60
77 class TestDriver: 61 class TestDriver:
78 """The main driver for an integration test. 62 """The main driver for an integration test.
79 63
80 This class is the tool that is used by every integration test to interact with 64 This class is the tool that is used by every integration test to interact with
81 the Chromium browser and validate proper functionality. This class sits on top 65 the Chromium browser and validate proper functionality. This class sits on top
82 of the Selenium Chrome Webdriver with added utility and helper functions for 66 of the Selenium Chrome Webdriver with added utility and helper functions for
83 Chrome-Proxy. This class should be used with Python's 'with' operator. 67 Chrome-Proxy. This class should be used with Python's 'with' operator.
84 68
85 Attributes: 69 Attributes:
(...skipping 25 matching lines...) Expand all
111 override the argument. 95 override the argument.
112 """ 96 """
113 def GetDictKey(argument): 97 def GetDictKey(argument):
114 return argument.split('=', 1)[0] 98 return argument.split('=', 1)[0]
115 if self._flags.browser_args and len(self._flags.browser_args) > 0: 99 if self._flags.browser_args and len(self._flags.browser_args) > 0:
116 # Build a dict of flags mapped to the whole argument. 100 # Build a dict of flags mapped to the whole argument.
117 original_args = {} 101 original_args = {}
118 for arg in self._chrome_args: 102 for arg in self._chrome_args:
119 original_args[GetDictKey(arg)] = arg 103 original_args[GetDictKey(arg)] = arg
120 # Override flags given in code with any command line arguments. 104 # Override flags given in code with any command line arguments.
121 for override_arg in shlex.split(self._flags.browser_args[0]): 105 for override_arg in shlex.split(self._flags.browser_args):
122 arg_key = GetDictKey(override_arg) 106 arg_key = GetDictKey(override_arg)
123 if arg_key in original_args: 107 if arg_key in original_args:
124 self._chrome_args.remove(original_args[arg_key]) 108 self._chrome_args.remove(original_args[arg_key])
125 self._chrome_args.add(override_arg) 109 self._chrome_args.add(override_arg)
126 # Always add the flag that allows histograms to be queried in javascript. 110 # Always add the flag that allows histograms to be queried in javascript.
127 self._chrome_args.add('--enable-stats-collection-bindings') 111 self._chrome_args.add('--enable-stats-collection-bindings')
128 112
129 def _StartDriver(self): 113 def _StartDriver(self):
130 """Parses the flags to pass to Chromium, then starts the ChromeDriver. 114 """Parses the flags to pass to Chromium, then starts the ChromeDriver.
131 115
132 If running Android, the Android package name is passed to ChromeDriver here. 116 If running Android, the Android package name is passed to ChromeDriver here.
133 """ 117 """
134 self._OverrideChromeArgs() 118 self._OverrideChromeArgs()
135 capabilities = { 119 capabilities = {
136 'loggingPrefs': {'performance': 'INFO'}, 120 'loggingPrefs': {'performance': 'INFO'},
137 'chromeOptions': { 121 'chromeOptions': {
138 'args': list(self._chrome_args) 122 'args': list(self._chrome_args)
139 } 123 }
140 } 124 }
141 if self._flags.android: 125 if self._flags.android:
142 capabilities['chromeOptions'].update({ 126 capabilities['chromeOptions'].update({
143 'androidPackage': self._flags.android_package, 127 'androidPackage': self._flags.android_package,
144 }) 128 })
145 elif self._flags.chrome_exec: 129 elif self._flags.chrome_exec:
146 capabilities['chrome.binary'] = self._flags.chrome_exec 130 capabilities['chrome.binary'] = self._flags.chrome_exec
147 driver = webdriver.Chrome(executable_path=self._flags.chrome_driver[0], 131 driver = webdriver.Chrome(executable_path=self._flags.chrome_driver,
148 desired_capabilities=capabilities) 132 desired_capabilities=capabilities)
149 driver.command_executor._commands.update({ 133 driver.command_executor._commands.update({
150 'getAvailableLogTypes': ('GET', '/session/$sessionId/log/types'), 134 'getAvailableLogTypes': ('GET', '/session/$sessionId/log/types'),
151 'getLog': ('POST', '/session/$sessionId/log')}) 135 'getLog': ('POST', '/session/$sessionId/log')})
152 self._driver = driver 136 self._driver = driver
153 137
154 def _StopDriver(self): 138 def _StopDriver(self):
155 """Nicely stops the ChromeDriver. 139 """Nicely stops the ChromeDriver.
156 """ 140 """
157 self._driver.quit() 141 self._driver.quit()
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 _url: the fetched url 340 _url: the fetched url
357 _protocol: The protocol used to get the response. 341 _protocol: The protocol used to get the response.
358 _port: The remote port number used to get the response. 342 _port: The remote port number used to get the response.
359 _status: The integer status code of the response 343 _status: The integer status code of the response
360 _request_type: What caused this request (Document, XHR, etc) 344 _request_type: What caused this request (Document, XHR, etc)
361 _flags: A Namespace object from ParseFlags() 345 _flags: A Namespace object from ParseFlags()
362 """ 346 """
363 347
364 def __init__(self, response_headers, request_headers, url, protocol, port, 348 def __init__(self, response_headers, request_headers, url, protocol, port,
365 status, request_type): 349 status, request_type):
366 self._response_headers = response_headers 350 self._response_headers = {}
367 self._request_headers = request_headers 351 self._request_headers = {}
368 self._url = url 352 self._url = url
369 self._protocol = protocol 353 self._protocol = protocol
370 self._port = port 354 self._port = port
371 self._status = status 355 self._status = status
372 self._request_type = request_type 356 self._request_type = request_type
373 self._flags = ParseFlags() 357 self._flags = ParseFlags()
358 # Make all header names lower case.
359 for name in response_headers:
360 self._response_headers[name.lower()] = response_headers[name]
361 for name in request_headers:
362 self._request_headers[name.lower()] = request_headers[name]
374 363
375 def __str__(self): 364 def __str__(self):
376 self_dict = { 365 self_dict = {
377 'response_headers': self._response_headers, 366 'response_headers': self._response_headers,
378 'request_headers': self._request_headers, 367 'request_headers': self._request_headers,
379 'url': self._url, 368 'url': self._url,
380 'protocol': self._protocol, 369 'protocol': self._protocol,
381 'port': self._port, 370 'port': self._port,
382 'status': self._status, 371 'status': self._status,
383 'request_type': self._request_type 372 'request_type': self._request_type
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 self._flags.via_header_value) 406 self._flags.via_header_value)
418 407
419 def WasXHR(self): 408 def WasXHR(self):
420 return self.request_type == 'XHR' 409 return self.request_type == 'XHR'
421 410
422 def UsedHTTP(self): 411 def UsedHTTP(self):
423 return self._protocol == 'http/1.1' 412 return self._protocol == 'http/1.1'
424 413
425 def UsedHTTP2(self): 414 def UsedHTTP2(self):
426 return self._protocol == 'h2' 415 return self._protocol == 'h2'
416
417 class IntegrationTest(unittest.TestCase):
418 """This class adds ChromeProxy-specific assertions to the generic
419 unittest.TestCase class.
420 """
421
422 def assertHasChromeProxyViaHeader(self, http_response):
423 """Asserts that the Via header in the given HTTPResponse matches the
424 expected value as given on the command line.
425
426 Args:
427 http_response: The HTTPResponse object to check.
428 """
429 expected_via_header = ParseFlags().via_header_value
430 self.assertIn('via', http_response.response_headers)
431 self.assertEqual(expected_via_header, http_response.response_headers['via'])
432
433 def assertNotHasChromeProxyViaHeader(self, http_response):
434 """Asserts that the Via header in the given HTTPResponse does not match the
435 expected value as given on the command line.
436
437 Args:
438 http_response: The HTTPResponse object to check.
439 """
440 expected_via_header = ParseFlags().via_header_value
441 self.assertNotIn('via', http_response.response_headers)
442 if 'via' in http_response.response_headers:
443 self.assertNotIn(expected_via_header,
444 http_response.response_headers['via'])
445
446 @staticmethod
447 def RunAllTests():
448 """A simple helper method to run all tests using unittest.main().
449 """
450 # The unittest library uses sys.argv itself and is easily confused by our
451 # command line options. Pass it a simpler argv instead, while working in the
452 # unittest command line args functionality.
453 flags = ParseFlags()
454 unittest.main(argv=[sys.argv[0]], verbosity=2, failfast=flags.failfast,
455 catchbreak=flags.catch, buffer=(not flags.disable_buffer))
OLDNEW
« no previous file with comments | « no previous file | tools/chrome_proxy/webdriver/simple_smoke.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698