Home | History | Annotate | Download | only in measurements
      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.
      4 
      5 import base64
      6 import logging
      7 import urlparse
      8 
      9 from metrics import chrome_proxy
     10 from metrics import loading
     11 from telemetry.core import util
     12 from telemetry.page import page_measurement
     13 
     14 
     15 class ChromeProxyLatency(page_measurement.PageMeasurement):
     16   """Chrome proxy latency measurement."""
     17 
     18   def __init__(self, *args, **kwargs):
     19     super(ChromeProxyLatency, self).__init__(*args, **kwargs)
     20 
     21   def WillNavigateToPage(self, page, tab):
     22     tab.ClearCache(force=True)
     23 
     24   def MeasurePage(self, page, tab, results):
     25     # Wait for the load event.
     26     tab.WaitForJavaScriptExpression('performance.timing.loadEventStart', 300)
     27     loading.LoadingMetric().AddResults(tab, results)
     28 
     29 
     30 class ChromeProxyDataSaving(page_measurement.PageMeasurement):
     31   """Chrome proxy data daving measurement."""
     32   def __init__(self, *args, **kwargs):
     33     super(ChromeProxyDataSaving, self).__init__(*args, **kwargs)
     34     self._metrics = chrome_proxy.ChromeProxyMetric()
     35 
     36   def WillNavigateToPage(self, page, tab):
     37     tab.ClearCache(force=True)
     38     self._metrics.Start(page, tab)
     39 
     40   def MeasurePage(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)
     45 
     46 
     47 class ChromeProxyValidation(page_measurement.PageMeasurement):
     48   """Base class for all chrome proxy correctness measurements."""
     49 
     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 = chrome_proxy.ChromeProxyMetric()
     54     self._page = None
     55     # Whether a timeout exception is expected during the test.
     56     self._expect_timeout = False
     57 
     58   def CustomizeBrowserOptions(self, options):
     59     # Enable the chrome proxy (data reduction proxy).
     60     options.AppendExtraBrowserArgs('--enable-spdy-proxy-auth')
     61 
     62   def WillNavigateToPage(self, page, tab):
     63     tab.ClearCache(force=True)
     64     assert self._metrics
     65     self._metrics.Start(page, tab)
     66 
     67   def MeasurePage(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)
     74 
     75   def AddResults(self, tab, results):
     76     raise NotImplementedError
     77 
     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
     82 
     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 util.TimeoutException, 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
     93 
     94 
     95 class ChromeProxyHeaders(ChromeProxyValidation):
     96   """Correctness measurement for response headers."""
     97 
     98   def __init__(self):
     99     super(ChromeProxyHeaders, self).__init__(restart_after_each_page=True)
    100 
    101   def AddResults(self, tab, results):
    102     self._metrics.AddResultsForHeaderValidation(tab, results)
    103 
    104 
    105 class ChromeProxyBypass(ChromeProxyValidation):
    106   """Correctness measurement for bypass responses."""
    107 
    108   def __init__(self):
    109     super(ChromeProxyBypass, self).__init__(restart_after_each_page=True)
    110 
    111   def AddResults(self, tab, results):
    112     self._metrics.AddResultsForBypass(tab, results)
    113 
    114 
    115 class ChromeProxySafebrowsing(ChromeProxyValidation):
    116   """Correctness measurement for safebrowsing."""
    117 
    118   def __init__(self):
    119     super(ChromeProxySafebrowsing, self).__init__()
    120 
    121   def WillNavigateToPage(self, page, tab):
    122     super(ChromeProxySafebrowsing, self).WillNavigateToPage(page, tab)
    123     self._expect_timeout = True
    124 
    125   def AddResults(self, tab, results):
    126     self._metrics.AddResultsForSafebrowsing(tab, results)
    127 
    128 
    129 _FAKE_PROXY_AUTH_VALUE = 'aabbccdd3b7579186c1b0620614fdb1f0000ffff'
    130 _TEST_SERVER = 'chromeproxy-test.appspot.com'
    131 _TEST_SERVER_DEFAULT_URL = 'http://' + _TEST_SERVER + '/default'
    132 
    133 
    134 # We rely on the chromeproxy-test server to facilitate some of the tests.
    135 # The test server code is at <TBD location> and runs at _TEST_SERVER
    136 #
    137 # The test server allow request to override response status, headers, and
    138 # body through query parameters. See GetResponseOverrideURL.
    139 def GetResponseOverrideURL(url, respStatus=0, respHeader="", respBody=""):
    140   """ Compose the request URL with query parameters to override
    141   the chromeproxy-test server response.
    142   """
    143 
    144   queries = []
    145   if respStatus > 0:
    146     queries.append('respStatus=%d' % respStatus)
    147   if respHeader:
    148     queries.append('respHeader=%s' % base64.b64encode(respHeader))
    149   if respBody:
    150     queries.append('respBody=%s' % base64.b64encode(respBody))
    151   if len(queries) == 0:
    152     return url
    153   "&".join(queries)
    154   # url has query already
    155   if urlparse.urlparse(url).query:
    156     return url + '&' + "&".join(queries)
    157   else:
    158     return url + '?' + "&".join(queries)
    159 
    160 
    161 class ChromeProxyHTTPFallbackProbeURL(ChromeProxyValidation):
    162   """Correctness measurement for proxy fallback.
    163 
    164   In this test, the probe URL does not return 'OK'. Chrome is expected
    165   to use the fallback proxy.
    166   """
    167 
    168   def __init__(self):
    169     super(ChromeProxyHTTPFallbackProbeURL, self).__init__()
    170 
    171   def CustomizeBrowserOptions(self, options):
    172     super(ChromeProxyHTTPFallbackProbeURL,
    173           self).CustomizeBrowserOptions(options)
    174     # Use the test server probe URL which returns the response
    175     # body as specified by respBody.
    176     probe_url = GetResponseOverrideURL(
    177         _TEST_SERVER_DEFAULT_URL,
    178         respBody='not OK')
    179     options.AppendExtraBrowserArgs(
    180         '--data-reduction-proxy-probe-url=%s' % probe_url)
    181 
    182   def AddResults(self, tab, results):
    183     self._metrics.AddResultsForHTTPFallback(tab, results)
    184 
    185 
    186 # Depends on the fix of http://crbug.com/330342.
    187 class ChromeProxyHTTPFallbackViaHeader(ChromeProxyValidation):
    188   """Correctness measurement for proxy fallback.
    189 
    190   In this test, the configured proxy is the chromeproxy-test server which
    191   will send back a response without the expected Via header. Chrome is
    192   expected to use the fallback proxy and add the configured proxy to the
    193   bad proxy list.
    194   """
    195 
    196   def __init__(self):
    197     super(ChromeProxyHTTPFallbackViaHeader, self).__init__()
    198 
    199   def CustomizeBrowserOptions(self, options):
    200     super(ChromeProxyHTTPFallbackViaHeader,
    201           self).CustomizeBrowserOptions(options)
    202     options.AppendExtraBrowserArgs('--ignore-certificate-errors')
    203     options.AppendExtraBrowserArgs(
    204         '--spdy-proxy-auth-origin=http://%s' % _TEST_SERVER)
    205     options.AppendExtraBrowserArgs(
    206         '--spdy-proxy-auth-value=%s' % _FAKE_PROXY_AUTH_VALUE)
    207 
    208   def AddResults(self, tab, results):
    209     proxies = [
    210         _TEST_SERVER + ":80",
    211         self._metrics.effective_proxies['fallback'],
    212         self._metrics.effective_proxies['direct']]
    213     bad_proxies = [_TEST_SERVER + ":80"]
    214     self._metrics.AddResultsForHTTPFallback(tab, results, proxies, bad_proxies)
    215 
    216 
    217 class ChromeProxySmoke(ChromeProxyValidation):
    218   """Smoke measurement for basic chrome proxy correctness."""
    219 
    220   def __init__(self):
    221     super(ChromeProxySmoke, self).__init__()
    222 
    223   def WillNavigateToPage(self, page, tab):
    224     super(ChromeProxySmoke, self).WillNavigateToPage(page, tab)
    225     if page.name == 'safebrowsing':
    226       self._expect_timeout = True
    227 
    228   def AddResults(self, tab, results):
    229     # Map a page name to its AddResults func.
    230     page_to_metrics = {
    231         'header validation': [self._metrics.AddResultsForHeaderValidation],
    232         'compression: image': [
    233             self._metrics.AddResultsForHeaderValidation,
    234             self._metrics.AddResultsForDataSaving,
    235             ],
    236         'compression: javascript': [
    237             self._metrics.AddResultsForHeaderValidation,
    238             self._metrics.AddResultsForDataSaving,
    239             ],
    240         'compression: css': [
    241             self._metrics.AddResultsForHeaderValidation,
    242             self._metrics.AddResultsForDataSaving,
    243             ],
    244         'bypass': [self._metrics.AddResultsForBypass],
    245         'safebrowsing': [self._metrics.AddResultsForSafebrowsing],
    246         }
    247     if not self._page.name in page_to_metrics:
    248       raise page_measurement.MeasurementFailure(
    249           'Invalid page name (%s) in smoke. Page name must be one of:\n%s' % (
    250           self._page.name, page_to_metrics.keys()))
    251     for add_result in page_to_metrics[self._page.name]:
    252       add_result(tab, results)
    253