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 collections
6import itertools
7import json
8
9from telemetry.internal.results import output_formatter
10from telemetry.value import trace
11
12def ResultsAsChartDict(benchmark_metadata, page_specific_values,
13                       summary_values):
14  """Produces a dict for serialization to Chart JSON format from raw values.
15
16  Chart JSON is a transformation of the basic Telemetry JSON format that
17  removes the page map, summarizes the raw values, and organizes the results
18  by chart and trace name. This function takes the key pieces of data needed to
19  perform this transformation (namely, lists of values and a benchmark metadata
20  object) and processes them into a dict which can be serialized using the json
21  module.
22
23  Design doc for schema: http://goo.gl/kOtf1Y
24
25  Args:
26    page_specific_values: list of page-specific values
27    summary_values: list of summary values
28    benchmark_metadata: a benchmark.BenchmarkMetadata object
29
30  Returns:
31    A Chart JSON dict corresponding to the given data.
32  """
33  values = itertools.chain(
34      output_formatter.SummarizePageSpecificValues(page_specific_values),
35      summary_values)
36  charts = collections.defaultdict(dict)
37
38  for value in values:
39    if value.page:
40      chart_name, trace_name = (value.GetChartAndTraceNameForPerPageResult())
41    else:
42      chart_name, trace_name = (
43          value.GetChartAndTraceNameForComputedSummaryResult(None))
44      if chart_name == trace_name:
45        trace_name = 'summary'
46
47    # Dashboard handles the chart_name of trace values specially: it
48    # strips out the field with chart_name 'trace'. Hence in case trace
49    # value has tir_label, we preserve the chart_name.
50    # For relevant section code of dashboard code that handles this, see:
51    # https://github.com/catapult-project/catapult/blob/25e660b/dashboard/dashboard/add_point.py#L199#L216
52    if value.tir_label and not isinstance(value, trace.TraceValue):
53      chart_name = value.tir_label + '@@' + chart_name
54
55    # This intentionally overwrites the trace if it already exists because this
56    # is expected of output from the buildbots currently.
57    # See: crbug.com/413393
58    charts[chart_name][trace_name] = value.AsDict()
59
60  result_dict = {
61    'format_version': '0.1',
62    'next_version': '0.2',
63    # TODO(sullivan): benchmark_name, benchmark_description, and
64    # trace_rerun_options should be removed when incrementing format_version
65    # to 0.1.
66    'benchmark_name': benchmark_metadata.name,
67    'benchmark_description': benchmark_metadata.description,
68    'trace_rerun_options': benchmark_metadata.rerun_options,
69    'benchmark_metadata': benchmark_metadata.AsDict(),
70    'charts': charts,
71    # Need to add this in for compatibility with disabled chartjson results.
72    'enabled': True
73  }
74
75  return result_dict
76
77
78def DisabledResultsDict(benchmark_name):
79  """Produces a dict for serialization to Chart JSON when a benchmark is
80    disabled.
81
82  Args:
83    benchmark_name: name of the disabled benchmark
84
85  Returns:
86    A Chart JSON dict corresponding to a disabled benchmark.
87  """
88  result_dict = {
89    'benchmark_name': benchmark_name,
90    'enabled': False
91  }
92
93  return result_dict
94
95
96# TODO(eakuefner): Transition this to translate Telemetry JSON.
97class ChartJsonOutputFormatter(output_formatter.OutputFormatter):
98  def __init__(self, output_stream, benchmark_metadata):
99    super(ChartJsonOutputFormatter, self).__init__(output_stream)
100    self._benchmark_metadata = benchmark_metadata
101
102  def FormatDisabled(self):
103    self._Dump(DisabledResultsDict(self._benchmark_metadata.name))
104
105  def Format(self, page_test_results):
106    self._Dump(ResultsAsChartDict(
107      self._benchmark_metadata,
108      page_test_results.all_page_specific_values,
109      page_test_results.all_summary_values))
110
111  def _Dump(self, results):
112    json.dump(results, self.output_stream, indent=2,
113      separators=(',', ': '))
114    self.output_stream.write('\n')
115