| 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 os | 7 import os |
| 8 import re | 8 import re |
| 9 import socket | 9 import socket |
| 10 import shlex | 10 import shlex |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 'be considered valid') | 35 'be considered valid') |
| 36 parser.add_argument('--android', help='If given, attempts to run the test on ' | 36 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') | 37 'Android via adb. Ignores usage of --chrome_exec', action='store_true') |
| 38 parser.add_argument('--android_package', nargs=1, | 38 parser.add_argument('--android_package', nargs=1, |
| 39 default='com.android.chrome', help='Set the android package for Chrome') | 39 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 ' | 40 parser.add_argument('--chrome_exec', nargs=1, type=str, help='The path to ' |
| 41 'the Chrome or Chromium executable') | 41 'the Chrome or Chromium executable') |
| 42 parser.add_argument('chrome_driver', nargs=1, type=str, help='The path to ' | 42 parser.add_argument('chrome_driver', nargs=1, type=str, help='The path to ' |
| 43 'the ChromeDriver executable. If not given, the default system chrome ' | 43 'the ChromeDriver executable. If not given, the default system chrome ' |
| 44 'will be used.') | 44 'will be used.') |
| 45 parser.add_argument('-b', '--buffer', help='The standard output and standard ' | 45 parser.add_argument('--disable_buffer', help='Causes stdout and stderr from ' |
| 46 'error streams are buffered during the test run. Output during a passing ' | 46 'tests to output normally. Otherwise, the standard output and standard ' |
| 47 'test is discarded. Output is echoed normally on test fail or error and is ' | 47 'error streams are buffered during the test run, and output during a ' |
| 48 'added to the failure messages.', action='store_true') | 48 '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') |
| 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 # TODO(robertogden): Log sys.argv here. | 56 # TODO(robertogden): Log sys.argv here. |
| 56 return parser.parse_args(sys.argv[1:]) | 57 return parser.parse_args(sys.argv[1:]) |
| 57 | 58 |
| 58 def HandleException(test_name=None): | 59 def HandleException(test_name=None): |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 all_messages = [] | 301 all_messages = [] |
| 301 for log in self._driver.execute('getLog', {'type': 'performance'})['value']: | 302 for log in self._driver.execute('getLog', {'type': 'performance'})['value']: |
| 302 message = json.loads(log['message'])['message'] | 303 message = json.loads(log['message'])['message'] |
| 303 if re.match(method_filter, message['method']): | 304 if re.match(method_filter, message['method']): |
| 304 all_messages.append(message) | 305 all_messages.append(message) |
| 305 return all_messages | 306 return all_messages |
| 306 | 307 |
| 307 def GetHTTPResponses(self, include_favicon=False): | 308 def GetHTTPResponses(self, include_favicon=False): |
| 308 """Parses the Performance Logs and returns a list of HTTPResponse objects. | 309 """Parses the Performance Logs and returns a list of HTTPResponse objects. |
| 309 | 310 |
| 310 This function should be called exactly once after every page load. | 311 Use caution when calling this function multiple times. Only responses |
| 312 since the last time this function was called are returned (or since Chrome |
| 313 started, whichever is later). |
| 311 | 314 |
| 312 Args: | 315 Args: |
| 313 include_favicon: A bool that if True will include responses for favicons. | 316 include_favicon: A bool that if True will include responses for favicons. |
| 314 Returns: | 317 Returns: |
| 315 A list of HTTPResponse objects, each representing a single completed HTTP | 318 A list of HTTPResponse objects, each representing a single completed HTTP |
| 316 transaction by Chrome. | 319 transaction by Chrome. |
| 317 """ | 320 """ |
| 318 def MakeHTTPResponse(log_dict): | 321 def MakeHTTPResponse(log_dict): |
| 319 params = log_dict['params'] | 322 params = log_dict['params'] |
| 320 response_dict = params['response'] | 323 response_dict = params['response'] |
| 321 http_response_dict = { | 324 http_response_dict = { |
| 322 'response_headers': response_dict['headers'], | 325 'response_headers': response_dict['headers'] if 'headers' in |
| 323 'request_headers': response_dict['requestHeaders'], | 326 response_dict else {}, |
| 324 'url': response_dict['url'], | 327 'request_headers': response_dict['requestHeaders'] if 'requestHeaders' |
| 325 'status': response_dict['status'], | 328 in response_dict else {}, |
| 326 'request_type': params['type'] | 329 'url': response_dict['url'] if 'url' in response_dict else '', |
| 330 'protocol': response_dict['protocol'] if 'protocol' in response_dict |
| 331 else '', |
| 332 'port': response_dict['remotePort'] if 'remotePort' in response_dict |
| 333 else -1, |
| 334 'status': response_dict['status'] if 'status' in response_dict else -1, |
| 335 'request_type': params['type'] if 'type' in params else '' |
| 327 } | 336 } |
| 328 return HTTPResponse(**http_response_dict) | 337 return HTTPResponse(**http_response_dict) |
| 329 all_responses = [] | 338 all_responses = [] |
| 330 for message in self.GetPerformanceLogs(): | 339 for message in self.GetPerformanceLogs(): |
| 331 response = MakeHTTPResponse(message) | 340 response = MakeHTTPResponse(message) |
| 332 is_favicon = response.url.endswith('favicon.ico') | 341 is_favicon = response.url.endswith('favicon.ico') |
| 333 if not is_favicon or include_favicon: | 342 if not is_favicon or include_favicon: |
| 334 all_responses.append(response) | 343 all_responses.append(response) |
| 335 return all_responses | 344 return all_responses |
| 336 | 345 |
| 337 class HTTPResponse: | 346 class HTTPResponse: |
| 338 """This class represents a single HTTP transaction (request and response) by | 347 """This class represents a single HTTP transaction (request and response) by |
| 339 Chrome. | 348 Chrome. |
| 340 | 349 |
| 341 This class also includes several convenience functions for ChromeProxy | 350 This class also includes several convenience functions for ChromeProxy |
| 342 specific assertions. | 351 specific assertions. |
| 343 | 352 |
| 344 Attributes: | 353 Attributes: |
| 345 _response_headers: A dict of response headers. | 354 _response_headers: A dict of response headers. |
| 346 _request_headers: A dict of request headers. | 355 _request_headers: A dict of request headers. |
| 347 _url: the fetched url | 356 _url: the fetched url |
| 357 _protocol: The protocol used to get the response. |
| 358 _port: The remote port number used to get the response. |
| 348 _status: The integer status code of the response | 359 _status: The integer status code of the response |
| 349 _request_type: What caused this request (Document, XHR, etc) | 360 _request_type: What caused this request (Document, XHR, etc) |
| 350 _flags: A Namespace object from ParseFlags() | 361 _flags: A Namespace object from ParseFlags() |
| 351 """ | 362 """ |
| 352 | 363 |
| 353 def __init__(self, response_headers, request_headers, url, status, | 364 def __init__(self, response_headers, request_headers, url, protocol, port, |
| 354 request_type): | 365 status, request_type): |
| 355 self._response_headers = response_headers | 366 self._response_headers = response_headers |
| 356 self._request_headers = request_headers | 367 self._request_headers = request_headers |
| 357 self._url = url | 368 self._url = url |
| 369 self._protocol = protocol |
| 370 self._port = port |
| 358 self._status = status | 371 self._status = status |
| 359 self._request_type = request_type | 372 self._request_type = request_type |
| 360 self._flags = ParseFlags() | 373 self._flags = ParseFlags() |
| 361 | 374 |
| 362 def __str__(self): | 375 def __str__(self): |
| 363 self_dict = { | 376 self_dict = { |
| 364 'response_headers': self._response_headers, | 377 'response_headers': self._response_headers, |
| 365 'request_headers': self._request_headers, | 378 'request_headers': self._request_headers, |
| 366 'url': self._url, | 379 'url': self._url, |
| 380 'protocol': self._protocol, |
| 381 'port': self._port, |
| 367 'status': self._status, | 382 'status': self._status, |
| 368 'request_type': self._request_type | 383 'request_type': self._request_type |
| 369 } | 384 } |
| 370 return json.dumps(self_dict) | 385 return json.dumps(self_dict) |
| 371 | 386 |
| 372 @property | 387 @property |
| 373 def response_headers(self): | 388 def response_headers(self): |
| 374 return self._response_headers | 389 return self._response_headers |
| 375 | 390 |
| 376 @property | 391 @property |
| 377 def request_headers(self): | 392 def request_headers(self): |
| 378 return self._request_headers | 393 return self._request_headers |
| 379 | 394 |
| 380 @property | 395 @property |
| 381 def url(self): | 396 def url(self): |
| 382 return self._url | 397 return self._url |
| 383 | 398 |
| 384 @property | 399 @property |
| 400 def protocol(self): |
| 401 return self._protocol |
| 402 |
| 403 @property |
| 404 def port(self): |
| 405 return self._port |
| 406 |
| 407 @property |
| 385 def status(self): | 408 def status(self): |
| 386 return self._status | 409 return self._status |
| 387 | 410 |
| 388 @property | 411 @property |
| 389 def request_type(self): | 412 def request_type(self): |
| 390 return self._request_type | 413 return self._request_type |
| 391 | 414 |
| 392 def ResponseHasViaHeader(self): | 415 def ResponseHasViaHeader(self): |
| 393 return 'via' in self._response_headers and (self._response_headers['via'] == | 416 return 'via' in self._response_headers and (self._response_headers['via'] == |
| 394 self._flags.via_header_value) | 417 self._flags.via_header_value) |
| 395 | 418 |
| 396 def WasXHR(self): | 419 def WasXHR(self): |
| 397 return self.request_type == 'XHR' | 420 return self.request_type == 'XHR' |
| 421 |
| 422 def UsedHTTP(self): |
| 423 return self._protocol == 'http/1.1' |
| 424 |
| 425 def UsedHTTP2(self): |
| 426 return self._protocol == 'h2' |
| OLD | NEW |