Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py

Issue 174073002: Overlay expected and actual repaint rects for LayoutTests. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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))
OLDNEW
« LayoutTests/fast/harness/results.html ('K') | « LayoutTests/fast/harness/results.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698