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 copy 7import traceback 8 9from telemetry import value as value_module 10from telemetry.results import page_run 11from telemetry.results import progress_reporter as progress_reporter_module 12from telemetry.value import failure 13from telemetry.value import skip 14 15 16class PageTestResults(object): 17 def __init__(self, output_stream=None, output_formatters=None, 18 progress_reporter=None, trace_tag=''): 19 """ 20 Args: 21 output_stream: The output stream to use to write test results. 22 output_formatters: A list of output formatters. The output 23 formatters are typically used to format the test results, such 24 as CsvOutputFormatter, which output the test results as CSV. 25 progress_reporter: An instance of progress_reporter.ProgressReporter, 26 to be used to output test status/results progressively. 27 trace_tag: A string to append to the buildbot trace 28 name. Currently only used for buildbot. 29 """ 30 # TODO(chrishenry): Figure out if trace_tag is still necessary. 31 32 super(PageTestResults, self).__init__() 33 self._output_stream = output_stream 34 self._progress_reporter = ( 35 progress_reporter if progress_reporter is not None 36 else progress_reporter_module.ProgressReporter()) 37 self._output_formatters = ( 38 output_formatters if output_formatters is not None else []) 39 self._trace_tag = trace_tag 40 41 self._current_page_run = None 42 self._all_page_runs = [] 43 self._representative_value_for_each_value_name = {} 44 self._all_summary_values = [] 45 46 def __copy__(self): 47 cls = self.__class__ 48 result = cls.__new__(cls) 49 for k, v in self.__dict__.items(): 50 if isinstance(v, collections.Container): 51 v = copy.copy(v) 52 setattr(result, k, v) 53 return result 54 55 @property 56 def all_page_specific_values(self): 57 values = [] 58 for run in self._all_page_runs: 59 values += run.values 60 if self._current_page_run: 61 values += self._current_page_run.values 62 return values 63 64 @property 65 def all_summary_values(self): 66 return self._all_summary_values 67 68 @property 69 def current_page(self): 70 assert self._current_page_run, 'Not currently running test.' 71 return self._current_page_run.page 72 73 @property 74 def current_page_run(self): 75 assert self._current_page_run, 'Not currently running test.' 76 return self._current_page_run 77 78 @property 79 def all_page_runs(self): 80 return self._all_page_runs 81 82 @property 83 def pages_that_succeeded(self): 84 """Returns the set of pages that succeeded.""" 85 pages = set(run.page for run in self.all_page_runs) 86 pages.difference_update(self.pages_that_failed) 87 return pages 88 89 @property 90 def pages_that_failed(self): 91 """Returns the set of failed pages.""" 92 failed_pages = set() 93 for run in self.all_page_runs: 94 if run.failed: 95 failed_pages.add(run.page) 96 return failed_pages 97 98 @property 99 def failures(self): 100 values = self.all_page_specific_values 101 return [v for v in values if isinstance(v, failure.FailureValue)] 102 103 @property 104 def skipped_values(self): 105 values = self.all_page_specific_values 106 return [v for v in values if isinstance(v, skip.SkipValue)] 107 108 def _GetStringFromExcInfo(self, err): 109 return ''.join(traceback.format_exception(*err)) 110 111 def WillRunPage(self, page): 112 assert not self._current_page_run, 'Did not call DidRunPage.' 113 self._current_page_run = page_run.PageRun(page) 114 self._progress_reporter.WillRunPage(self) 115 116 def DidRunPage(self, page, discard_run=False): # pylint: disable=W0613 117 """ 118 Args: 119 page: The current page under test. 120 discard_run: Whether to discard the entire run and all of its 121 associated results. 122 """ 123 assert self._current_page_run, 'Did not call WillRunPage.' 124 self._progress_reporter.DidRunPage(self) 125 if not discard_run: 126 self._all_page_runs.append(self._current_page_run) 127 self._current_page_run = None 128 129 def WillAttemptPageRun(self, attempt_count, max_attempts): 130 """To be called when a single attempt on a page run is starting. 131 132 This is called between WillRunPage and DidRunPage and can be 133 called multiple times, one for each attempt. 134 135 Args: 136 attempt_count: The current attempt number, start at 1 137 (attempt_count == 1 for the first attempt, 2 for second 138 attempt, and so on). 139 max_attempts: Maximum number of page run attempts before failing. 140 """ 141 self._progress_reporter.WillAttemptPageRun( 142 self, attempt_count, max_attempts) 143 # Clear any values from previous attempts for this page run. 144 self._current_page_run.ClearValues() 145 146 def AddValue(self, value): 147 assert self._current_page_run, 'Not currently running test.' 148 self._ValidateValue(value) 149 # TODO(eakuefner/chrishenry): Add only one skip per pagerun assert here 150 self._current_page_run.AddValue(value) 151 self._progress_reporter.DidAddValue(value) 152 153 def AddSummaryValue(self, value): 154 assert value.page is None 155 self._ValidateValue(value) 156 self._all_summary_values.append(value) 157 158 def _ValidateValue(self, value): 159 assert isinstance(value, value_module.Value) 160 if value.name not in self._representative_value_for_each_value_name: 161 self._representative_value_for_each_value_name[value.name] = value 162 representative_value = self._representative_value_for_each_value_name[ 163 value.name] 164 assert value.IsMergableWith(representative_value) 165 166 def PrintSummary(self): 167 self._progress_reporter.DidFinishAllTests(self) 168 for output_formatter in self._output_formatters: 169 output_formatter.Format(self) 170 171 def FindPageSpecificValuesForPage(self, page, value_name): 172 values = [] 173 for value in self.all_page_specific_values: 174 if value.page == page and value.name == value_name: 175 values.append(value) 176 return values 177 178 def FindAllPageSpecificValuesNamed(self, value_name): 179 values = [] 180 for value in self.all_page_specific_values: 181 if value.name == value_name: 182 values.append(value) 183 return values 184