OLD | NEW |
---|---|
1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
11 # in the documentation and/or other materials provided with the | 11 # in the documentation and/or other materials provided with the |
12 # distribution. | 12 # distribution. |
13 # * Neither the name of Google Inc. nor the names of its | 13 # * Neither the name of Google Inc. nor the names of its |
14 # contributors may be used to endorse or promote products derived from | 14 # contributors may be used to endorse or promote products derived from |
15 # this software without specific prior written permission. | 15 # this software without specific prior written permission. |
16 # | 16 # |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | 28 |
29 | |
30 import logging | 29 import logging |
30 import re | |
31 | 31 |
32 from webkitpy.layout_tests.models import test_failures | 32 from webkitpy.layout_tests.models import test_failures |
33 | 33 |
34 | 34 |
35 _log = logging.getLogger(__name__) | 35 _log = logging.getLogger(__name__) |
36 | 36 |
37 | 37 |
38 def write_test_result(filesystem, port, results_directory, test_name, driver_out put, | 38 def write_test_result(filesystem, port, results_directory, test_name, driver_out put, |
39 expected_driver_output, failures): | 39 expected_driver_output, failures): |
40 """Write the test result to the result output directory.""" | 40 """Write the test result to the result output directory.""" |
41 root_output_dir = results_directory | 41 root_output_dir = results_directory |
42 writer = TestResultWriter(filesystem, port, root_output_dir, test_name) | 42 writer = TestResultWriter(filesystem, port, root_output_dir, test_name) |
43 | 43 |
44 if driver_output.error: | 44 if driver_output.error: |
45 writer.write_stderr(driver_output.error) | 45 writer.write_stderr(driver_output.error) |
46 | 46 |
47 for failure in failures: | 47 for failure in failures: |
48 # FIXME: Instead of this long 'if' block, each failure class might | 48 # FIXME: Instead of this long 'if' block, each failure class might |
49 # have a responsibility for writing a test result. | 49 # have a responsibility for writing a test result. |
50 if isinstance(failure, (test_failures.FailureMissingResult, | 50 if isinstance(failure, (test_failures.FailureMissingResult, |
51 test_failures.FailureTextMismatch, | 51 test_failures.FailureTextMismatch, |
52 test_failures.FailureTestHarnessAssertion)): | 52 test_failures.FailureTestHarnessAssertion)): |
53 writer.write_text_files(driver_output.text, expected_driver_output.t ext) | 53 writer.write_text_files(driver_output.text, expected_driver_output.t ext) |
54 writer.create_text_diff_and_write_result(driver_output.text, expecte d_driver_output.text) | 54 writer.create_text_diff_and_write_result(driver_output.text, expecte d_driver_output.text) |
55 writer.create_overlay_invalidation_result(driver_output.text, expect ed_driver_output.text) | |
55 elif isinstance(failure, test_failures.FailureMissingImage): | 56 elif isinstance(failure, test_failures.FailureMissingImage): |
56 writer.write_image_files(driver_output.image, expected_image=None) | 57 writer.write_image_files(driver_output.image, expected_image=None) |
57 elif isinstance(failure, test_failures.FailureMissingImageHash): | 58 elif isinstance(failure, test_failures.FailureMissingImageHash): |
58 writer.write_image_files(driver_output.image, expected_driver_output .image) | 59 writer.write_image_files(driver_output.image, expected_driver_output .image) |
59 elif isinstance(failure, test_failures.FailureImageHashMismatch): | 60 elif isinstance(failure, test_failures.FailureImageHashMismatch): |
60 writer.write_image_files(driver_output.image, expected_driver_output .image) | 61 writer.write_image_files(driver_output.image, expected_driver_output .image) |
61 writer.write_image_diff_files(driver_output.image_diff) | 62 writer.write_image_diff_files(driver_output.image_diff) |
62 elif isinstance(failure, (test_failures.FailureAudioMismatch, | 63 elif isinstance(failure, (test_failures.FailureAudioMismatch, |
63 test_failures.FailureMissingAudio)): | 64 test_failures.FailureMissingAudio)): |
64 writer.write_audio_files(driver_output.audio, expected_driver_output .audio) | 65 writer.write_audio_files(driver_output.audio, expected_driver_output .audio) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
97 FILENAME_SUFFIX_ACTUAL = "-actual" | 98 FILENAME_SUFFIX_ACTUAL = "-actual" |
98 FILENAME_SUFFIX_EXPECTED = "-expected" | 99 FILENAME_SUFFIX_EXPECTED = "-expected" |
99 FILENAME_SUFFIX_DIFF = "-diff" | 100 FILENAME_SUFFIX_DIFF = "-diff" |
100 FILENAME_SUFFIX_STDERR = "-stderr" | 101 FILENAME_SUFFIX_STDERR = "-stderr" |
101 FILENAME_SUFFIX_CRASH_LOG = "-crash-log" | 102 FILENAME_SUFFIX_CRASH_LOG = "-crash-log" |
102 FILENAME_SUFFIX_SAMPLE = "-sample" | 103 FILENAME_SUFFIX_SAMPLE = "-sample" |
103 FILENAME_SUFFIX_WDIFF = "-wdiff.html" | 104 FILENAME_SUFFIX_WDIFF = "-wdiff.html" |
104 FILENAME_SUFFIX_PRETTY_PATCH = "-pretty-diff.html" | 105 FILENAME_SUFFIX_PRETTY_PATCH = "-pretty-diff.html" |
105 FILENAME_SUFFIX_IMAGE_DIFF = "-diff.png" | 106 FILENAME_SUFFIX_IMAGE_DIFF = "-diff.png" |
106 FILENAME_SUFFIX_IMAGE_DIFFS_HTML = "-diffs.html" | 107 FILENAME_SUFFIX_IMAGE_DIFFS_HTML = "-diffs.html" |
108 FILENAME_SUFFIX_OVERLAY = "-overlay.html" | |
107 | 109 |
108 def __init__(self, filesystem, port, root_output_dir, test_name): | 110 def __init__(self, filesystem, port, root_output_dir, test_name): |
109 self._filesystem = filesystem | 111 self._filesystem = filesystem |
110 self._port = port | 112 self._port = port |
111 self._root_output_dir = root_output_dir | 113 self._root_output_dir = root_output_dir |
112 self._test_name = test_name | 114 self._test_name = test_name |
113 | 115 |
114 def _make_output_directory(self): | 116 def _make_output_directory(self): |
115 """Creates the output directory (if needed) for a given test filename."" " | 117 """Creates the output directory (if needed) for a given test filename."" " |
116 fs = self._filesystem | 118 fs = self._filesystem |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
196 wdiff = self._port.wdiff_text(expected_filename, actual_filename) | 198 wdiff = self._port.wdiff_text(expected_filename, actual_filename) |
197 wdiff_filename = self.output_filename(self.FILENAME_SUFFIX_WDIFF) | 199 wdiff_filename = self.output_filename(self.FILENAME_SUFFIX_WDIFF) |
198 self._write_file(wdiff_filename, wdiff) | 200 self._write_file(wdiff_filename, wdiff) |
199 | 201 |
200 # Use WebKit's PrettyPatch.rb to get an HTML diff. | 202 # Use WebKit's PrettyPatch.rb to get an HTML diff. |
201 if self._port.pretty_patch_available(): | 203 if self._port.pretty_patch_available(): |
202 pretty_patch = self._port.pretty_patch_text(diff_filename) | 204 pretty_patch = self._port.pretty_patch_text(diff_filename) |
203 pretty_patch_filename = self.output_filename(self.FILENAME_SUFFIX_PR ETTY_PATCH) | 205 pretty_patch_filename = self.output_filename(self.FILENAME_SUFFIX_PR ETTY_PATCH) |
204 self._write_file(pretty_patch_filename, pretty_patch) | 206 self._write_file(pretty_patch_filename, pretty_patch) |
205 | 207 |
208 def create_overlay_invalidation_result(self, actual_text, expected_text): | |
Dirk Pranke
2014/02/27 20:57:44
Can you pull this whole function out of this file
| |
209 repaintPattern = re.compile('^\(repaint rects$') | |
210 repaintMatch = repaintPattern.search(expected_text) | |
Dirk Pranke
2014/02/27 20:57:44
this can probably be just re.search('^\(repaint re
| |
211 if repaintMatch == 'None': | |
212 return | |
213 | |
214 def make_js_rect(input_str): | |
215 rect_pattern = '\(rect\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\)' | |
216 ret = '[' | |
217 count = 0 | |
218 for m in re.finditer(rect_pattern, input_str): | |
Dirk Pranke
2014/02/27 20:57:44
hm. didn't know about finditer(); nifty.
| |
219 if count > 0: | |
220 ret += ',' | |
221 count += 1 | |
222 | |
223 ret += '[' + m.group(1) + ',' | |
224 ret += m.group(2) + ',' | |
225 ret += m.group(3) + ',' | |
226 ret += m.group(4) + ']' | |
227 return ret + ']' | |
Dirk Pranke
2014/02/27 20:57:44
hm. didn't know about finditer(); nifty.
This can
| |
228 | |
229 expected_rects = make_js_rect(expected_text) | |
230 actual_rects = make_js_rect(actual_text) | |
231 | |
232 html = """<!DOCTYPE HTML> | |
Dirk Pranke
2014/02/27 20:57:44
I defer to ojan re: the html template, but it look
| |
233 <html> | |
234 <head> | |
235 <title>%(title)s</title> | |
236 <style> | |
237 body { | |
238 margin: 0; | |
239 padding: 0; | |
240 } | |
241 iframe { | |
242 position: absolute; | |
243 top: 30px; | |
244 left: 0; | |
245 border: 0; | |
246 z-index: -1; | |
247 } | |
248 canvas { | |
249 z-index: 1; | |
250 top: 30px; | |
251 left: 0; | |
252 position: absolute; | |
253 } | |
254 #actual { display: none; } | |
255 </style> | |
256 </head> | |
257 <body> | |
258 <span id='type'>Expected Difference</span> | |
259 <div id=overlay> | |
260 <canvas id='expected' width='800' height='600'></canvas> | |
261 <canvas id='actual' width='800' height='600'></canvas> | |
262 </div> | |
263 <script> | |
264 (function() { | |
265 var expected_rects = %(expected_rects)s; | |
266 var actual_rects = %(actual_rects)s; | |
267 | |
268 var path = decodeURIComponent(location.search).substr(1); | |
269 var iframe = document.createElement('iframe'); | |
270 iframe.width = 800; | |
271 iframe.height = 600; | |
272 iframe.src = path; | |
273 | |
274 var overlay = document.getElementById('overlay'); | |
275 overlay.appendChild(iframe); | |
276 | |
277 for (var i = expected_rects.length - 1; i >= 0; i--) { | |
278 var expected_rect = expected_rects[i]; | |
279 for (var k = actual_rects.length - 1; k >= 0; k--) { | |
280 var actual_rect = actual_rects[k]; | |
281 if ((actual_rect[0] == expected_rect[0]) && | |
282 (actual_rect[1] == expected_rect[1]) && | |
283 (actual_rect[2] == expected_rect[2]) && | |
284 (actual_rect[3] == expected_rect[3])) { | |
285 expected_rects.splice(i, 1); | |
286 actual_rects.splice(k, 1); | |
287 } | |
288 } | |
289 } | |
290 | |
291 var expected_canvas = document.getElementById('expected'); | |
292 var expected_ctx = expected_canvas.getContext("2d"); | |
293 expected_ctx.fillStyle = 'rgba(255, 0, 0, 0.25)'; | |
294 for (var i = 0; i < expected_rects.length; i++) { | |
295 var rect = expected_rects[i]; | |
296 expected_ctx.fillRect(rect[0], rect[1], rect[2], rect[3]); | |
297 } | |
298 | |
299 var actual_canvas = document.getElementById('actual'); | |
300 var actual_ctx = actual_canvas.getContext("2d"); | |
301 actual_ctx.fillStyle = 'rgba(0, 255, 0, 0.25)'; | |
302 for (var i = 0; i < actual_rects.length; i++) { | |
303 var rect = actual_rects[i]; | |
304 actual_ctx.fillRect(rect[0], rect[1], rect[2], rect[3]); | |
305 } | |
306 | |
307 var type = document.getElementById('type'); | |
308 var expected_showing = true; | |
309 var flip = function() { | |
310 if (expected_showing) { | |
311 type.innerText = 'Actual Difference'; | |
ojan
2014/02/25 00:43:42
Nit: innerText is crazy. Use textContent.
| |
312 expected_canvas.style.display = 'none'; | |
313 actual_canvas.style.display = 'block'; | |
314 } else { | |
315 type.innerText = 'Expected Difference'; | |
316 actual_canvas.style.display = 'none'; | |
317 expected_canvas.style.display = 'block'; | |
318 } | |
319 expected_showing = !expected_showing | |
320 setTimeout(flip, 3000); | |
321 } | |
322 setTimeout(flip, 3000); | |
323 })(); | |
324 </script> | |
325 </body> | |
326 </html> | |
327 """ % { | |
328 'title': self._test_name, | |
329 'expected_rects': expected_rects, | |
330 'actual_rects': actual_rects, | |
331 } | |
332 overlay_filename = self.output_filename(self.FILENAME_SUFFIX_OVERLAY) | |
333 self._write_file(overlay_filename, html) | |
334 | |
206 def write_audio_files(self, actual_audio, expected_audio): | 335 def write_audio_files(self, actual_audio, expected_audio): |
207 self.write_output_files('.wav', actual_audio, expected_audio) | 336 self.write_output_files('.wav', actual_audio, expected_audio) |
208 | 337 |
209 def write_image_files(self, actual_image, expected_image): | 338 def write_image_files(self, actual_image, expected_image): |
210 self.write_output_files('.png', actual_image, expected_image) | 339 self.write_output_files('.png', actual_image, expected_image) |
211 | 340 |
212 def write_image_diff_files(self, image_diff): | 341 def write_image_diff_files(self, image_diff): |
213 diff_filename = self.output_filename(self.FILENAME_SUFFIX_IMAGE_DIFF) | 342 diff_filename = self.output_filename(self.FILENAME_SUFFIX_IMAGE_DIFF) |
214 self._write_file(diff_filename, image_diff) | 343 self._write_file(diff_filename, image_diff) |
215 | 344 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
267 'diff_filename': self._output_testname(self.FILENAME_SUFFIX_IMAGE_DI FF), | 396 'diff_filename': self._output_testname(self.FILENAME_SUFFIX_IMAGE_DI FF), |
268 'prefix': self._output_testname(''), | 397 'prefix': self._output_testname(''), |
269 } | 398 } |
270 self._write_file(diffs_html_filename, html) | 399 self._write_file(diffs_html_filename, html) |
271 | 400 |
272 def write_reftest(self, src_filepath): | 401 def write_reftest(self, src_filepath): |
273 fs = self._filesystem | 402 fs = self._filesystem |
274 dst_dir = fs.dirname(fs.join(self._root_output_dir, self._test_name)) | 403 dst_dir = fs.dirname(fs.join(self._root_output_dir, self._test_name)) |
275 dst_filepath = fs.join(dst_dir, fs.basename(src_filepath)) | 404 dst_filepath = fs.join(dst_dir, fs.basename(src_filepath)) |
276 self._write_file(dst_filepath, fs.read_binary_file(src_filepath)) | 405 self._write_file(dst_filepath, fs.read_binary_file(src_filepath)) |
OLD | NEW |