Commit cf0afed8 authored by mihaip@chromium.org's avatar mihaip@chromium.org

2010-11-23 Mihai Parparita <mihaip@chromium.org>

        Reviewed by Tony Chang.

        Rebaseline server: list current baselines and platforms
        https://bugs.webkit.org/show_bug.cgi?id=49991

        List existing baselines (with Trac links) for tests.

        Add dropdowns for choosing with platform baselines to target (and what
        to do with existing ones).

        Also fix MockFileSystem.join to behave more like os.path.join (unit
        test was ending up with two consecutive slashes in a layout test
        path).

        * Scripts/webkitpy/common/system/filesystem_mock.py:
        * Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html:
        * Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css:
        * Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js:
        * Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js:
        * Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js:
        * Scripts/webkitpy/tool/commands/rebaselineserver.py:
        * Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@72640 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 9a4e5045
2010-11-23 Mihai Parparita <mihaip@chromium.org>
Reviewed by Tony Chang.
Rebaseline server: list current baselines and platforms
https://bugs.webkit.org/show_bug.cgi?id=49991
List existing baselines (with Trac links) for tests.
Add dropdowns for choosing with platform baselines to target (and what
to do with existing ones).
Also fix MockFileSystem.join to behave more like os.path.join (unit
test was ending up with two consecutive slashes in a layout test
path).
* Scripts/webkitpy/common/system/filesystem_mock.py:
* Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html:
* Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css:
* Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js:
* Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js:
* Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js:
* Scripts/webkitpy/tool/commands/rebaselineserver.py:
* Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py: Added.
2010-11-23 Ojan Vafai <ojan@chromium.org>
Reviewed by Tony Chang.
......
......@@ -39,11 +39,7 @@ class MockFileSystem(object):
Args:
files: a dict of filenames -> file contents. A file contents
value of None is used to indicate that the file should
not exist (even if standalone is False).
standalone: If True, only the files listed in _files_ exist.
If False, the object will pass through read calls to the
underlying filesystem. Writes are never passed through.
not exist.
"""
self.files = files
......@@ -53,7 +49,16 @@ class MockFileSystem(object):
return False
def join(self, *comps):
return '/'.join(comps)
# os.path.join ignores trailing slashes on components (i.e.
# join('foo/', 'bar') and join('foo', 'bar') produce the same result),
# we emulate that behavior.
trimmed_comps = []
for comp in comps:
if len(comp) and comp[-1] == '/':
trimmed_comps.append(comp[0:-1])
else:
trimmed_comps.append(comp)
return '/'.join(trimmed_comps)
def maybe_make_directory(self, *path):
# FIXME: Implement such that subsequent calls to isdir() work?
......
......@@ -73,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>
......@@ -121,9 +121,20 @@
<div id="footer">
<label>State: <span id="state"></span></label>
<!-- Add a dummy <button> node so that this lines up with the text on the left -->
<button style="visibility: hidden"></button>
<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>
......
......@@ -109,7 +109,8 @@ a, .link {
margin-right: 1em;
}
#header label span {
#header label span,
#footer label span {
color: #fff;
font-weight: bold;
}
......
......@@ -86,6 +86,20 @@ function main()
}
});
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();
......@@ -235,6 +249,37 @@ 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('.');
for (var platform in baselines) {
if (currentBaselines.firstChild) {
currentBaselines.appendChild(document.createTextNode('; '));
}
currentBaselines.appendChild(document.createTextNode(platform + ' ('));
for (var i = 0, extension; extension = baselines[platform][i]; i++) {
if (i != 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';
currentBaselines.appendChild(link);
}
currentBaselines.appendChild(document.createTextNode(')'));
}
updateState();
loupe.hide();
......@@ -265,8 +310,7 @@ 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;
......
......@@ -143,13 +143,16 @@ RebaselineQueue.prototype.rebaseline = function()
RebaselineQueue.prototype._rebaselineTest = function(testName)
{
var baselineTarget = getSelectValue('baseline-target');
var baselineMoveTo = getSelectValue('baseline-move-to');
// FIXME: actually rebaseline
log('Rebaselining ' + testName + '...');
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);
log.SUCCESS);
};
......@@ -87,3 +87,8 @@ function toggle(id)
toggler.className = 'link';
}
}
function getTracUrl(layoutTestPath)
{
return 'http://trac.webkit.org/browser/trunk/LayoutTests/' + layoutTestPath;
}
\ No newline at end of file
......@@ -45,18 +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):
......@@ -146,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):
......@@ -173,6 +183,43 @@ class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
shutil.copyfileobj(static_file, self.wfile)
def _get_test_baselines(test_file, 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]
all_platforms_port = AllPlatformsPort()
test_baselines = {}
for baseline_extension in ('.txt', '.checksum', '.png'):
test_path = filesystem.join(layout_tests_directory, test_file)
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)
if platform not in test_baselines:
test_baselines[platform] = []
test_baselines[platform].append(baseline_extension)
for platform, extensions in test_baselines.items():
test_baselines[platform] = tuple(extensions)
return test_baselines
class RebaselineServer(AbstractDeclarativeCommand):
name = "rebaseline-server"
help_text = __doc__
......@@ -186,16 +233,25 @@ 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)
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_json['baselines'] = _get_test_baselines(
test_file, layout_tests_directory, platforms, filesystem)
print "Starting server at http://localhost:%d/" % options.httpd_port
print ("Use the 'Exit' link in the UI, http://localhost:%d/"
......@@ -204,5 +260,9 @@ class RebaselineServer(AbstractDeclarativeCommand):
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()
# 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.tool.commands import rebaselineserver
class GetBaselinesTest(unittest.TestCase):
def test_no_baselines(self):
self._assertBaselines(
test_files=(),
test='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='fast/text.html',
expected_baselines={'mac': ('.txt',), 'base': ('.txt',)})
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='fast/image.html',
expected_baselines={
'base': ('.txt',),
'mac': ('.checksum', '.png'),
'win': ('.checksum', '.png'),
})
def test_extra_baselines(self):
self._assertBaselines(
test_files=(
'fast/text-expected.txt',
'platform/nosuchplatform/fast/text-expected.txt',
),
test='fast/text.html',
expected_baselines={'base': ('.txt',)})
def _assertBaselines(self, test_files, test, expected_baselines):
layout_tests_directory = base.Port().layout_tests_dir()
mock_filesystem = filesystem_mock.MockFileSystem()
for file in test_files + (test,):
file_path = mock_filesystem.join(layout_tests_directory, file)
mock_filesystem.files[file_path] = ''
actual_baselines = rebaselineserver._get_test_baselines(
test,
layout_tests_directory,
('mac', 'win', 'linux'),
mock_filesystem)
self.assertEqual(actual_baselines, expected_baselines)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment