Index: tools/telemetry/telemetry/value/__init__.py |
diff --git a/tools/telemetry/telemetry/value/__init__.py b/tools/telemetry/telemetry/value/__init__.py |
deleted file mode 100644 |
index a2ab23e29165a2724e2061a4b74e386d701c789f..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/telemetry/value/__init__.py |
+++ /dev/null |
@@ -1,336 +0,0 @@ |
-# Copyright 2014 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
-""" |
-The Value hierarchy provides a way of representing the values measurements |
-produce such that they can be merged across runs, grouped by page, and output |
-to different targets. |
- |
-The core Value concept provides the basic functionality: |
-- association with a page, may be none |
-- naming and units |
-- importance tracking [whether a value will show up on a waterfall or output |
- file by default] |
-- other metadata, such as a description of what was measured |
-- default conversion to scalar and string |
-- merging properties |
- |
-A page may actually run a few times during a single telemetry session. |
-Downstream consumers of test results typically want to group these runs |
-together, then compute summary statistics across runs. Value provides the |
-Merge* family of methods for this kind of aggregation. |
-""" |
-import os |
- |
-from telemetry.core import discover |
-from telemetry.core import util |
- |
-# When combining a pair of Values togehter, it is sometimes ambiguous whether |
-# the values should be concatenated, or one should be picked as representative. |
-# The possible merging policies are listed here. |
-CONCATENATE = 'concatenate' |
-PICK_FIRST = 'pick-first' |
- |
-# When converting a Value to its buildbot equivalent, the context in which the |
-# value is being interpreted actually affects the conversion. This is insane, |
-# but there you have it. There are three contexts in which Values are converted |
-# for use by buildbot, represented by these output-intent values. |
-PER_PAGE_RESULT_OUTPUT_CONTEXT = 'per-page-result-output-context' |
-COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT = 'merged-pages-result-output-context' |
-SUMMARY_RESULT_OUTPUT_CONTEXT = 'summary-result-output-context' |
- |
-class Value(object): |
- """An abstract value produced by a telemetry page test. |
- """ |
- def __init__(self, page, name, units, important, description, |
- tir_label): |
- """A generic Value object. |
- |
- Args: |
- page: A Page object, may be given as None to indicate that the value |
- represents results for multiple pages. |
- name: A value name string, may contain a dot. Values from the same test |
- with the same prefix before the dot may be considered to belong to |
- the same chart. |
- units: A units string. |
- important: Whether the value is "important". Causes the value to appear |
- by default in downstream UIs. |
- description: A string explaining in human-understandable terms what this |
- value represents. |
- tir_label: The string label of the TimelineInteractionRecord with |
- which this value is associated. |
- """ |
- # TODO(eakuefner): Check story here after migration (crbug.com/442036) |
- if not isinstance(name, basestring): |
- raise ValueError('name field of Value must be string.') |
- if not isinstance(units, basestring): |
- raise ValueError('units field of Value must be string.') |
- if not isinstance(important, bool): |
- raise ValueError('important field of Value must be bool.') |
- if not ((description is None) or isinstance(description, basestring)): |
- raise ValueError('description field of Value must absent or string.') |
- if not ((tir_label is None) or |
- isinstance(tir_label, basestring)): |
- raise ValueError('tir_label field of Value must absent or ' |
- 'string.') |
- |
- self.page = page |
- self.name = name |
- self.units = units |
- self.important = important |
- self.description = description |
- self.tir_label = tir_label |
- |
- def __eq__(self, other): |
- return hash(self) == hash(other) |
- |
- def __hash__(self): |
- return hash(str(self)) |
- |
- def IsMergableWith(self, that): |
- return (self.units == that.units and |
- type(self) == type(that) and |
- self.important == that.important) |
- |
- @classmethod |
- def MergeLikeValuesFromSamePage(cls, values): |
- """Combines the provided list of values into a single compound value. |
- |
- When a page runs multiple times, it may produce multiple values. This |
- function is given the same-named values across the multiple runs, and has |
- the responsibility of producing a single result. |
- |
- It must return a single Value. If merging does not make sense, the |
- implementation must pick a representative value from one of the runs. |
- |
- For instance, it may be given |
- [ScalarValue(page, 'a', 1), ScalarValue(page, 'a', 2)] |
- and it might produce |
- ListOfScalarValues(page, 'a', [1, 2]) |
- """ |
- raise NotImplementedError() |
- |
- @classmethod |
- def MergeLikeValuesFromDifferentPages(cls, values): |
- """Combines the provided values into a single compound value. |
- |
- When a full pageset runs, a single value_name will usually end up getting |
- collected for multiple pages. For instance, we may end up with |
- [ScalarValue(page1, 'a', 1), |
- ScalarValue(page2, 'a', 2)] |
- |
- This function takes in the values of the same name, but across multiple |
- pages, and produces a single summary result value. In this instance, it |
- could produce a ScalarValue(None, 'a', 1.5) to indicate averaging, or even |
- ListOfScalarValues(None, 'a', [1, 2]) if concatenated output was desired. |
- |
- Some results are so specific to a page that they make no sense when |
- aggregated across pages. If merging values of this type across pages is |
- non-sensical, this method may return None. |
- """ |
- raise NotImplementedError() |
- |
- def _IsImportantGivenOutputIntent(self, output_context): |
- if output_context == PER_PAGE_RESULT_OUTPUT_CONTEXT: |
- return False |
- elif output_context == COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT: |
- return self.important |
- elif output_context == SUMMARY_RESULT_OUTPUT_CONTEXT: |
- return self.important |
- |
- def GetBuildbotDataType(self, output_context): |
- """Returns the buildbot's equivalent data_type. |
- |
- This should be one of the values accepted by perf_tests_results_helper.py. |
- """ |
- raise NotImplementedError() |
- |
- def GetBuildbotValue(self): |
- """Returns the buildbot's equivalent value.""" |
- raise NotImplementedError() |
- |
- def GetChartAndTraceNameForPerPageResult(self): |
- chart_name, _ = _ConvertValueNameToChartAndTraceName(self.name) |
- trace_name = self.page.display_name |
- return chart_name, trace_name |
- |
- @property |
- def name_suffix(self): |
- """Returns the string after a . in the name, or the full name otherwise.""" |
- if '.' in self.name: |
- return self.name.split('.', 1)[1] |
- else: |
- return self.name |
- |
- def GetChartAndTraceNameForComputedSummaryResult( |
- self, trace_tag): |
- chart_name, trace_name = ( |
- _ConvertValueNameToChartAndTraceName(self.name)) |
- if trace_tag: |
- return chart_name, trace_name + trace_tag |
- else: |
- return chart_name, trace_name |
- |
- def GetRepresentativeNumber(self): |
- """Gets a single scalar value that best-represents this value. |
- |
- Returns None if not possible. |
- """ |
- raise NotImplementedError() |
- |
- def GetRepresentativeString(self): |
- """Gets a string value that best-represents this value. |
- |
- Returns None if not possible. |
- """ |
- raise NotImplementedError() |
- |
- @staticmethod |
- def GetJSONTypeName(): |
- """Gets the typename for serialization to JSON using AsDict.""" |
- raise NotImplementedError() |
- |
- def AsDict(self): |
- """Pre-serializes a value to a dict for output as JSON.""" |
- return self._AsDictImpl() |
- |
- def _AsDictImpl(self): |
- d = { |
- 'name': self.name, |
- 'type': self.GetJSONTypeName(), |
- 'units': self.units, |
- 'important': self.important |
- } |
- |
- if self.description: |
- d['description'] = self.description |
- |
- if self.tir_label: |
- d['tir_label'] = self.tir_label |
- |
- if self.page: |
- d['page_id'] = self.page.id |
- |
- return d |
- |
- def AsDictWithoutBaseClassEntries(self): |
- full_dict = self.AsDict() |
- base_dict_keys = set(self._AsDictImpl().keys()) |
- |
- # Extracts only entries added by the subclass. |
- return dict([(k, v) for (k, v) in full_dict.iteritems() |
- if k not in base_dict_keys]) |
- |
- @staticmethod |
- def FromDict(value_dict, page_dict): |
- """Produces a value from a value dict and a page dict. |
- |
- Value dicts are produced by serialization to JSON, and must be accompanied |
- by a dict mapping page IDs to pages, also produced by serialization, in |
- order to be completely deserialized. If deserializing multiple values, use |
- ListOfValuesFromListOfDicts instead. |
- |
- value_dict: a dictionary produced by AsDict() on a value subclass. |
- page_dict: a dictionary mapping IDs to page objects. |
- """ |
- return Value.ListOfValuesFromListOfDicts([value_dict], page_dict)[0] |
- |
- @staticmethod |
- def ListOfValuesFromListOfDicts(value_dicts, page_dict): |
- """Takes a list of value dicts to values. |
- |
- Given a list of value dicts produced by AsDict, this method |
- deserializes the dicts given a dict mapping page IDs to pages. |
- This method performs memoization for deserializing a list of values |
- efficiently, where FromDict is meant to handle one-offs. |
- |
- values: a list of value dicts produced by AsDict() on a value subclass. |
- page_dict: a dictionary mapping IDs to page objects. |
- """ |
- value_dir = os.path.dirname(__file__) |
- value_classes = discover.DiscoverClasses( |
- value_dir, util.GetTelemetryDir(), |
- Value, index_by_class_name=True) |
- |
- value_json_types = dict((value_classes[x].GetJSONTypeName(), x) for x in |
- value_classes) |
- |
- values = [] |
- for value_dict in value_dicts: |
- value_class = value_classes[value_json_types[value_dict['type']]] |
- assert 'FromDict' in value_class.__dict__, \ |
- 'Subclass doesn\'t override FromDict' |
- values.append(value_class.FromDict(value_dict, page_dict)) |
- |
- return values |
- |
- @staticmethod |
- def GetConstructorKwArgs(value_dict, page_dict): |
- """Produces constructor arguments from a value dict and a page dict. |
- |
- Takes a dict parsed from JSON and an index of pages and recovers the |
- keyword arguments to be passed to the constructor for deserializing the |
- dict. |
- |
- value_dict: a dictionary produced by AsDict() on a value subclass. |
- page_dict: a dictionary mapping IDs to page objects. |
- """ |
- d = { |
- 'name': value_dict['name'], |
- 'units': value_dict['units'] |
- } |
- |
- description = value_dict.get('description', None) |
- if description: |
- d['description'] = description |
- else: |
- d['description'] = None |
- |
- page_id = value_dict.get('page_id', None) |
- if page_id: |
- d['page'] = page_dict[int(page_id)] |
- else: |
- d['page'] = None |
- |
- d['important'] = False |
- |
- tir_label = value_dict.get('tir_label', None) |
- if tir_label: |
- d['tir_label'] = tir_label |
- else: |
- d['tir_label'] = None |
- |
- return d |
- |
-def ValueNameFromTraceAndChartName(trace_name, chart_name=None): |
- """Mangles a trace name plus optional chart name into a standard string. |
- |
- A value might just be a bareword name, e.g. numPixels. In that case, its |
- chart may be None. |
- |
- But, a value might also be intended for display with other values, in which |
- case the chart name indicates that grouping. So, you might have |
- screen.numPixels, screen.resolution, where chartName='screen'. |
- """ |
- assert trace_name != 'url', 'The name url cannot be used' |
- if chart_name: |
- return '%s.%s' % (chart_name, trace_name) |
- else: |
- assert '.' not in trace_name, ('Trace names cannot contain "." with an ' |
- 'empty chart_name since this is used to delimit chart_name.trace_name.') |
- return trace_name |
- |
-def _ConvertValueNameToChartAndTraceName(value_name): |
- """Converts a value_name into the equivalent chart-trace name pair. |
- |
- Buildbot represents values by the measurement name and an optional trace name, |
- whereas telemetry represents values with a chart_name.trace_name convention, |
- where chart_name is optional. This convention is also used by chart_json. |
- |
- This converts from the telemetry convention to the buildbot convention, |
- returning a 2-tuple (measurement_name, trace_name). |
- """ |
- if '.' in value_name: |
- return value_name.split('.', 1) |
- else: |
- return value_name, value_name |