Index: tools/chrome_proxy/webdriver/common.py |
diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py |
index 45585eb19466fba72327b7cd7ac4483f887b4660..05b2c89dc0e0bc453b78d2f0d2ce3cf131d8cc49 100644 |
--- a/tools/chrome_proxy/webdriver/common.py |
+++ b/tools/chrome_proxy/webdriver/common.py |
@@ -4,6 +4,7 @@ |
import argparse |
import json |
+import logging |
import os |
import re |
import socket |
@@ -18,9 +19,6 @@ sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, |
from selenium import webdriver |
from selenium.webdriver.chrome.options import Options |
-# TODO(robertogden): Add logging. |
- |
- |
def ParseFlags(): |
"""Parses the given command line arguments. |
@@ -45,7 +43,7 @@ def ParseFlags(): |
'will be used.') |
parser.add_argument('--disable_buffer', help='Causes stdout and stderr from ' |
'tests to output normally. Otherwise, the standard output and standard ' |
- 'error streams are buffered during the test run, and output during a ' |
+ 'error streams are buffered during the test run, and output from a ' |
'passing test is discarded. Output will always be echoed normally on test ' |
'fail or error and is added to the failure messages.', action='store_true') |
parser.add_argument('-c', '--catch', help='Control-C during the test run ' |
@@ -54,9 +52,53 @@ def ParseFlags(): |
action='store_true') |
parser.add_argument('-f', '--failfast', help='Stop the test run on the first ' |
'error or failure.', action='store_true') |
- # TODO(robertogden): Log sys.argv here. |
+ parser.add_argument('--logging_level', choices=['DEBUG', 'INFO', 'WARN', |
+ 'ERROR', 'CRIT'], default='ERROR', help='The logging verbosity for log ' |
+ 'messages, printed to stderr. To see stderr logging output during a ' |
+ 'successful test run, also pass --disable_buffer. Default=ERROR') |
+ parser.add_argument('--log_file', help='If given, write logging statements ' |
+ 'to the given file instead of stderr.') |
return parser.parse_args(sys.argv[1:]) |
+def GetLogger(name='common'): |
+ """Creates a Logger instance with the given name and returns it. |
+ |
+ If a logger has already been created with the same name, that instance is |
+ returned instead. |
+ |
+ Args: |
+ name: The name of the logger to return. |
+ Returns: |
+ A logger with the given name. |
+ """ |
+ logger = logging.getLogger(name) |
+ if hasattr(logger, "initialized"): |
+ return logger |
+ logging_level = ParseFlags().logging_level |
+ if logging_level == 'DEBUG': |
+ logger.setLevel(logging.DEBUG) |
+ elif logging_level == 'INFO': |
+ logger.setLevel(logging.INFO) |
+ elif logging_level == 'WARN': |
+ logger.setLevel(logging.WARNING) |
+ elif logging_level == 'ERROR': |
+ logger.setLevel(logging.ERROR) |
+ elif logging_level == 'CRIT': |
+ logger.setLevel(logging.CRITICAL) |
+ else: |
+ logger.setLevel(logging.NOTSET) |
+ formatter = logging.Formatter('%(asctime)s %(funcName)s() %(levelname)s: ' |
+ '%(message)s') |
+ if ParseFlags().log_file: |
+ fh = logging.FileHandler(ParseFlags().log_file) |
+ fh.setFormatter(formatter) |
+ logger.addHandler(fh) |
+ else: |
+ ch = logging.StreamHandler(sys.stderr) |
+ ch.setFormatter(formatter) |
+ logger.addHandler(ch) |
+ logger.initialized = True |
+ return logger |
class TestDriver: |
"""The main driver for an integration test. |
@@ -78,6 +120,7 @@ class TestDriver: |
self._driver = None |
self._chrome_args = set() |
self._url = '' |
+ self._logger = GetLogger(name='TestDriver') |
def __enter__(self): |
return self |
@@ -106,7 +149,9 @@ class TestDriver: |
arg_key = GetDictKey(override_arg) |
if arg_key in original_args: |
self._chrome_args.remove(original_args[arg_key]) |
+ self._logger.info('Removed Chrome flag. %s', original_args[arg_key]) |
self._chrome_args.add(override_arg) |
+ self._logger.info('Added Chrome flag. %s', override_arg) |
# Always add the flag that allows histograms to be queried in javascript. |
self._chrome_args.add('--enable-stats-collection-bindings') |
@@ -126,8 +171,15 @@ class TestDriver: |
capabilities['chromeOptions'].update({ |
'androidPackage': self._flags.android_package, |
}) |
+ self._logger.debug('Will use Chrome on Android') |
elif self._flags.chrome_exec: |
capabilities['chrome.binary'] = self._flags.chrome_exec |
+ self._logger.info('Using the Chrome binary at this path: %s', |
+ self._flags.chrome_exec) |
+ self._logger.info('Starting Chrome with these flags: %s', |
+ str(self._chrome_args)) |
+ self._logger.debug('Starting ChromeDriver with these capabilities: %s', |
+ json.dumps(capabilities)) |
driver = webdriver.Chrome(executable_path=self._flags.chrome_driver, |
desired_capabilities=capabilities) |
driver.command_executor._commands.update({ |
@@ -138,6 +190,7 @@ class TestDriver: |
def _StopDriver(self): |
"""Nicely stops the ChromeDriver. |
""" |
+ self._logger.debug('Stopping ChromeDriver') |
self._driver.quit() |
self._driver = None |
@@ -157,6 +210,7 @@ class TestDriver: |
arg: a string argument to pass to Chrome at start |
""" |
self._chrome_args.add(arg) |
+ self._logger.debug('Adding Chrome arg: %s', arg) |
def RemoveChromeArgs(self, args): |
"""Removes multiple arguments that will no longer be passed to Chromium at |
@@ -177,11 +231,13 @@ class TestDriver: |
arg: A string flag to no longer use the next time Chrome starts. |
""" |
self._chrome_args.discard(arg) |
+ self._logger.debug('Removing Chrome arg: %s', arg) |
def ClearChromeArgs(self): |
"""Removes all arguments from Chromium at start. |
""" |
self._chrome_args.clear() |
+ self._logger.debug('Clearing all Chrome args') |
def ClearCache(self): |
"""Clears the browser cache. |
@@ -189,10 +245,11 @@ class TestDriver: |
Important note: ChromeDriver automatically starts |
a clean copy of Chrome on every instantiation. |
""" |
- self.ExecuteJavascript('if(window.chrome && chrome.benchmarking && ' |
+ res = self.ExecuteJavascript('if(window.chrome && chrome.benchmarking && ' |
'chrome.benchmarking.clearCache){chrome.benchmarking.clearCache(); ' |
'chrome.benchmarking.clearPredictorCache();chrome.benchmarking.' |
'clearHostResolverCache();}') |
+ self._logger.info('Cleared browser cache. Returned=%s', str(res)) |
def LoadURL(self, url, timeout=30): |
"""Starts Chromium with any arguments previously given and navigates to the |
@@ -206,7 +263,9 @@ class TestDriver: |
if not self._driver: |
self._StartDriver() |
self._driver.set_page_load_timeout(timeout) |
+ self._logger.debug('Set page load timeout to %f seconds', timeout) |
self._driver.get(self._url) |
+ self._logger.debug('Loaded page %s', url) |
def ExecuteJavascript(self, script, timeout=30): |
"""Executes the given javascript in the browser's current page in an |
@@ -227,8 +286,11 @@ class TestDriver: |
# crbug/672114 is fixed. |
default_timeout = socket.getdefaulttimeout() |
socket.setdefaulttimeout(timeout) |
+ self._logger.debug('Set socket timeout to %f seconds', timeout) |
script_result = self._driver.execute_script(script) |
+ self._logger.debug('Executed Javascript in browser: %s', script) |
socket.setdefaulttimeout(default_timeout) |
+ self._logger.debug('Set socket timeout to %s', str(default_timeout)) |
return script_result |
def ExecuteJavascriptStatement(self, script, timeout=30): |
@@ -247,6 +309,7 @@ class TestDriver: |
def GetHistogram(self, histogram): |
js_query = 'statsCollectionController.getBrowserHistogram("%s")' % histogram |
string_response = self.ExecuteJavascriptStatement(js_query) |
+ self._logger.debug('Got %s histogram=%s', histogram, string_response) |
return json.loads(string_response) |
def WaitForJavascriptExpression(self, expression, timeout, min_poll=0.1, |
@@ -262,6 +325,7 @@ class TestDriver: |
Returns: The result of the expression. |
""" |
poll_interval = max(min(max_poll, float(timeout) / 10.0), min_poll) |
+ self._logger.debug('Poll interval=%f seconds', poll_interval) |
result = self.ExecuteJavascriptStatement(expression) |
total_waited_time = 0 |
while not result and total_waited_time < timeout: |
@@ -269,6 +333,7 @@ class TestDriver: |
total_waited_time += poll_interval |
result = self.ExecuteJavascriptStatement(expression) |
if not result: |
+ self._logger.error('%s not true after %f seconds' % (expression, timeout)) |
raise Exception('%s not true after %f seconds' % (expression, timeout)) |
return result |
@@ -285,8 +350,11 @@ class TestDriver: |
all_messages = [] |
for log in self._driver.execute('getLog', {'type': 'performance'})['value']: |
message = json.loads(log['message'])['message'] |
+ self._logger.debug('Got Performance log: %s', log['message']) |
if re.match(method_filter, message['method']): |
all_messages.append(message) |
+ self._logger.info('Got %d performance logs with filter method=%s', |
+ len(all_messages), method_filter) |
return all_messages |
def GetHTTPResponses(self, include_favicon=False): |
@@ -322,9 +390,13 @@ class TestDriver: |
all_responses = [] |
for message in self.GetPerformanceLogs(): |
response = MakeHTTPResponse(message) |
+ self._logger.debug('New HTTPResponse: %s', str(response)) |
is_favicon = response.url.endswith('favicon.ico') |
if not is_favicon or include_favicon: |
all_responses.append(response) |
+ self._logger.info('%d new HTTPResponse objects found in the logs %s ' |
+ 'favicons', len(all_responses), ('including' if include_favicon else |
+ 'not including')) |
return all_responses |
class HTTPResponse: |
@@ -447,9 +519,12 @@ class IntegrationTest(unittest.TestCase): |
def RunAllTests(): |
"""A simple helper method to run all tests using unittest.main(). |
""" |
+ flags = ParseFlags() |
+ logger = GetLogger() |
+ logger.debug('Command line args: %s', str(sys.argv)) |
+ logger.info('sys.argv parsed to %s', str(flags)) |
# The unittest library uses sys.argv itself and is easily confused by our |
# command line options. Pass it a simpler argv instead, while working in the |
# unittest command line args functionality. |
- flags = ParseFlags() |
unittest.main(argv=[sys.argv[0]], verbosity=2, failfast=flags.failfast, |
catchbreak=flags.catch, buffer=(not flags.disable_buffer)) |