• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 datetime
6import json
7import logging
8import os
9import re
10
11from telemetry.core import util
12from telemetry.results import buildbot_output_formatter
13from telemetry.util import cloud_storage
14
15util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'util')
16import lastchange  # pylint: disable=F0401
17
18
19_TEMPLATE_HTML_PATH = os.path.join(
20    util.GetTelemetryDir(), 'support', 'html_output', 'results-template.html')
21_PLUGINS = [('third_party', 'flot', 'jquery.flot.min.js'),
22            ('third_party', 'WebKit', 'PerformanceTests', 'resources',
23             'jquery.tablesorter.min.js'),
24            ('third_party', 'WebKit', 'PerformanceTests', 'resources',
25             'statistics.js')]
26_UNIT_JSON = ('tools', 'perf', 'unit-info.json')
27
28
29# TODO(chrishenry): This should not really extend BuildbotOutputFormatter.
30# Leaving as-is now since we are going to move HtmlOutputFormatter to be
31# based on JSON anyway.
32class HtmlOutputFormatter(buildbot_output_formatter.BuildbotOutputFormatter):
33  def __init__(self, output_stream, metadata, reset_results, upload_results,
34      browser_type, results_label=None, trace_tag=''):
35    # Pass output_stream=None so that we blow up if
36    # BuildbotOutputFormatter ever use the output_stream.
37    super(HtmlOutputFormatter, self).__init__(None, trace_tag)
38    self._metadata = metadata
39    self._reset_results = reset_results
40    self._upload_results = upload_results
41    self._html_output_stream = output_stream
42    self._existing_results = self._ReadExistingResults(output_stream)
43    self._result = {
44        'buildTime': self._GetBuildTime(),
45        'revision': self._GetRevision(),
46        'label': results_label,
47        'platform': browser_type,
48        'tests': {}
49        }
50
51  def _GetBuildTime(self):
52    def _DatetimeInEs5CompatibleFormat(dt):
53      return dt.strftime('%Y-%m-%dT%H:%M:%S.%f')
54    return _DatetimeInEs5CompatibleFormat(datetime.datetime.utcnow())
55
56  def _GetRevision(self):
57    return lastchange.FetchVersionInfo(None).revision
58
59  def _GetHtmlTemplate(self):
60    with open(_TEMPLATE_HTML_PATH) as f:
61      return f.read()
62
63  def _GetPlugins(self):
64    plugins = ''
65    for p in _PLUGINS:
66      with open(os.path.join(util.GetChromiumSrcDir(), *p)) as f:
67        plugins += f.read()
68    return plugins
69
70  def _GetUnitJson(self):
71    with open(os.path.join(util.GetChromiumSrcDir(), *_UNIT_JSON)) as f:
72      return f.read()
73
74  def _ReadExistingResults(self, output_stream):
75    results_html = output_stream.read()
76    if self._reset_results or not results_html:
77      return []
78    m = re.search(
79        '^<script id="results-json" type="application/json">(.*?)</script>$',
80        results_html, re.MULTILINE | re.DOTALL)
81    if not m:
82      logging.warn('Failed to extract previous results from HTML output')
83      return []
84    return json.loads(m.group(1))[:512]
85
86  def _SaveResults(self, results):
87    self._html_output_stream.seek(0)
88    self._html_output_stream.write(results)
89    self._html_output_stream.truncate()
90
91  def _PrintPerfResult(self, measurement, trace, values, units,
92                       result_type='default'):
93    metric_name = measurement
94    if trace != measurement:
95      metric_name += '.' + trace
96    self._result['tests'].setdefault(self._test_name, {})
97    self._result['tests'][self._test_name].setdefault('metrics', {})
98    self._result['tests'][self._test_name]['metrics'][metric_name] = {
99        'current': values,
100        'units': units,
101        'important': result_type == 'default'
102        }
103
104  @property
105  def _test_name(self):
106    return self._metadata.name
107
108  def GetResults(self):
109    return self._result
110
111  def GetCombinedResults(self):
112    all_results = list(self._existing_results)
113    all_results.append(self.GetResults())
114    return all_results
115
116  def Format(self, page_test_results):
117    super(HtmlOutputFormatter, self).Format(page_test_results)
118
119    html = self._GetHtmlTemplate()
120    html = html.replace('%json_results%', json.dumps(self.GetCombinedResults()))
121    html = html.replace('%json_units%', self._GetUnitJson())
122    html = html.replace('%plugins%', self._GetPlugins())
123    self._SaveResults(html)
124
125    if self._upload_results:
126      file_path = os.path.abspath(self._html_output_stream.name)
127      file_name = 'html-results/results-%s' % datetime.datetime.now().strftime(
128          '%Y-%m-%d_%H-%M-%S')
129      cloud_storage.Insert(cloud_storage.PUBLIC_BUCKET, file_name, file_path)
130      print
131      print ('View online at '
132          'http://storage.googleapis.com/chromium-telemetry/%s' % file_name)
133    print
134    print 'View result at file://%s' % os.path.abspath(
135        self._html_output_stream.name)
136