Chromium Code Reviews| 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): | |
| 209 repaintPattern = re.compile('^\(repaint rects$') | |
| 210 repaintMatch = repaintPattern.search(expected_text) | |
| 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): | |
| 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 + ']' | |
| 228 | |
| 229 expected_rects = make_js_rect(expected_text) | |
| 230 actual_rects = make_js_rect(actual_text) | |
| 231 | |
| 232 html = """<!DOCTYPE HTML> | |
| 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 class=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.querySelector('.overlay'); | |
|
esprehn
2014/02/21 02:00:59
Use an id and getElementById
dsinclair
2014/02/21 02:24:55
Done.
| |
| 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.querySelector('#expected') | |
|
esprehn
2014/02/21 02:00:59
same
dsinclair
2014/02/21 02:24:55
Done.
| |
| 292 var expected_ctx = expected_canvas.getContext("2d"); | |
| 293 expected_ctx.fillStyle = 'rgba(255, 0, 0, 0.25)'; | |
| 294 expected_ctx.strokeStyle = 'rgba(0, 0, 0, 1.0)'; | |
| 295 for (var i = 0; i < expected_rects.length; i++) { | |
| 296 var rect = expected_rects[i]; | |
| 297 expected_ctx.fillRect(rect[0], rect[1], rect[2], rect[3]); | |
| 298 expected_ctx.strokeRect(rect[0] - 2, rect[1] - 2, rect[2] + 4, rect[3] + 4); | |
| 299 } | |
| 300 | |
| 301 var actual_canvas = document.querySelector('#actual') | |
| 302 var actual_ctx = actual_canvas.getContext("2d"); | |
| 303 actual_ctx.fillStyle = 'rgba(0, 255, 0, 0.25)'; | |
| 304 actual_ctx.strokeStyle = 'rgba(0, 0, 0, 1.0)'; | |
| 305 for (var i = 0; i < actual_rects.length; i++) { | |
| 306 var rect = actual_rects[i]; | |
| 307 actual_ctx.fillRect(rect[0], rect[1], rect[2], rect[3]); | |
| 308 actual_ctx.strokeRect(rect[0] - 2, rect[1] - 2, rect[2] + 4, rect[3] + 4 ); | |
| 309 } | |
| 310 | |
| 311 var type = document.querySelector('#type'); | |
|
esprehn
2014/02/21 02:00:59
getElementById?
dsinclair
2014/02/21 02:24:55
Done.
| |
| 312 var expected_showing = true; | |
| 313 var flip = function() { | |
| 314 if (expected_showing) { | |
| 315 type.innerText = 'Actual Difference'; | |
| 316 expected_canvas.style.display = 'none'; | |
| 317 actual_canvas.style.display = 'block'; | |
| 318 } else { | |
| 319 type.innerText = 'Expected Difference'; | |
| 320 actual_canvas.style.display = 'none'; | |
| 321 expected_canvas.style.display = 'block'; | |
| 322 } | |
| 323 expected_showing = !expected_showing | |
| 324 setTimeout(flip, 3000); | |
| 325 } | |
| 326 setTimeout(flip, 3000); | |
| 327 })(); | |
| 328 </script> | |
| 329 </body> | |
| 330 </html> | |
| 331 """ % { | |
| 332 'title': self._test_name, | |
| 333 'expected_rects': expected_rects, | |
| 334 'actual_rects': actual_rects, | |
| 335 } | |
| 336 overlay_filename = self.output_filename(self.FILENAME_SUFFIX_OVERLAY) | |
| 337 self._write_file(overlay_filename, html) | |
| 338 | |
| 206 def write_audio_files(self, actual_audio, expected_audio): | 339 def write_audio_files(self, actual_audio, expected_audio): |
| 207 self.write_output_files('.wav', actual_audio, expected_audio) | 340 self.write_output_files('.wav', actual_audio, expected_audio) |
| 208 | 341 |
| 209 def write_image_files(self, actual_image, expected_image): | 342 def write_image_files(self, actual_image, expected_image): |
| 210 self.write_output_files('.png', actual_image, expected_image) | 343 self.write_output_files('.png', actual_image, expected_image) |
| 211 | 344 |
| 212 def write_image_diff_files(self, image_diff): | 345 def write_image_diff_files(self, image_diff): |
| 213 diff_filename = self.output_filename(self.FILENAME_SUFFIX_IMAGE_DIFF) | 346 diff_filename = self.output_filename(self.FILENAME_SUFFIX_IMAGE_DIFF) |
| 214 self._write_file(diff_filename, image_diff) | 347 self._write_file(diff_filename, image_diff) |
| 215 | 348 |
| (...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), | 400 'diff_filename': self._output_testname(self.FILENAME_SUFFIX_IMAGE_DI FF), |
| 268 'prefix': self._output_testname(''), | 401 'prefix': self._output_testname(''), |
| 269 } | 402 } |
| 270 self._write_file(diffs_html_filename, html) | 403 self._write_file(diffs_html_filename, html) |
| 271 | 404 |
| 272 def write_reftest(self, src_filepath): | 405 def write_reftest(self, src_filepath): |
| 273 fs = self._filesystem | 406 fs = self._filesystem |
| 274 dst_dir = fs.dirname(fs.join(self._root_output_dir, self._test_name)) | 407 dst_dir = fs.dirname(fs.join(self._root_output_dir, self._test_name)) |
| 275 dst_filepath = fs.join(dst_dir, fs.basename(src_filepath)) | 408 dst_filepath = fs.join(dst_dir, fs.basename(src_filepath)) |
| 276 self._write_file(dst_filepath, fs.read_binary_file(src_filepath)) | 409 self._write_file(dst_filepath, fs.read_binary_file(src_filepath)) |
| OLD | NEW |