blob: 3f0baeace033c23d125df2bd9f4e78514edcbcb0 [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import copy
import json
import sys
import unittest
from request_track import (TimeBetween, Request, CachingPolicy, RequestTrack,
Timing, _ParseStringToInt)
class TimeBetweenTestCase(unittest.TestCase):
_REQUEST = Request.FromJsonDict({'url': 'http://bla.com',
'request_id': '1234.1',
'frame_id': '123.1',
'initiator': {'type': 'other'},
'timestamp': 2,
'timing': {}})
def setUp(self):
super(TimeBetweenTestCase, self).setUp()
self.first = copy.deepcopy(self._REQUEST)
self.first.timing = Timing.FromDevToolsDict({'requestTime': 123456,
'receiveHeadersEnd': 100,
'loadingFinished': 500})
self.second = copy.deepcopy(self._REQUEST)
self.second.timing = Timing.FromDevToolsDict({'requestTime': 123456 + 1,
'receiveHeadersEnd': 200,
'loadingFinished': 600})
def testTimeBetweenParser(self):
self.assertEquals(900, TimeBetween(self.first, self.second, 'parser'))
def testTimeBetweenScript(self):
self.assertEquals(500, TimeBetween(self.first, self.second, 'script'))
class RequestTestCase(unittest.TestCase):
def testContentType(self):
r = Request()
r.response_headers = {}
self.assertEquals(None, r.GetContentType())
r.response_headers = {'Content-Type': 'application/javascript'}
self.assertEquals('application/javascript', r.GetContentType())
# Case-insensitive match.
r.response_headers = {'content-type': 'application/javascript'}
self.assertEquals('application/javascript', r.GetContentType())
# Parameters are filtered out.
r.response_headers = {'Content-Type': 'application/javascript;bla'}
self.assertEquals('application/javascript', r.GetContentType())
# MIME type takes precedence over 'Content-Type' header.
r.mime_type = 'image/webp'
self.assertEquals('image/webp', r.GetContentType())
r.mime_type = None
# Test for 'ping' type.
r.status = 204
self.assertEquals('ping', r.GetContentType())
r.status = None
r.response_headers = {'Content-Type': 'application/javascript',
'content-length': '0'}
self.assertEquals('ping', r.GetContentType())
# Test for 'redirect' type.
r.response_headers = {'Content-Type': 'application/javascript',
'location': 'http://foo',
'content-length': '0'}
self.assertEquals('redirect', r.GetContentType())
def testGetHTTPResponseHeader(self):
r = Request()
r.response_headers = {}
self.assertEquals(None, r.GetHTTPResponseHeader('Foo'))
r.response_headers = {'Foo': 'Bar', 'Baz': 'Foo'}
self.assertEquals('Bar', r.GetHTTPResponseHeader('Foo'))
r.response_headers = {'foo': 'Bar', 'Baz': 'Foo'}
self.assertEquals('Bar', r.GetHTTPResponseHeader('Foo'))
def testGetRawResponseHeaders(self):
r = Request()
r.protocol = 'http/1.1'
r.status = 200
r.status_text = 'Hello world'
r.response_headers = {'Foo': 'Bar', 'Baz': 'Foo'}
self.assertEquals('HTTP/1.1 200 Hello world\x00Baz: Foo\x00Foo: Bar\x00',
r.GetRawResponseHeaders())
class ParseStringToIntTestCase(unittest.TestCase):
def runTest(self):
MININT = -sys.maxint - 1
# Same test cases as in string_number_conversions_unittest.cc
CASES = [
("0", 0),
("42", 42),
("-2147483648", -2147483648),
("2147483647", 2147483647),
("-2147483649", -2147483649),
("-99999999999", -99999999999),
("2147483648", 2147483648),
("99999999999", 99999999999),
("9223372036854775807", sys.maxint),
("-9223372036854775808", MININT),
("09", 9),
("-09", -9),
("", 0),
(" 42", 42),
("42 ", 42),
("0x42", 0),
("\t\n\v\f\r 42", 42),
("blah42", 0),
("42blah", 42),
("blah42blah", 0),
("-273.15", -273),
("+98.6", 98),
("--123", 0),
("++123", 0),
("-+123", 0),
("+-123", 0),
("-", 0),
("-9223372036854775809", MININT),
("-99999999999999999999", MININT),
("9223372036854775808", sys.maxint),
("99999999999999999999", sys.maxint)]
for string, expected_int in CASES:
parsed_int = _ParseStringToInt(string)
self.assertEquals(expected_int, parsed_int)
class CachingPolicyTestCase(unittest.TestCase):
_REQUEST = {
'encoded_data_length': 14726,
'request_id': '2291.1',
'response_headers': {
'Age': '866',
'Content-Length': '14187',
'Date': 'Fri, 22 Apr 2016 08:56:19 -0200',
'Vary': 'Accept-Encoding',
},
'timestamp': 5535648.730768,
'timing': {
'connect_end': 34.0510001406074,
'connect_start': 21.6859998181462,
'dns_end': 21.6859998181462,
'dns_start': 0,
'loading_finished': 58.76399949193001,
'receive_headers_end': 47.0650000497699,
'request_time': 5535648.73264,
'send_end': 34.6099995076656,
'send_start': 34.2979999259114
},
'url': 'http://www.example.com/',
'status': 200,
'wall_time': 1461322579.59422}
def testHasValidators(self):
r = self._MakeRequest()
self.assertFalse(CachingPolicy(r).HasValidators())
r.response_headers['Last-Modified'] = 'Yesterday all my troubles'
self.assertTrue(CachingPolicy(r).HasValidators())
r = self._MakeRequest()
r.response_headers['ETAG'] = 'ABC'
self.assertTrue(CachingPolicy(r).HasValidators())
def testIsCacheable(self):
r = self._MakeRequest()
self.assertTrue(CachingPolicy(r).IsCacheable())
r.response_headers['Cache-Control'] = 'Whatever,no-store'
self.assertFalse(CachingPolicy(r).IsCacheable())
def testPolicyNoStore(self):
r = self._MakeRequest()
r.response_headers['Cache-Control'] = 'Whatever,no-store'
self.assertEqual(CachingPolicy.FETCH, CachingPolicy(r).PolicyAtDate(0))
def testPolicyMaxAge(self):
r = self._MakeRequest()
r.response_headers['Cache-Control'] = 'whatever,max-age= 1000,whatever'
self.assertEqual(
CachingPolicy.VALIDATION_NONE,
CachingPolicy(r).PolicyAtDate(r.wall_time))
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 10000))
# Take current age into account.
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 500))
# Max-Age before Expires.
r.response_headers['Expires'] = 'Thu, 21 Apr 2016 00:00:00 -0200'
self.assertEqual(
CachingPolicy.VALIDATION_NONE,
CachingPolicy(r).PolicyAtDate(r.wall_time))
# Max-Age < age
r.response_headers['Cache-Control'] = 'whatever,max-age=100crap,whatever'
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 2))
def testPolicyExpires(self):
r = self._MakeRequest()
# Already expired
r.response_headers['Expires'] = 'Thu, 21 Apr 2016 00:00:00 -0200'
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time))
r.response_headers['Expires'] = 'Thu, 25 Apr 2016 00:00:00 -0200'
self.assertEqual(
CachingPolicy.VALIDATION_NONE,\
CachingPolicy(r).PolicyAtDate(r.wall_time))
self.assertEqual(
CachingPolicy.VALIDATION_NONE,
CachingPolicy(r).PolicyAtDate(r.wall_time + 86400))
self.assertEqual(CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 86400 * 5))
def testStaleWhileRevalidate(self):
r = self._MakeRequest()
r.response_headers['Cache-Control'] = (
'whatever,max-age=1000,stale-while-revalidate=2000')
self.assertEqual(
CachingPolicy.VALIDATION_ASYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 200))
self.assertEqual(
CachingPolicy.VALIDATION_ASYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 2000))
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 3100))
# must-revalidate overrides stale-while-revalidate.
r.response_headers['Cache-Control'] += ',must-revalidate'
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 200))
def test301NeverExpires(self):
r = self._MakeRequest()
r.status = 301
self.assertEqual(
CachingPolicy.VALIDATION_NONE,
CachingPolicy(r).PolicyAtDate(r.wall_time + 2000))
def testLastModifiedHeuristic(self):
r = self._MakeRequest()
# 8 hours ago.
r.response_headers['Last-Modified'] = 'Fri, 22 Apr 2016 00:56:19 -0200'
del r.response_headers['Age']
self.assertEqual(
CachingPolicy.VALIDATION_NONE,
CachingPolicy(r).PolicyAtDate(r.wall_time + 60))
self.assertEqual(
CachingPolicy.VALIDATION_SYNC,
CachingPolicy(r).PolicyAtDate(r.wall_time + 3600))
@classmethod
def _MakeRequest(cls):
return Request.FromJsonDict(copy.deepcopy(cls._REQUEST))
class RequestTrackTestCase(unittest.TestCase):
_REQUEST_WILL_BE_SENT = {
'method': 'Network.requestWillBeSent',
'params': {
'documentURL': 'http://example.com/',
'frameId': '32493.1',
'initiator': {
'type': 'other'
},
'loaderId': '32493.3',
'request': {
'headers': {
'Accept': 'text/html',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0'
},
'initialPriority': 'VeryHigh',
'method': 'GET',
'mixedContentType': 'none',
'url': 'http://example.com/'
},
'requestId': '32493.1',
'timestamp': 5571441.535053,
'type': 'Document',
'wallTime': 1452691674.08878}}
_REDIRECT = {
'method': 'Network.requestWillBeSent',
'params': {
'documentURL': 'http://www.example.com/',
'frameId': '32493.1',
'initiator': {
'type': 'other'
},
'loaderId': '32493.3',
'redirectResponse': {
'connectionId': 18,
'connectionReused': False,
'encodedDataLength': 198,
'fromDiskCache': False,
'fromServiceWorker': False,
'headers': {},
'headersText': 'HTTP/1.1 301 Moved Permanently\r\n',
'mimeType': 'text/html',
'protocol': 'http/1.1',
'remoteIPAddress': '216.146.46.10',
'remotePort': 80,
'requestHeaders': {
'Accept': 'text/html',
'User-Agent': 'Mozilla/5.0'
},
'securityState': 'neutral',
'status': 301,
'statusText': 'Moved Permanently',
'timing': {
'connectEnd': 137.435999698937,
'connectStart': 51.1459996923804,
'dnsEnd': 51.1459996923804,
'dnsStart': 0,
'proxyEnd': -1,
'proxyStart': -1,
'receiveHeadersEnd': 228.187000378966,
'requestTime': 5571441.55002,
'sendEnd': 138.841999694705,
'sendStart': 138.031999580562,
'sslEnd': -1,
'sslStart': -1,
'workerReady': -1,
'workerStart': -1
},
'url': 'http://example.com/'
},
'request': {
'headers': {
'Accept': 'text/html',
'User-Agent': 'Mozilla/5.0'
},
'initialPriority': 'VeryLow',
'method': 'GET',
'mixedContentType': 'none',
'url': 'http://www.example.com/'
},
'requestId': '32493.1',
'timestamp': 5571441.795948,
'type': 'Document',
'wallTime': 1452691674.34968}}
_RESPONSE_RECEIVED = {
'method': 'Network.responseReceived',
'params': {
'frameId': '32493.1',
'loaderId': '32493.3',
'requestId': '32493.1',
'response': {
'connectionId': 26,
'connectionReused': False,
'encodedDataLength': -1,
'fromDiskCache': False,
'fromServiceWorker': False,
'headers': {
'Age': '67',
'Cache-Control': 'max-age=0,must-revalidate',
},
'headersText': 'HTTP/1.1 200 OK\r\n',
'mimeType': 'text/html',
'protocol': 'http/1.1',
'requestHeaders': {
'Accept': 'text/html',
'Host': 'www.example.com',
'User-Agent': 'Mozilla/5.0'
},
'status': 200,
'timing': {
'connectEnd': 37.9800004884601,
'connectStart': 26.8250005319715,
'dnsEnd': 26.8250005319715,
'dnsStart': 0,
'proxyEnd': -1,
'proxyStart': -1,
'receiveHeadersEnd': 54.9750002101064,
'requestTime': 5571441.798671,
'sendEnd': 38.3980004116893,
'sendStart': 38.1810003891587,
'sslEnd': -1,
'sslStart': -1,
'workerReady': -1,
'workerStart': -1
},
'url': 'http://www.example.com/'
},
'timestamp': 5571441.865639,
'type': 'Document'}}
_DATA_RECEIVED_1 = {
"method": "Network.dataReceived",
"params": {
"dataLength": 1803,
"encodedDataLength": 1326,
"requestId": "32493.1",
"timestamp": 5571441.867347}}
_DATA_RECEIVED_2 = {
"method": "Network.dataReceived",
"params": {
"dataLength": 32768,
"encodedDataLength": 32768,
"requestId": "32493.1",
"timestamp": 5571441.893121}}
_SERVED_FROM_CACHE = {
"method": "Network.requestServedFromCache",
"params": {
"requestId": "32493.1"}}
_LOADING_FINISHED = {'method': 'Network.loadingFinished',
'params': {
'encodedDataLength': 101829,
'requestId': '32493.1',
'timestamp': 5571441.891189}}
_LOADING_FAILED = {'method': 'Network.loadingFailed',
'params': {
'canceled': False,
'blockedReason': None,
'encodedDataLength': 101829,
'errorText': 'net::ERR_TOO_MANY_REDIRECTS',
'requestId': '32493.1',
'timestamp': 5571441.891189,
'type': 'Document'}}
def setUp(self):
self.request_track = RequestTrack(None)
def testParseRequestWillBeSent(self):
msg = RequestTrackTestCase._REQUEST_WILL_BE_SENT
request_id = msg['params']['requestId']
self.request_track.Handle('Network.requestWillBeSent', msg)
self.assertTrue(request_id in self.request_track._requests_in_flight)
(_, status) = self.request_track._requests_in_flight[request_id]
self.assertEquals(RequestTrack._STATUS_SENT, status)
def testRejectsUnknownMethod(self):
with self.assertRaises(AssertionError):
self.request_track.Handle(
'unknown', RequestTrackTestCase._REQUEST_WILL_BE_SENT)
def testHandleRedirect(self):
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REQUEST_WILL_BE_SENT)
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REDIRECT)
self.assertEquals(1, len(self.request_track._requests_in_flight))
self.assertEquals(1, len(self.request_track.GetEvents()))
redirect_request = self.request_track.GetEvents()[0]
self.assertTrue(redirect_request.request_id.endswith(
RequestTrack._REDIRECT_SUFFIX + '.1'))
request = self.request_track._requests_in_flight.values()[0][0]
self.assertEquals('redirect', request.initiator['type'])
self.assertEquals(
redirect_request.request_id,
request.initiator[Request.INITIATING_REQUEST])
self.assertEquals(0, self.request_track.inconsistent_initiators_count)
def testMultipleRedirects(self):
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REQUEST_WILL_BE_SENT)
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REDIRECT)
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REDIRECT)
self.assertEquals(1, len(self.request_track._requests_in_flight))
self.assertEquals(2, len(self.request_track.GetEvents()))
first_redirect_request = self.request_track.GetEvents()[0]
self.assertTrue(first_redirect_request.request_id.endswith(
RequestTrack._REDIRECT_SUFFIX + '.1'))
second_redirect_request = self.request_track.GetEvents()[1]
self.assertTrue(second_redirect_request.request_id.endswith(
RequestTrack._REDIRECT_SUFFIX + '.2'))
self.assertEquals('redirect', second_redirect_request.initiator['type'])
self.assertEquals(
first_redirect_request.request_id,
second_redirect_request.initiator[Request.INITIATING_REQUEST])
request = self.request_track._requests_in_flight.values()[0][0]
self.assertEquals('redirect', request.initiator['type'])
self.assertEquals(
second_redirect_request.request_id,
request.initiator[Request.INITIATING_REQUEST])
self.assertEquals(0, self.request_track.inconsistent_initiators_count)
def testInconsistentInitiators(self):
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REQUEST_WILL_BE_SENT)
request = copy.deepcopy(RequestTrackTestCase._REDIRECT)
request['params']['initiator']['type'] = 'script'
self.request_track.Handle('Network.requestWillBeSent', request)
self.assertEquals(1, self.request_track.inconsistent_initiators_count)
def testRejectDuplicates(self):
msg = RequestTrackTestCase._REQUEST_WILL_BE_SENT
self.request_track.Handle('Network.requestWillBeSent', msg)
with self.assertRaises(AssertionError):
self.request_track.Handle('Network.requestWillBeSent', msg)
def testIgnoreCompletedDuplicates(self):
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REQUEST_WILL_BE_SENT)
self.request_track.Handle('Network.responseReceived',
RequestTrackTestCase._RESPONSE_RECEIVED)
self.request_track.Handle('Network.loadingFinished',
RequestTrackTestCase._LOADING_FINISHED)
# Should not raise an AssertionError.
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REQUEST_WILL_BE_SENT)
def testSequenceOfGeneratedResponse(self):
self.request_track.Handle('Network.requestServedFromCache',
RequestTrackTestCase._SERVED_FROM_CACHE)
self.request_track.Handle('Network.loadingFinished',
RequestTrackTestCase._LOADING_FINISHED)
self.assertEquals(0, len(self.request_track.GetEvents()))
def testInvalidSequence(self):
msg1 = RequestTrackTestCase._REQUEST_WILL_BE_SENT
msg2 = RequestTrackTestCase._LOADING_FINISHED
self.request_track.Handle('Network.requestWillBeSent', msg1)
with self.assertRaises(AssertionError):
self.request_track.Handle('Network.loadingFinished', msg2)
def testValidSequence(self):
self._ValidSequence(self.request_track)
self.assertEquals(1, len(self.request_track.GetEvents()))
self.assertEquals(0, len(self.request_track._requests_in_flight))
r = self.request_track.GetEvents()[0]
self.assertEquals('32493.1', r.request_id)
self.assertEquals('32493.1', r.frame_id)
self.assertEquals('32493.3', r.loader_id)
self.assertEquals('http://example.com/', r.document_url)
self.assertEquals('http://example.com/', r.url)
self.assertEquals('http/1.1', r.protocol)
self.assertEquals('GET', r.method)
response = RequestTrackTestCase._RESPONSE_RECEIVED['params']['response']
self.assertEquals(response['requestHeaders'], r.request_headers)
self.assertEquals(response['headers'], r.response_headers)
self.assertEquals('VeryHigh', r.initial_priority)
request_will_be_sent = (
RequestTrackTestCase._REQUEST_WILL_BE_SENT['params'])
self.assertEquals(request_will_be_sent['timestamp'], r.timestamp)
self.assertEquals(request_will_be_sent['wallTime'], r.wall_time)
self.assertEquals(request_will_be_sent['initiator'], r.initiator)
self.assertEquals(request_will_be_sent['type'], r.resource_type)
self.assertEquals(False, r.served_from_cache)
self.assertEquals(False, r.from_disk_cache)
self.assertEquals(False, r.from_service_worker)
timing = Timing.FromDevToolsDict(response['timing'])
loading_finished = RequestTrackTestCase._LOADING_FINISHED['params']
loading_finished_offset = r._TimestampOffsetFromStartMs(
loading_finished['timestamp'])
timing.loading_finished = loading_finished_offset
self.assertEquals(timing, r.timing)
self.assertEquals(200, r.status)
self.assertEquals(
loading_finished['encodedDataLength'], r.encoded_data_length)
self.assertEquals(False, r.failed)
def testDataReceived(self):
self._ValidSequence(self.request_track)
self.assertEquals(1, len(self.request_track.GetEvents()))
r = self.request_track.GetEvents()[0]
self.assertEquals(2, len(r.data_chunks))
self.assertEquals(
RequestTrackTestCase._DATA_RECEIVED_1['params']['encodedDataLength'],
r.data_chunks[0][1])
self.assertEquals(
RequestTrackTestCase._DATA_RECEIVED_2['params']['encodedDataLength'],
r.data_chunks[1][1])
def testDuplicatedResponseReceived(self):
msg1 = RequestTrackTestCase._REQUEST_WILL_BE_SENT
msg2 = copy.deepcopy(RequestTrackTestCase._RESPONSE_RECEIVED)
msg2_other_timestamp = copy.deepcopy(msg2)
msg2_other_timestamp['params']['timestamp'] += 12
msg2_different = copy.deepcopy(msg2)
msg2_different['params']['response']['encodedDataLength'] += 1
self.request_track.Handle('Network.requestWillBeSent', msg1)
self.request_track.Handle('Network.responseReceived', msg2)
# Should not raise an AssertionError.
self.request_track.Handle('Network.responseReceived', msg2)
self.assertEquals(1, self.request_track.duplicates_count)
with self.assertRaises(AssertionError):
self.request_track.Handle('Network.responseReceived', msg2_different)
def testLoadingFailed(self):
self.request_track.Handle('Network.requestWillBeSent',
RequestTrackTestCase._REQUEST_WILL_BE_SENT)
self.request_track.Handle('Network.responseReceived',
RequestTrackTestCase._RESPONSE_RECEIVED)
self.request_track.Handle('Network.loadingFailed',
RequestTrackTestCase._LOADING_FAILED)
r = self.request_track.GetEvents()[0]
self.assertTrue(r.failed)
self.assertEquals('net::ERR_TOO_MANY_REDIRECTS', r.error_text)
def testCanSerialize(self):
self._ValidSequence(self.request_track)
json_dict = self.request_track.ToJsonDict()
_ = json.dumps(json_dict) # Should not raise an exception.
def testCanDeserialize(self):
self._ValidSequence(self.request_track)
self.request_track.duplicates_count = 142
self.request_track.inconsistent_initiators_count = 123
json_dict = self.request_track.ToJsonDict()
request_track = RequestTrack.FromJsonDict(json_dict)
self.assertEquals(self.request_track, request_track)
def testMaxAge(self):
rq = Request()
self.assertEqual(-1, rq.MaxAge())
rq.response_headers = {}
self.assertEqual(-1, rq.MaxAge())
rq.response_headers[
'Cache-Control'] = 'private,s-maxage=0,max-age=0,must-revalidate'
self.assertEqual(0, rq.MaxAge())
rq.response_headers[
'Cache-Control'] = 'private,s-maxage=0,no-store,max-age=100'
self.assertEqual(-1, rq.MaxAge())
rq.response_headers[
'Cache-Control'] = 'private,s-maxage=0'
self.assertEqual(-1, rq.MaxAge())
# Case-insensitive match.
rq.response_headers['cache-control'] = 'max-age=600'
self.assertEqual(600, rq.MaxAge())
@classmethod
def _ValidSequence(cls, request_track):
request_track.Handle(
'Network.requestWillBeSent', cls._REQUEST_WILL_BE_SENT)
request_track.Handle('Network.responseReceived', cls._RESPONSE_RECEIVED)
request_track.Handle('Network.dataReceived', cls._DATA_RECEIVED_1)
request_track.Handle('Network.dataReceived', cls._DATA_RECEIVED_2)
request_track.Handle('Network.loadingFinished', cls._LOADING_FINISHED)
if __name__ == '__main__':
unittest.main()