1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
9 from integration_tests
import chrome_proxy_metrics
as metrics
10 from metrics
import loading
11 from telemetry
.core
import exceptions
12 from telemetry
.page
import page_test
15 class ChromeProxyLatency(page_test
.PageTest
):
16 """Chrome proxy latency measurement."""
18 def __init__(self
, *args
, **kwargs
):
19 super(ChromeProxyLatency
, self
).__init
__(*args
, **kwargs
)
21 def WillNavigateToPage(self
, page
, tab
):
22 tab
.ClearCache(force
=True)
24 def ValidateAndMeasurePage(self
, page
, tab
, results
):
25 # Wait for the load event.
26 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
27 loading
.LoadingMetric().AddResults(tab
, results
)
30 class ChromeProxyDataSaving(page_test
.PageTest
):
31 """Chrome proxy data daving measurement."""
32 def __init__(self
, *args
, **kwargs
):
33 super(ChromeProxyDataSaving
, self
).__init
__(*args
, **kwargs
)
34 self
._metrics
= metrics
.ChromeProxyMetric()
36 def WillNavigateToPage(self
, page
, tab
):
37 tab
.ClearCache(force
=True)
38 self
._metrics
.Start(page
, tab
)
40 def ValidateAndMeasurePage(self
, page
, tab
, results
):
41 # Wait for the load event.
42 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
43 self
._metrics
.Stop(page
, tab
)
44 self
._metrics
.AddResultsForDataSaving(tab
, results
)
47 class ChromeProxyValidation(page_test
.PageTest
):
48 """Base class for all chrome proxy correctness measurements."""
50 def __init__(self
, restart_after_each_page
=False):
51 super(ChromeProxyValidation
, self
).__init
__(
52 needs_browser_restart_after_each_page
=restart_after_each_page
)
53 self
._metrics
= metrics
.ChromeProxyMetric()
55 # Whether a timeout exception is expected during the test.
56 self
._expect
_timeout
= False
58 def CustomizeBrowserOptions(self
, options
):
59 # Enable the chrome proxy (data reduction proxy).
60 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
62 def WillNavigateToPage(self
, page
, tab
):
63 tab
.ClearCache(force
=True)
65 self
._metrics
.Start(page
, tab
)
67 def ValidateAndMeasurePage(self
, page
, tab
, results
):
69 # Wait for the load event.
70 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
72 self
._metrics
.Stop(page
, tab
)
73 self
.AddResults(tab
, results
)
75 def AddResults(self
, tab
, results
):
76 raise NotImplementedError
78 def StopBrowserAfterPage(self
, browser
, page
): # pylint: disable=W0613
79 if hasattr(page
, 'restart_after') and page
.restart_after
:
83 def RunNavigateSteps(self
, page
, tab
):
84 # The redirect from safebrowsing causes a timeout. Ignore that.
86 super(ChromeProxyValidation
, self
).RunNavigateSteps(page
, tab
)
87 except exceptions
.DevtoolsTargetCrashException
, e
:
88 if self
._expect
_timeout
:
89 logging
.warning('Navigation timeout on page %s',
90 page
.name
if page
.name
else page
.url
)
95 class ChromeProxyHeaders(ChromeProxyValidation
):
96 """Correctness measurement for response headers."""
99 super(ChromeProxyHeaders
, self
).__init
__(restart_after_each_page
=True)
101 def AddResults(self
, tab
, results
):
102 self
._metrics
.AddResultsForHeaderValidation(tab
, results
)
105 class ChromeProxyBypass(ChromeProxyValidation
):
106 """Correctness measurement for bypass responses."""
109 super(ChromeProxyBypass
, self
).__init
__(restart_after_each_page
=True)
111 def AddResults(self
, tab
, results
):
112 self
._metrics
.AddResultsForBypass(tab
, results
)
115 class ChromeProxyCorsBypass(ChromeProxyValidation
):
116 """Correctness measurement for bypass responses for CORS requests."""
119 super(ChromeProxyCorsBypass
, self
).__init
__(restart_after_each_page
=True)
121 def ValidateAndMeasurePage(self
, page
, tab
, results
):
122 # The test page sets window.xhrRequestCompleted to true when the XHR fetch
124 tab
.WaitForJavaScriptExpression('window.xhrRequestCompleted', 300)
125 super(ChromeProxyCorsBypass
,
126 self
).ValidateAndMeasurePage(page
, tab
, results
)
128 def AddResults(self
, tab
, results
):
129 self
._metrics
.AddResultsForCorsBypass(tab
, results
)
132 class ChromeProxyBlockOnce(ChromeProxyValidation
):
133 """Correctness measurement for block-once responses."""
136 super(ChromeProxyBlockOnce
, self
).__init
__(restart_after_each_page
=True)
138 def AddResults(self
, tab
, results
):
139 self
._metrics
.AddResultsForBlockOnce(tab
, results
)
142 class ChromeProxySafebrowsing(ChromeProxyValidation
):
143 """Correctness measurement for safebrowsing."""
146 super(ChromeProxySafebrowsing
, self
).__init
__()
148 def WillNavigateToPage(self
, page
, tab
):
149 super(ChromeProxySafebrowsing
, self
).WillNavigateToPage(page
, tab
)
150 self
._expect
_timeout
= True
152 def AddResults(self
, tab
, results
):
153 self
._metrics
.AddResultsForSafebrowsing(tab
, results
)
156 _FAKE_PROXY_AUTH_VALUE
= 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
157 _TEST_SERVER
= 'chromeproxy-test.appspot.com'
158 _TEST_SERVER_DEFAULT_URL
= 'http://' + _TEST_SERVER
+ '/default'
161 # We rely on the chromeproxy-test server to facilitate some of the tests.
162 # The test server code is at <TBD location> and runs at _TEST_SERVER
164 # The test server allow request to override response status, headers, and
165 # body through query parameters. See GetResponseOverrideURL.
166 def GetResponseOverrideURL(url
=_TEST_SERVER_DEFAULT_URL
, respStatus
=0,
167 respHeader
="", respBody
=""):
168 """ Compose the request URL with query parameters to override
169 the chromeproxy-test server response.
174 queries
.append('respStatus=%d' % respStatus
)
176 queries
.append('respHeader=%s' % base64
.b64encode(respHeader
))
178 queries
.append('respBody=%s' % base64
.b64encode(respBody
))
179 if len(queries
) == 0:
182 # url has query already
183 if urlparse
.urlparse(url
).query
:
184 return url
+ '&' + "&".join(queries
)
186 return url
+ '?' + "&".join(queries
)
189 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation
):
190 """Correctness measurement for proxy fallback.
192 In this test, the probe URL does not return 'OK'. Chrome is expected
193 to use the fallback proxy.
197 super(ChromeProxyHTTPFallbackProbeURL
, self
).__init
__()
199 def CustomizeBrowserOptions(self
, options
):
200 super(ChromeProxyHTTPFallbackProbeURL
,
201 self
).CustomizeBrowserOptions(options
)
202 # Use the test server probe URL which returns the response
203 # body as specified by respBody.
204 probe_url
= GetResponseOverrideURL(
206 options
.AppendExtraBrowserArgs(
207 '--data-reduction-proxy-probe-url=%s' % probe_url
)
209 def AddResults(self
, tab
, results
):
210 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
213 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation
):
214 """Correctness measurement for proxy fallback.
216 In this test, the configured proxy is the chromeproxy-test server which
217 will send back a response without the expected Via header. Chrome is
218 expected to use the fallback proxy and add the configured proxy to the
223 super(ChromeProxyHTTPFallbackViaHeader
, self
).__init
__(
224 restart_after_each_page
=True)
226 def CustomizeBrowserOptions(self
, options
):
227 super(ChromeProxyHTTPFallbackViaHeader
,
228 self
).CustomizeBrowserOptions(options
)
229 options
.AppendExtraBrowserArgs('--ignore-certificate-errors')
230 options
.AppendExtraBrowserArgs(
231 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER
)
232 options
.AppendExtraBrowserArgs(
233 '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE
)
235 def AddResults(self
, tab
, results
):
237 _TEST_SERVER
+ ":80",
238 self
._metrics
.effective_proxies
['fallback'],
239 self
._metrics
.effective_proxies
['direct']]
240 bad_proxies
= [_TEST_SERVER
+ ":80", metrics
.PROXY_SETTING_HTTP
]
241 self
._metrics
.AddResultsForHTTPFallback(tab
, results
, proxies
, bad_proxies
)
244 class ChromeProxyClientVersion(ChromeProxyValidation
):
245 """Correctness measurement for version directives in Chrome-Proxy header.
247 The test verifies that the version information provided in the Chrome-Proxy
248 request header overrides any version, if specified, that is provided in the
253 super(ChromeProxyClientVersion
, self
).__init
__()
255 def CustomizeBrowserOptions(self
, options
):
256 super(ChromeProxyClientVersion
,
257 self
).CustomizeBrowserOptions(options
)
258 options
.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"')
260 def AddResults(self
, tab
, results
):
261 self
._metrics
.AddResultsForClientVersion(tab
, results
)
264 class ChromeProxyClientType(ChromeProxyValidation
):
265 """Correctness measurement for Chrome-Proxy header client type directives."""
268 super(ChromeProxyClientType
, self
).__init
__(restart_after_each_page
=True)
269 self
._chrome
_proxy
_client
_type
= None
271 def AddResults(self
, tab
, results
):
272 # Get the Chrome-Proxy client type from the first page in the page set, so
273 # that the client type value can be used to determine which of the later
274 # pages in the page set should be bypassed.
275 if not self
._chrome
_proxy
_client
_type
:
276 client_type
= self
._metrics
.GetClientTypeFromRequests(tab
)
278 self
._chrome
_proxy
_client
_type
= client_type
280 self
._metrics
.AddResultsForClientType(tab
,
282 self
._chrome
_proxy
_client
_type
,
283 self
._page
.bypass_for_client_type
)
286 class ChromeProxyHTTPToDirectFallback(ChromeProxyValidation
):
287 """Correctness measurement for HTTP proxy fallback to direct."""
290 super(ChromeProxyHTTPToDirectFallback
, self
).__init
__(
291 restart_after_each_page
=True)
293 def CustomizeBrowserOptions(self
, options
):
294 super(ChromeProxyHTTPToDirectFallback
,
295 self
).CustomizeBrowserOptions(options
)
296 # Set the primary proxy to something that will fail to be resolved so that
297 # this test will run using the HTTP fallback proxy.
298 options
.AppendExtraBrowserArgs(
299 '--spdy-proxy-auth-origin=http://nonexistent.googlezip.net')
301 def WillNavigateToPage(self
, page
, tab
):
302 super(ChromeProxyHTTPToDirectFallback
, self
).WillNavigateToPage(page
, tab
)
303 # Attempt to load a page through the nonexistent primary proxy in order to
304 # cause a proxy fallback, and have this test run starting from the HTTP
306 tab
.Navigate(_TEST_SERVER_DEFAULT_URL
)
307 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
310 'nonexistent.googlezip.net:80',
311 self
._metrics
.effective_proxies
['fallback'],
312 self
._metrics
.effective_proxies
['direct']]
313 self
._metrics
.VerifyProxyInfo(tab
, proxies
, proxies
[:1])
315 def AddResults(self
, tab
, results
):
316 self
._metrics
.AddResultsForHTTPToDirectFallback(tab
, results
)
319 class ChromeProxyExplicitBypass(ChromeProxyValidation
):
320 """Correctness measurement for explicit proxy bypasses.
322 In this test, the configured proxy is the chromeproxy-test server which
323 will send back a response without the expected Via header. Chrome is
324 expected to use the fallback proxy and add the configured proxy to the
329 super(ChromeProxyExplicitBypass
, self
).__init
__(
330 restart_after_each_page
=True)
332 def CustomizeBrowserOptions(self
, options
):
333 super(ChromeProxyExplicitBypass
,
334 self
).CustomizeBrowserOptions(options
)
335 options
.AppendExtraBrowserArgs('--ignore-certificate-errors')
336 options
.AppendExtraBrowserArgs(
337 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER
)
338 options
.AppendExtraBrowserArgs(
339 '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE
)
341 def AddResults(self
, tab
, results
):
343 'proxy': _TEST_SERVER
+ ':80',
344 'retry_seconds_low': self
._page
.bypass_seconds_low
,
345 'retry_seconds_high': self
._page
.bypass_seconds_high
347 if self
._page
.num_bypassed_proxies
== 2:
349 'proxy': self
._metrics
.effective_proxies
['fallback'],
350 'retry_seconds_low': self
._page
.bypass_seconds_low
,
351 'retry_seconds_high': self
._page
.bypass_seconds_high
354 # Even if the test page only causes the primary proxy to be bypassed,
355 # Chrome will attempt to fetch the favicon for the test server through
356 # the data reduction proxy, which will cause a "block=0" bypass.
357 bad_proxies
.append({'proxy': self
._metrics
.effective_proxies
['fallback']})
359 self
._metrics
.AddResultsForExplicitBypass(tab
, results
, bad_proxies
)
362 class ChromeProxySmoke(ChromeProxyValidation
):
363 """Smoke measurement for basic chrome proxy correctness."""
366 super(ChromeProxySmoke
, self
).__init
__()
368 def WillNavigateToPage(self
, page
, tab
):
369 super(ChromeProxySmoke
, self
).WillNavigateToPage(page
, tab
)
370 if page
.name
== 'safebrowsing':
371 self
._expect
_timeout
= True
373 def AddResults(self
, tab
, results
):
374 # Map a page name to its AddResults func.
376 'header validation': [self
._metrics
.AddResultsForHeaderValidation
],
377 'compression: image': [
378 self
._metrics
.AddResultsForHeaderValidation
,
379 self
._metrics
.AddResultsForDataSaving
,
381 'compression: javascript': [
382 self
._metrics
.AddResultsForHeaderValidation
,
383 self
._metrics
.AddResultsForDataSaving
,
385 'compression: css': [
386 self
._metrics
.AddResultsForHeaderValidation
,
387 self
._metrics
.AddResultsForDataSaving
,
389 'bypass': [self
._metrics
.AddResultsForBypass
],
390 'safebrowsing': [self
._metrics
.AddResultsForSafebrowsing
],
392 if not self
._page
.name
in page_to_metrics
:
393 raise page_test
.MeasurementFailure(
394 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
395 self
._page
.name
, page_to_metrics
.keys()))
396 for add_result
in page_to_metrics
[self
._page
.name
]:
397 add_result(tab
, results
)