tuple: update to make use of C++11
[chromium-blink-merge.git] / tools / chrome_proxy / integration_tests / chrome_proxy_measurements.py
blob1be3f9254c13ca578ffaa2bce8cd37407d31aafb
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 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()
54 self._page = None
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)
64 assert self._metrics
65 self._metrics.Start(page, tab)
67 def ValidateAndMeasurePage(self, page, tab, results):
68 self._page = page
69 # Wait for the load event.
70 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
71 assert self._metrics
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:
80 return True
81 return False
83 def RunNavigateSteps(self, page, tab):
84 # The redirect from safebrowsing causes a timeout. Ignore that.
85 try:
86 super(ChromeProxyValidation, self).RunNavigateSteps(page, tab)
87 except exceptions.DevtoolsTargetCrashException, e:
88 if self._expect_timeout:
89 logging.warning('Navigation timeout on page %s',
90 page.name if page.name else page.url)
91 else:
92 raise e
95 class ChromeProxyHeaders(ChromeProxyValidation):
96 """Correctness measurement for response headers."""
98 def __init__(self):
99 super(ChromeProxyHeaders, self).__init__(restart_after_each_page=True)
101 def AddResults(self, tab, results):
102 self._metrics.AddResultsForHeaderValidation(tab, results)
105 class ChromeProxyBypass(ChromeProxyValidation):
106 """Correctness measurement for bypass responses."""
108 def __init__(self):
109 super(ChromeProxyBypass, self).__init__(restart_after_each_page=True)
111 def AddResults(self, tab, results):
112 self._metrics.AddResultsForBypass(tab, results)
115 class ChromeProxyCorsBypass(ChromeProxyValidation):
116 """Correctness measurement for bypass responses for CORS requests."""
118 def __init__(self):
119 super(ChromeProxyCorsBypass, self).__init__(restart_after_each_page=True)
121 def ValidateAndMeasurePage(self, page, tab, results):
122 # The test page sets window.xhrRequestCompleted to true when the XHR fetch
123 # finishes.
124 tab.WaitForJavaScriptExpression('window.xhrRequestCompleted', 300)
125 super(ChromeProxyCorsBypass,
126 self).ValidateAndMeasurePage(page, tab, results)
128 def AddResults(self, tab, results):
129 self._metrics.AddResultsForCorsBypass(tab, results)
132 class ChromeProxyBlockOnce(ChromeProxyValidation):
133 """Correctness measurement for block-once responses."""
135 def __init__(self):
136 super(ChromeProxyBlockOnce, self).__init__(restart_after_each_page=True)
138 def AddResults(self, tab, results):
139 self._metrics.AddResultsForBlockOnce(tab, results)
142 class ChromeProxySafebrowsing(ChromeProxyValidation):
143 """Correctness measurement for safebrowsing."""
145 def __init__(self):
146 super(ChromeProxySafebrowsing, self).__init__()
148 def WillNavigateToPage(self, page, tab):
149 super(ChromeProxySafebrowsing, self).WillNavigateToPage(page, tab)
150 self._expect_timeout = True
152 def AddResults(self, tab, results):
153 self._metrics.AddResultsForSafebrowsing(tab, results)
156 _FAKE_PROXY_AUTH_VALUE = 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
157 _TEST_SERVER = 'chromeproxy-test.appspot.com'
158 _TEST_SERVER_DEFAULT_URL = 'http://' + _TEST_SERVER + '/default'
161 # We rely on the chromeproxy-test server to facilitate some of the tests.
162 # The test server code is at <TBD location> and runs at _TEST_SERVER
164 # The test server allow request to override response status, headers, and
165 # body through query parameters. See GetResponseOverrideURL.
166 def GetResponseOverrideURL(url=_TEST_SERVER_DEFAULT_URL, respStatus=0,
167 respHeader="", respBody=""):
168 """ Compose the request URL with query parameters to override
169 the chromeproxy-test server response.
172 queries = []
173 if respStatus > 0:
174 queries.append('respStatus=%d' % respStatus)
175 if respHeader:
176 queries.append('respHeader=%s' % base64.b64encode(respHeader))
177 if respBody:
178 queries.append('respBody=%s' % base64.b64encode(respBody))
179 if len(queries) == 0:
180 return url
181 "&".join(queries)
182 # url has query already
183 if urlparse.urlparse(url).query:
184 return url + '&' + "&".join(queries)
185 else:
186 return url + '?' + "&".join(queries)
189 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation):
190 """Correctness measurement for proxy fallback.
192 In this test, the probe URL does not return 'OK'. Chrome is expected
193 to use the fallback proxy.
196 def __init__(self):
197 super(ChromeProxyHTTPFallbackProbeURL, self).__init__()
199 def CustomizeBrowserOptions(self, options):
200 super(ChromeProxyHTTPFallbackProbeURL,
201 self).CustomizeBrowserOptions(options)
202 # Use the test server probe URL which returns the response
203 # body as specified by respBody.
204 probe_url = GetResponseOverrideURL(
205 respBody='not OK')
206 options.AppendExtraBrowserArgs(
207 '--data-reduction-proxy-probe-url=%s' % probe_url)
209 def AddResults(self, tab, results):
210 self._metrics.AddResultsForHTTPFallback(tab, results)
213 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation):
214 """Correctness measurement for proxy fallback.
216 In this test, the configured proxy is the chromeproxy-test server which
217 will send back a response without the expected Via header. Chrome is
218 expected to use the fallback proxy and add the configured proxy to the
219 bad proxy list.
222 def __init__(self):
223 super(ChromeProxyHTTPFallbackViaHeader, self).__init__(
224 restart_after_each_page=True)
226 def CustomizeBrowserOptions(self, options):
227 super(ChromeProxyHTTPFallbackViaHeader,
228 self).CustomizeBrowserOptions(options)
229 options.AppendExtraBrowserArgs('--ignore-certificate-errors')
230 options.AppendExtraBrowserArgs(
231 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER)
232 options.AppendExtraBrowserArgs(
233 '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE)
235 def AddResults(self, tab, results):
236 proxies = [
237 _TEST_SERVER + ":80",
238 self._metrics.effective_proxies['fallback'],
239 self._metrics.effective_proxies['direct']]
240 bad_proxies = [_TEST_SERVER + ":80", metrics.PROXY_SETTING_HTTP]
241 self._metrics.AddResultsForHTTPFallback(tab, results, proxies, bad_proxies)
244 class ChromeProxyClientVersion(ChromeProxyValidation):
245 """Correctness measurement for version directives in Chrome-Proxy header.
247 The test verifies that the version information provided in the Chrome-Proxy
248 request header overrides any version, if specified, that is provided in the
249 user agent string.
252 def __init__(self):
253 super(ChromeProxyClientVersion, self).__init__()
255 def CustomizeBrowserOptions(self, options):
256 super(ChromeProxyClientVersion,
257 self).CustomizeBrowserOptions(options)
258 options.AppendExtraBrowserArgs('--user-agent="Chrome/32.0.1700.99"')
260 def AddResults(self, tab, results):
261 self._metrics.AddResultsForClientVersion(tab, results)
264 class ChromeProxyClientType(ChromeProxyValidation):
265 """Correctness measurement for Chrome-Proxy header client type directives."""
267 def __init__(self):
268 super(ChromeProxyClientType, self).__init__(restart_after_each_page=True)
269 self._chrome_proxy_client_type = None
271 def AddResults(self, tab, results):
272 # Get the Chrome-Proxy client type from the first page in the page set, so
273 # that the client type value can be used to determine which of the later
274 # pages in the page set should be bypassed.
275 if not self._chrome_proxy_client_type:
276 client_type = self._metrics.GetClientTypeFromRequests(tab)
277 if client_type:
278 self._chrome_proxy_client_type = client_type
280 self._metrics.AddResultsForClientType(tab,
281 results,
282 self._chrome_proxy_client_type,
283 self._page.bypass_for_client_type)
286 class ChromeProxyHTTPToDirectFallback(ChromeProxyValidation):
287 """Correctness measurement for HTTP proxy fallback to direct."""
289 def __init__(self):
290 super(ChromeProxyHTTPToDirectFallback, self).__init__(
291 restart_after_each_page=True)
293 def CustomizeBrowserOptions(self, options):
294 super(ChromeProxyHTTPToDirectFallback,
295 self).CustomizeBrowserOptions(options)
296 # Set the primary proxy to something that will fail to be resolved so that
297 # this test will run using the HTTP fallback proxy.
298 options.AppendExtraBrowserArgs(
299 '--spdy-proxy-auth-origin=http://nonexistent.googlezip.net')
301 def WillNavigateToPage(self, page, tab):
302 super(ChromeProxyHTTPToDirectFallback, self).WillNavigateToPage(page, tab)
303 # Attempt to load a page through the nonexistent primary proxy in order to
304 # cause a proxy fallback, and have this test run starting from the HTTP
305 # fallback proxy.
306 tab.Navigate(_TEST_SERVER_DEFAULT_URL)
307 tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
309 proxies = [
310 'nonexistent.googlezip.net:80',
311 self._metrics.effective_proxies['fallback'],
312 self._metrics.effective_proxies['direct']]
313 self._metrics.VerifyProxyInfo(tab, proxies, proxies[:1])
315 def AddResults(self, tab, results):
316 self._metrics.AddResultsForHTTPToDirectFallback(tab, results)
319 class ChromeProxyExplicitBypass(ChromeProxyValidation):
320 """Correctness measurement for explicit proxy bypasses.
322 In this test, the configured proxy is the chromeproxy-test server which
323 will send back a response without the expected Via header. Chrome is
324 expected to use the fallback proxy and add the configured proxy to the
325 bad proxy list.
328 def __init__(self):
329 super(ChromeProxyExplicitBypass, self).__init__(
330 restart_after_each_page=True)
332 def CustomizeBrowserOptions(self, options):
333 super(ChromeProxyExplicitBypass,
334 self).CustomizeBrowserOptions(options)
335 options.AppendExtraBrowserArgs('--ignore-certificate-errors')
336 options.AppendExtraBrowserArgs(
337 '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER)
338 options.AppendExtraBrowserArgs(
339 '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE)
341 def AddResults(self, tab, results):
342 bad_proxies = [{
343 'proxy': _TEST_SERVER + ':80',
344 'retry_seconds_low': self._page.bypass_seconds_low,
345 'retry_seconds_high': self._page.bypass_seconds_high
347 if self._page.num_bypassed_proxies == 2:
348 bad_proxies.append({
349 'proxy': self._metrics.effective_proxies['fallback'],
350 'retry_seconds_low': self._page.bypass_seconds_low,
351 'retry_seconds_high': self._page.bypass_seconds_high
353 else:
354 # Even if the test page only causes the primary proxy to be bypassed,
355 # Chrome will attempt to fetch the favicon for the test server through
356 # the data reduction proxy, which will cause a "block=0" bypass.
357 bad_proxies.append({'proxy': self._metrics.effective_proxies['fallback']})
359 self._metrics.AddResultsForExplicitBypass(tab, results, bad_proxies)
362 class ChromeProxySmoke(ChromeProxyValidation):
363 """Smoke measurement for basic chrome proxy correctness."""
365 def __init__(self):
366 super(ChromeProxySmoke, self).__init__()
368 def WillNavigateToPage(self, page, tab):
369 super(ChromeProxySmoke, self).WillNavigateToPage(page, tab)
370 if page.name == 'safebrowsing':
371 self._expect_timeout = True
373 def AddResults(self, tab, results):
374 # Map a page name to its AddResults func.
375 page_to_metrics = {
376 'header validation': [self._metrics.AddResultsForHeaderValidation],
377 'compression: image': [
378 self._metrics.AddResultsForHeaderValidation,
379 self._metrics.AddResultsForDataSaving,
381 'compression: javascript': [
382 self._metrics.AddResultsForHeaderValidation,
383 self._metrics.AddResultsForDataSaving,
385 'compression: css': [
386 self._metrics.AddResultsForHeaderValidation,
387 self._metrics.AddResultsForDataSaving,
389 'bypass': [self._metrics.AddResultsForBypass],
390 'safebrowsing': [self._metrics.AddResultsForSafebrowsing],
392 if not self._page.name in page_to_metrics:
393 raise page_test.MeasurementFailure(
394 'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
395 self._page.name, page_to_metrics.keys()))
396 for add_result in page_to_metrics[self._page.name]:
397 add_result(tab, results)