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
5import base64
6import unittest
7
8from integration_tests import network_metrics
9from metrics import test_page_test_results
10from telemetry.timeline import event
11
12
13HTML_BODY = """<!DOCTYPE HTML>
14  <html>
15  <head> </head>
16 <body>
17 <div id="test"> TEST HTML</div>
18 </body>
19 </html>"""
20IMAGE_BODY = """fake image data"""
21GZIPPED_HTML_LEN = network_metrics.HTTPResponse.GetGizppedBodyLength(HTML_BODY)
22# Make up original content length for the image.
23IMAGE_OCL = 3 * len(IMAGE_BODY)
24
25
26class NetworkMetricTest(unittest.TestCase):
27  @staticmethod
28  def MakeNetworkTimelineEvent(
29      url, response_headers, body=None, base64_encoded_body=False,
30      served_from_cache=False, request_headers=None, status=200):
31    if not request_headers:
32      request_headers = {}
33    e = event.TimelineEvent('network', 'HTTPResponse', 0, 0)
34    e.args = {}
35    e.args['requestId'] = 0
36    e.args['response'] = {
37        'status': status,
38        'url': url,
39        'headers': response_headers,
40        'requestHeaders': request_headers,
41        }
42    e.args['body'] = body
43    e.args['base64_encoded_body'] = base64_encoded_body
44    e.args['served_from_cache'] = served_from_cache
45    return e
46
47  def testHTTPResponse(self):
48    url = 'http://test.url'
49    self.assertLess(GZIPPED_HTML_LEN, len(HTML_BODY))
50
51    # A plain text HTML response
52    resp = network_metrics.HTTPResponse(self.MakeNetworkTimelineEvent(
53        url=url,
54        response_headers={
55            'Content-Type': 'text/html',
56            'Content-Length': str(len(HTML_BODY)),
57            },
58        body=HTML_BODY))
59    self.assertEqual(url, resp.response.url)
60    body, base64_encoded = resp.response.GetBody()
61    self.assertEqual(HTML_BODY, body)
62    self.assertFalse(base64_encoded)
63    self.assertEqual('text/html', resp.response.GetHeader('Content-Type'))
64
65    self.assertEqual(len(HTML_BODY), resp.content_length)
66    self.assertEqual(None, resp.response.GetHeader('Content-Encoding'))
67    self.assertFalse(resp.has_original_content_length)
68    self.assertEqual(0.0, resp.data_saving_rate)
69
70    # A gzipped HTML response
71    resp = network_metrics.HTTPResponse(self.MakeNetworkTimelineEvent(
72        url=url,
73        response_headers={
74            'Content-Type': 'text/html',
75            'Content-Encoding': 'gzip',
76            'X-Original-Content-Length': str(len(HTML_BODY)),
77            },
78        body=HTML_BODY))
79    body, base64_encoded = resp.response.GetBody()
80    self.assertFalse(base64_encoded)
81    self.assertEqual(GZIPPED_HTML_LEN, resp.content_length)
82    self.assertEqual('gzip', resp.response.GetHeader('Content-Encoding'))
83    self.assertTrue(resp.has_original_content_length)
84    self.assertEqual(len(HTML_BODY), resp.original_content_length)
85    self.assertEqual(
86        float(len(HTML_BODY) - GZIPPED_HTML_LEN) / len(HTML_BODY),
87        resp.data_saving_rate)
88
89    # A JPEG image response.
90    resp = network_metrics.HTTPResponse(self.MakeNetworkTimelineEvent(
91        url='http://test.image',
92        response_headers={
93            'Content-Type': 'image/jpeg',
94            'Content-Encoding': 'gzip',
95            'X-Original-Content-Length': str(IMAGE_OCL),
96            },
97        body=base64.b64encode(IMAGE_BODY),
98        base64_encoded_body=True))
99    body, base64_encoded = resp.response.GetBody()
100    self.assertTrue(base64_encoded)
101    self.assertEqual(IMAGE_BODY, base64.b64decode(body))
102    self.assertEqual(len(IMAGE_BODY), resp.content_length)
103    self.assertTrue(resp.has_original_content_length)
104    self.assertEqual(IMAGE_OCL, resp.original_content_length)
105    self.assertFalse(resp.response.served_from_cache)
106    self.assertEqual(float(IMAGE_OCL - len(IMAGE_BODY)) / IMAGE_OCL,
107                     resp.data_saving_rate)
108
109    # A JPEG image response from cache.
110    resp = network_metrics.HTTPResponse(self.MakeNetworkTimelineEvent(
111        url='http://test.image',
112        response_headers={
113            'Content-Type': 'image/jpeg',
114            'Content-Encoding': 'gzip',
115            'X-Original-Content-Length': str(IMAGE_OCL),
116            },
117        body=base64.b64encode(IMAGE_BODY),
118        base64_encoded_body=True,
119        served_from_cache=True))
120    self.assertEqual(len(IMAGE_BODY), resp.content_length)
121    self.assertTrue(resp.has_original_content_length)
122    self.assertEqual(IMAGE_OCL, resp.original_content_length)
123    # Cached resource has zero saving.
124    self.assertTrue(resp.response.served_from_cache)
125    self.assertEqual(0.0, resp.data_saving_rate)
126
127  def testNetworkMetricResults(self):
128    events = [
129        # A plain text HTML.
130        self.MakeNetworkTimelineEvent(
131            url='http://test.html1',
132            response_headers={
133                'Content-Type': 'text/html',
134                'Content-Length': str(len(HTML_BODY)),
135                },
136            body=HTML_BODY),
137        # A compressed HTML.
138        self.MakeNetworkTimelineEvent(
139            url='http://test.html2',
140            response_headers={
141                'Content-Type': 'text/html',
142                'Content-Encoding': 'gzip',
143                'X-Original-Content-Length': str(len(HTML_BODY)),
144                },
145            body=HTML_BODY),
146        # A base64 encoded image.
147        self.MakeNetworkTimelineEvent(
148            url='http://test.image',
149            response_headers={
150                'Content-Type': 'image/jpeg',
151                'Content-Encoding': 'gzip',
152                'X-Original-Content-Length': str(IMAGE_OCL),
153                },
154            body=base64.b64encode(IMAGE_BODY),
155            base64_encoded_body=True),
156        ]
157    metric = network_metrics.NetworkMetric()
158    metric._events = events
159    metric.compute_data_saving = True
160
161    self.assertTrue(len(events), len(list(metric.IterResponses(None))))
162    results = test_page_test_results.TestPageTestResults(self)
163    metric.AddResults(None, results)
164
165    cl = len(HTML_BODY) + GZIPPED_HTML_LEN + len(IMAGE_BODY)
166    results.AssertHasPageSpecificScalarValue('content_length', 'bytes', cl)
167
168    ocl = len(HTML_BODY) + len(HTML_BODY) + IMAGE_OCL
169    results.AssertHasPageSpecificScalarValue(
170        'original_content_length', 'bytes', ocl)
171
172    saving_percent = float(ocl - cl) * 100/ ocl
173    results.AssertHasPageSpecificScalarValue(
174        'data_saving', 'percent', saving_percent)
175