summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/tool/commands
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/tool/commands')
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html45
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css108
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js130
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js158
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js47
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py90
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py99
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">&laquo;</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)