Commit a277bd76 authored by chrishenry@google.com's avatar chrishenry@google.com

Add FailureValue and wire it through PageTestResults.

BUG=392901

Review URL: https://codereview.chromium.org/399263003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284459 0039d316-1c4b-4281-b951-d872f2087c98
parent 696c3ef3
......@@ -9,6 +9,7 @@ from measurements import skpicture_printer
from telemetry.page import page_measurement_unittest_base
from telemetry.unittest import options_for_unittests
from telemetry.unittest import test
from telemetry.value import failure
class SkpicturePrinterUnitTest(
......@@ -28,7 +29,8 @@ class SkpicturePrinterUnitTest(
# Picture printing is not supported on all platforms.
if results.failures:
assert 'not supported' in results.failures[0][1]
assert 'not supported' in failure.GetStringFromExcInfo(
results.failures[0].exc_info)
return
saved_picture_count = results.FindAllPageSpecificValuesNamed(
......
......@@ -27,7 +27,7 @@ from telemetry.page.actions import page_action
from telemetry.results import results_options
from telemetry.util import cloud_storage
from telemetry.util import exception_formatter
from telemetry.value import failure
class _RunState(object):
def __init__(self):
......@@ -498,7 +498,8 @@ def _CheckArchives(page_set, pages, results):
for page in pages_missing_archive_path + pages_missing_archive_data:
results.StartTest(page)
results.AddFailureMessage(page, 'Page set archive doesn\'t exist.')
results.AddValue(failure.FailureValue.FromMessage(
page, 'Page set archive doesn\'t exist.'))
results.StopTest(page)
return [page for page in pages if page not in
......@@ -521,7 +522,7 @@ def _RunPage(test, page, state, expectation, results, finder_options):
results.AddSuccess(page)
else:
msg = 'Exception while running %s' % page.url
results.AddFailure(page, sys.exc_info())
results.AddValue(failure.FailureValue(page, sys.exc_info()))
exception_formatter.PrintFormattedException(msg=msg)
try:
......@@ -541,7 +542,7 @@ def _RunPage(test, page, state, expectation, results, finder_options):
else:
exception_formatter.PrintFormattedException(
msg='Failure while running %s' % page.url)
results.AddFailure(page, sys.exc_info())
results.AddValue(failure.FailureValue(page, sys.exc_info()))
except (util.TimeoutException, exceptions.LoginException,
exceptions.ProfilingException):
ProcessError()
......@@ -554,7 +555,7 @@ def _RunPage(test, page, state, expectation, results, finder_options):
except Exception:
exception_formatter.PrintFormattedException(
msg='Unhandled exception while running %s' % page.url)
results.AddFailure(page, sys.exc_info())
results.AddValue(failure.FailureValue(page, sys.exc_info()))
else:
if expectation == 'fail':
logging.warning('%s was expected to fail, but passed.\n', page.url)
......
......@@ -49,7 +49,7 @@ def GenerateProfiles(profile_creator_class, profile_creator_name, options):
if results.failures:
logging.warning('Some pages failed.')
logging.warning('Failed pages:\n%s',
'\n'.join(zip(*results.failures)[0]))
'\n'.join(results.pages_that_had_failures))
return 1
# Everything is a-ok, move results to final destination.
......
......@@ -136,7 +136,7 @@ def Main(base_dir):
logging.warning('Some pages failed. The recording has not been updated for '
'these pages.')
logging.warning('Failed pages:\n%s', '\n'.join(
p.display_name for p in zip(*results.failures)[0]))
p.display_name for p in results.pages_that_had_failures))
if results.skipped:
logging.warning('Some pages were skipped. The recording has not been '
......
......@@ -18,10 +18,11 @@ class BlockPageMeasurementResults(
return
lines = ['name: %s' % values[0].page.display_name]
for value in sorted(values, key=lambda x: x.name):
lines.append('%s (%s): %s' %
(value.name,
value.units,
value.GetRepresentativeString()))
if value.GetRepresentativeString() is not None:
lines.append('%s (%s): %s' %
(value.name,
value.units,
value.GetRepresentativeString()))
for line in lines:
self._output_stream.write(line)
self._output_stream.write(os.linesep)
......
......@@ -52,6 +52,9 @@ class BuildbotPageMeasurementResults(
buildbot_value = value.GetBuildbotValue()
buildbot_data_type = value.GetBuildbotDataType(
output_context=value_module.PER_PAGE_RESULT_OUTPUT_CONTEXT)
if buildbot_value is None or buildbot_data_type is None:
return
buildbot_measurement_name, buildbot_trace_name = (
value.GetBuildbotMeasurementAndTraceNameForPerPageResult())
self._PrintPerfResult(buildbot_measurement_name,
......@@ -68,10 +71,12 @@ class BuildbotPageMeasurementResults(
buildbot_value = value.GetBuildbotValue()
buildbot_data_type = value.GetBuildbotDataType(
output_context=value_module.COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT)
if buildbot_value is None or buildbot_data_type is None:
return
buildbot_measurement_name, buildbot_trace_name = (
value.GetBuildbotMeasurementAndTraceNameForComputedSummaryResult(
self._trace_tag))
self._PrintPerfResult(buildbot_measurement_name,
buildbot_trace_name,
buildbot_value, value.units, buildbot_data_type)
......
......@@ -5,11 +5,12 @@ import os
from telemetry import perf_tests_helper
from telemetry.page import page_set
from telemetry.results import base_test_results_unittest
from telemetry.results import buildbot_page_measurement_results
from telemetry.value import failure
from telemetry.value import histogram
from telemetry.value import list_of_scalar_values
from telemetry.value import scalar
from telemetry.results import base_test_results_unittest
from telemetry.results import buildbot_page_measurement_results
def _MakePageSet():
......@@ -130,7 +131,8 @@ class BuildbotPageMeasurementResultsTest(
measurement_results.AddValue(scalar.ScalarValue(
test_page_set.pages[0], 'a', 'seconds', 3))
measurement_results.DidMeasurePage()
measurement_results.AddFailureMessage(test_page_set.pages[0], 'message')
measurement_results.AddValue(
failure.FailureValue.FromMessage(test_page_set.pages[0], 'message'))
measurement_results.WillMeasurePage(test_page_set.pages[1])
measurement_results.AddValue(scalar.ScalarValue(
......@@ -161,7 +163,8 @@ class BuildbotPageMeasurementResultsTest(
measurement_results.AddValue(scalar.ScalarValue(
test_page_set.pages[1], 'a', 'seconds', 7))
measurement_results.DidMeasurePage()
measurement_results.AddFailureMessage(test_page_set.pages[1], 'message')
measurement_results.AddValue(
failure.FailureValue.FromMessage(test_page_set.pages[1], 'message'))
measurement_results.WillMeasurePage(test_page_set.pages[0])
measurement_results.AddValue(scalar.ScalarValue(
......
......@@ -6,6 +6,7 @@ import logging
import time
from telemetry.results import page_test_results
from telemetry.value import failure
class GTestTestResults(page_test_results.PageTestResults):
......@@ -16,20 +17,19 @@ class GTestTestResults(page_test_results.PageTestResults):
def _GetMs(self):
return (time.time() - self._timestamp) * 1000
def _emitFailure(self, page, err):
print >> self._output_stream, self._GetStringFromExcInfo(err)
print >> self._output_stream, '[ FAILED ]', page.display_name, (
def _emitFailure(self, failure_value):
print >> self._output_stream, failure.GetStringFromExcInfo(
failure_value.exc_info)
display_name = failure_value.page.display_name
print >> self._output_stream, '[ FAILED ]', display_name, (
'(%0.f ms)' % self._GetMs())
self._output_stream.flush()
def ValidateValue(self, value):
# TODO(chrishenry): When FailureValue is added, this should instead
# validate that isinstance(value, FailureValue) is true.
raise Exception('GTestTestResults does not support AddValue().')
def AddFailure(self, page, err):
super(GTestTestResults, self).AddFailure(page, err)
self._emitFailure(page, err)
def AddValue(self, value):
assert isinstance(value, failure.FailureValue), (
'GTestTestResults only accepts FailureValue.')
super(GTestTestResults, self).AddValue(value)
self._emitFailure(value)
def StartTest(self, page):
super(GTestTestResults, self).StartTest(page)
......@@ -61,9 +61,9 @@ class GTestTestResults(page_test_results.PageTestResults):
unit = 'test' if len(self.failures) == 1 else 'tests'
print >> self._output_stream, '[ FAILED ]', (
'%d %s, listed below:' % (len(self.failures), unit))
for page, _ in self.failures:
for failure_value in self.failures:
print >> self._output_stream, '[ FAILED ] ', (
page.display_name)
failure_value.page.display_name)
print >> self._output_stream
count = len(self.failures)
unit = 'TEST' if count == 1 else 'TESTS'
......
......@@ -8,6 +8,7 @@ from telemetry.page import page_set
from telemetry.results import base_test_results_unittest
from telemetry.results import gtest_test_results
from telemetry.unittest import simple_mock
from telemetry.value import failure
def _MakePageSet():
......@@ -56,10 +57,10 @@ class GTestTestResultsTest(
results = SummaryGtestTestResults()
results.StartTest(test_page_set.pages[0])
exception = self.CreateException()
results.AddFailure(test_page_set.pages[0], exception)
exc_info = self.CreateException()
results.AddValue(failure.FailureValue(test_page_set.pages[0], exc_info))
results.PrintSummary()
exception_trace = ''.join(traceback.format_exception(*exception))
exception_trace = ''.join(traceback.format_exception(*exc_info))
expected = ('[ RUN ] http://www.foo.com/\n'
'%s\n'
'[ FAILED ] http://www.foo.com/ (0 ms)\n'
......@@ -84,7 +85,7 @@ class GTestTestResultsTest(
def testPassAndFailedPages(self):
test_page_set = _MakePageSet()
results = SummaryGtestTestResults()
exception = self.CreateException()
exc_info = self.CreateException()
results.StartTest(test_page_set.pages[0])
self._mock_timer.SetTime(0.007)
......@@ -92,18 +93,18 @@ class GTestTestResultsTest(
results.StartTest(test_page_set.pages[1])
self._mock_timer.SetTime(0.009)
results.AddFailure(test_page_set.pages[1], exception)
results.AddValue(failure.FailureValue(test_page_set.pages[1], exc_info))
results.StartTest(test_page_set.pages[2])
self._mock_timer.SetTime(0.015)
results.AddFailure(test_page_set.pages[2], exception)
results.AddValue(failure.FailureValue(test_page_set.pages[2], exc_info))
results.StartTest(test_page_set.pages[3])
self._mock_timer.SetTime(0.020)
results.AddSuccess(test_page_set.pages[3])
results.PrintSummary()
exception_trace = ''.join(traceback.format_exception(*exception))
exception_trace = ''.join(traceback.format_exception(*exc_info))
expected = ('[ RUN ] http://www.foo.com/\n'
'[ OK ] http://www.foo.com/ (7 ms)\n'
'[ RUN ] http://www.bar.com/\n'
......@@ -124,7 +125,7 @@ class GTestTestResultsTest(
def testStreamingResults(self):
test_page_set = _MakePageSet()
results = SummaryGtestTestResults()
exception = self.CreateException()
exc_info = self.CreateException()
results.StartTest(test_page_set.pages[0])
self._mock_timer.SetTime(0.007)
......@@ -135,8 +136,8 @@ class GTestTestResultsTest(
results.StartTest(test_page_set.pages[1])
self._mock_timer.SetTime(0.009)
exception_trace = ''.join(traceback.format_exception(*exception))
results.AddFailure(test_page_set.pages[1], exception)
exception_trace = ''.join(traceback.format_exception(*exc_info))
results.AddValue(failure.FailureValue(test_page_set.pages[1], exc_info))
expected = ('[ RUN ] http://www.foo.com/\n'
'[ OK ] http://www.foo.com/ (7 ms)\n'
'[ RUN ] http://www.bar.com/\n'
......
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
from telemetry.results import page_test_results
from telemetry.value import failure
class PageMeasurementResults(page_test_results.PageTestResults):
def __init__(self, output_stream=None, trace_tag=''):
......@@ -28,7 +29,8 @@ class PageMeasurementResults(page_test_results.PageTestResults):
def AddValue(self, value):
super(PageMeasurementResults, self).AddValue(value)
self._page_specific_values_for_current_page.append(value)
if not isinstance(value, failure.FailureValue):
self._page_specific_values_for_current_page.append(value)
def DidMeasurePage(self):
assert self._current_page, 'Failed to call WillMeasurePage'
......
......@@ -7,6 +7,7 @@ import unittest
from telemetry import perf_tests_helper
from telemetry.page import page_set
from telemetry.results import page_measurement_results
from telemetry.value import failure
from telemetry.value import histogram
from telemetry.value import scalar
......@@ -112,12 +113,12 @@ class PageMeasurementResultsTest(unittest.TestCase):
results.WillMeasurePage(self.pages[0])
results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
results.DidMeasurePage()
results.AddFailureMessage(self.pages[0], 'message')
results.AddValue(failure.FailureValue.FromMessage(self.pages[0], 'message'))
results.WillMeasurePage(self.pages[1])
results.AddValue(scalar.ScalarValue(self.pages[1], 'a', 'seconds', 7))
results.DidMeasurePage()
results.AddFailureMessage(self.pages[1], 'message')
results.AddValue(failure.FailureValue.FromMessage(self.pages[1], 'message'))
results.PrintSummary()
self.assertEquals(results.results, [])
......@@ -158,7 +159,7 @@ class PageMeasurementResultsTest(unittest.TestCase):
results.DidMeasurePage()
results.WillMeasurePage(self.pages[1])
results.AddFailureMessage(self.pages[1], "Failure")
results.AddValue(failure.FailureValue.FromMessage(self.pages[1], 'Failure'))
results.DidMeasurePage()
results.WillMeasurePage(self.pages[2])
......@@ -166,6 +167,7 @@ class PageMeasurementResultsTest(unittest.TestCase):
results.DidMeasurePage()
values = results.all_page_specific_values
self.assertEquals(2, len(values))
self.assertEquals([self.pages[0], self.pages[2]],
[v.page for v in values])
self.assertEquals(3, len(values))
self.assertEquals(
[self.pages[0], self.pages[2]], results.pages_that_succeeded)
self.assertEquals([self.pages[1]], results.pages_that_had_failures)
......@@ -5,18 +5,18 @@
import collections
import copy
import logging
import sys
import traceback
from telemetry import value as value_module
from telemetry.value import failure
class PageTestResults(object):
def __init__(self, output_stream=None):
super(PageTestResults, self).__init__()
self._output_stream = output_stream
self.pages_that_had_failures = set()
# TODO(chrishenry,eakuefner): Remove self.successes once they can
# be inferred.
self.successes = []
self.failures = []
self.skipped = []
self._representative_value_for_each_value_name = {}
......@@ -44,7 +44,16 @@ class PageTestResults(object):
def pages_that_succeeded(self):
pages = set([value.page for value in self._all_page_specific_values])
pages.difference_update(self.pages_that_had_failures)
return pages
return list(pages)
@property
def pages_that_had_failures(self):
return list(set([v.page for v in self.failures]))
@property
def failures(self):
values = self._all_page_specific_values
return [v for v in values if isinstance(v, failure.FailureValue)]
def _GetStringFromExcInfo(self, err):
return ''.join(traceback.format_exception(*err))
......@@ -56,15 +65,15 @@ class PageTestResults(object):
pass
def AddValue(self, value):
self.ValidateValue(value)
self._ValidateValue(value)
self._all_page_specific_values.append(value)
def AddSummaryValue(self, value):
assert value.page is None
self.ValidateValue(value)
self._ValidateValue(value)
self._all_summary_values.append(value)
def ValidateValue(self, value):
def _ValidateValue(self, value):
assert isinstance(value, value_module.Value)
if value.name not in self._representative_value_for_each_value_name:
self._representative_value_for_each_value_name[value.name] = value
......@@ -72,26 +81,16 @@ class PageTestResults(object):
value.name]
assert value.IsMergableWith(representative_value)
def AddFailure(self, page, err):
self.pages_that_had_failures.add(page)
self.failures.append((page, self._GetStringFromExcInfo(err)))
def AddSkip(self, page, reason):
self.skipped.append((page, reason))
def AddSuccess(self, page):
self.successes.append(page)
def AddFailureMessage(self, page, message):
try:
raise Exception(message)
except Exception:
self.AddFailure(page, sys.exc_info())
def PrintSummary(self):
if self.failures:
logging.error('Failed pages:\n%s', '\n'.join(
p.display_name for p in zip(*self.failures)[0]))
p.display_name for p in self.pages_that_had_failures))
if self.skipped:
logging.warning('Skipped pages:\n%s', '\n'.join(
......
......@@ -6,6 +6,7 @@ from telemetry.results import base_test_results_unittest
from telemetry.page import page_set
from telemetry.results import page_test_results
from telemetry.value import failure
class NonPrintingPageTestResults(
page_test_results.PageTestResults):
......@@ -28,8 +29,8 @@ class PageTestResultsTest(base_test_results_unittest.BaseTestResultsUnittest):
def test_failures(self):
results = NonPrintingPageTestResults()
results.AddFailure(self.pages[0], self.CreateException())
results.AddValue(
failure.FailureValue(self.pages[0], self.CreateException()))
results.AddSuccess(self.pages[1])
self.assertEquals(results.pages_that_had_failures,
set([self.pages[0]]))
self.assertEquals(results.pages_that_had_failures, [self.pages[0]])
self.assertEquals(results.successes, [self.pages[1]])
# 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.
import sys
import traceback
from telemetry import value as value_module
class FailureValue(value_module.Value):
def __init__(self, page, exc_info):
"""A value representing a failure when running the page.
Args:
page: The page where this failure occurs.
exc_info: The exception info (sys.exc_info()) corresponding to
this failure.
"""
exc_type = exc_info[0].__name__
super(FailureValue, self).__init__(page, exc_type, '', True)
self._exc_info = exc_info
@classmethod
def FromMessage(cls, page, message):
"""Creates a failure value for a given string message.
Args:
page: The page where this failure occurs.
message: A string message describing the failure.
"""
try:
raise Exception(message)
except Exception:
return FailureValue(page, sys.exc_info())
def __repr__(self):
if self.page:
page_name = self.page.url
else:
page_name = None
return 'FailureValue(%s, %s)' % (
page_name, GetStringFromExcInfo(self._exc_info))
@property
def exc_info(self):
return self._exc_info
def GetBuildbotDataType(self, output_context):
return None
def GetBuildbotValue(self):
return None
def GetBuildbotMeasurementAndTraceNameForPerPageResult(self):
return None
def GetRepresentativeNumber(self):
return None
def GetRepresentativeString(self):
return None
@classmethod
def GetJSONTypeName(cls):
return 'failure'
def AsDict(self):
d = super(FailureValue, self).AsDict()
d['value'] = GetStringFromExcInfo(self.exc_info)
return d
@classmethod
def MergeLikeValuesFromSamePage(cls, values):
assert False, 'Should not be called.'
@classmethod
def MergeLikeValuesFromDifferentPages(cls, values,
group_by_name_suffix=False):
assert False, 'Should not be called.'
def GetStringFromExcInfo(exc_info):
return ''.join(traceback.format_exception(*exc_info))
# 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.
import os
import sys
import unittest
from telemetry import value
from telemetry.page import page_set
from telemetry.value import failure
class TestBase(unittest.TestCase):
def setUp(self):
self.page_set = page_set.PageSet(file_path=os.path.dirname(__file__))
self.page_set.AddPageWithDefaultRunNavigate("http://www.bar.com/")
@property
def pages(self):
return self.page_set.pages
class ValueTest(TestBase):
def testName(self):
v0 = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
self.assertEqual('Exception', v0.name)
try:
raise NotImplementedError()
except Exception:
v1 = failure.FailureValue(self.pages[0], sys.exc_info())
self.assertEqual('NotImplementedError', v1.name)
def testBuildbotAndRepresentativeValue(self):
v = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
self.assertIsNone(v.GetBuildbotValue())
self.assertIsNone(v.GetBuildbotDataType(
value.COMPUTED_PER_PAGE_SUMMARY_OUTPUT_CONTEXT))
self.assertIsNone(v.GetBuildbotMeasurementAndTraceNameForPerPageResult())
self.assertIsNone(v.GetRepresentativeNumber())
self.assertIsNone(v.GetRepresentativeString())
def testAsDict(self):
v = failure.FailureValue.FromMessage(self.pages[0], 'Failure')
d = v.AsDictWithoutBaseClassEntries()
self.assertTrue(d['value'].find('Exception: Failure') > -1)
# Copyright 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.
from telemetry.value import failure
def MergeLikeValuesFromSamePage(all_values):
"""Merges values that measure the same thing on the same page.
......@@ -108,6 +111,12 @@ def GroupStably(all_values, key_func):
merge_groups = {}
merge_groups_in_creation_order = []
for value in all_values:
# TODO(chrishenry): This is temporary. When we figure out the
# right summarization strategy for page runs with failures, we
# should use that instead.
if isinstance(value, failure.FailureValue):
continue
key = key_func(value)
if key not in merge_groups:
merge_groups[key] = []
......
......@@ -4,6 +4,7 @@
from collections import defaultdict
from telemetry.value import failure
from telemetry.value import merge_values
class Summary(object):
......@@ -58,7 +59,9 @@ class Summary(object):
return self._interleaved_computed_per_page_values_and_summaries
def _ComputePerPageValues(self, all_page_specific_values):
all_successful_page_values = all_page_specific_values
all_successful_page_values = [
v for v in all_page_specific_values if not isinstance(
v, failure.FailureValue)]
# 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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment