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 numbers
6
7from telemetry import value as value_module
8from telemetry.value import list_of_scalar_values
9from telemetry.value import none_values
10
11
12class ScalarValue(value_module.Value):
13  def __init__(self, page, name, units, value, important=True,
14               description=None, none_value_reason=None):
15    """A single value (float or integer) result from a test.
16
17    A test that counts the number of DOM elements in a page might produce a
18    scalar value:
19       ScalarValue(page, 'num_dom_elements', 'count', num_elements)
20    """
21    super(ScalarValue, self).__init__(page, name, units, important, description)
22    assert value is None or isinstance(value, numbers.Number)
23    none_values.ValidateNoneValueReason(value, none_value_reason)
24    self.value = value
25    self.none_value_reason = none_value_reason
26
27  def __repr__(self):
28    if self.page:
29      page_name = self.page.url
30    else:
31      page_name = None
32    return 'ScalarValue(%s, %s, %s, %s, important=%s, description=%s)' % (
33      page_name,
34      self.name,
35      self.units,
36      self.value,
37      self.important,
38      self.description)
39
40  def GetBuildbotDataType(self, output_context):
41    if self._IsImportantGivenOutputIntent(output_context):
42      return 'default'
43    return 'unimportant'
44
45  def GetBuildbotValue(self):
46    # Buildbot's print_perf_results method likes to get lists for all values,
47    # even when they are scalar, so list-ize the return value.
48    return [self.value]
49
50  def GetRepresentativeNumber(self):
51    return self.value
52
53  def GetRepresentativeString(self):
54    return str(self.value)
55
56  @staticmethod
57  def GetJSONTypeName():
58    return 'scalar'
59
60  def AsDict(self):
61    d = super(ScalarValue, self).AsDict()
62    d['value'] = self.value
63
64    if self.none_value_reason is not None:
65      d['none_value_reason'] = self.none_value_reason
66
67    return d
68
69  @staticmethod
70  def FromDict(value_dict, page_dict):
71    kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict)
72    kwargs['value'] = value_dict['value']
73
74    if 'none_value_reason' in value_dict:
75      kwargs['none_value_reason'] = value_dict['none_value_reason']
76
77    return ScalarValue(**kwargs)
78
79  @classmethod
80  def MergeLikeValuesFromSamePage(cls, values):
81    assert len(values) > 0
82    v0 = values[0]
83    return cls._MergeLikeValues(values, v0.page, v0.name)
84
85  @classmethod
86  def MergeLikeValuesFromDifferentPages(cls, values,
87                                        group_by_name_suffix=False):
88    assert len(values) > 0
89    v0 = values[0]
90    name = v0.name_suffix if group_by_name_suffix else v0.name
91    return cls._MergeLikeValues(values, None, name)
92
93  @classmethod
94  def _MergeLikeValues(cls, values, page, name):
95    v0 = values[0]
96    merged_value = [v.value for v in values]
97    none_value_reason = None
98    if None in merged_value:
99      merged_value = None
100      none_value_reason = none_values.MERGE_FAILURE_REASON
101    return list_of_scalar_values.ListOfScalarValues(
102        page, name, v0.units, merged_value, important=v0.important,
103        none_value_reason=none_value_reason)
104