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 CustomizeBrowserOptions(self
, options
):
22 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
24 def WillNavigateToPage(self
, page
, tab
):
25 tab
.ClearCache(force
=True)
27 def ValidateAndMeasurePage(self
, page
, tab
, results
):
28 # Wait for the load event.
29 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
30 loading
.LoadingMetric().AddResults(tab
, results
)
33 class ChromeProxyDataSaving(page_test
.PageTest
):
34 """Chrome proxy data saving measurement."""
35 def __init__(self
, *args
, **kwargs
):
36 super(ChromeProxyDataSaving
, self
).__init
__(*args
, **kwargs
)
37 self
._metrics
= metrics
.ChromeProxyMetric()
39 def CustomizeBrowserOptions(self
, options
):
40 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
42 def WillNavigateToPage(self
, page
, tab
):
43 tab
.ClearCache(force
=True)
44 self
._metrics
.Start(page
, tab
)
46 def ValidateAndMeasurePage(self
, page
, tab
, results
):
47 # Wait for the load event.
48 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
49 self
._metrics
.Stop(page
, tab
)
50 self
._metrics
.AddResultsForDataSaving(tab
, results
)
53 class ChromeProxyValidation(page_test
.PageTest
):
54 """Base class for all chrome proxy correctness measurements."""
56 def __init__(self
, restart_after_each_page
=False):
57 super(ChromeProxyValidation
, self
).__init
__(
58 needs_browser_restart_after_each_page
=restart_after_each_page
)
59 self
._metrics
= metrics
.ChromeProxyMetric()
61 # Whether a timeout exception is expected during the test.
62 self
._expect
_timeout
= False
64 def CustomizeBrowserOptions(self
, options
):
65 # Enable the chrome proxy (data reduction proxy).
66 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
68 def WillNavigateToPage(self
, page
, tab
):
69 tab
.ClearCache(force
=True)
71 self
._metrics
.Start(page
, tab
)
73 def ValidateAndMeasurePage(self
, page
, tab
, results
):
75 # Wait for the load event.
76 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
78 self
._metrics
.Stop(page
, tab
)
79 self
.AddResults(tab
, results
)
81 def AddResults(self
, tab
, results
):
82 raise NotImplementedError
84 def StopBrowserAfterPage(self
, browser
, page
): # pylint: disable=W0613
85 if hasattr(page
, 'restart_after') and page
.restart_after
:
89 def RunNavigateSteps(self
, page
, tab
):
90 # The redirect from safebrowsing causes a timeout. Ignore that.
92 super(ChromeProxyValidation
, self
).RunNavigateSteps(page
, tab
)
93 if self
._expect
_timeout
:
94 raise metrics
.ChromeProxyMetricException
, (
95 'Timeout was expected, but did not occur')
96 except exceptions
.DevtoolsTargetCrashException
, e
:
97 if self
._expect
_timeout
:
98 logging
.warning('Navigation timeout on page %s',
99 page
.name
if page
.name
else page
.url
)
104 class ChromeProxyHeaders(ChromeProxyValidation
):
105 """Correctness measurement for response headers."""
108 super(ChromeProxyHeaders
, self
).__init
__(restart_after_each_page
=True)
110 def AddResults(self
, tab
, results
):
111 self
._metrics
.AddResultsForHeaderValidation(tab
, results
)
114 class ChromeProxyBypass(ChromeProxyValidation
):
115 """Correctness measurement for bypass responses."""
118 super(ChromeProxyBypass
, self
).__init
__(restart_after_each_page
=True)
120 def AddResults(self
, tab
, results
):
121 self
._metrics
.AddResultsForBypass(tab
, results
)
124 class ChromeProxyCorsBypass(ChromeProxyValidation
):
125 """Correctness measurement for bypass responses for CORS requests."""
128 super(ChromeProxyCorsBypass
, self
).__init
__(restart_after_each_page
=True)
130 def ValidateAndMeasurePage(self
, page
, tab
, results
):
131 # The test page sets window.xhrRequestCompleted to true when the XHR fetch
133 tab
.WaitForJavaScriptExpression('window.xhrRequestCompleted', 300)
134 super(ChromeProxyCorsBypass
,
135 self
).ValidateAndMeasurePage(page
, tab
, results
)
137 def AddResults(self
, tab
, results
):
138 self
._metrics
.AddResultsForCorsBypass(tab
, results
)
141 class ChromeProxyBlockOnce(ChromeProxyValidation
):
142 """Correctness measurement for block-once responses."""
145 super(ChromeProxyBlockOnce
, self
).__init
__(restart_after_each_page
=True)
147 def AddResults(self
, tab
, results
):
148 self
._metrics
.AddResultsForBlockOnce(tab
, results
)
151 class ChromeProxySafebrowsingOn(ChromeProxyValidation
):
152 """Correctness measurement for safebrowsing."""
155 super(ChromeProxySafebrowsingOn
, self
).__init
__()
157 def WillNavigateToPage(self
, page
, tab
):
158 super(ChromeProxySafebrowsingOn
, self
).WillNavigateToPage(page
, tab
)
159 self
._expect
_timeout
= True
161 def AddResults(self
, tab
, results
):
162 self
._metrics
.AddResultsForSafebrowsingOn(tab
, results
)
164 class ChromeProxySafebrowsingOff(ChromeProxyValidation
):
165 """Correctness measurement for safebrowsing."""
168 super(ChromeProxySafebrowsingOff
, self
).__init
__()
170 def AddResults(self
, tab
, results
):
171 self
._metrics
.AddResultsForSafebrowsingOff(tab
, results
)
173 _FAKE_PROXY_AUTH_VALUE
= 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
174 _TEST_SERVER
= 'chromeproxy-test.appspot.com'
175 _TEST_SERVER_DEFAULT_URL
= 'http://' + _TEST_SERVER
+ '/default'
178 # We rely on the chromeproxy-test server to facilitate some of the tests.
179 # The test server code is at <TBD location> and runs at _TEST_SERVER
181 # The test server allow request to override response status, headers, and
182 # body through query parameters. See GetResponseOverrideURL.
183 def GetResponseOverrideURL(url
=_TEST_SERVER_DEFAULT_URL
, respStatus
=0,
184 respHeader
="", respBody
=""):
185 """ Compose the request URL with query parameters to override
186 the chromeproxy-test server response.
191 queries
.append('respStatus=%d' % respStatus
)
193 queries
.append('respHeader=%s' % base64
.b64encode(respHeader
))
195 queries
.append('respBody=%s' % base64
.b64encode(respBody
))
196 if len(queries
) == 0:
199 # url has query already
200 if urlparse
.urlparse(url
).query
:
201 return url
+ '&' + "&".join(queries
)
203 return url
+ '?' + "&".join(queries
)
206 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation
):
207 """Correctness measurement for proxy fallback.
209 In this test, the probe URL does not return 'OK'. Chrome is expected
210 to use the fallback proxy.
214 super(ChromeProxyHTTPFallbackProbeURL
, self
).__init
__(
215 restart_after_each_page
=True)
217 def CustomizeBrowserOptions(self
, options
):
218 super(ChromeProxyHTTPFallbackProbeURL
,
219 self
).CustomizeBrowserOptions(options
)
220 # Use the test server probe URL which returns the response
221 # body as specified by respBody.
222 probe_url
= GetResponseOverrideURL(respBody
='not OK')
223 options
.AppendExtraBrowserArgs(
224 '--data-reduction-proxy-secure-proxy-check-url=%s' % probe_url
)
226 def AddResults(self
, tab
, results
):
227 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
230 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation
):
231 """Correctness measurement for proxy fallback.
233 In this test, the configured proxy is the chromeproxy-test server which
234 will send back a response without the expected Via header. Chrome is
235 expected to use the fallback proxy and add the configured proxy to the
240 super(ChromeProxyHTTPFallbackViaHeader
, self
).__init
__(
241 restart_after_each_page
=True)
243 def CustomizeBrowserOptions(self
, options
):
244 super(ChromeProxyHTTPFallbackViaHeader
,
245 self
).CustomizeBrowserOptions(options
)
246 options
.AppendExtraBrowserArgs('--ignore-certificate-errors')
247 options
.AppendExtraBrowserArgs(
248 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER
)
250 def AddResults(self
, tab
, results
):
251 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
254 class ChromeProxyClientVersion(ChromeProxyValidation
):
255 """Correctness measurement for version directives in Chrome-Proxy header.
257 The test verifies that the version information provided in the Chrome-Proxy
258 request header overrides any version, if specified, that is provided in the
263 super(ChromeProxyClientVersion
, self
).__init
__()
265 def CustomizeBrowserOptions(self
, options
):
266 super(ChromeProxyClientVersion
,
267 self
).CustomizeBrowserOptions(options
)
268 options
.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"')
270 def AddResults(self
, tab
, results
):
271 self
._metrics
.AddResultsForClientVersion(tab
, results
)
274 class ChromeProxyClientType(ChromeProxyValidation
):
275 """Correctness measurement for Chrome-Proxy header client type directives."""
278 super(ChromeProxyClientType
, self
).__init
__(restart_after_each_page
=True)
279 self
._chrome
_proxy
_client
_type
= None
281 def AddResults(self
, tab
, results
):
282 # Get the Chrome-Proxy client type from the first page in the page set, so
283 # that the client type value can be used to determine which of the later
284 # pages in the page set should be bypassed.
285 if not self
._chrome
_proxy
_client
_type
:
286 client_type
= self
._metrics
.GetClientTypeFromRequests(tab
)
288 self
._chrome
_proxy
_client
_type
= client_type
290 self
._metrics
.AddResultsForClientType(tab
,
292 self
._chrome
_proxy
_client
_type
,
293 self
._page
.bypass_for_client_type
)
296 class ChromeProxyLoFi(ChromeProxyValidation
):
297 """Correctness measurement for Lo-Fi in Chrome-Proxy header."""
300 super(ChromeProxyLoFi
, self
).__init
__(restart_after_each_page
=True)
302 def CustomizeBrowserOptions(self
, options
):
303 super(ChromeProxyLoFi
, self
).CustomizeBrowserOptions(options
)
304 options
.AppendExtraBrowserArgs('--enable-data-reduction-proxy-lo-fi')
306 def AddResults(self
, tab
, results
):
307 self
._metrics
.AddResultsForLoFi(tab
, results
)
310 class ChromeProxyHTTPToDirectFallback(ChromeProxyValidation
):
311 """Correctness measurement for HTTP proxy fallback to direct."""
314 super(ChromeProxyHTTPToDirectFallback
, self
).__init
__(
315 restart_after_each_page
=True)
317 def CustomizeBrowserOptions(self
, options
):
318 super(ChromeProxyHTTPToDirectFallback
,
319 self
).CustomizeBrowserOptions(options
)
320 # Set the primary proxy to something that will fail to be resolved so that
321 # this test will run using the HTTP fallback proxy.
322 options
.AppendExtraBrowserArgs(
323 '--spdy-proxy-auth-origin=http://nonexistent.googlezip.net')
325 def WillNavigateToPage(self
, page
, tab
):
326 super(ChromeProxyHTTPToDirectFallback
, self
).WillNavigateToPage(page
, tab
)
327 # Attempt to load a page through the nonexistent primary proxy in order to
328 # cause a proxy fallback, and have this test run starting from the HTTP
330 tab
.Navigate(_TEST_SERVER_DEFAULT_URL
)
331 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
333 def AddResults(self
, tab
, results
):
334 self
._metrics
.AddResultsForHTTPToDirectFallback(tab
, results
, _TEST_SERVER
)
337 class ChromeProxyReenableAfterBypass(ChromeProxyValidation
):
338 """Correctness measurement for re-enabling proxies after bypasses.
340 This test loads a page that causes all data reduction proxies to be bypassed
341 for 1 to 5 minutes, then waits 5 minutes and verifies that the proxy is no
346 super(ChromeProxyReenableAfterBypass
, self
).__init
__(
347 restart_after_each_page
=True)
349 def AddResults(self
, tab
, results
):
350 self
._metrics
.AddResultsForReenableAfterBypass(
351 tab
, results
, self
._page
.bypass_seconds_min
,
352 self
._page
.bypass_seconds_max
)
355 class ChromeProxySmoke(ChromeProxyValidation
):
356 """Smoke measurement for basic chrome proxy correctness."""
359 super(ChromeProxySmoke
, self
).__init
__(restart_after_each_page
=True)
361 def WillNavigateToPage(self
, page
, tab
):
362 super(ChromeProxySmoke
, self
).WillNavigateToPage(page
, tab
)
364 def AddResults(self
, tab
, results
):
365 # Map a page name to its AddResults func.
367 'header validation': [self
._metrics
.AddResultsForHeaderValidation
],
368 'compression: image': [
369 self
._metrics
.AddResultsForHeaderValidation
,
370 self
._metrics
.AddResultsForDataSaving
,
372 'compression: javascript': [
373 self
._metrics
.AddResultsForHeaderValidation
,
374 self
._metrics
.AddResultsForDataSaving
,
376 'compression: css': [
377 self
._metrics
.AddResultsForHeaderValidation
,
378 self
._metrics
.AddResultsForDataSaving
,
380 'bypass': [self
._metrics
.AddResultsForBypass
],
382 if not self
._page
.name
in page_to_metrics
:
383 raise page_test
.MeasurementFailure(
384 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
385 self
._page
.name
, page_to_metrics
.keys()))
386 for add_result
in page_to_metrics
[self
._page
.name
]:
387 add_result(tab
, results
)