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 common
import chrome_proxy_measurements
as measurements
10 from common
.chrome_proxy_measurements
import ChromeProxyValidation
11 from integration_tests
import chrome_proxy_metrics
as metrics
12 from metrics
import loading
13 from telemetry
.core
import exceptions
14 from telemetry
.page
import page_test
17 class ChromeProxyDataSaving(page_test
.PageTest
):
18 """Chrome proxy data saving measurement."""
19 def __init__(self
, *args
, **kwargs
):
20 super(ChromeProxyDataSaving
, self
).__init
__(*args
, **kwargs
)
21 self
._metrics
= metrics
.ChromeProxyMetric()
22 self
._enable
_proxy
= True
24 def CustomizeBrowserOptions(self
, options
):
25 if self
._enable
_proxy
:
26 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
28 def WillNavigateToPage(self
, page
, tab
):
29 if self
._enable
_proxy
:
30 measurements
.WaitForViaHeader(tab
)
31 tab
.ClearCache(force
=True)
32 self
._metrics
.Start(page
, tab
)
34 def ValidateAndMeasurePage(self
, page
, tab
, results
):
35 # Wait for the load event.
36 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
37 self
._metrics
.Stop(page
, tab
)
38 self
._metrics
.AddResultsForDataSaving(tab
, results
)
41 class ChromeProxyHeaders(ChromeProxyValidation
):
42 """Correctness measurement for response headers."""
45 super(ChromeProxyHeaders
, self
).__init
__(
46 restart_after_each_page
=True,
47 metrics
=metrics
.ChromeProxyMetric())
49 def AddResults(self
, tab
, results
):
50 self
._metrics
.AddResultsForHeaderValidation(tab
, results
)
53 class ChromeProxyBypass(ChromeProxyValidation
):
54 """Correctness measurement for bypass responses."""
57 super(ChromeProxyBypass
, self
).__init
__(
58 restart_after_each_page
=True,
59 metrics
=metrics
.ChromeProxyMetric())
61 def AddResults(self
, tab
, results
):
62 self
._metrics
.AddResultsForBypass(tab
, results
)
65 class ChromeProxyCorsBypass(ChromeProxyValidation
):
66 """Correctness measurement for bypass responses for CORS requests."""
69 super(ChromeProxyCorsBypass
, self
).__init
__(
70 restart_after_each_page
=True,
71 metrics
=metrics
.ChromeProxyMetric())
73 def ValidateAndMeasurePage(self
, page
, tab
, results
):
74 # The test page sets window.xhrRequestCompleted to true when the XHR fetch
76 tab
.WaitForJavaScriptExpression('window.xhrRequestCompleted', 300)
77 super(ChromeProxyCorsBypass
,
78 self
).ValidateAndMeasurePage(page
, tab
, results
)
80 def AddResults(self
, tab
, results
):
81 self
._metrics
.AddResultsForCorsBypass(tab
, results
)
84 class ChromeProxyBlockOnce(ChromeProxyValidation
):
85 """Correctness measurement for block-once responses."""
88 super(ChromeProxyBlockOnce
, self
).__init
__(
89 restart_after_each_page
=True,
90 metrics
=metrics
.ChromeProxyMetric())
92 def AddResults(self
, tab
, results
):
93 self
._metrics
.AddResultsForBlockOnce(tab
, results
)
96 class ChromeProxySafebrowsingOn(ChromeProxyValidation
):
97 """Correctness measurement for safebrowsing."""
100 super(ChromeProxySafebrowsingOn
, self
).__init
__(
101 metrics
=metrics
.ChromeProxyMetric())
103 def AddResults(self
, tab
, results
):
104 self
._metrics
.AddResultsForSafebrowsingOn(tab
, results
)
106 class ChromeProxySafebrowsingOff(ChromeProxyValidation
):
107 """Correctness measurement for safebrowsing."""
110 super(ChromeProxySafebrowsingOff
, self
).__init
__(
111 metrics
=metrics
.ChromeProxyMetric())
113 def AddResults(self
, tab
, results
):
114 self
._metrics
.AddResultsForSafebrowsingOff(tab
, results
)
116 _FAKE_PROXY_AUTH_VALUE
= 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
117 _TEST_SERVER
= 'chromeproxy-test.appspot.com'
118 _TEST_SERVER_DEFAULT_URL
= 'http://' + _TEST_SERVER
+ '/default'
121 # We rely on the chromeproxy-test server to facilitate some of the tests.
122 # The test server code is at <TBD location> and runs at _TEST_SERVER
124 # The test server allow request to override response status, headers, and
125 # body through query parameters. See GetResponseOverrideURL.
126 def GetResponseOverrideURL(url
=_TEST_SERVER_DEFAULT_URL
, respStatus
=0,
127 respHeader
="", respBody
=""):
128 """ Compose the request URL with query parameters to override
129 the chromeproxy-test server response.
134 queries
.append('respStatus=%d' % respStatus
)
136 queries
.append('respHeader=%s' % base64
.b64encode(respHeader
))
138 queries
.append('respBody=%s' % base64
.b64encode(respBody
))
139 if len(queries
) == 0:
142 # url has query already
143 if urlparse
.urlparse(url
).query
:
144 return url
+ '&' + "&".join(queries
)
146 return url
+ '?' + "&".join(queries
)
149 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation
):
150 """Correctness measurement for proxy fallback.
152 In this test, the probe URL does not return 'OK'. Chrome is expected
153 to use the fallback proxy.
157 super(ChromeProxyHTTPFallbackProbeURL
, self
).__init
__(
158 restart_after_each_page
=True,
159 metrics
=metrics
.ChromeProxyMetric())
161 def CustomizeBrowserOptions(self
, options
):
162 super(ChromeProxyHTTPFallbackProbeURL
,
163 self
).CustomizeBrowserOptions(options
)
164 # Set the secure proxy check URL to the google.com favicon, which will be
165 # interpreted as a secure proxy check failure since the response body is not
166 # "OK". The google.com favicon is used because it will load reliably fast,
167 # and there have been problems with chromeproxy-test.appspot.com being slow
168 # and causing tests to flake.
169 options
.AppendExtraBrowserArgs(
170 '--data-reduction-proxy-secure-proxy-check-url='
171 'http://www.google.com/favicon.ico')
173 def AddResults(self
, tab
, results
):
174 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
177 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation
):
178 """Correctness measurement for proxy fallback.
180 In this test, the configured proxy is the chromeproxy-test server which
181 will send back a response without the expected Via header. Chrome is
182 expected to use the fallback proxy and add the configured proxy to the
187 super(ChromeProxyHTTPFallbackViaHeader
, self
).__init
__(
188 restart_after_each_page
=True,
189 metrics
=metrics
.ChromeProxyMetric())
191 def CustomizeBrowserOptions(self
, options
):
192 super(ChromeProxyHTTPFallbackViaHeader
,
193 self
).CustomizeBrowserOptions(options
)
194 options
.AppendExtraBrowserArgs('--ignore-certificate-errors')
195 options
.AppendExtraBrowserArgs(
196 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER
)
198 def AddResults(self
, tab
, results
):
199 self
._metrics
.AddResultsForHTTPFallback(tab
, results
)
202 class ChromeProxyClientVersion(ChromeProxyValidation
):
203 """Correctness measurement for version directives in Chrome-Proxy header.
205 The test verifies that the version information provided in the Chrome-Proxy
206 request header overrides any version, if specified, that is provided in the
211 super(ChromeProxyClientVersion
, self
).__init
__(
212 metrics
=metrics
.ChromeProxyMetric())
214 def CustomizeBrowserOptions(self
, options
):
215 super(ChromeProxyClientVersion
,
216 self
).CustomizeBrowserOptions(options
)
217 options
.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"')
219 def AddResults(self
, tab
, results
):
220 self
._metrics
.AddResultsForClientVersion(tab
, results
)
223 class ChromeProxyClientType(ChromeProxyValidation
):
224 """Correctness measurement for Chrome-Proxy header client type directives."""
227 super(ChromeProxyClientType
, self
).__init
__(
228 restart_after_each_page
=True,
229 metrics
=metrics
.ChromeProxyMetric())
230 self
._chrome
_proxy
_client
_type
= None
232 def AddResults(self
, tab
, results
):
233 # Get the Chrome-Proxy client type from the first page in the page set, so
234 # that the client type value can be used to determine which of the later
235 # pages in the page set should be bypassed.
236 if not self
._chrome
_proxy
_client
_type
:
237 client_type
= self
._metrics
.GetClientTypeFromRequests(tab
)
239 self
._chrome
_proxy
_client
_type
= client_type
241 self
._metrics
.AddResultsForClientType(tab
,
243 self
._chrome
_proxy
_client
_type
,
244 self
._page
.bypass_for_client_type
)
247 class ChromeProxyLoFi(ChromeProxyValidation
):
248 """Correctness measurement for Lo-Fi in Chrome-Proxy header."""
251 super(ChromeProxyLoFi
, self
).__init
__(restart_after_each_page
=True,
252 metrics
=metrics
.ChromeProxyMetric())
254 def CustomizeBrowserOptions(self
, options
):
255 super(ChromeProxyLoFi
, self
).CustomizeBrowserOptions(options
)
256 options
.AppendExtraBrowserArgs('--data-reduction-proxy-lo-fi=always-on')
258 def AddResults(self
, tab
, results
):
259 self
._metrics
.AddResultsForLoFi(tab
, results
)
261 class ChromeProxyExpDirective(ChromeProxyValidation
):
262 """Correctness measurement for experiment directives in Chrome-Proxy header.
264 This test verifies that "exp=test" in the Chrome-Proxy request header
265 causes a bypass on the experiment test page.
269 super(ChromeProxyExpDirective
, self
).__init
__(
270 restart_after_each_page
=True,
271 metrics
=metrics
.ChromeProxyMetric())
273 def CustomizeBrowserOptions(self
, options
):
274 super(ChromeProxyExpDirective
, self
).CustomizeBrowserOptions(options
)
275 options
.AppendExtraBrowserArgs('--data-reduction-proxy-experiment=test')
277 def AddResults(self
, tab
, results
):
278 self
._metrics
.AddResultsForBypass(tab
, results
, url_pattern
='/exp/')
280 class ChromeProxyPassThrough(ChromeProxyValidation
):
281 """Correctness measurement for Chrome-Proxy pass-through directives.
283 This test verifies that "pass-through" in the Chrome-Proxy request header
284 causes a resource to be loaded without Data Reduction Proxy transformations.
288 super(ChromeProxyPassThrough
, self
).__init
__(
289 restart_after_each_page
=True,
290 metrics
=metrics
.ChromeProxyMetric())
292 def CustomizeBrowserOptions(self
, options
):
293 super(ChromeProxyPassThrough
, self
).CustomizeBrowserOptions(options
)
295 def AddResults(self
, tab
, results
):
296 self
._metrics
.AddResultsForPassThrough(tab
, results
)
298 class ChromeProxyHTTPToDirectFallback(ChromeProxyValidation
):
299 """Correctness measurement for HTTP proxy fallback to direct."""
302 super(ChromeProxyHTTPToDirectFallback
, self
).__init
__(
303 restart_after_each_page
=True,
304 metrics
=metrics
.ChromeProxyMetric())
306 def CustomizeBrowserOptions(self
, options
):
307 super(ChromeProxyHTTPToDirectFallback
,
308 self
).CustomizeBrowserOptions(options
)
309 # Set the primary proxy to something that will fail to be resolved so that
310 # this test will run using the HTTP fallback proxy.
311 options
.AppendExtraBrowserArgs(
312 '--spdy-proxy-auth-origin=http://nonexistent.googlezip.net')
314 def WillNavigateToPage(self
, page
, tab
):
315 super(ChromeProxyHTTPToDirectFallback
, self
).WillNavigateToPage(page
, tab
)
316 # Attempt to load a page through the nonexistent primary proxy in order to
317 # cause a proxy fallback, and have this test run starting from the HTTP
319 tab
.Navigate(_TEST_SERVER_DEFAULT_URL
)
320 tab
.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
322 def AddResults(self
, tab
, results
):
323 self
._metrics
.AddResultsForHTTPToDirectFallback(tab
, results
, _TEST_SERVER
)
326 class ChromeProxyReenableAfterBypass(ChromeProxyValidation
):
327 """Correctness measurement for re-enabling proxies after bypasses.
329 This test loads a page that causes all data reduction proxies to be bypassed
330 for 1 to 5 minutes, then waits 5 minutes and verifies that the proxy is no
335 super(ChromeProxyReenableAfterBypass
, self
).__init
__(
336 restart_after_each_page
=True,
337 metrics
=metrics
.ChromeProxyMetric())
339 def AddResults(self
, tab
, results
):
340 self
._metrics
.AddResultsForReenableAfterBypass(
341 tab
, results
, self
._page
.bypass_seconds_min
,
342 self
._page
.bypass_seconds_max
)
345 class ChromeProxySmoke(ChromeProxyValidation
):
346 """Smoke measurement for basic chrome proxy correctness."""
349 super(ChromeProxySmoke
, self
).__init
__(restart_after_each_page
=True,
350 metrics
=metrics
.ChromeProxyMetric())
352 def AddResults(self
, tab
, results
):
353 # Map a page name to its AddResults func.
355 'header validation': [self
._metrics
.AddResultsForHeaderValidation
],
356 'compression: image': [
357 self
._metrics
.AddResultsForHeaderValidation
,
358 self
._metrics
.AddResultsForDataSaving
,
360 'compression: javascript': [
361 self
._metrics
.AddResultsForHeaderValidation
,
362 self
._metrics
.AddResultsForDataSaving
,
364 'compression: css': [
365 self
._metrics
.AddResultsForHeaderValidation
,
366 self
._metrics
.AddResultsForDataSaving
,
368 'bypass': [self
._metrics
.AddResultsForBypass
],
370 if not self
._page
.name
in page_to_metrics
:
371 raise page_test
.MeasurementFailure(
372 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
373 self
._page
.name
, page_to_metrics
.keys()))
374 for add_result
in page_to_metrics
[self
._page
.name
]:
375 add_result(tab
, results
)
378 PROXIED
= metrics
.PROXIED
379 DIRECT
= metrics
.DIRECT
381 class ChromeProxyClientConfig(ChromeProxyValidation
):
382 """Chrome proxy client configuration service validation."""
385 super(ChromeProxyClientConfig
, self
).__init
__(
386 restart_after_each_page
=True,
387 metrics
=metrics
.ChromeProxyMetric())
389 def CustomizeBrowserOptions(self
, options
):
390 super(ChromeProxyClientConfig
, self
).CustomizeBrowserOptions(options
)
391 options
.AppendExtraBrowserArgs(
392 '--enable-data-reduction-proxy-config-client')
394 def AddResults(self
, tab
, results
):
395 self
._metrics
.AddResultsForClientConfig(tab
, results
)
397 class ChromeProxyVideoValidation(page_test
.PageTest
):
398 """Validation for video pages.
400 Measures pages using metrics.ChromeProxyVideoMetric. Pages can be fetched
401 either direct from the origin server or via the proxy. If a page is fetched
402 both ways, then the PROXIED and DIRECT measurements are compared to ensure
403 the same video was loaded in both cases.
407 super(ChromeProxyVideoValidation
, self
).__init
__(
408 needs_browser_restart_after_each_page
=True,
409 clear_cache_before_each_run
=True)
410 # The type is _allMetrics[url][PROXIED,DIRECT][metricName] = value,
411 # where (metricName,value) is a metric computed by videowrapper.js.
412 self
._allMetrics
= {}
414 def WillNavigateToPage(self
, page
, tab
):
415 if page
.use_chrome_proxy
:
416 measurements
.WaitForViaHeader(tab
)
417 super(ChromeProxyVideoValidation
, self
).WillNavigateToPage(page
, tab
)
419 def DidNavigateToPage(self
, page
, tab
):
420 self
._currMetrics
= metrics
.ChromeProxyVideoMetric(tab
)
421 self
._currMetrics
.Start(page
, tab
)
423 def ValidateAndMeasurePage(self
, page
, tab
, results
):
424 assert self
._currMetrics
425 self
._currMetrics
.Stop(page
, tab
)
426 if page
.url
not in self
._allMetrics
:
427 self
._allMetrics
[page
.url
] = {}
430 if page
.use_chrome_proxy
:
431 self
._currMetrics
.AddResultsForProxied(tab
, results
)
432 self
._allMetrics
[page
.url
][PROXIED
] = self
._currMetrics
.videoMetrics
434 self
._currMetrics
.AddResultsForDirect(tab
, results
)
435 self
._allMetrics
[page
.url
][DIRECT
] = self
._currMetrics
.videoMetrics
436 self
._currMetrics
= None
438 # Compare proxied and direct results for this url, if they exist.
439 m
= self
._allMetrics
[page
.url
]
440 if PROXIED
in m
and DIRECT
in m
:
441 self
._CompareProxiedAndDirectMetrics
(page
.url
, m
[PROXIED
], m
[DIRECT
])
443 def _CompareProxiedAndDirectMetrics(self
, url
, pm
, dm
):
444 """Compare metrics from PROXIED and DIRECT fetches.
446 Compares video metrics computed by videowrapper.js for pages that were
447 fetch both PROXIED and DIRECT.
450 url: The url for the page being tested.
451 pm: Metrics when loaded by the Flywheel proxy.
452 dm: Metrics when loaded directly from the origin server.
455 ChromeProxyMetricException on failure.
458 raise ChromeProxyMetricException
, s
461 err('Proxied page did not load video: %s' % page
.url
)
463 err('Direct page did not load video: %s' % page
.url
)
465 # Compare metrics that should match for PROXIED and DIRECT.
466 for x
in ('video_height', 'video_width', 'video_duration',
469 err('Proxied page has no %s: %s' % (x
, page
.url
))
471 err('Direct page has no %s: %s' % (x
, page
.url
))
473 err('Mismatch for %s (proxied=%s direct=%s): %s' %
474 (x
, str(pm
[x
]), str(dm
[x
]), page
.url
))
476 # Proxied XOCL should match direct CL.
477 pxocl
= pm
['x_original_content_length_header']
478 dcl
= dm
['content_length_header']
480 err('Mismatch for content length (proxied=%s direct=%s): %s' %
481 (str(pxocl
), str(dcl
), page
.url
))
483 class ChromeProxyInstrumentedVideoValidation(page_test
.PageTest
):
484 """Tests a specially instrumented page for correct video transcoding."""
487 super(ChromeProxyInstrumentedVideoValidation
, self
).__init
__(
488 needs_browser_restart_after_each_page
=True,
489 clear_cache_before_each_run
=True)
490 self
._metrics
= metrics
.ChromeProxyInstrumentedVideoMetric()
492 def CustomizeBrowserOptions(self
, options
):
493 options
.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
495 def WillNavigateToPage(self
, page
, tab
):
496 measurements
.WaitForViaHeader(tab
)
497 tab
.ClearCache(force
=True)
498 self
._metrics
.Start(page
, tab
)
500 def ValidateAndMeasurePage(self
, page
, tab
, results
):
501 self
._metrics
.Stop(page
, tab
)
502 self
._metrics
.AddResults(tab
, results
)