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 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 ' | |
|
RyanSturm
2016/12/13 21:30:11
These are awesome. Can you make the buffer one def
Robert Ogden
2016/12/13 21:51:26
Done.
| |
| 46 'error streams are buffered during the test run. Output during a passing ' | |
| 47 'test is discarded. Output is echoed normally on test fail or error and is ' | |
| 48 'added to the failure messages.', action='store_true') | |
| 49 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 'far. A second Control-C raises the normal KeyboardInterrupt exception.', | |
| 52 action='store_true') | |
| 53 parser.add_argument('-f', '--failfast', help='Stop the test run on the first ' | |
| 54 'error or failure.', action='store_true') | |
| 45 # TODO(robertogden): Log sys.argv here. | 55 # TODO(robertogden): Log sys.argv here. |
| 46 return parser.parse_args(sys.argv[1:]) | 56 return parser.parse_args(sys.argv[1:]) |
| 47 | 57 |
| 48 def HandleException(test_name=None): | 58 def HandleException(test_name=None): |
| 49 """Writes the exception being handled and a stack trace to stderr. | 59 """Writes the exception being handled and a stack trace to stderr. |
| 50 | 60 |
| 51 Args: | 61 Args: |
| 52 test_name: The string name of the test that led to this exception. | 62 test_name: The string name of the test that led to this exception. |
| 53 """ | 63 """ |
| 54 sys.stderr.write("**************************************\n") | 64 sys.stderr.write("**************************************\n") |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 290 all_messages = [] | 300 all_messages = [] |
| 291 for log in self._driver.execute('getLog', {'type': 'performance'})['value']: | 301 for log in self._driver.execute('getLog', {'type': 'performance'})['value']: |
| 292 message = json.loads(log['message'])['message'] | 302 message = json.loads(log['message'])['message'] |
| 293 if re.match(method_filter, message['method']): | 303 if re.match(method_filter, message['method']): |
| 294 all_messages.append(message) | 304 all_messages.append(message) |
| 295 return all_messages | 305 return all_messages |
| 296 | 306 |
| 297 def GetHTTPResponses(self, include_favicon=False): | 307 def GetHTTPResponses(self, include_favicon=False): |
| 298 """Parses the Performance Logs and returns a list of HTTPResponse objects. | 308 """Parses the Performance Logs and returns a list of HTTPResponse objects. |
| 299 | 309 |
| 300 This function should be called exactly once after every page load. | 310 This function should be called exactly once after every page load. |
|
RyanSturm
2016/12/13 21:30:11
nit: s/exactly once/at most once/
nit, maybe: You
Robert Ogden
2016/12/13 21:51:26
Clarified
| |
| 301 | 311 |
| 302 Args: | 312 Args: |
| 303 include_favicon: A bool that if True will include responses for favicons. | 313 include_favicon: A bool that if True will include responses for favicons. |
| 304 Returns: | 314 Returns: |
| 305 A list of HTTPResponse objects, each representing a single completed HTTP | 315 A list of HTTPResponse objects, each representing a single completed HTTP |
| 306 transaction by Chrome. | 316 transaction by Chrome. |
| 307 """ | 317 """ |
| 308 def MakeHTTPResponse(log_dict): | 318 def MakeHTTPResponse(log_dict): |
| 309 params = log_dict['params'] | 319 params = log_dict['params'] |
| 310 response_dict = params['response'] | 320 response_dict = params['response'] |
| 311 http_response_dict = { | 321 http_response_dict = { |
| 312 'response_headers': response_dict['headers'], | 322 'response_headers': response_dict['headers'] if 'headers' in |
| 313 'request_headers': response_dict['requestHeaders'], | 323 response_dict else {}, |
| 314 'url': response_dict['url'], | 324 'request_headers': response_dict['requestHeaders'] if 'requestHeaders' |
| 315 'status': response_dict['status'], | 325 in response_dict else {}, |
| 316 'request_type': params['type'] | 326 'url': response_dict['url'] if 'url' in response_dict else '', |
| 327 'protocol': response_dict['protocol'] if 'protocol' in response_dict | |
| 328 else '', | |
| 329 'port': response_dict['remotePort'] if 'remotePort' in response_dict | |
| 330 else -1, | |
| 331 'status': response_dict['status'] if 'status' in response_dict else -1, | |
| 332 'request_type': params['type'] if 'type' in params else '' | |
| 317 } | 333 } |
| 318 return HTTPResponse(**http_response_dict) | 334 return HTTPResponse(**http_response_dict) |
| 319 all_responses = [] | 335 all_responses = [] |
| 320 for message in self.GetPerformanceLogs(): | 336 for message in self.GetPerformanceLogs(): |
| 321 response = MakeHTTPResponse(message) | 337 response = MakeHTTPResponse(message) |
| 322 is_favicon = response.url.endswith('favicon.ico') | 338 is_favicon = response.url.endswith('favicon.ico') |
| 323 if not is_favicon or include_favicon: | 339 if not is_favicon or include_favicon: |
| 324 all_responses.append(response) | 340 all_responses.append(response) |
| 325 return all_responses | 341 return all_responses |
| 326 | 342 |
| 327 class HTTPResponse: | 343 class HTTPResponse: |
| 328 """This class represents a single HTTP transaction (request and response) by | 344 """This class represents a single HTTP transaction (request and response) by |
| 329 Chrome. | 345 Chrome. |
| 330 | 346 |
| 331 This class also includes several convenience functions for ChromeProxy | 347 This class also includes several convenience functions for ChromeProxy |
| 332 specific assertions. | 348 specific assertions. |
| 333 | 349 |
| 334 Attributes: | 350 Attributes: |
| 335 _response_headers: A dict of response headers. | 351 _response_headers: A dict of response headers. |
| 336 _request_headers: A dict of request headers. | 352 _request_headers: A dict of request headers. |
| 337 _url: the fetched url | 353 _url: the fetched url |
| 354 _protocol: The protocol used to get the response. | |
| 355 _port: The remote port number used to get the response. | |
| 338 _status: The integer status code of the response | 356 _status: The integer status code of the response |
| 339 _request_type: What caused this request (Document, XHR, etc) | 357 _request_type: What caused this request (Document, XHR, etc) |
| 340 _flags: A Namespace object from ParseFlags() | 358 _flags: A Namespace object from ParseFlags() |
| 341 """ | 359 """ |
| 342 | 360 |
| 343 def __init__(self, response_headers, request_headers, url, status, | 361 def __init__(self, response_headers, request_headers, url, protocol, port, |
| 344 request_type): | 362 status, request_type): |
| 345 self._response_headers = response_headers | 363 self._response_headers = response_headers |
| 346 self._request_headers = request_headers | 364 self._request_headers = request_headers |
| 347 self._url = url | 365 self._url = url |
| 366 self._protocol = protocol | |
| 367 self._port = port | |
| 348 self._status = status | 368 self._status = status |
| 349 self._request_type = request_type | 369 self._request_type = request_type |
| 350 self._flags = ParseFlags() | 370 self._flags = ParseFlags() |
| 351 | 371 |
| 352 def __str__(self): | 372 def __str__(self): |
| 353 self_dict = { | 373 self_dict = { |
| 354 'response_headers': self._response_headers, | 374 'response_headers': self._response_headers, |
| 355 'request_headers': self._request_headers, | 375 'request_headers': self._request_headers, |
| 356 'url': self._url, | 376 'url': self._url, |
| 377 'protocol': self._protocol, | |
| 378 'port': self._port, | |
| 357 'status': self._status, | 379 'status': self._status, |
| 358 'request_type': self._request_type | 380 'request_type': self._request_type |
| 359 } | 381 } |
| 360 return json.dumps(self_dict) | 382 return json.dumps(self_dict) |
| 361 | 383 |
| 362 @property | 384 @property |
| 363 def response_headers(self): | 385 def response_headers(self): |
| 364 return self._response_headers | 386 return self._response_headers |
| 365 | 387 |
| 366 @property | 388 @property |
| 367 def request_headers(self): | 389 def request_headers(self): |
| 368 return self._request_headers | 390 return self._request_headers |
| 369 | 391 |
| 370 @property | 392 @property |
| 371 def url(self): | 393 def url(self): |
| 372 return self._url | 394 return self._url |
| 373 | 395 |
| 374 @property | 396 @property |
| 397 def protocol(self): | |
| 398 return self._protocol | |
| 399 | |
| 400 @property | |
| 401 def port(self): | |
| 402 return self._port | |
| 403 | |
| 404 @property | |
| 375 def status(self): | 405 def status(self): |
| 376 return self._status | 406 return self._status |
| 377 | 407 |
| 378 @property | 408 @property |
| 379 def request_type(self): | 409 def request_type(self): |
| 380 return self._request_type | 410 return self._request_type |
| 381 | 411 |
| 382 def ResponseHasViaHeader(self): | 412 def ResponseHasViaHeader(self): |
| 383 return 'via' in self._response_headers and (self._response_headers['via'] == | 413 return 'via' in self._response_headers and (self._response_headers['via'] == |
| 384 self._flags.via_header_value) | 414 self._flags.via_header_value) |
| 385 | 415 |
| 386 def WasXHR(self): | 416 def WasXHR(self): |
| 387 return self.request_type == 'XHR' | 417 return self.request_type == 'XHR' |
| 418 | |
| 419 def UsedHTTP(self): | |
| 420 return self._protocol == 'http/1.1' | |
| 421 | |
| 422 def UsedHTTP2(self): | |
| 423 return self._protocol == 'h2' | |
| OLD | NEW |