1f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)# found in the LICENSE file. 490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import collections 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import copy 790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import traceback 890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 9116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom telemetry import value as value_module 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.results import page_run 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.results import progress_reporter as progress_reporter_module 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.value import failure 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from telemetry.value import skip 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) 166d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)class PageTestResults(object): 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self, output_stream=None, output_formatters=None, 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) progress_reporter=None, trace_tag=''): 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) output_stream: The output stream to use to write test results. 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) output_formatters: A list of output formatters. The output 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) formatters are typically used to format the test results, such 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) as CsvOutputFormatter, which output the test results as CSV. 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) progress_reporter: An instance of progress_reporter.ProgressReporter, 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) to be used to output test status/results progressively. 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) trace_tag: A string to append to the buildbot trace 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) name. Currently only used for buildbot. 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # TODO(chrishenry): Figure out if trace_tag is still necessary. 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) super(PageTestResults, self).__init__() 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._output_stream = output_stream 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._progress_reporter = ( 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) progress_reporter if progress_reporter is not None 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else progress_reporter_module.ProgressReporter()) 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._output_formatters = ( 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) output_formatters if output_formatters is not None else []) 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._trace_tag = trace_tag 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._current_page_run = None 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._all_page_runs = [] 43116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._representative_value_for_each_value_name = {} 44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._all_summary_values = [] 45116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def __copy__(self): 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) cls = self.__class__ 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result = cls.__new__(cls) 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for k, v in self.__dict__.items(): 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if isinstance(v, collections.Container): 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) v = copy.copy(v) 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) setattr(result, k, v) 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return result 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @property 56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def all_page_specific_values(self): 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) values = [] 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for run in self._all_page_runs: 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) values += run.values 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self._current_page_run: 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) values += self._current_page_run.values 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return values 63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @property 65116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def all_summary_values(self): 66116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return self._all_summary_values 67116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @property 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def current_page(self): 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert self._current_page_run, 'Not currently running test.' 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self._current_page_run.page 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @property 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def current_page_run(self): 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert self._current_page_run, 'Not currently running test.' 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self._current_page_run 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @property 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def all_page_runs(self): 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self._all_page_runs 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @property 83116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def pages_that_succeeded(self): 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Returns the set of pages that succeeded.""" 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pages = set(run.page for run in self.all_page_runs) 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pages.difference_update(self.pages_that_failed) 87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return pages 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @property 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def pages_that_failed(self): 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Returns the set of failed pages.""" 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) failed_pages = set() 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for run in self.all_page_runs: 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if run.failed: 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) failed_pages.add(run.page) 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return failed_pages 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @property 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def failures(self): 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) values = self.all_page_specific_values 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return [v for v in values if isinstance(v, failure.FailureValue)] 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) @property 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def skipped_values(self): 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) values = self.all_page_specific_values 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return [v for v in values if isinstance(v, skip.SkipValue)] 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1086d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) def _GetStringFromExcInfo(self, err): 1096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) return ''.join(traceback.format_exception(*err)) 11090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def WillRunPage(self, page): 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert not self._current_page_run, 'Did not call DidRunPage.' 1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._current_page_run = page_run.PageRun(page) 1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._progress_reporter.WillRunPage(self) 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def DidRunPage(self, page, discard_run=False): # pylint: disable=W0613 1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) page: The current page under test. 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) discard_run: Whether to discard the entire run and all of its 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) associated results. 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert self._current_page_run, 'Did not call WillRunPage.' 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._progress_reporter.DidRunPage(self) 1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not discard_run: 1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._all_page_runs.append(self._current_page_run) 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._current_page_run = None 1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def WillAttemptPageRun(self, attempt_count, max_attempts): 1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """To be called when a single attempt on a page run is starting. 1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) This is called between WillRunPage and DidRunPage and can be 1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) called multiple times, one for each attempt. 1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) attempt_count: The current attempt number, start at 1 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (attempt_count == 1 for the first attempt, 2 for second 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) attempt, and so on). 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) max_attempts: Maximum number of page run attempts before failing. 1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._progress_reporter.WillAttemptPageRun( 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self, attempt_count, max_attempts) 1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Clear any values from previous attempts for this page run. 1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._current_page_run.ClearValues() 14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def AddValue(self, value): 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert self._current_page_run, 'Not currently running test.' 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._ValidateValue(value) 1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # TODO(eakuefner/chrishenry): Add only one skip per pagerun assert here 1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._current_page_run.AddValue(value) 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._progress_reporter.DidAddValue(value) 152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 153116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def AddSummaryValue(self, value): 154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch assert value.page is None 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._ValidateValue(value) 156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._all_summary_values.append(value) 157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def _ValidateValue(self, value): 159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch assert isinstance(value, value_module.Value) 160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if value.name not in self._representative_value_for_each_value_name: 161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._representative_value_for_each_value_name[value.name] = value 162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch representative_value = self._representative_value_for_each_value_name[ 163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch value.name] 164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch assert value.IsMergableWith(representative_value) 16590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 16690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) def PrintSummary(self): 1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._progress_reporter.DidFinishAllTests(self) 1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for output_formatter in self._output_formatters: 1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) output_formatter.Format(self) 170116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 171116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def FindPageSpecificValuesForPage(self, page, value_name): 172116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch values = [] 173116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch for value in self.all_page_specific_values: 174116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if value.page == page and value.name == value_name: 175116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch values.append(value) 176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return values 177116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 178116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def FindAllPageSpecificValuesNamed(self, value_name): 179116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch values = [] 180116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch for value in self.all_page_specific_values: 181116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if value.name == value_name: 182116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch values.append(value) 183116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return values 184