Index: tools/telemetry/telemetry/value/__init__.py |
diff --git a/tools/telemetry/telemetry/value/__init__.py b/tools/telemetry/telemetry/value/__init__.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..08d623c85d9030f4a49234d34523e64d979c046b |
--- /dev/null |
+++ b/tools/telemetry/telemetry/value/__init__.py |
@@ -0,0 +1,182 @@ |
+# Copyright (c) 2013 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] |
+- 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. |
+""" |
+ |
+# 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' |
+MERGED_PAGES_RESULT_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): |
+ """A generic Value object. |
+ |
+ Note: page may be given as None to indicate that the value represents |
+ results multiple pages. |
+ """ |
+ self.page = page |
+ self.name = name |
+ self.units = units |
+ self.important = important |
+ |
+ 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, |
+ group_by_name_suffix=False): |
+ """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. |
+ |
+ If group_by_name_suffix is True, then x.z and y.z are considered to be the |
+ same value and are grouped together. If false, then x.z and y.z are |
+ considered different. |
+ """ |
+ raise NotImplementedError() |
+ |
+ def _IsImportantGivenOutputIntent(self, output_context): |
+ if output_context == PER_PAGE_RESULT_OUTPUT_CONTEXT: |
+ return False |
+ elif output_context == MERGED_PAGES_RESULT_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 GetBuildbotMeasurementAndTraceNameForPerPageResult(self): |
+ measurement, _ = ( |
+ _ConvertValueNameToBuildbotChartAndTraceName(self.name)) |
+ return measurement + '_by_url', self.page.display_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 GetBuildbotMeasurementAndTraceNameForMergedPagesResult(self, trace_tag): |
+ measurement, bb_trace_name = ( |
+ _ConvertValueNameToBuildbotChartAndTraceName(self.name)) |
+ if trace_tag: |
+ return measurement, bb_trace_name + trace_tag |
+ else: |
+ return measurement, bb_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() |
+ |
+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: |
+ return trace_name |
+ |
+def _ConvertValueNameToBuildbotChartAndTraceName(value_name): |
+ """Converts a value_name into the buildbot equivalent 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 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 |