1# Copyright 2013 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
10import sys
11
12from telemetry.core import util
13from telemetry.page import buildbot_page_measurement_results
14
15# Get build/util scripts into our path.
16sys.path.append(os.path.abspath(os.path.join(
17    util.GetChromiumSrcDir(), 'build', 'util')))
18import lastchange  # pylint: disable=F0401
19
20
21_TEMPLATE_HTML_PATH = os.path.join(
22    util.GetTelemetryDir(), 'support', 'html_output', 'results-template.html')
23_PLUGINS = [('third_party', 'flot', 'jquery.flot.min.js'),
24            ('third_party', 'WebKit', 'PerformanceTests', 'resources',
25             'jquery.tablesorter.min.js'),
26            ('third_party', 'WebKit', 'PerformanceTests', 'resources',
27             'statistics.js')]
28_UNIT_JSON = ('tools', 'perf', 'unit-info.json')
29
30
31class HtmlPageMeasurementResults(
32    buildbot_page_measurement_results.BuildbotPageMeasurementResults):
33  def __init__(self, output_stream, test_name, reset_results, browser_type,
34               trace_tag=''):
35    super(HtmlPageMeasurementResults, self).__init__(trace_tag)
36
37    self._output_stream = output_stream
38    self._reset_results = reset_results
39    self._test_name = test_name
40    self._result_json = {
41        'buildTime': self._GetBuildTime(),
42        'revision': self._GetRevision(),
43        'platform': browser_type,
44        'tests': {}
45        }
46
47  def _GetBuildTime(self):
48    def _DatetimeInEs5CompatibleFormat(dt):
49      return dt.strftime('%Y-%m-%dT%H:%M:%S.%f')
50    return _DatetimeInEs5CompatibleFormat(datetime.datetime.utcnow())
51
52  def _GetRevision(self):
53    return lastchange.FetchVersionInfo(None).revision
54
55  def _GetHtmlTemplate(self):
56    return open(_TEMPLATE_HTML_PATH, 'r').read()
57
58  def _GetPlugins(self):
59    plugins = ''
60    for p in _PLUGINS:
61      plugins += open(os.path.join(util.GetChromiumSrcDir(), *p), 'r').read()
62    return plugins
63
64  def _GetUnitJson(self):
65    return open(os.path.join(util.GetChromiumSrcDir(), *_UNIT_JSON), 'r').read()
66
67  def _GetResultsJson(self):
68    results_html = self._output_stream.read()
69    if self._reset_results or not results_html:
70      return []
71    m = re.search(
72        '^<script id="results-json" type="application/json">(.*?)</script>$',
73        results_html, re.MULTILINE | re.DOTALL)
74    if not m:
75      logging.warn('Failed to extract previous results from HTML output')
76      return []
77    return json.loads(m.group(1))
78
79  def _SaveResults(self, results):
80    self._output_stream.seek(0)
81    self._output_stream.write(results)
82    self._output_stream.truncate()
83
84  def _PrintPerfResult(self, measurement, trace, values, units,
85                       result_type='default'):
86    super(HtmlPageMeasurementResults, self)._PrintPerfResult(
87        measurement, trace, values, units, result_type)
88
89    metric_name = measurement
90    if trace != measurement:
91      metric_name += '.' + trace
92    self._result_json['tests'].setdefault(self._test_name, {})
93    self._result_json['tests'][self._test_name].setdefault('metrics', {})
94    self._result_json['tests'][self._test_name]['metrics'][metric_name] = {
95        'current': values,
96        'units': units,
97        'important': result_type == 'default'
98        }
99
100  def PrintSummary(self):
101    super(HtmlPageMeasurementResults, self).PrintSummary()
102
103    json_results = self._GetResultsJson()
104    json_results.append(self._result_json)
105    html = self._GetHtmlTemplate()
106    html = html.replace('%json_results%', json.dumps(json_results))
107    html = html.replace('%json_units%', self._GetUnitJson())
108    html = html.replace('%plugins%', self._GetPlugins())
109    self._SaveResults(html)
110
111    print
112    print 'View result at file://%s' % os.path.abspath(self._output_stream.name)
113