Minor Python style clean-up
[chromium-blink-merge.git] / tools / chrome_proxy / integration_tests / chrome_proxy_measurements.py
blobe156d31ab49a2fd749b222453a52a82fcdf5be89
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.
5 import base64
6 import logging
7 import urlparse
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."""
44 def __init__(self):
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."""
56 def __init__(self):
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."""
68 def __init__(self):
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
75 # finishes.
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."""
87 def __init__(self):
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."""
99 def __init__(self):
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."""
109 def __init__(self):
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.
132 queries = []
133 if respStatus > 0:
134 queries.append('respStatus=%d' % respStatus)
135 if respHeader:
136 queries.append('respHeader=%s' % base64.b64encode(respHeader))
137 if respBody:
138 queries.append('respBody=%s' % base64.b64encode(respBody))
139 if len(queries) == 0:
140 return url
141 "&".join(queries)
142 # url has query already
143 if urlparse.urlparse(url).query:
144 return url + '&' + "&".join(queries)
145 else:
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.
156 def __init__(self):
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
183 bad proxy list.
186 def __init__(self):
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
207 user agent string.
210 def __init__(self):
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."""
226 def __init__(self):
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)
238 if client_type:
239 self._chrome_proxy_client_type = client_type
241 self._metrics.AddResultsForClientType(tab,
242 results,
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."""
250 def __init__(self):
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.
268 def __init__(self):
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.
287 def __init__(self):
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."""
301 def __init__(self):
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
318 # fallback proxy.
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
331 longer bypassed.
334 def __init__(self):
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."""
348 def __init__(self):
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.
354 page_to_metrics = {
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."""
384 def __init__(self):
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.
406 def __init__(self):
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] = {}
429 # Verify this page.
430 if page.use_chrome_proxy:
431 self._currMetrics.AddResultsForProxied(tab, results)
432 self._allMetrics[page.url][PROXIED] = self._currMetrics.videoMetrics
433 else:
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.
449 Args:
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.
454 Raises:
455 ChromeProxyMetricException on failure.
457 def err(s):
458 raise ChromeProxyMetricException, s
460 if not pm['ready']:
461 err('Proxied page did not load video: %s' % page.url)
462 if not dm['ready']:
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',
467 'decoded_frames'):
468 if x not in pm:
469 err('Proxied page has no %s: %s' % (x, page.url))
470 if x not in dm:
471 err('Direct page has no %s: %s' % (x, page.url))
472 if pm[x] != dm[x]:
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']
479 if pxocl != dcl:
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."""
486 def __init__(self):
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)