diff options
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/tool/commands')
7 files changed, 649 insertions, 28 deletions
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html index 8cc48c1..5b58301 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html +++ b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html @@ -35,13 +35,24 @@ <script src="/util.js"></script> <script src="/loupe.js"></script> <script src="/main.js"></script> + <script src="/queue.js"></script> </head> <body class="loading"> +<pre id="log" style="display: none"></pre> +<div id="queue" style="display: none"> + Queue: + <select id="queue-select" size="10"></select> + <button id="remove-queue-selection">Remove selection</button> + <button id="rebaseline-queue">Rebaseline queue</button> +</div> + <div id="header"> <div id="controls"> <!-- Add a dummy <select> node so that this lines up with the text on the left --> <select style="visibility: hidden"></select> + <span id="toggle-log" class="link">Log</span> + <span class="divider">|</span> <a href="/quitquitquit">Exit</a> </div> @@ -62,7 +73,7 @@ </label> </span> - <a id="test-link">View test</a> + <a id="test-link" target="_blank">View test</a> <span id="nav-buttons"> <button id="previous-test">«</button> @@ -86,7 +97,14 @@ <tr> <td><img id="expected-image"></td> <td><img id="actual-image"></td> - <td><canvas id="diff-canvas" width="800" height="600"></canvas></td> + <td> + <canvas id="diff-canvas" width="800" height="600"></canvas> + <div id="diff-checksum" style="display: none"> + <h3>Checksum mismatch</h3> + Expected: <span id="expected-checksum"></span><br> + Actual: <span id="actual-checksum"></span> + </div> + </td> </tr> </tbody> <tbody id="text-outputs" style="display: none"> @@ -101,6 +119,29 @@ </tbody> </table> +<div id="footer"> + <label>State: <span id="state"></span></label> + <label>Existing baselines: <span id="current-baselines"></span></label> + <label> + Baseline target: + <select id="baseline-target"></select> + </label> + <label> + Move current baselines to: + <select id="baseline-move-to"> + <option value="none">Nowhere (replace)</option> + </select> + </label> + + <!-- Add a dummy <button> node so that this lines up with the text on the right --> + <button style="visibility: hidden; padding-left: 0; padding-right: 0;"></button> + + <div id="action-buttons"> + <span id="toggle-queue" class="link">Queue</span> + <button id="add-to-rebaseline-queue">Add to rebaseline queue</button> + </div> +</div> + <table id="loupe" style="display: none"> <tr> <td colspan="3" id="loupe-info"> diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css index 6e90fe4..aff2bf6 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css +++ b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css @@ -55,15 +55,52 @@ a, .link { text-decoration: none; } -#header { +#log, +#queue { + padding: .25em 0 0 .25em; + position: absolute; + right: 0; + height: 200px; + overflow: auto; + background: #fff; + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .5); +} + +#log { + top: 2em; + width: 500px; +} + +#queue { + bottom: 3em; + width: 400px; +} + +#queue-select { + display: block; + width: 390px; +} + +#header, +#footer { padding: .5em 1em; background: #333; color: #fff; -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5); +} + +#header { margin-bottom: 1em; } -#header label { +#header .divider, +#footer .divider { + opacity: .3; + padding: 0 .5em; +} + +#header label, +#footer label { padding-right: 1em; color: #ccc; } @@ -72,7 +109,8 @@ a, .link { margin-right: 1em; } -#header label span { +#header label span, +#footer label span { color: #fff; font-weight: bold; } @@ -114,13 +152,18 @@ a, .link { } #image-outputs img, -#image-outputs canvas { +#image-outputs canvas, +#image-outputs #diff-checksum { width: 800px; height: 600px; border: solid 1px #ddd; -webkit-user-select: none; -webkit-user-drag: none; - cursor: crosshair; +} + +#image-outputs img, +#image-outputs canvas { + cursor: crosshair; } #image-outputs img.loading, @@ -150,6 +193,59 @@ a, .link { background: #eee; } +#footer { + position: absolute; + bottom: 0; + left: 0; + right: 0; + margin-top: 1em; +} + +#state.needs_rebaseline { + color: yellow; +} + +#state.rebaseline_failed { + color: red; +} + +#state.rebaseline_succeeded { + color: green; +} + +#state.in_queue { + color: gray; +} + +#current-baselines { + font-weight: normal !important; +} + +#current-baselines .platform { + font-weight: bold; +} + +#current-baselines a { + color: #ddf; +} + +#current-baselines .was-used-for-test { + color: #aaf; + font-weight: bold; +} + +#action-buttons { + float: right; +} + +#action-buttons .link { + margin-right: 1em; +} + +#footer button { + padding: 1em; +} + #loupe { -webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, .5); position: absolute; @@ -165,7 +261,7 @@ a, .link { #loupe td { padding: 0; - border: solid 1px #ccc; + border: solid 1px #ccc; } #loupe label { diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js index fa037b3..66990d0 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js +++ b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js @@ -30,11 +30,22 @@ var ALL_DIRECTORY_PATH = '[all]'; +var STATE_NEEDS_REBASELINE = 'needs_rebaseline'; +var STATE_REBASELINE_FAILED = 'rebaseline_failed'; +var STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded'; +var STATE_IN_QUEUE = 'in_queue'; +var STATE_TO_DISPLAY_STATE = {}; +STATE_TO_DISPLAY_STATE[STATE_NEEDS_REBASELINE] = 'Needs rebaseline'; +STATE_TO_DISPLAY_STATE[STATE_REBASELINE_FAILED] = 'Rebaseline failed'; +STATE_TO_DISPLAY_STATE[STATE_REBASELINE_SUCCEEDED] = 'Rebaseline succeeded'; +STATE_TO_DISPLAY_STATE[STATE_IN_QUEUE] = 'In queue'; + var results; var testsByFailureType = {}; var testsByDirectory = {}; var selectedTests = []; var loupe; +var queue; function main() { @@ -44,7 +55,10 @@ function main() $('next-test').addEventListener('click', nextTest); $('previous-test').addEventListener('click', previousTest); + $('toggle-log').addEventListener('click', function() { toggle('log'); }); + loupe = new Loupe(); + queue = new RebaselineQueue(); document.addEventListener('keydown', function(event) { if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { @@ -60,9 +74,32 @@ function main() event.preventDefault(); nextTest(); break; + case 'U+0051': // q + queue.addCurrentTest(); + break; + case 'U+0058': // x + queue.removeCurrentTest(); + break; + case 'U+0052': // r + queue.rebaseline(); + break; } }); + loadText('/platforms.json', function(text) { + var platforms = JSON.parse(text); + platforms.platforms.forEach(function(platform) { + var platformOption = document.createElement('option'); + platformOption.value = platform; + platformOption.textContent = platform; + + var targetOption = platformOption.cloneNode(true); + targetOption.selected = platform == platforms.defaultPlatform; + $('baseline-target').appendChild(targetOption); + $('baseline-move-to').appendChild(platformOption.cloneNode(true)); + }); + }); + loadText('/results.json', function(text) { results = JSON.parse(text); displayResults(); @@ -104,7 +141,7 @@ function displayResults() selectFailureType(); - document.body.classList.remove('loading'); + document.body.className = ''; } /** @@ -212,8 +249,61 @@ function selectTest() $('text-outputs').style.display = 'none'; } + var currentBaselines = $('current-baselines'); + currentBaselines.textContent = ''; + var baselines = results.tests[selectedTest].baselines; + var testName = selectedTest.split('.').slice(0, -1).join('.'); + getSortedKeys(baselines).forEach(function(platform, i) { + if (i != 0) { + currentBaselines.appendChild(document.createTextNode('; ')); + } + var platformName = document.createElement('span'); + platformName.className = 'platform'; + platformName.textContent = platform; + currentBaselines.appendChild(platformName); + currentBaselines.appendChild(document.createTextNode(' (')); + getSortedKeys(baselines[platform]).forEach(function(extension, j) { + if (j != 0) { + currentBaselines.appendChild(document.createTextNode(', ')); + } + var link = document.createElement('a'); + var baselinePath = ''; + if (platform != 'base') { + baselinePath += 'platform/' + platform + '/'; + } + baselinePath += testName + '-expected' + extension; + link.href = getTracUrl(baselinePath); + if (extension == '.checksum') { + link.textContent = 'chk'; + } else { + link.textContent = extension.substring(1); + } + link.target = '_blank'; + if (baselines[platform][extension]) { + link.className = 'was-used-for-test'; + } + currentBaselines.appendChild(link); + }); + currentBaselines.appendChild(document.createTextNode(')')); + }); + updateState(); loupe.hide(); + + prefetchNextImageTest(); +} + +function prefetchNextImageTest() +{ + var testSelector = $('test-selector'); + if (testSelector.selectedIndex == testSelector.options.length - 1) { + return; + } + var nextTest = testSelector.options[testSelector.selectedIndex + 1].value; + if (results.tests[nextTest].actual.indexOf('IMAGE') != -1) { + new Image().src = getTestResultUrl(nextTest, 'expected-image'); + new Image().src = getTestResultUrl(nextTest, 'actual-image'); + } } function updateState() @@ -227,8 +317,13 @@ function updateState() $('next-test').disabled = testIndex == testCount - 1; $('previous-test').disabled = testIndex == 0; - $('test-link').href = - 'http://trac.webkit.org/browser/trunk/LayoutTests/' + testName; + $('test-link').href = getTracUrl(testName); + + var state = results.tests[testName].state; + $('state').className = state; + $('state').innerHTML = STATE_TO_DISPLAY_STATE[state]; + + queue.updateState(); } function getTestResultUrl(testName, mode) @@ -266,6 +361,7 @@ function displayImageResults(testName) $('diff-canvas').className = 'loading'; $('diff-canvas').style.display = ''; + $('diff-checksum').style.display = 'none'; } /** @@ -317,6 +413,7 @@ function updateImageDiff() { var diffWidth = diffImageData.width; var diff = diffImageData.data; + var hadDiff = false; for (var x = 0; x < expectedWidth; x++) { for (var y = 0; y < expectedHeight; y++) { var expectedOffset = (y * expectedWidth + x) * 4; @@ -326,6 +423,7 @@ function updateImageDiff() { expected[expectedOffset + 1] != actual[actualOffset + 1] || expected[expectedOffset + 2] != actual[actualOffset + 2] || expected[expectedOffset + 3] != actual[actualOffset + 3]) { + hadDiff = true; diff[diffOffset] = 255; diff[diffOffset + 1] = 0; diff[diffOffset + 2] = 0; @@ -345,19 +443,27 @@ function updateImageDiff() { 0, 0, diffImageData.width, diffImageData.height); diffCanvas.className = ''; + + if (!hadDiff) { + diffCanvas.style.display = 'none'; + $('diff-checksum').style.display = ''; + loadTextResult(currentExpectedImageTest, 'expected-checksum'); + loadTextResult(currentExpectedImageTest, 'actual-checksum'); + } } -function displayTextResults(testName) +function loadTextResult(testName, mode) { - function loadTextResult(mode) { - loadText(getTestResultUrl(testName, mode), function(text) { - $(mode).textContent = text; - }); - } + loadText(getTestResultUrl(testName, mode), function(text) { + $(mode).textContent = text; + }); +} - loadTextResult('expected-text'); - loadTextResult('actual-text'); - loadTextResult('diff-text'); +function displayTextResults(testName) +{ + loadTextResult(testName, 'expected-text'); + loadTextResult(testName, 'actual-text'); + loadTextResult(testName, 'diff-text'); } function nextTest() diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js new file mode 100644 index 0000000..f57c919 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +function RebaselineQueue() +{ + this._selectNode = $('queue-select'); + this._rebaselineButtonNode = $('rebaseline-queue'); + this._toggleNode = $('toggle-queue'); + this._removeSelectionButtonNode = $('remove-queue-selection'); + + this._inProgressRebaselineCount = 0; + + var self = this; + $('add-to-rebaseline-queue').addEventListener( + 'click', function() { self.addCurentTest(); }); + this._selectNode.addEventListener('change', updateState); + this._removeSelectionButtonNode.addEventListener( + 'click', function() { self._removeSelection(); }); + this._rebaselineButtonNode.addEventListener( + 'click', function() { self.rebaseline(); }); + this._toggleNode.addEventListener( + 'click', function() { toggle('queue'); }); +} + +RebaselineQueue.prototype.updateState = function() +{ + var testName = getSelectedTest(); + + var state = results.tests[testName].state; + $('add-to-rebaseline-queue').disabled = state != STATE_NEEDS_REBASELINE; + + var queueLength = this._selectNode.options.length; + if (this._inProgressRebaselineCount > 0) { + this._rebaselineButtonNode.disabled = true; + this._rebaselineButtonNode.textContent = + 'Rebaseline in progress (' + this._inProgressRebaselineCount + + ' tests left)'; + } else if (queueLength == 0) { + this._rebaselineButtonNode.disabled = true; + this._rebaselineButtonNode.textContent = 'Rebaseline queue'; + this._toggleNode.textContent = 'Queue'; + } else { + this._rebaselineButtonNode.disabled = false; + this._rebaselineButtonNode.textContent = + 'Rebaseline queue (' + queueLength + ' tests)'; + this._toggleNode.textContent = 'Queue (' + queueLength + ' tests)'; + } + this._removeSelectionButtonNode.disabled = + this._selectNode.selectedIndex == -1; +}; + +RebaselineQueue.prototype.addCurrentTest = function() +{ + var testName = getSelectedTest(); + var test = results.tests[testName]; + + if (test.state != STATE_NEEDS_REBASELINE) { + log('Cannot add test with state "' + test.state + '" to queue.', + log.WARNING); + return; + } + + var queueOption = document.createElement('option'); + queueOption.value = testName; + queueOption.textContent = testName; + this._selectNode.appendChild(queueOption); + test.state = STATE_IN_QUEUE; + updateState(); +}; + +RebaselineQueue.prototype.removeCurrentTest = function() +{ + this._removeTest(getSelectedTest()); +}; + +RebaselineQueue.prototype._removeSelection = function() +{ + if (this._selectNode.selectedIndex == -1) + return; + + this._removeTest( + this._selectNode.options[this._selectNode.selectedIndex].value); +}; + +RebaselineQueue.prototype._removeTest = function(testName) +{ + var queueOption = this._selectNode.firstChild; + + while (queueOption && queueOption.value != testName) { + queueOption = queueOption.nextSibling; + } + + if (!queueOption) + return; + + this._selectNode.removeChild(queueOption); + var test = results.tests[testName]; + test.state = STATE_NEEDS_REBASELINE; + updateState(); +}; + +RebaselineQueue.prototype.rebaseline = function() +{ + var testNames = []; + for (var queueOption = this._selectNode.firstChild; + queueOption; + queueOption = queueOption.nextSibling) { + testNames.push(queueOption.value); + } + + this._inProgressRebaselineCount = testNames.length; + updateState(); + + testNames.forEach(this._rebaselineTest, this); +}; + +RebaselineQueue.prototype._rebaselineTest = function(testName) +{ + var baselineTarget = getSelectValue('baseline-target'); + var baselineMoveTo = getSelectValue('baseline-move-to'); + + // FIXME: actually rebaseline + log('Rebaselining ' + testName + ' for platform ' + baselineTarget + '...'); + var test = results.tests[testName]; + this._removeTest(testName); + this._inProgressRebaselineCount--; + test.state = STATE_REBASELINE_SUCCEEDED; + updateState(); + log('Rebaselined add test with state ' + test.state + ' to queue', + log.SUCCESS); +}; diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js index 1c8782b..5ad7612 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js +++ b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js @@ -55,3 +55,50 @@ function loadText(url, callback) xhr.addEventListener('load', function() { callback(xhr.responseText); }); xhr.send(); } + +function log(text, type) +{ + var node = $('log'); + + if (type) { + var typeNode = document.createElement('span'); + typeNode.textContent = type.text; + typeNode.style.color = type.color; + node.appendChild(typeNode); + } + + node.appendChild(document.createTextNode(text + '\n')); + node.scrollTop = node.scrollHeight; +} + +log.WARNING = {text: 'Warning: ', color: '#aa3'}; +log.SUCCESS = {text: 'Success: ', color: 'green'}; +log.ERROR = {text: 'Error: ', color: 'red'}; + +function toggle(id) +{ + var element = $(id); + var toggler = $('toggle-' + id); + if (element.style.display == 'none') { + element.style.display = ''; + toggler.className = 'link selected'; + } else { + element.style.display = 'none'; + toggler.className = 'link'; + } +} + +function getTracUrl(layoutTestPath) +{ + return 'http://trac.webkit.org/browser/trunk/LayoutTests/' + layoutTestPath; +} + +function getSortedKeys(obj) +{ + var keys = []; + for (var key in obj) { + keys.push(key); + } + keys.sort(); + return keys; +}
\ No newline at end of file diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py index abb2af4..2dcc566 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py @@ -45,14 +45,22 @@ import BaseHTTPServer from optparse import make_option from wsgiref.handlers import format_date_time +from webkitpy.common import system +from webkitpy.layout_tests.port import factory +from webkitpy.layout_tests.port.webkit import WebKitPort from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand -import webkitpy.thirdparty.simplejson as simplejson +from webkitpy.thirdparty import simplejson + +STATE_NEEDS_REBASELINE = 'needs_rebaseline' +STATE_REBASELINE_FAILED = 'rebaseline_failed' +STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded' class RebaselineHTTPServer(BaseHTTPServer.HTTPServer): - def __init__(self, httpd_port, results_directory, results_json): + def __init__(self, httpd_port, results_directory, results_json, platforms_json): BaseHTTPServer.HTTPServer.__init__(self, ("", httpd_port), RebaselineHTTPRequestHandler) self.results_directory = results_directory self.results_json = results_json + self.platforms_json = platforms_json class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): @@ -61,6 +69,7 @@ class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): "loupe.js", "main.js", "main.css", + "queue.js", "util.js", ]) @@ -141,10 +150,16 @@ class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self._serve_file(file_path, cacheable_seconds=60) def results_json(self): + self._serve_json(self.server.results_json) + + def platforms_json(self): + self._serve_json(self.server.platforms_json) + + def _serve_json(self, json): self.send_response(200) self.send_header('Content-type', 'application/json') self.end_headers() - simplejson.dump(self.server.results_json, self.wfile) + simplejson.dump(json, self.wfile) def _serve_file(self, file_path, cacheable_seconds=0): if not os.path.exists(file_path): @@ -168,6 +183,44 @@ class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): shutil.copyfileobj(static_file, self.wfile) +def _get_test_baselines(test_file, test_port, layout_tests_directory, platforms, filesystem): + class AllPlatformsPort(WebKitPort): + def __init__(self): + WebKitPort.__init__(self, filesystem=filesystem) + self._platforms_by_directory = dict( + [(self._webkit_baseline_path(p), p) for p in platforms]) + + def baseline_search_path(self): + return self._platforms_by_directory.keys() + + def platform_from_directory(self, directory): + return self._platforms_by_directory[directory] + + test_path = filesystem.join(layout_tests_directory, test_file) + + all_platforms_port = AllPlatformsPort() + + all_test_baselines = {} + for baseline_extension in ('.txt', '.checksum', '.png'): + test_baselines = test_port.expected_baselines( + test_path, baseline_extension) + baselines = all_platforms_port.expected_baselines( + test_path, baseline_extension, all_baselines=True) + for platform_directory, expected_filename in baselines: + if not platform_directory: + continue + if platform_directory == layout_tests_directory: + platform = 'base' + else: + platform = all_platforms_port.platform_from_directory( + platform_directory) + platform_baselines = all_test_baselines.setdefault(platform, {}) + was_used_for_test = ( + platform_directory, expected_filename) in test_baselines + platform_baselines[baseline_extension] = was_used_for_test + + return all_test_baselines + class RebaselineServer(AbstractDeclarativeCommand): name = "rebaseline-server" help_text = __doc__ @@ -181,20 +234,41 @@ class RebaselineServer(AbstractDeclarativeCommand): def execute(self, options, args, tool): results_directory = args[0] + filesystem = system.filesystem.FileSystem() print 'Parsing unexpected_results.json...' - results_json_path = os.path.join( + results_json_path = filesystem.join( results_directory, 'unexpected_results.json') with codecs.open(results_json_path, "r") as results_json_file: results_json_file = file(results_json_path) results_json = simplejson.load(results_json_file) - print "Starting server at http://localhost:%d/" % options.httpd_port - print ("Use the 'Exit' link in the UI, http://localhost:%d/" - "quitquitquit or Ctrl-C to stop") % options.httpd_port + port = factory.get() + layout_tests_directory = port.layout_tests_dir() + platforms = filesystem.listdir( + filesystem.join(layout_tests_directory, 'platform')) + + print 'Gathering current baselines...' + for test_file, test_json in results_json['tests'].items(): + test_json['state'] = STATE_NEEDS_REBASELINE + test_path = filesystem.join(layout_tests_directory, test_file) + test_json['baselines'] = _get_test_baselines( + test_file, port, layout_tests_directory, platforms, filesystem) + + server_url = "http://localhost:%d/" % options.httpd_port + print "Starting server at %s" % server_url + print ("Use the 'Exit' link in the UI, %squitquitquit " + "or Ctrl-C to stop") % server_url + + threading.Timer( + .1, lambda: self._tool.user.open_url(server_url)).start() httpd = RebaselineHTTPServer( httpd_port=options.httpd_port, results_directory=results_directory, - results_json=results_json) + results_json=results_json, + platforms_json={ + 'platforms': platforms, + 'defaultPlatform': port.name(), + }) httpd.serve_forever() diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py new file mode 100644 index 0000000..b37da3d --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py @@ -0,0 +1,99 @@ +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import unittest + +from webkitpy.common.system import filesystem_mock +from webkitpy.layout_tests.port import base +from webkitpy.layout_tests.port.webkit import WebKitPort +from webkitpy.tool.commands import rebaselineserver + + +class GetBaselinesTest(unittest.TestCase): + def test_no_baselines(self): + self._assertBaselines( + test_files=(), + test_name='fast/missing.html', + expected_baselines={}) + + def test_text_baselines(self): + self._assertBaselines( + test_files=( + 'fast/text-expected.txt', + 'platform/mac/fast/text-expected.txt', + ), + test_name='fast/text.html', + expected_baselines={ + 'mac': {'.txt': True}, + 'base': {'.txt': False}, + }) + + def test_image_and_text_baselines(self): + self._assertBaselines( + test_files=( + 'fast/image-expected.txt', + 'platform/mac/fast/image-expected.png', + 'platform/mac/fast/image-expected.checksum', + 'platform/win/fast/image-expected.png', + 'platform/win/fast/image-expected.checksum', + ), + test_name='fast/image.html', + expected_baselines={ + 'base': {'.txt': True}, + 'mac': {'.checksum': True, '.png': True}, + 'win': {'.checksum': False, '.png': False}, + }) + + def test_extra_baselines(self): + self._assertBaselines( + test_files=( + 'fast/text-expected.txt', + 'platform/nosuchplatform/fast/text-expected.txt', + ), + test_name='fast/text.html', + expected_baselines={'base': {'.txt': True}}) + + def _assertBaselines(self, test_files, test_name, expected_baselines): + layout_tests_directory = base.Port().layout_tests_dir() + mock_filesystem = filesystem_mock.MockFileSystem() + for file in test_files + (test_name,): + file_path = mock_filesystem.join(layout_tests_directory, file) + mock_filesystem.files[file_path] = '' + + class TestMacPort(WebKitPort): + def __init__(self): + WebKitPort.__init__(self, filesystem=mock_filesystem) + self._name = 'mac' + + actual_baselines = rebaselineserver._get_test_baselines( + test_name, + TestMacPort(), + layout_tests_directory, + ('mac', 'win', 'linux'), + mock_filesystem) + self.assertEqual(expected_baselines, actual_baselines) |