1# Copyright (C) 2010 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29import cPickle 30 31from webkitpy.layout_tests.models import test_expectations 32 33 34def is_reftest_failure(failure_list): 35 failure_types = [type(f) for f in failure_list] 36 return set((FailureReftestMismatch, FailureReftestMismatchDidNotOccur, FailureReftestNoImagesGenerated)).intersection(failure_types) 37 38# FIXME: This is backwards. Each TestFailure subclass should know what 39# test_expectation type it corresponds too. Then this method just 40# collects them all from the failure list and returns the worst one. 41def determine_result_type(failure_list): 42 """Takes a set of test_failures and returns which result type best fits 43 the list of failures. "Best fits" means we use the worst type of failure. 44 45 Returns: 46 one of the test_expectations result types - PASS, FAIL, CRASH, etc.""" 47 48 if not failure_list or len(failure_list) == 0: 49 return test_expectations.PASS 50 51 failure_types = [type(f) for f in failure_list] 52 if FailureCrash in failure_types: 53 return test_expectations.CRASH 54 elif FailureTimeout in failure_types: 55 return test_expectations.TIMEOUT 56 elif FailureEarlyExit in failure_types: 57 return test_expectations.SKIP 58 elif (FailureMissingResult in failure_types or 59 FailureMissingImage in failure_types or 60 FailureMissingImageHash in failure_types or 61 FailureMissingAudio in failure_types): 62 return test_expectations.MISSING 63 else: 64 is_text_failure = FailureTextMismatch in failure_types 65 is_image_failure = (FailureImageHashIncorrect in failure_types or 66 FailureImageHashMismatch in failure_types) 67 is_audio_failure = (FailureAudioMismatch in failure_types) 68 if is_text_failure and is_image_failure: 69 return test_expectations.IMAGE_PLUS_TEXT 70 elif is_text_failure: 71 return test_expectations.TEXT 72 elif is_image_failure or is_reftest_failure(failure_list): 73 return test_expectations.IMAGE 74 elif is_audio_failure: 75 return test_expectations.AUDIO 76 else: 77 raise ValueError("unclassifiable set of failures: " 78 + str(failure_types)) 79 80 81class TestFailure(object): 82 """Abstract base class that defines the failure interface.""" 83 84 @staticmethod 85 def loads(s): 86 """Creates a TestFailure object from the specified string.""" 87 return cPickle.loads(s) 88 89 def message(self): 90 """Returns a string describing the failure in more detail.""" 91 raise NotImplementedError 92 93 def __eq__(self, other): 94 return self.__class__.__name__ == other.__class__.__name__ 95 96 def __ne__(self, other): 97 return self.__class__.__name__ != other.__class__.__name__ 98 99 def __hash__(self): 100 return hash(self.__class__.__name__) 101 102 def dumps(self): 103 """Returns the string/JSON representation of a TestFailure.""" 104 return cPickle.dumps(self) 105 106 def driver_needs_restart(self): 107 """Returns True if we should kill the driver before the next test.""" 108 return False 109 110 111class FailureTimeout(TestFailure): 112 def __init__(self, is_reftest=False): 113 super(FailureTimeout, self).__init__() 114 self.is_reftest = is_reftest 115 116 def message(self): 117 return "test timed out" 118 119 def driver_needs_restart(self): 120 return True 121 122 123class FailureCrash(TestFailure): 124 def __init__(self, is_reftest=False, process_name='content_shell', pid=None): 125 super(FailureCrash, self).__init__() 126 self.process_name = process_name 127 self.pid = pid 128 self.is_reftest = is_reftest 129 130 def message(self): 131 if self.pid: 132 return "%s crashed [pid=%d]" % (self.process_name, self.pid) 133 return self.process_name + " crashed" 134 135 def driver_needs_restart(self): 136 return True 137 138 139class FailureMissingResult(TestFailure): 140 def message(self): 141 return "-expected.txt was missing" 142 143 144class FailureTextMismatch(TestFailure): 145 def message(self): 146 return "text diff" 147 148class FailureMissingImageHash(TestFailure): 149 def message(self): 150 return "-expected.png was missing an embedded checksum" 151 152 153class FailureMissingImage(TestFailure): 154 def message(self): 155 return "-expected.png was missing" 156 157 158class FailureImageHashMismatch(TestFailure): 159 def message(self): 160 return "image diff" 161 162 163class FailureImageHashIncorrect(TestFailure): 164 def message(self): 165 return "-expected.png embedded checksum is incorrect" 166 167 168class FailureReftestMismatch(TestFailure): 169 def __init__(self, reference_filename=None): 170 super(FailureReftestMismatch, self).__init__() 171 self.reference_filename = reference_filename 172 173 def message(self): 174 return "reference mismatch" 175 176 177class FailureReftestMismatchDidNotOccur(TestFailure): 178 def __init__(self, reference_filename=None): 179 super(FailureReftestMismatchDidNotOccur, self).__init__() 180 self.reference_filename = reference_filename 181 182 def message(self): 183 return "reference mismatch didn't happen" 184 185 186class FailureReftestNoImagesGenerated(TestFailure): 187 def __init__(self, reference_filename=None): 188 super(FailureReftestNoImagesGenerated, self).__init__() 189 self.reference_filename = reference_filename 190 191 def message(self): 192 return "reference didn't generate pixel results." 193 194 195class FailureMissingAudio(TestFailure): 196 def message(self): 197 return "expected audio result was missing" 198 199 200class FailureAudioMismatch(TestFailure): 201 def message(self): 202 return "audio mismatch" 203 204 205class FailureEarlyExit(TestFailure): 206 def message(self): 207 return "skipped due to early exit" 208 209 210# Convenient collection of all failure classes for anything that might 211# need to enumerate over them all. 212ALL_FAILURE_CLASSES = (FailureTimeout, FailureCrash, FailureMissingResult, 213 FailureTextMismatch, FailureMissingImageHash, 214 FailureMissingImage, FailureImageHashMismatch, 215 FailureImageHashIncorrect, FailureReftestMismatch, 216 FailureReftestMismatchDidNotOccur, FailureReftestNoImagesGenerated, 217 FailureMissingAudio, FailureAudioMismatch, 218 FailureEarlyExit) 219