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
)
20 self
._metrics
= metrics
.ChromeProxyMetric()
22 def CustomizeBrowserOptions(self
, options
):
23 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
25 def WillNavigateToPage(self
, page
, tab
):
26 tab
.ClearCache(force
=True)
28 def ValidateAndMeasurePage(self
, page
, tab
, results
):
29 # Wait for the load event.
30 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
31 self
._metrics
.AddResultsForLatency(tab
, results
)
34 class ChromeProxyDataSaving(page_test
.PageTest
):
35 """Chrome proxy data saving measurement."""
36 def __init__(self
, *args
, **kwargs
):
37 super(ChromeProxyDataSaving
, self
).__init
__(*args
, **kwargs
)
38 self
._metrics
= metrics
.ChromeProxyMetric()
40 def CustomizeBrowserOptions(self
, options
):
41 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
43 def WillNavigateToPage(self
, page
, tab
):
44 tab
.ClearCache(force
=True)
45 self
._metrics
.Start(page
, tab
)
47 def ValidateAndMeasurePage(self
, page
, tab
, results
):
48 # Wait for the load event.
49 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
50 self
._metrics
.Stop(page
, tab
)
51 self
._metrics
.AddResultsForDataSaving(tab
, results
)
54 class ChromeProxyValidation(page_test
.PageTest
):
55 """Base class for all chrome proxy correctness measurements."""
57 def __init__(self
, restart_after_each_page
=False):
58 super(ChromeProxyValidation
, self
).__init
__(
59 needs_browser_restart_after_each_page
=restart_after_each_page
)
60 self
._metrics
= metrics
.ChromeProxyMetric()
62 # Whether a timeout exception is expected during the test.
63 self
._expect
_timeout
= False
65 def CustomizeBrowserOptions(self
, options
):
66 # Enable the chrome proxy (data reduction proxy).
67 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
69 def WillNavigateToPage(self
, page
, tab
):
70 tab
.ClearCache(force
=True)
72 self
._metrics
.Start(page
, tab
)
74 def ValidateAndMeasurePage(self
, page
, tab
, results
):
76 # Wait for the load event.
77 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
79 self
._metrics
.Stop(page
, tab
)
80 self
.AddResults(tab
, results
)
82 def AddResults(self
, tab
, results
):
83 raise NotImplementedError
85 def StopBrowserAfterPage(self
, browser
, page
): # pylint: disable=W0613
86 if hasattr(page
, 'restart_after') and page
.restart_after
:
90 def RunNavigateSteps(self
, page
, tab
):
91 # The redirect from safebrowsing causes a timeout. Ignore that.
93 super(ChromeProxyValidation
, self
).RunNavigateSteps(page
, tab
)
94 if self
._expect
_timeout
:
95 raise metrics
.ChromeProxyMetricException
, (
96 'Timeout was expected, but did not occur')
97 except exceptions
.TimeoutException
as e
:
98 if self
._expect
_timeout
:
99 logging
.warning('Navigation timeout on page %s',
100 page
.name
if page
.name
else page
.url
)
105 class ChromeProxyHeaders(ChromeProxyValidation
):
106 """Correctness measurement for response headers."""
109 super(ChromeProxyHeaders
, self
).__init
__(restart_after_each_page
=True)
111 def AddResults(self
, tab
, results
):
112 self
._metrics
.AddResultsForHeaderValidation(tab
, results
)
115 class ChromeProxyBypass(ChromeProxyValidation
):
116 """Correctness measurement for bypass responses."""
119 super(ChromeProxyBypass
, self
).__init
__(restart_after_each_page
=True)
121 def AddResults(self
, tab
, results
):
122 self
._metrics
.AddResultsForBypass(tab
, results
)
125 class ChromeProxyCorsBypass(ChromeProxyValidation
):
126 """Correctness measurement for bypass responses for CORS requests."""
129 super(ChromeProxyCorsBypass
, self
).__init
__(restart_after_each_page
=True)
131 def ValidateAndMeasurePage(self
, page
, tab
, results
):
132 # The test page sets window.xhrRequestCompleted to true when the XHR fetch
134 tab
.WaitForJavaScriptExpression('window.xhrRequestCompleted', 300)
135 super(ChromeProxyCorsBypass
,
136 self
).ValidateAndMeasurePage(page
, tab
, results
)
138 def AddResults(self
, tab
, results
):
139 self
._metrics
.AddResultsForCorsBypass(tab
, results
)
142 class ChromeProxyBlockOnce(ChromeProxyValidation
):
143 """Correctness measurement for block-once responses."""
146 super(ChromeProxyBlockOnce
, self
).__init
__(restart_after_each_page
=True)
148 def AddResults(self
, tab
, results
):
149 self
._metrics
.AddResultsForBlockOnce(tab
, results
)
152 class ChromeProxySafebrowsingOn(ChromeProxyValidation
):
153 """Correctness measurement for safebrowsing."""
156 super(ChromeProxySafebrowsingOn
, self
).__init
__()
158 def WillNavigateToPage(self
, page
, tab
):
159 super(ChromeProxySafebrowsingOn
, self
).WillNavigateToPage(page
, tab
)
160 self
._expect
_timeout
= True
162 def AddResults(self
, tab
, results
):
163 self
._metrics
.AddResultsForSafebrowsingOn(tab
, results
)
165 class ChromeProxySafebrowsingOff(ChromeProxyValidation
):
166 """Correctness measurement for safebrowsing."""
169 super(ChromeProxySafebrowsingOff
, self
).__init
__()
171 def AddResults(self
, tab
, results
):
172 self
._metrics
.AddResultsForSafebrowsingOff(tab
, results
)
174 _FAKE_PROXY_AUTH_VALUE
= 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
175 _TEST_SERVER
= 'chromeproxy-test.appspot.com'
176 _TEST_SERVER_DEFAULT_URL
= 'http://' + _TEST_SERVER
+ '/default'
179 # We rely on the chromeproxy-test server to facilitate some of the tests.
180 # The test server code is at <TBD location> and runs at _TEST_SERVER
182 # The test server allow request to override response status, headers, and
183 # body through query parameters. See GetResponseOverrideURL.
184 def GetResponseOverrideURL(url
=_TEST_SERVER_DEFAULT_URL
, respStatus
=0,
185 respHeader
="", respBody
=""):
186 """ Compose the request URL with query parameters to override
187 the chromeproxy-test server response.
192 queries
.append('respStatus=%d' % respStatus
)
194 queries
.append('respHeader=%s' % base64
.b64encode(respHeader
))
196 queries
.append('respBody=%s' % base64
.b64encode(respBody
))
197 if len(queries
) == 0:
200 # url has query already
201 if urlparse
.urlparse(url
).query
:
202 return url
+ '&' + "&".join(queries
)
204 return url
+ '?' + "&".join(queries
)
207 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation
):
208 """Correctness measurement for proxy fallback.
210 In this test, the probe URL does not return 'OK'. Chrome is expected
211 to use the fallback proxy.
215 super(ChromeProxyHTTPFallbackProbeURL
, self
).__init
__(
216 restart_after_each_page
=True)
218 def CustomizeBrowserOptions(self
, options
):
219 super(ChromeProxyHTTPFallbackProbeURL
,
220 self
).CustomizeBrowserOptions(options
)
221 # Set the secure proxy check URL to the google.com favicon, which will be
222 # interpreted as a secure proxy check failure since the response body is not
223 # "OK". The google.com favicon is used because it will load reliably fast,
224 # and there have been problems with chromeproxy-test.appspot.com being slow
225 # and causing tests to flake.
226 options
.AppendExtraBrowserArgs(
227 '--data-reduction-proxy-secure-proxy-check-url='
228 'http://www.google.com/favicon.ico')
230 def AddResults(self
, tab
, results
):
231 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
234 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation
):
235 """Correctness measurement for proxy fallback.
237 In this test, the configured proxy is the chromeproxy-test server which
238 will send back a response without the expected Via header. Chrome is
239 expected to use the fallback proxy and add the configured proxy to the
244 super(ChromeProxyHTTPFallbackViaHeader
, self
).__init
__(
245 restart_after_each_page
=True)
247 def CustomizeBrowserOptions(self
, options
):
248 super(ChromeProxyHTTPFallbackViaHeader
,
249 self
).CustomizeBrowserOptions(options
)
250 options
.AppendExtraBrowserArgs('--ignore-certificate-errors')
251 options
.AppendExtraBrowserArgs(
252 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER
)
254 def AddResults(self
, tab
, results
):
255 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
258 class ChromeProxyClientVersion(ChromeProxyValidation
):
259 """Correctness measurement for version directives in Chrome-Proxy header.
261 The test verifies that the version information provided in the Chrome-Proxy
262 request header overrides any version, if specified, that is provided in the
267 super(ChromeProxyClientVersion
, self
).__init
__()
269 def CustomizeBrowserOptions(self
, options
):
270 super(ChromeProxyClientVersion
,
271 self
).CustomizeBrowserOptions(options
)
272 options
.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"')
274 def AddResults(self
, tab
, results
):
275 self
._metrics
.AddResultsForClientVersion(tab
, results
)
278 class ChromeProxyClientType(ChromeProxyValidation
):
279 """Correctness measurement for Chrome-Proxy header client type directives."""
282 super(ChromeProxyClientType
, self
).__init
__(restart_after_each_page
=True)
283 self
._chrome
_proxy
_client
_type
= None
285 def AddResults(self
, tab
, results
):
286 # Get the Chrome-Proxy client type from the first page in the page set, so
287 # that the client type value can be used to determine which of the later
288 # pages in the page set should be bypassed.
289 if not self
._chrome
_proxy
_client
_type
:
290 client_type
= self
._metrics
.GetClientTypeFromRequests(tab
)
292 self
._chrome
_proxy
_client
_type
= client_type
294 self
._metrics
.AddResultsForClientType(tab
,
296 self
._chrome
_proxy
_client
_type
,
297 self
._page
.bypass_for_client_type
)
300 class ChromeProxyLoFi(ChromeProxyValidation
):
301 """Correctness measurement for Lo-Fi in Chrome-Proxy header."""
304 super(ChromeProxyLoFi
, self
).__init
__(restart_after_each_page
=True)
306 def CustomizeBrowserOptions(self
, options
):
307 super(ChromeProxyLoFi
, self
).CustomizeBrowserOptions(options
)
308 options
.AppendExtraBrowserArgs('--enable-data-reduction-proxy-lo-fi')
310 def AddResults(self
, tab
, results
):
311 self
._metrics
.AddResultsForLoFi(tab
, results
)
314 class ChromeProxyHTTPToDirectFallback(ChromeProxyValidation
):
315 """Correctness measurement for HTTP proxy fallback to direct."""
318 super(ChromeProxyHTTPToDirectFallback
, self
).__init
__(
319 restart_after_each_page
=True)
321 def CustomizeBrowserOptions(self
, options
):
322 super(ChromeProxyHTTPToDirectFallback
,
323 self
).CustomizeBrowserOptions(options
)
324 # Set the primary proxy to something that will fail to be resolved so that
325 # this test will run using the HTTP fallback proxy.
326 options
.AppendExtraBrowserArgs(
327 '--spdy-proxy-auth-origin=http://nonexistent.googlezip.net')
329 def WillNavigateToPage(self
, page
, tab
):
330 super(ChromeProxyHTTPToDirectFallback
, self
).WillNavigateToPage(page
, tab
)
331 # Attempt to load a page through the nonexistent primary proxy in order to
332 # cause a proxy fallback, and have this test run starting from the HTTP
334 tab
.Navigate(_TEST_SERVER_DEFAULT_URL
)
335 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
337 def AddResults(self
, tab
, results
):
338 self
._metrics
.AddResultsForHTTPToDirectFallback(tab
, results
, _TEST_SERVER
)
341 class ChromeProxyReenableAfterBypass(ChromeProxyValidation
):
342 """Correctness measurement for re-enabling proxies after bypasses.
344 This test loads a page that causes all data reduction proxies to be bypassed
345 for 1 to 5 minutes, then waits 5 minutes and verifies that the proxy is no
350 super(ChromeProxyReenableAfterBypass
, self
).__init
__(
351 restart_after_each_page
=True)
353 def AddResults(self
, tab
, results
):
354 self
._metrics
.AddResultsForReenableAfterBypass(
355 tab
, results
, self
._page
.bypass_seconds_min
,
356 self
._page
.bypass_seconds_max
)
359 class ChromeProxySmoke(ChromeProxyValidation
):
360 """Smoke measurement for basic chrome proxy correctness."""
363 super(ChromeProxySmoke
, self
).__init
__(restart_after_each_page
=True)
365 def WillNavigateToPage(self
, page
, tab
):
366 super(ChromeProxySmoke
, self
).WillNavigateToPage(page
, tab
)
368 def AddResults(self
, tab
, results
):
369 # Map a page name to its AddResults func.
371 'header validation': [self
._metrics
.AddResultsForHeaderValidation
],
372 'compression: image': [
373 self
._metrics
.AddResultsForHeaderValidation
,
374 self
._metrics
.AddResultsForDataSaving
,
376 'compression: javascript': [
377 self
._metrics
.AddResultsForHeaderValidation
,
378 self
._metrics
.AddResultsForDataSaving
,
380 'compression: css': [
381 self
._metrics
.AddResultsForHeaderValidation
,
382 self
._metrics
.AddResultsForDataSaving
,
384 'bypass': [self
._metrics
.AddResultsForBypass
],
386 if not self
._page
.name
in page_to_metrics
:
387 raise page_test
.MeasurementFailure(
388 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
389 self
._page
.name
, page_to_metrics
.keys()))
390 for add_result
in page_to_metrics
[self
._page
.name
]:
391 add_result(tab
, results
)