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 datetime | 5 import datetime |
6 import json | 6 import json |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import re | 9 import re |
10 | 10 |
| 11 from telemetry import value as value_module |
11 from telemetry.core import util | 12 from telemetry.core import util |
12 from telemetry.results import buildbot_output_formatter | 13 from telemetry.results import chart_json_output_formatter |
| 14 from telemetry.results import output_formatter |
13 from telemetry.util import cloud_storage | 15 from telemetry.util import cloud_storage |
14 | 16 |
15 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'util') | 17 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'util') |
16 import lastchange # pylint: disable=F0401 | 18 import lastchange # pylint: disable=F0401 |
17 | 19 |
18 | 20 |
19 _TEMPLATE_HTML_PATH = os.path.join( | 21 _TEMPLATE_HTML_PATH = os.path.join( |
20 util.GetTelemetryDir(), 'support', 'html_output', 'results-template.html') | 22 util.GetTelemetryDir(), 'support', 'html_output', 'results-template.html') |
21 _PLUGINS = [('third_party', 'flot', 'jquery.flot.min.js'), | 23 _PLUGINS = [('third_party', 'flot', 'jquery.flot.min.js'), |
22 ('third_party', 'WebKit', 'PerformanceTests', 'resources', | 24 ('third_party', 'WebKit', 'PerformanceTests', 'resources', |
23 'jquery.tablesorter.min.js'), | 25 'jquery.tablesorter.min.js'), |
24 ('third_party', 'WebKit', 'PerformanceTests', 'resources', | 26 ('third_party', 'WebKit', 'PerformanceTests', 'resources', |
25 'statistics.js')] | 27 'statistics.js')] |
26 _UNIT_JSON = ('tools', 'perf', 'unit-info.json') | 28 _UNIT_JSON = ('tools', 'perf', 'unit-info.json') |
27 | 29 |
28 | 30 |
29 # TODO(chrishenry): This should not really extend BuildbotOutputFormatter. | 31 # TODO(eakuefner): rewrite template to use Telemetry JSON directly |
30 # Leaving as-is now since we are going to move HtmlOutputFormatter to be | 32 class HtmlOutputFormatter(output_formatter.OutputFormatter): |
31 # based on JSON anyway. | |
32 class HtmlOutputFormatter(buildbot_output_formatter.BuildbotOutputFormatter): | |
33 def __init__(self, output_stream, metadata, reset_results, upload_results, | 33 def __init__(self, output_stream, metadata, reset_results, upload_results, |
34 browser_type, results_label=None, trace_tag=''): | 34 browser_type, results_label=None): |
35 # Pass output_stream=None so that we blow up if | 35 super(HtmlOutputFormatter, self).__init__(output_stream) |
36 # BuildbotOutputFormatter ever use the output_stream. | |
37 super(HtmlOutputFormatter, self).__init__(None, trace_tag) | |
38 self._metadata = metadata | 36 self._metadata = metadata |
39 self._reset_results = reset_results | 37 self._reset_results = reset_results |
40 self._upload_results = upload_results | 38 self._upload_results = upload_results |
41 self._html_output_stream = output_stream | |
42 self._existing_results = self._ReadExistingResults(output_stream) | 39 self._existing_results = self._ReadExistingResults(output_stream) |
43 self._result = { | 40 self._result = { |
44 'buildTime': self._GetBuildTime(), | 41 'buildTime': self._GetBuildTime(), |
45 'revision': self._GetRevision(), | 42 'revision': self._GetRevision(), |
46 'label': results_label, | 43 'label': results_label, |
47 'platform': browser_type, | 44 'platform': browser_type, |
48 'tests': {} | 45 'tests': {} |
49 } | 46 } |
50 | 47 |
51 def _GetBuildTime(self): | 48 def _GetBuildTime(self): |
(...skipping 25 matching lines...) Expand all Loading... |
77 return [] | 74 return [] |
78 m = re.search( | 75 m = re.search( |
79 '^<script id="results-json" type="application/json">(.*?)</script>$', | 76 '^<script id="results-json" type="application/json">(.*?)</script>$', |
80 results_html, re.MULTILINE | re.DOTALL) | 77 results_html, re.MULTILINE | re.DOTALL) |
81 if not m: | 78 if not m: |
82 logging.warn('Failed to extract previous results from HTML output') | 79 logging.warn('Failed to extract previous results from HTML output') |
83 return [] | 80 return [] |
84 return json.loads(m.group(1))[:512] | 81 return json.loads(m.group(1))[:512] |
85 | 82 |
86 def _SaveResults(self, results): | 83 def _SaveResults(self, results): |
87 self._html_output_stream.seek(0) | 84 self._output_stream.seek(0) |
88 self._html_output_stream.write(results) | 85 self._output_stream.write(results) |
89 self._html_output_stream.truncate() | 86 self._output_stream.truncate() |
90 | 87 |
91 def _PrintPerfResult(self, measurement, trace, values, units, | 88 def _PrintPerfResult(self, measurement, trace, values, units, |
92 result_type='default'): | 89 result_type='default'): |
93 metric_name = measurement | 90 metric_name = measurement |
94 if trace != measurement: | 91 if trace != measurement: |
95 metric_name += '.' + trace | 92 metric_name += '.' + trace |
96 self._result['tests'].setdefault(self._test_name, {}) | 93 self._result['tests'].setdefault(self._test_name, {}) |
97 self._result['tests'][self._test_name].setdefault('metrics', {}) | 94 self._result['tests'][self._test_name].setdefault('metrics', {}) |
98 self._result['tests'][self._test_name]['metrics'][metric_name] = { | 95 self._result['tests'][self._test_name]['metrics'][metric_name] = { |
99 'current': values, | 96 'current': values, |
100 'units': units, | 97 'units': units, |
101 'important': result_type == 'default' | 98 'important': result_type == 'default' |
102 } | 99 } |
103 | 100 |
| 101 def _TranslateChartJson(self, chart_json_dict): |
| 102 dummy_dict = dict() |
| 103 |
| 104 for chart_name, traces in chart_json_dict['charts'].iteritems(): |
| 105 for trace_name, value_dict in traces.iteritems(): |
| 106 # TODO(eakuefner): refactor summarization so we don't have to jump |
| 107 # through hoops like this. |
| 108 if 'page_id' in value_dict: |
| 109 del value_dict['page_id'] |
| 110 result_type = 'nondefault' |
| 111 else: |
| 112 result_type = 'default' |
| 113 |
| 114 # Note: we explicitly ignore TraceValues because Buildbot did. |
| 115 if value_dict['type'] == 'trace': |
| 116 continue |
| 117 value = value_module.Value.FromDict(value_dict, dummy_dict) |
| 118 |
| 119 perf_value = value.GetBuildbotValue() |
| 120 |
| 121 if trace_name == 'summary': |
| 122 trace_name = chart_name |
| 123 |
| 124 self._PrintPerfResult(chart_name, trace_name, perf_value, |
| 125 value.units, result_type) |
| 126 |
104 @property | 127 @property |
105 def _test_name(self): | 128 def _test_name(self): |
106 return self._metadata.name | 129 return self._metadata.name |
107 | 130 |
108 def GetResults(self): | 131 def GetResults(self): |
109 return self._result | 132 return self._result |
110 | 133 |
111 def GetCombinedResults(self): | 134 def GetCombinedResults(self): |
112 all_results = list(self._existing_results) | 135 all_results = list(self._existing_results) |
113 all_results.append(self.GetResults()) | 136 all_results.append(self.GetResults()) |
114 return all_results | 137 return all_results |
115 | 138 |
116 def Format(self, page_test_results): | 139 def Format(self, page_test_results): |
117 super(HtmlOutputFormatter, self).Format(page_test_results) | 140 chart_json_dict = chart_json_output_formatter.ResultsAsChartDict( |
| 141 self._metadata, page_test_results.all_page_specific_values, |
| 142 page_test_results.all_summary_values) |
| 143 |
| 144 self._TranslateChartJson(chart_json_dict) |
| 145 self._PrintPerfResult('telemetry_page_measurement_results', 'num_failed', |
| 146 [len(page_test_results.failures)], 'count', |
| 147 'unimportant') |
118 | 148 |
119 html = self._GetHtmlTemplate() | 149 html = self._GetHtmlTemplate() |
120 html = html.replace('%json_results%', json.dumps(self.GetCombinedResults())) | 150 html = html.replace('%json_results%', json.dumps(self.GetCombinedResults())) |
121 html = html.replace('%json_units%', self._GetUnitJson()) | 151 html = html.replace('%json_units%', self._GetUnitJson()) |
122 html = html.replace('%plugins%', self._GetPlugins()) | 152 html = html.replace('%plugins%', self._GetPlugins()) |
123 self._SaveResults(html) | 153 self._SaveResults(html) |
124 | 154 |
125 if self._upload_results: | 155 if self._upload_results: |
126 file_path = os.path.abspath(self._html_output_stream.name) | 156 file_path = os.path.abspath(self._output_stream.name) |
127 file_name = 'html-results/results-%s' % datetime.datetime.now().strftime( | 157 file_name = 'html-results/results-%s' % datetime.datetime.now().strftime( |
128 '%Y-%m-%d_%H-%M-%S') | 158 '%Y-%m-%d_%H-%M-%S') |
129 try: | 159 try: |
130 cloud_storage.Insert(cloud_storage.PUBLIC_BUCKET, file_name, file_path) | 160 cloud_storage.Insert(cloud_storage.PUBLIC_BUCKET, file_name, file_path) |
131 print | 161 print |
132 print ('View online at ' | 162 print ('View online at ' |
133 'http://storage.googleapis.com/chromium-telemetry/%s' | 163 'http://storage.googleapis.com/chromium-telemetry/%s' |
134 % file_name) | 164 % file_name) |
135 except cloud_storage.PermissionError as e: | 165 except cloud_storage.PermissionError as e: |
136 logging.error('Cannot upload profiling files to cloud storage due to ' | 166 logging.error('Cannot upload profiling files to cloud storage due to ' |
137 ' permission error: %s' % e.message) | 167 ' permission error: %s' % e.message) |
138 print | 168 print |
139 print 'View result at file://%s' % os.path.abspath( | 169 print 'View result at file://%s' % os.path.abspath( |
140 self._html_output_stream.name) | 170 self._output_stream.name) |
OLD | NEW |