Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 logging | 7 import logging |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 import socket | 10 import socket |
| 11 import shlex | 11 import shlex |
| 12 import sys | 12 import sys |
| 13 import time | 13 import time |
| 14 import traceback | 14 import traceback |
| 15 import unittest | 15 import unittest |
| 16 import urlparse | |
| 16 | 17 |
| 17 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, | 18 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, |
| 18 os.pardir, 'third_party', 'webdriver', 'pylib')) | 19 os.pardir, 'third_party', 'webdriver', 'pylib')) |
| 19 from selenium import webdriver | 20 from selenium import webdriver |
| 20 from selenium.webdriver.chrome.options import Options | 21 from selenium.webdriver.chrome.options import Options |
| 21 | 22 |
| 22 def ParseFlags(): | 23 def ParseFlags(): |
| 23 """Parses the given command line arguments. | 24 """Parses the given command line arguments. |
| 24 | 25 |
| 25 Returns: | 26 Returns: |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 46 'error streams are buffered during the test run, and output from a ' | 47 'error streams are buffered during the test run, and output from a ' |
| 47 'passing test is discarded. Output will always be echoed normally on test ' | 48 'passing test is discarded. Output will always be echoed normally on test ' |
| 48 'fail or error and is added to the failure messages.', action='store_true') | 49 'fail or error and is added to the failure messages.', action='store_true') |
| 49 parser.add_argument('-c', '--catch', help='Control-C during the test run ' | 50 parser.add_argument('-c', '--catch', help='Control-C during the test run ' |
| 50 'waits for the current test to end and then reports all the results so ' | 51 'waits for the current test to end and then reports all the results so ' |
| 51 'far. A second Control-C raises the normal KeyboardInterrupt exception.', | 52 'far. A second Control-C raises the normal KeyboardInterrupt exception.', |
| 52 action='store_true') | 53 action='store_true') |
| 53 parser.add_argument('-f', '--failfast', help='Stop the test run on the first ' | 54 parser.add_argument('-f', '--failfast', help='Stop the test run on the first ' |
| 54 'error or failure.', action='store_true') | 55 'error or failure.', action='store_true') |
| 55 parser.add_argument('--logging_level', choices=['DEBUG', 'INFO', 'WARN', | 56 parser.add_argument('--logging_level', choices=['DEBUG', 'INFO', 'WARN', |
| 56 'ERROR', 'CRIT'], default='ERROR', help='The logging verbosity for log ' | 57 'ERROR', 'CRIT'], default='WARN', help='The logging verbosity for log ' |
| 57 'messages, printed to stderr. To see stderr logging output during a ' | 58 'messages, printed to stderr. To see stderr logging output during a ' |
| 58 'successful test run, also pass --disable_buffer. Default=ERROR') | 59 'successful test run, also pass --disable_buffer. Default=ERROR') |
| 59 parser.add_argument('--log_file', help='If given, write logging statements ' | 60 parser.add_argument('--log_file', help='If given, write logging statements ' |
| 60 'to the given file instead of stderr.') | 61 'to the given file instead of stderr.') |
| 61 return parser.parse_args(sys.argv[1:]) | 62 return parser.parse_args(sys.argv[1:]) |
| 62 | 63 |
| 63 def GetLogger(name='common'): | 64 def GetLogger(name='common'): |
| 64 """Creates a Logger instance with the given name and returns it. | 65 """Creates a Logger instance with the given name and returns it. |
| 65 | 66 |
| 66 If a logger has already been created with the same name, that instance is | 67 If a logger has already been created with the same name, that instance is |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 253 | 254 |
| 254 def LoadURL(self, url, timeout=30): | 255 def LoadURL(self, url, timeout=30): |
| 255 """Starts Chromium with any arguments previously given and navigates to the | 256 """Starts Chromium with any arguments previously given and navigates to the |
| 256 given URL. | 257 given URL. |
| 257 | 258 |
| 258 Args: | 259 Args: |
| 259 url: The URL to navigate to. | 260 url: The URL to navigate to. |
| 260 timeout: The time in seconds to load the page before timing out. | 261 timeout: The time in seconds to load the page before timing out. |
| 261 """ | 262 """ |
| 262 self._url = url | 263 self._url = url |
| 264 if len(urlparse.urlparse(url).netloc) == 0: | |
| 265 self._logger.warn('No net_loc given in %s. See RFC 1808', url) | |
|
sclittle
2016/12/19 22:38:15
nit: could you make this message a bit clearer? e.
Robert Ogden
2016/12/19 22:56:44
Done.
| |
| 263 if not self._driver: | 266 if not self._driver: |
| 264 self._StartDriver() | 267 self._StartDriver() |
| 265 self._driver.set_page_load_timeout(timeout) | 268 self._driver.set_page_load_timeout(timeout) |
| 266 self._logger.debug('Set page load timeout to %f seconds', timeout) | 269 self._logger.debug('Set page load timeout to %f seconds', timeout) |
| 267 self._driver.get(self._url) | 270 self._driver.get(self._url) |
| 268 self._logger.debug('Loaded page %s', url) | 271 self._logger.debug('Loaded page %s', url) |
| 269 | 272 |
| 270 def ExecuteJavascript(self, script, timeout=30): | 273 def ExecuteJavascript(self, script, timeout=30): |
| 271 """Executes the given javascript in the browser's current page in an | 274 """Executes the given javascript in the browser's current page in an |
| 272 anonymous function. | 275 anonymous function. |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 all_messages = [] | 353 all_messages = [] |
| 351 for log in self._driver.execute('getLog', {'type': 'performance'})['value']: | 354 for log in self._driver.execute('getLog', {'type': 'performance'})['value']: |
| 352 message = json.loads(log['message'])['message'] | 355 message = json.loads(log['message'])['message'] |
| 353 self._logger.debug('Got Performance log: %s', log['message']) | 356 self._logger.debug('Got Performance log: %s', log['message']) |
| 354 if re.match(method_filter, message['method']): | 357 if re.match(method_filter, message['method']): |
| 355 all_messages.append(message) | 358 all_messages.append(message) |
| 356 self._logger.info('Got %d performance logs with filter method=%s', | 359 self._logger.info('Got %d performance logs with filter method=%s', |
| 357 len(all_messages), method_filter) | 360 len(all_messages), method_filter) |
| 358 return all_messages | 361 return all_messages |
| 359 | 362 |
| 360 def GetHTTPResponses(self, include_favicon=False): | 363 def GetHTTPResponses(self, include_favicon=False, skip_domainless_pages=True): |
| 361 """Parses the Performance Logs and returns a list of HTTPResponse objects. | 364 """Parses the Performance Logs and returns a list of HTTPResponse objects. |
| 362 | 365 |
| 363 Use caution when calling this function multiple times. Only responses | 366 Use caution when calling this function multiple times. Only responses |
| 364 since the last time this function was called are returned (or since Chrome | 367 since the last time this function was called are returned (or since Chrome |
| 365 started, whichever is later). | 368 started, whichever is later). |
| 366 | 369 |
| 367 Args: | 370 Args: |
| 368 include_favicon: A bool that if True will include responses for favicons. | 371 include_favicon: A bool that if True will include responses for favicons. |
| 372 skip_domainless_pages: If True, only responses with a net_loc as in RFC | |
| 373 1808 will be included. Pages such as about:blank will be skipped. | |
| 369 Returns: | 374 Returns: |
| 370 A list of HTTPResponse objects, each representing a single completed HTTP | 375 A list of HTTPResponse objects, each representing a single completed HTTP |
| 371 transaction by Chrome. | 376 transaction by Chrome. |
| 372 """ | 377 """ |
| 373 def MakeHTTPResponse(log_dict): | 378 def MakeHTTPResponse(log_dict): |
| 374 params = log_dict['params'] | 379 params = log_dict['params'] |
| 375 response_dict = params['response'] | 380 response_dict = params['response'] |
| 376 http_response_dict = { | 381 http_response_dict = { |
| 377 'response_headers': response_dict['headers'] if 'headers' in | 382 'response_headers': response_dict['headers'] if 'headers' in |
| 378 response_dict else {}, | 383 response_dict else {}, |
| 379 'request_headers': response_dict['requestHeaders'] if 'requestHeaders' | 384 'request_headers': response_dict['requestHeaders'] if 'requestHeaders' |
| 380 in response_dict else {}, | 385 in response_dict else {}, |
| 381 'url': response_dict['url'] if 'url' in response_dict else '', | 386 'url': response_dict['url'] if 'url' in response_dict else '', |
| 382 'protocol': response_dict['protocol'] if 'protocol' in response_dict | 387 'protocol': response_dict['protocol'] if 'protocol' in response_dict |
| 383 else '', | 388 else '', |
| 384 'port': response_dict['remotePort'] if 'remotePort' in response_dict | 389 'port': response_dict['remotePort'] if 'remotePort' in response_dict |
| 385 else -1, | 390 else -1, |
| 386 'status': response_dict['status'] if 'status' in response_dict else -1, | 391 'status': response_dict['status'] if 'status' in response_dict else -1, |
| 387 'request_type': params['type'] if 'type' in params else '' | 392 'request_type': params['type'] if 'type' in params else '' |
| 388 } | 393 } |
| 389 return HTTPResponse(**http_response_dict) | 394 return HTTPResponse(**http_response_dict) |
| 390 all_responses = [] | 395 all_responses = [] |
| 391 for message in self.GetPerformanceLogs(): | 396 for message in self.GetPerformanceLogs(): |
| 392 response = MakeHTTPResponse(message) | 397 response = MakeHTTPResponse(message) |
| 393 self._logger.debug('New HTTPResponse: %s', str(response)) | 398 self._logger.debug('New HTTPResponse: %s', str(response)) |
| 394 is_favicon = response.url.endswith('favicon.ico') | 399 is_favicon = response.url.endswith('favicon.ico') |
| 395 if not is_favicon or include_favicon: | 400 has_domain = len(urlparse.urlparse(response.url).netloc) > 0 |
| 401 if (not is_favicon or include_favicon) and (not skip_domainless_pages or | |
| 402 has_domain): | |
| 396 all_responses.append(response) | 403 all_responses.append(response) |
| 404 else: | |
| 405 self._logger.info("Skipping HTTPResponse with url=%s in returned logs.", | |
| 406 response.url) | |
| 397 self._logger.info('%d new HTTPResponse objects found in the logs %s ' | 407 self._logger.info('%d new HTTPResponse objects found in the logs %s ' |
| 398 'favicons', len(all_responses), ('including' if include_favicon else | 408 'favicons', len(all_responses), ('including' if include_favicon else |
| 399 'not including')) | 409 'not including')) |
| 400 return all_responses | 410 return all_responses |
| 401 | 411 |
| 402 class HTTPResponse: | 412 class HTTPResponse: |
| 403 """This class represents a single HTTP transaction (request and response) by | 413 """This class represents a single HTTP transaction (request and response) by |
| 404 Chrome. | 414 Chrome. |
| 405 | 415 |
| 406 This class also includes several convenience functions for ChromeProxy | 416 This class also includes several convenience functions for ChromeProxy |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 521 """ | 531 """ |
| 522 flags = ParseFlags() | 532 flags = ParseFlags() |
| 523 logger = GetLogger() | 533 logger = GetLogger() |
| 524 logger.debug('Command line args: %s', str(sys.argv)) | 534 logger.debug('Command line args: %s', str(sys.argv)) |
| 525 logger.info('sys.argv parsed to %s', str(flags)) | 535 logger.info('sys.argv parsed to %s', str(flags)) |
| 526 # The unittest library uses sys.argv itself and is easily confused by our | 536 # The unittest library uses sys.argv itself and is easily confused by our |
| 527 # command line options. Pass it a simpler argv instead, while working in the | 537 # command line options. Pass it a simpler argv instead, while working in the |
| 528 # unittest command line args functionality. | 538 # unittest command line args functionality. |
| 529 unittest.main(argv=[sys.argv[0]], verbosity=2, failfast=flags.failfast, | 539 unittest.main(argv=[sys.argv[0]], verbosity=2, failfast=flags.failfast, |
| 530 catchbreak=flags.catch, buffer=(not flags.disable_buffer)) | 540 catchbreak=flags.catch, buffer=(not flags.disable_buffer)) |
| OLD | NEW |