Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import os | |
| 6 import time | 7 import time |
| 7 | 8 |
| 8 from integration_tests import network_metrics | 9 from integration_tests import network_metrics |
| 9 from telemetry.page import page_test | 10 from telemetry.page import page_test |
| 10 from telemetry.value import scalar | 11 from telemetry.value import scalar |
| 11 | 12 |
| 12 | 13 |
| 13 class ChromeProxyMetricException(page_test.MeasurementFailure): | 14 class ChromeProxyMetricException(page_test.MeasurementFailure): |
| 14 pass | 15 pass |
| 15 | 16 |
| 16 | 17 |
| 17 CHROME_PROXY_VIA_HEADER = 'Chrome-Compression-Proxy' | 18 CHROME_PROXY_VIA_HEADER = 'Chrome-Compression-Proxy' |
| 18 | 19 |
| 19 | 20 |
| 20 class ChromeProxyResponse(network_metrics.HTTPResponse): | 21 class ChromeProxyResponse(network_metrics.HTTPResponse): |
| 21 """ Represents an HTTP response from a timeleine event.""" | 22 """ Represents an HTTP response from a timeline event.""" |
| 22 def __init__(self, event): | 23 def __init__(self, event): |
| 23 super(ChromeProxyResponse, self).__init__(event) | 24 super(ChromeProxyResponse, self).__init__(event) |
| 24 | 25 |
| 25 def ShouldHaveChromeProxyViaHeader(self): | 26 def ShouldHaveChromeProxyViaHeader(self): |
| 26 resp = self.response | 27 resp = self.response |
| 27 # Ignore https and data url | 28 # Ignore https and data url |
| 28 if resp.url.startswith('https') or resp.url.startswith('data:'): | 29 if resp.url.startswith('https') or resp.url.startswith('data:'): |
| 29 return False | 30 return False |
| 30 # Ignore 304 Not Modified and cache hit. | 31 # Ignore 304 Not Modified and cache hit. |
| 31 if resp.status == 304 or resp.served_from_cache: | 32 if resp.status == 304 or resp.served_from_cache: |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 81 def HasChromeProxyLoFi(self): | 82 def HasChromeProxyLoFi(self): |
| 82 if 'Chrome-Proxy' not in self.response.request_headers: | 83 if 'Chrome-Proxy' not in self.response.request_headers: |
| 83 return False | 84 return False |
| 84 chrome_proxy_request_header = self.response.request_headers['Chrome-Proxy'] | 85 chrome_proxy_request_header = self.response.request_headers['Chrome-Proxy'] |
| 85 values = [v.strip() for v in chrome_proxy_request_header.split(',')] | 86 values = [v.strip() for v in chrome_proxy_request_header.split(',')] |
| 86 for value in values: | 87 for value in values: |
| 87 if len(value) == 5 and value == 'q=low': | 88 if len(value) == 5 and value == 'q=low': |
| 88 return True | 89 return True |
| 89 return False | 90 return False |
| 90 | 91 |
| 92 | |
| 91 class ChromeProxyMetric(network_metrics.NetworkMetric): | 93 class ChromeProxyMetric(network_metrics.NetworkMetric): |
| 92 """A Chrome proxy timeline metric.""" | 94 """A Chrome proxy timeline metric.""" |
| 93 | 95 |
| 94 def __init__(self): | 96 def __init__(self): |
| 95 super(ChromeProxyMetric, self).__init__() | 97 super(ChromeProxyMetric, self).__init__() |
| 96 self.compute_data_saving = True | 98 self.compute_data_saving = True |
| 97 | 99 |
| 98 def SetEvents(self, events): | 100 def SetEvents(self, events): |
| 99 """Used for unittest.""" | 101 """Used for unittest.""" |
| 100 self._events = events | 102 self._events = events |
| (...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 573 via_count += 1 | 575 via_count += 1 |
| 574 if via_count == 0: | 576 if via_count == 0: |
| 575 raise ChromeProxyMetricException, ( | 577 raise ChromeProxyMetricException, ( |
| 576 'Expected at least one response through the proxy after the bypass ' | 578 'Expected at least one response through the proxy after the bypass ' |
| 577 'expired, but zero such responses were received.') | 579 'expired, but zero such responses were received.') |
| 578 | 580 |
| 579 results.AddValue(scalar.ScalarValue( | 581 results.AddValue(scalar.ScalarValue( |
| 580 results.current_page, 'bypass', 'count', bypass_count)) | 582 results.current_page, 'bypass', 'count', bypass_count)) |
| 581 results.AddValue(scalar.ScalarValue( | 583 results.AddValue(scalar.ScalarValue( |
| 582 results.current_page, 'via', 'count', via_count)) | 584 results.current_page, 'via', 'count', via_count)) |
| 585 | |
| 586 | |
| 587 PROXIED = 'proxied' | |
| 588 DIRECT = 'direct' | |
| 589 | |
| 590 class ChromeProxyVideoMetric(network_metrics.NetworkMetric): | |
| 591 """Metrics for video pages. | |
| 592 | |
| 593 Wraps the video metrics produced by videowrapper.js. Also checks a few | |
|
sclittle
2015/05/12 18:05:40
Which metrics? You don't necessarily need to list
Tom Bergan
2015/05/20 00:42:05
Done.
| |
| 594 basic HTTP response headers such as Content-Type and Content-Length in | |
| 595 the video responses. | |
| 596 """ | |
| 597 | |
| 598 def __init__(self, tab): | |
| 599 super(ChromeProxyVideoMetric, self).__init__() | |
| 600 with open(os.path.join(os.path.dirname(__file__), 'videowrapper.js')) as f: | |
| 601 js = f.read() | |
| 602 tab.ExecuteJavaScript(js) | |
| 603 | |
| 604 def Start(self, page, tab): | |
| 605 tab.ExecuteJavaScript('window.__chromeProxyCreateVideoWrappers()') | |
| 606 self.videoMetrics = None | |
| 607 super(ChromeProxyVideoMetric, self).Start(page, tab) | |
| 608 | |
| 609 def Stop(self, page, tab): | |
| 610 tab.WaitForJavaScriptExpression('window.__chromeProxyVideoLoaded', 30) | |
| 611 m = tab.EvaluateJavaScript('window.__chromeProxyVideoMetrics') | |
| 612 | |
| 613 # Now wait for the video to stop playing. | |
| 614 # Give it 2x the total duration to account for buffering. | |
| 615 waitTime = 2 * m['video_duration'] | |
| 616 tab.WaitForJavaScriptExpression('window.__chromeProxyVideoEnded', waitTime) | |
| 617 | |
| 618 # Load the final metrics. | |
| 619 m = tab.EvaluateJavaScript('window.__chromeProxyVideoMetrics') | |
| 620 self.videoMetrics = m | |
| 621 # Cast this to an integer as it is often approximate (for an unknown reason) | |
| 622 m['video_duration'] = int(m['video_duration']) | |
| 623 super(ChromeProxyVideoMetric, self).Stop(page, tab) | |
| 624 | |
| 625 def ResponseFromEvent(self, event): | |
| 626 return ChromeProxyResponse(event) | |
| 627 | |
| 628 def AddResults(self, tab, results): | |
| 629 raise NotImplementedError | |
| 630 | |
| 631 def AddResultsForProxied(self, tab, results): | |
| 632 return self._AddResultsShared(PROXIED, tab, results) | |
| 633 | |
| 634 def AddResultsForDirect(self, tab, results): | |
| 635 return self._AddResultsShared(DIRECT, tab, results) | |
| 636 | |
| 637 def _AddResultsShared(self, kind, tab, results): | |
| 638 def err(s): | |
| 639 raise ChromeProxyMetricException, s | |
| 640 | |
| 641 # Should have played the video. | |
| 642 if not self.videoMetrics['ready']: | |
| 643 err('%s: video not played' % kind) | |
| 644 | |
| 645 # Should have an HTTP response for the video. | |
| 646 wantContentType = 'video/webm' if kind == PROXIED else 'video/mp4' | |
| 647 found = False | |
| 648 for r in self.IterResponses(tab): | |
| 649 resp = r.response | |
| 650 if kind == DIRECT and r.HasChromeProxyViaHeader(): | |
| 651 err('%s: page has proxied Via header' % kind) | |
| 652 if resp.GetHeader('Content-Type') != wantContentType: | |
| 653 continue | |
| 654 if found: | |
| 655 err('%s: multiple video responses' % kind) | |
| 656 found = True | |
| 657 | |
| 658 cl = resp.GetHeader('Content-Length') | |
| 659 xocl = resp.GetHeader('X-Original-Content-Length') | |
| 660 if cl != None: | |
| 661 self.videoMetrics['content_length_header'] = int(cl) | |
| 662 if xocl != None: | |
| 663 self.videoMetrics['x_original_content_length_header'] = int(xocl) | |
| 664 | |
| 665 # Should have CL always. | |
| 666 if cl == None: | |
| 667 err('%s: missing ContentLength' % kind) | |
| 668 # Proxied: should have CL < XOCL | |
| 669 # Direct: should not have XOCL | |
| 670 if kind == PROXIED: | |
| 671 if xocl == None or int(cl) >= int(xocl): | |
| 672 err('%s: bigger response (%s > %s)' % (kind, str(cl), str(xocl))) | |
| 673 else: | |
| 674 if xocl != None: | |
| 675 err('%s: has XOriginalContentLength' % kind) | |
| 676 | |
| 677 if not found: | |
| 678 err('%s: missing video response' % kind) | |
| 679 | |
| 680 # Finally, add all the metrics to the results. | |
| 681 for (k,v) in self.videoMetrics.iteritems(): | |
| 682 k = "%s_%s" % (k, kind) | |
| 683 results.AddValue(scalar.ScalarValue(results.current_page, k, "", v)) | |
| OLD | NEW |