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