OLD | NEW |
| (Empty) |
1 # Copyright (c) 2006-2009 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 | |
5 """Classes for failures that occur during tests.""" | |
6 | |
7 import os | |
8 import test_expectations | |
9 | |
10 | |
11 def DetermineResultType(failure_list): | |
12 """Takes a set of test_failures and returns which result type best fits | |
13 the list of failures. "Best fits" means we use the worst type of failure. | |
14 | |
15 Returns: | |
16 one of the test_expectations result types - PASS, TEXT, CRASH, etc.""" | |
17 | |
18 if not failure_list or len(failure_list) == 0: | |
19 return test_expectations.PASS | |
20 | |
21 failure_types = [type(f) for f in failure_list] | |
22 if FailureCrash in failure_types: | |
23 return test_expectations.CRASH | |
24 elif FailureTimeout in failure_types: | |
25 return test_expectations.TIMEOUT | |
26 elif (FailureMissingResult in failure_types or | |
27 FailureMissingImage in failure_types or | |
28 FailureMissingImageHash in failure_types): | |
29 return test_expectations.MISSING | |
30 else: | |
31 is_text_failure = FailureTextMismatch in failure_types | |
32 is_image_failure = (FailureImageHashIncorrect in failure_types or | |
33 FailureImageHashMismatch in failure_types) | |
34 if is_text_failure and is_image_failure: | |
35 return test_expectations.IMAGE_PLUS_TEXT | |
36 elif is_text_failure: | |
37 return test_expectations.TEXT | |
38 elif is_image_failure: | |
39 return test_expectations.IMAGE | |
40 else: | |
41 raise ValueError("unclassifiable set of failures: " | |
42 + str(failure_types)) | |
43 | |
44 | |
45 class TestFailure(object): | |
46 """Abstract base class that defines the failure interface.""" | |
47 | |
48 @staticmethod | |
49 def Message(): | |
50 """Returns a string describing the failure in more detail.""" | |
51 raise NotImplemented | |
52 | |
53 def ResultHtmlOutput(self, filename): | |
54 """Returns an HTML string to be included on the results.html page.""" | |
55 raise NotImplemented | |
56 | |
57 def ShouldKillTestShell(self): | |
58 """Returns True if we should kill the test shell before the next | |
59 test.""" | |
60 return False | |
61 | |
62 def RelativeOutputFilename(self, filename, modifier): | |
63 """Returns a relative filename inside the output dir that contains | |
64 modifier. | |
65 | |
66 For example, if filename is fast\dom\foo.html and modifier is | |
67 "-expected.txt", the return value is fast\dom\foo-expected.txt | |
68 | |
69 Args: | |
70 filename: relative filename to test file | |
71 modifier: a string to replace the extension of filename with | |
72 | |
73 Return: | |
74 The relative windows path to the output filename | |
75 """ | |
76 return os.path.splitext(filename)[0] + modifier | |
77 | |
78 | |
79 class FailureWithType(TestFailure): | |
80 """Base class that produces standard HTML output based on the test type. | |
81 | |
82 Subclasses may commonly choose to override the ResultHtmlOutput, but still | |
83 use the standard OutputLinks. | |
84 """ | |
85 | |
86 def __init__(self, test_type): | |
87 TestFailure.__init__(self) | |
88 # TODO(ojan): This class no longer needs to know the test_type. | |
89 self._test_type = test_type | |
90 | |
91 # Filename suffixes used by ResultHtmlOutput. | |
92 OUT_FILENAMES = [] | |
93 | |
94 def OutputLinks(self, filename, out_names): | |
95 """Returns a string holding all applicable output file links. | |
96 | |
97 Args: | |
98 filename: the test filename, used to construct the result file names | |
99 out_names: list of filename suffixes for the files. If three or more | |
100 suffixes are in the list, they should be [actual, expected, diff, | |
101 wdiff]. Two suffixes should be [actual, expected], and a | |
102 single item is the [actual] filename suffix. | |
103 If out_names is empty, returns the empty string. | |
104 """ | |
105 links = [''] | |
106 uris = [self.RelativeOutputFilename(filename, fn) for fn in out_names] | |
107 if len(uris) > 1: | |
108 links.append("<a href='%s'>expected</a>" % uris[1]) | |
109 if len(uris) > 0: | |
110 links.append("<a href='%s'>actual</a>" % uris[0]) | |
111 if len(uris) > 2: | |
112 links.append("<a href='%s'>diff</a>" % uris[2]) | |
113 if len(uris) > 3: | |
114 links.append("<a href='%s'>wdiff</a>" % uris[3]) | |
115 return ' '.join(links) | |
116 | |
117 def ResultHtmlOutput(self, filename): | |
118 return self.Message() + self.OutputLinks(filename, self.OUT_FILENAMES) | |
119 | |
120 | |
121 class FailureTimeout(TestFailure): | |
122 """Test timed out. We also want to restart the test shell if this | |
123 happens.""" | |
124 | |
125 @staticmethod | |
126 def Message(): | |
127 return "Test timed out" | |
128 | |
129 def ResultHtmlOutput(self, filename): | |
130 return "<strong>%s</strong>" % self.Message() | |
131 | |
132 def ShouldKillTestShell(self): | |
133 return True | |
134 | |
135 | |
136 class FailureCrash(TestFailure): | |
137 """Test shell crashed.""" | |
138 | |
139 @staticmethod | |
140 def Message(): | |
141 return "Test shell crashed" | |
142 | |
143 def ResultHtmlOutput(self, filename): | |
144 # TODO(tc): create a link to the minidump file | |
145 stack = self.RelativeOutputFilename(filename, "-stack.txt") | |
146 return "<strong>%s</strong> <a href=%s>stack</a>" % (self.Message(), | |
147 stack) | |
148 | |
149 def ShouldKillTestShell(self): | |
150 return True | |
151 | |
152 | |
153 class FailureMissingResult(FailureWithType): | |
154 """Expected result was missing.""" | |
155 OUT_FILENAMES = ["-actual.txt"] | |
156 | |
157 @staticmethod | |
158 def Message(): | |
159 return "No expected results found" | |
160 | |
161 def ResultHtmlOutput(self, filename): | |
162 return ("<strong>%s</strong>" % self.Message() + | |
163 self.OutputLinks(filename, self.OUT_FILENAMES)) | |
164 | |
165 | |
166 class FailureTextMismatch(FailureWithType): | |
167 """Text diff output failed.""" | |
168 # Filename suffixes used by ResultHtmlOutput. | |
169 OUT_FILENAMES = ["-actual.txt", "-expected.txt", "-diff.txt"] | |
170 OUT_FILENAMES_WDIFF = ["-actual.txt", "-expected.txt", "-diff.txt", | |
171 "-wdiff.html"] | |
172 | |
173 def __init__(self, test_type, has_wdiff): | |
174 FailureWithType.__init__(self, test_type) | |
175 if has_wdiff: | |
176 self.OUT_FILENAMES = self.OUT_FILENAMES_WDIFF | |
177 | |
178 @staticmethod | |
179 def Message(): | |
180 return "Text diff mismatch" | |
181 | |
182 | |
183 class FailureMissingImageHash(FailureWithType): | |
184 """Actual result hash was missing.""" | |
185 # Chrome doesn't know to display a .checksum file as text, so don't bother | |
186 # putting in a link to the actual result. | |
187 OUT_FILENAMES = [] | |
188 | |
189 @staticmethod | |
190 def Message(): | |
191 return "No expected image hash found" | |
192 | |
193 def ResultHtmlOutput(self, filename): | |
194 return "<strong>%s</strong>" % self.Message() | |
195 | |
196 | |
197 class FailureMissingImage(FailureWithType): | |
198 """Actual result image was missing.""" | |
199 OUT_FILENAMES = ["-actual.png"] | |
200 | |
201 @staticmethod | |
202 def Message(): | |
203 return "No expected image found" | |
204 | |
205 def ResultHtmlOutput(self, filename): | |
206 return ("<strong>%s</strong>" % self.Message() + | |
207 self.OutputLinks(filename, self.OUT_FILENAMES)) | |
208 | |
209 | |
210 class FailureImageHashMismatch(FailureWithType): | |
211 """Image hashes didn't match.""" | |
212 OUT_FILENAMES = ["-actual.png", "-expected.png", "-diff.png"] | |
213 | |
214 @staticmethod | |
215 def Message(): | |
216 # We call this a simple image mismatch to avoid confusion, since | |
217 # we link to the PNGs rather than the checksums. | |
218 return "Image mismatch" | |
219 | |
220 | |
221 class FailureFuzzyFailure(FailureWithType): | |
222 """Image hashes didn't match.""" | |
223 OUT_FILENAMES = ["-actual.png", "-expected.png"] | |
224 | |
225 @staticmethod | |
226 def Message(): | |
227 return "Fuzzy image match also failed" | |
228 | |
229 | |
230 class FailureImageHashIncorrect(FailureWithType): | |
231 """Actual result hash is incorrect.""" | |
232 # Chrome doesn't know to display a .checksum file as text, so don't bother | |
233 # putting in a link to the actual result. | |
234 OUT_FILENAMES = [] | |
235 | |
236 @staticmethod | |
237 def Message(): | |
238 return "Images match, expected image hash incorrect. " | |
239 | |
240 def ResultHtmlOutput(self, filename): | |
241 return "<strong>%s</strong>" % self.Message() | |
OLD | NEW |