# 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. from collections import defaultdict from telemetry.value import failure from telemetry.value import merge_values from telemetry.value import skip class Summary(object): """Computes summary values from the per-page-run values produced by a test. Some telemetry benchmark repeat a number of times in order to get a reliable measurement. The test does not have to handle merging of these runs: summarizer does it for you. For instance, if two pages run, 3 and 1 time respectively: ScalarValue(page1, 'foo', units='ms', 1) ScalarValue(page1, 'foo', units='ms', 1) ScalarValue(page1, 'foo', units='ms', 1) ScalarValue(page2, 'foo', units='ms', 2) Then summarizer will produce two sets of values. First, computed_per_page_values: [ ListOfScalarValues(page1, 'foo', units='ms', [1,1,1])], ListOfScalarValues(page2, 'foo', units='ms', [2])] ] In addition, it will produce a summary value: [ ListOfScalarValues(page=None, 'foo', units='ms', [1,1,1,2])] ] """ def __init__(self, all_page_specific_values, key_func=merge_values.DefaultKeyFunc): had_failures = any(isinstance(v, failure.FailureValue) for v in all_page_specific_values) self.had_failures = had_failures self._computed_per_page_values = [] self._computed_summary_values = [] self._interleaved_computed_per_page_values_and_summaries = [] self._key_func = key_func self._ComputePerPageValues(all_page_specific_values) @property def computed_per_page_values(self): return self._computed_per_page_values @property def computed_summary_values(self): return self._computed_summary_values @property def interleaved_computed_per_page_values_and_summaries(self): """Returns the computed per page values and summary values interleaved. All the results for a given name are printed together. First per page values, then summary values. """ return self._interleaved_computed_per_page_values_and_summaries def _ComputePerPageValues(self, all_page_specific_values): all_successful_page_values = [ v for v in all_page_specific_values if not (isinstance( v, failure.FailureValue) or isinstance(v, skip.SkipValue))] # We will later need to determine how many values were originally created # for each value name, to apply a workaround meant to clean up the printf # output. num_successful_pages_for_key = defaultdict(int) for v in all_successful_page_values: num_successful_pages_for_key[self._key_func(v)] += 1 # By here, due to page repeat options, all_values_from_successful_pages # contains values of the same name not only from mulitple pages, but also # from the same name. So even if, for instance, only one page ran, it may # have run twice, producing two 'x' values. # # So, get rid of the repeated pages by merging. merged_page_values = merge_values.MergeLikeValuesFromSamePage( all_successful_page_values, self._key_func) # Now we have a bunch of values, but there is only one value_name per page. # Suppose page1 and page2 ran, producing values x and y. We want to print # x for page1 # x for page2 # x for page1, page2 combined # # y for page1 # y for page2 # y for page1, page2 combined # # We already have the x values in the values array. But, we will need # them indexable by summary key. # # The following dict maps summary_key -> list of pages that have values of # that name. per_page_values_by_key = defaultdict(list) for value in merged_page_values: per_page_values_by_key[self._key_func(value)].append(value) # We already have the x values in the values array. But, we also need # the values merged across the pages. And, we will need them indexed by # summary key so that we can find them when printing out value names in # alphabetical order. merged_pages_value_by_key = {} if not self.had_failures: for value in merge_values.MergeLikeValuesFromDifferentPages( all_successful_page_values, self._key_func): value_key = self._key_func(value) assert value_key not in merged_pages_value_by_key merged_pages_value_by_key[value_key] = value keys = sorted(set([self._key_func(v) for v in merged_page_values])) # Time to walk through the values by key, printing first the page-specific # values and then the merged_site value. for key in keys: per_page_values = per_page_values_by_key.get(key, []) # Sort the values by their URL. sorted_per_page_values = list(per_page_values) sorted_per_page_values.sort( key=lambda per_page_values: per_page_values.page.display_name) # Output the page-specific results. num_successful_pages_for_this_key = ( num_successful_pages_for_key[key]) for per_page_value in sorted_per_page_values: self._ComputePerPageValue(per_page_value, num_successful_pages_for_this_key) # Output the combined values. merged_pages_value = merged_pages_value_by_key.get(key, None) if merged_pages_value: self._computed_summary_values.append(merged_pages_value) self._interleaved_computed_per_page_values_and_summaries.append( merged_pages_value) def _ComputePerPageValue( self, value, num_successful_pages_for_this_value_name): if num_successful_pages_for_this_value_name >= 1: # Save the result. self._computed_per_page_values.append(value) self._interleaved_computed_per_page_values_and_summaries.append(value)