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 if self
._expect
_timeout
:
88 raise metrics
.ChromeProxyMetricException
, (
89 'Timeout was expected, but did not occur')
90 except exceptions
.DevtoolsTargetCrashException
, e
:
91 if self
._expect
_timeout
:
92 logging
.warning('Navigation timeout on page %s',
93 page
.name
if page
.name
else page
.url
)
98 class ChromeProxyHeaders(ChromeProxyValidation
):
99 """Correctness measurement for response headers."""
102 super(ChromeProxyHeaders
, self
).__init
__(restart_after_each_page
=True)
104 def AddResults(self
, tab
, results
):
105 self
._metrics
.AddResultsForHeaderValidation(tab
, results
)
108 class ChromeProxyBypass(ChromeProxyValidation
):
109 """Correctness measurement for bypass responses."""
112 super(ChromeProxyBypass
, self
).__init
__(restart_after_each_page
=True)
114 def AddResults(self
, tab
, results
):
115 self
._metrics
.AddResultsForBypass(tab
, results
)
118 class ChromeProxyCorsBypass(ChromeProxyValidation
):
119 """Correctness measurement for bypass responses for CORS requests."""
122 super(ChromeProxyCorsBypass
, self
).__init
__(restart_after_each_page
=True)
124 def ValidateAndMeasurePage(self
, page
, tab
, results
):
125 # The test page sets window.xhrRequestCompleted to true when the XHR fetch
127 tab
.WaitForJavaScriptExpression('window.xhrRequestCompleted', 300)
128 super(ChromeProxyCorsBypass
,
129 self
).ValidateAndMeasurePage(page
, tab
, results
)
131 def AddResults(self
, tab
, results
):
132 self
._metrics
.AddResultsForCorsBypass(tab
, results
)
135 class ChromeProxyBlockOnce(ChromeProxyValidation
):
136 """Correctness measurement for block-once responses."""
139 super(ChromeProxyBlockOnce
, self
).__init
__(restart_after_each_page
=True)
141 def AddResults(self
, tab
, results
):
142 self
._metrics
.AddResultsForBlockOnce(tab
, results
)
145 class ChromeProxySafebrowsingOn(ChromeProxyValidation
):
146 """Correctness measurement for safebrowsing."""
149 super(ChromeProxySafebrowsingOn
, self
).__init
__()
151 def WillNavigateToPage(self
, page
, tab
):
152 super(ChromeProxySafebrowsingOn
, self
).WillNavigateToPage(page
, tab
)
153 self
._expect
_timeout
= True
155 def AddResults(self
, tab
, results
):
156 self
._metrics
.AddResultsForSafebrowsingOn(tab
, results
)
158 class ChromeProxySafebrowsingOff(ChromeProxyValidation
):
159 """Correctness measurement for safebrowsing."""
162 super(ChromeProxySafebrowsingOff
, self
).__init
__()
164 def AddResults(self
, tab
, results
):
165 self
._metrics
.AddResultsForSafebrowsingOff(tab
, results
)
167 _FAKE_PROXY_AUTH_VALUE
= 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
168 _TEST_SERVER
= 'chromeproxy-test.appspot.com'
169 _TEST_SERVER_DEFAULT_URL
= 'http://' + _TEST_SERVER
+ '/default'
172 # We rely on the chromeproxy-test server to facilitate some of the tests.
173 # The test server code is at <TBD location> and runs at _TEST_SERVER
175 # The test server allow request to override response status, headers, and
176 # body through query parameters. See GetResponseOverrideURL.
177 def GetResponseOverrideURL(url
=_TEST_SERVER_DEFAULT_URL
, respStatus
=0,
178 respHeader
="", respBody
=""):
179 """ Compose the request URL with query parameters to override
180 the chromeproxy-test server response.
185 queries
.append('respStatus=%d' % respStatus
)
187 queries
.append('respHeader=%s' % base64
.b64encode(respHeader
))
189 queries
.append('respBody=%s' % base64
.b64encode(respBody
))
190 if len(queries
) == 0:
193 # url has query already
194 if urlparse
.urlparse(url
).query
:
195 return url
+ '&' + "&".join(queries
)
197 return url
+ '?' + "&".join(queries
)
200 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation
):
201 """Correctness measurement for proxy fallback.
203 In this test, the probe URL does not return 'OK'. Chrome is expected
204 to use the fallback proxy.
208 super(ChromeProxyHTTPFallbackProbeURL
, self
).__init
__()
210 def CustomizeBrowserOptions(self
, options
):
211 super(ChromeProxyHTTPFallbackProbeURL
,
212 self
).CustomizeBrowserOptions(options
)
213 # Use the test server probe URL which returns the response
214 # body as specified by respBody.
215 probe_url
= GetResponseOverrideURL(
217 options
.AppendExtraBrowserArgs(
218 '--data-reduction-proxy-probe-url=%s' % probe_url
)
220 def AddResults(self
, tab
, results
):
221 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
224 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation
):
225 """Correctness measurement for proxy fallback.
227 In this test, the configured proxy is the chromeproxy-test server which
228 will send back a response without the expected Via header. Chrome is
229 expected to use the fallback proxy and add the configured proxy to the
234 super(ChromeProxyHTTPFallbackViaHeader
, self
).__init
__(
235 restart_after_each_page
=True)
237 def CustomizeBrowserOptions(self
, options
):
238 super(ChromeProxyHTTPFallbackViaHeader
,
239 self
).CustomizeBrowserOptions(options
)
240 options
.AppendExtraBrowserArgs('--ignore-certificate-errors')
241 options
.AppendExtraBrowserArgs(
242 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER
)
244 def AddResults(self
, tab
, results
):
245 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
248 class ChromeProxyClientVersion(ChromeProxyValidation
):
249 """Correctness measurement for version directives in Chrome-Proxy header.
251 The test verifies that the version information provided in the Chrome-Proxy
252 request header overrides any version, if specified, that is provided in the
257 super(ChromeProxyClientVersion
, self
).__init
__()
259 def CustomizeBrowserOptions(self
, options
):
260 super(ChromeProxyClientVersion
,
261 self
).CustomizeBrowserOptions(options
)
262 options
.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"')
264 def AddResults(self
, tab
, results
):
265 self
._metrics
.AddResultsForClientVersion(tab
, results
)
268 class ChromeProxyClientType(ChromeProxyValidation
):
269 """Correctness measurement for Chrome-Proxy header client type directives."""
272 super(ChromeProxyClientType
, self
).__init
__(restart_after_each_page
=True)
273 self
._chrome
_proxy
_client
_type
= None
275 def AddResults(self
, tab
, results
):
276 # Get the Chrome-Proxy client type from the first page in the page set, so
277 # that the client type value can be used to determine which of the later
278 # pages in the page set should be bypassed.
279 if not self
._chrome
_proxy
_client
_type
:
280 client_type
= self
._metrics
.GetClientTypeFromRequests(tab
)
282 self
._chrome
_proxy
_client
_type
= client_type
284 self
._metrics
.AddResultsForClientType(tab
,
286 self
._chrome
_proxy
_client
_type
,
287 self
._page
.bypass_for_client_type
)
290 class ChromeProxyHTTPToDirectFallback(ChromeProxyValidation
):
291 """Correctness measurement for HTTP proxy fallback to direct."""
294 super(ChromeProxyHTTPToDirectFallback
, self
).__init
__(
295 restart_after_each_page
=True)
297 def CustomizeBrowserOptions(self
, options
):
298 super(ChromeProxyHTTPToDirectFallback
,
299 self
).CustomizeBrowserOptions(options
)
300 # Set the primary proxy to something that will fail to be resolved so that
301 # this test will run using the HTTP fallback proxy.
302 options
.AppendExtraBrowserArgs(
303 '--spdy-proxy-auth-origin=http://nonexistent.googlezip.net')
305 def WillNavigateToPage(self
, page
, tab
):
306 super(ChromeProxyHTTPToDirectFallback
, self
).WillNavigateToPage(page
, tab
)
307 # Attempt to load a page through the nonexistent primary proxy in order to
308 # cause a proxy fallback, and have this test run starting from the HTTP
310 tab
.Navigate(_TEST_SERVER_DEFAULT_URL
)
311 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
313 def AddResults(self
, tab
, results
):
314 self
._metrics
.AddResultsForHTTPToDirectFallback(tab
, results
, _TEST_SERVER
)
317 class ChromeProxyReenableAfterBypass(ChromeProxyValidation
):
318 """Correctness measurement for re-enabling proxies after bypasses.
320 This test loads a page that causes all data reduction proxies to be bypassed
321 for 1 to 5 minutes, then waits 5 minutes and verifies that the proxy is no
326 super(ChromeProxyReenableAfterBypass
, self
).__init
__(
327 restart_after_each_page
=True)
329 def AddResults(self
, tab
, results
):
330 self
._metrics
.AddResultsForReenableAfterBypass(
331 tab
, results
, self
._page
.bypass_seconds_min
,
332 self
._page
.bypass_seconds_max
)
335 class ChromeProxySmoke(ChromeProxyValidation
):
336 """Smoke measurement for basic chrome proxy correctness."""
339 super(ChromeProxySmoke
, self
).__init
__(restart_after_each_page
=True)
341 def WillNavigateToPage(self
, page
, tab
):
342 super(ChromeProxySmoke
, self
).WillNavigateToPage(page
, tab
)
344 def AddResults(self
, tab
, results
):
345 # Map a page name to its AddResults func.
347 'header validation': [self
._metrics
.AddResultsForHeaderValidation
],
348 'compression: image': [
349 self
._metrics
.AddResultsForHeaderValidation
,
350 self
._metrics
.AddResultsForDataSaving
,
352 'compression: javascript': [
353 self
._metrics
.AddResultsForHeaderValidation
,
354 self
._metrics
.AddResultsForDataSaving
,
356 'compression: css': [
357 self
._metrics
.AddResultsForHeaderValidation
,
358 self
._metrics
.AddResultsForDataSaving
,
360 'bypass': [self
._metrics
.AddResultsForBypass
],
362 if not self
._page
.name
in page_to_metrics
:
363 raise page_test
.MeasurementFailure(
364 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
365 self
._page
.name
, page_to_metrics
.keys()))
366 for add_result
in page_to_metrics
[self
._page
.name
]:
367 add_result(tab
, results
)