diff options
Diffstat (limited to 'Tools/RebaselineQueueServer')
-rwxr-xr-x | Tools/RebaselineQueueServer/app.yaml | 11 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/handlers/__init__.py | 1 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/handlers/builderqueue.py | 95 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/handlers/pages.py | 47 | ||||
-rwxr-xr-x | Tools/RebaselineQueueServer/index.yaml | 11 | ||||
-rwxr-xr-x | Tools/RebaselineQueueServer/main.py | 56 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/model/__init__.py | 1 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/model/queueentry.py | 63 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/static/builder-frame-empty.html | 10 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/static/styles.css | 71 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/templates/builder-picker.html | 74 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/templates/builder-queue-edit.html | 176 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/templates/builder-queue-list.html | 23 | ||||
-rw-r--r-- | Tools/RebaselineQueueServer/templates/home.html | 28 |
14 files changed, 667 insertions, 0 deletions
diff --git a/Tools/RebaselineQueueServer/app.yaml b/Tools/RebaselineQueueServer/app.yaml new file mode 100755 index 0000000..c425cfd --- /dev/null +++ b/Tools/RebaselineQueueServer/app.yaml @@ -0,0 +1,11 @@ +application: rebaseline-queue +version: 1 +runtime: python +api_version: 1 + +handlers: +- url: /static + static_dir: static + +- url: .* + script: main.py diff --git a/Tools/RebaselineQueueServer/handlers/__init__.py b/Tools/RebaselineQueueServer/handlers/__init__.py new file mode 100644 index 0000000..ef65bee --- /dev/null +++ b/Tools/RebaselineQueueServer/handlers/__init__.py @@ -0,0 +1 @@ +# Required for Python to search this directory for module files diff --git a/Tools/RebaselineQueueServer/handlers/builderqueue.py b/Tools/RebaselineQueueServer/handlers/builderqueue.py new file mode 100644 index 0000000..c84e07b --- /dev/null +++ b/Tools/RebaselineQueueServer/handlers/builderqueue.py @@ -0,0 +1,95 @@ +# Copyright (C) 2011 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. + +from urllib import unquote_plus + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from django.utils import simplejson + +from model.queueentry import QueueEntry + + +class QueueHandler(webapp.RequestHandler): + def get(self, builder_name): + self._get(unquote_plus(builder_name)) + + def post(self, builder_name): + self._post(unquote_plus(builder_name)) + + def _queued_test_names(self, builder_name): + return [entry.test for entry in QueueEntry.entries_for_builder(builder_name)] + + def _queue_list_url(self, builder_name): + return '/builder/%s/queue' % builder_name + + +class QueueEdit(QueueHandler): + def _get(self, builder_name): + test_names = self._queued_test_names(builder_name) + self.response.out.write( + template.render("templates/builder-queue-edit.html", { + 'builder_name': builder_name, + 'queued_test_names': simplejson.dumps(test_names), + })) + + +class QueueAdd(QueueHandler): + def _post(self, builder_name): + current_tests = set(self._queued_test_names(builder_name)) + tests = set(self.request.get_all('test')).difference(current_tests) + + for test in tests: + QueueEntry.add(builder_name, test) + + self.redirect(self._queue_list_url(builder_name)) + + +class QueueRemove(QueueHandler): + def _post(self, builder_name): + tests = self.request.get_all('test') + + for test in tests: + QueueEntry.remove(builder_name, test) + + self.redirect(self._queue_list_url(builder_name)) + + +class QueueHtml(QueueHandler): + def _get(self, builder_name): + self.response.out.write( + template.render("templates/builder-queue-list.html", { + 'builder_name': builder_name, + 'entries': QueueEntry.entries_for_builder(builder_name), + })) + + +class QueueJson(QueueHandler): + def _get(self, builder_name): + queue_json = {'tests': self._queued_test_names(builder_name)} + self.response.out.write(simplejson.dumps(queue_json)) diff --git a/Tools/RebaselineQueueServer/handlers/pages.py b/Tools/RebaselineQueueServer/handlers/pages.py new file mode 100644 index 0000000..8fcf2e3 --- /dev/null +++ b/Tools/RebaselineQueueServer/handlers/pages.py @@ -0,0 +1,47 @@ +# Copyright (C) 2011 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. + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template + +from model.queueentry import QueueEntry + + +class Home(webapp.RequestHandler): + def get(self): + builder_names = QueueEntry.builder_names() + self.response.out.write( + template.render("templates/home.html", { + 'builder_names': builder_names, + })) + + +class BuilderPicker(webapp.RequestHandler): + def get(self): + self.response.out.write( + template.render("templates/builder-picker.html", {})) diff --git a/Tools/RebaselineQueueServer/index.yaml b/Tools/RebaselineQueueServer/index.yaml new file mode 100755 index 0000000..a3b9e05 --- /dev/null +++ b/Tools/RebaselineQueueServer/index.yaml @@ -0,0 +1,11 @@ +indexes: + +# AUTOGENERATED + +# This index.yaml is automatically updated whenever the dev_appserver +# detects that a new type of query is run. If you want to manage the +# index.yaml file manually, remove the above marker line (the line +# saying "# AUTOGENERATED"). If you want to manage some indexes +# manually, move them above the marker line. The index.yaml file is +# automatically uploaded to the admin console when you next deploy +# your application using appcfg.py. diff --git a/Tools/RebaselineQueueServer/main.py b/Tools/RebaselineQueueServer/main.py new file mode 100755 index 0000000..4497d63 --- /dev/null +++ b/Tools/RebaselineQueueServer/main.py @@ -0,0 +1,56 @@ +# Copyright (C) 2011 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. + +# Request a modern Django +from google.appengine.dist import use_library +use_library('django', '1.2') + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import util + +from handlers import pages +from handlers import builderqueue + + +def main(): + application = webapp.WSGIApplication([ + ('/', pages.Home), + ('/builder/picker', pages.BuilderPicker), + + # Queue CRUD operations + ('/builder/(.+)/queue/edit', builderqueue.QueueEdit), + ('/builder/(.+)/queue/add', builderqueue.QueueAdd), + ('/builder/(.+)/queue/remove', builderqueue.QueueRemove), + ('/builder/(.+)/queue', builderqueue.QueueHtml), + ('/builder/(.+)/queue/json', builderqueue.QueueJson), + ], + debug=True) + util.run_wsgi_app(application) + +if __name__ == '__main__': + main() diff --git a/Tools/RebaselineQueueServer/model/__init__.py b/Tools/RebaselineQueueServer/model/__init__.py new file mode 100644 index 0000000..ef65bee --- /dev/null +++ b/Tools/RebaselineQueueServer/model/__init__.py @@ -0,0 +1 @@ +# Required for Python to search this directory for module files diff --git a/Tools/RebaselineQueueServer/model/queueentry.py b/Tools/RebaselineQueueServer/model/queueentry.py new file mode 100644 index 0000000..6570fc0 --- /dev/null +++ b/Tools/RebaselineQueueServer/model/queueentry.py @@ -0,0 +1,63 @@ +# Copyright (C) 2011 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. + +from google.appengine.ext import db + + +class QueueEntry(db.Model): + test = db.StringProperty() + builder = db.StringProperty() + + @staticmethod + def add(builder_name, test): + entry = QueueEntry() + entry.builder = builder_name + entry.test = test + entry.put() + return entry + + @staticmethod + def remove(builder_name, test): + query = QueueEntry.all() + query = query.filter('builder =', builder_name).filter('test =', test) + for entry in query: + entry.delete() + + @staticmethod + def entries_for_builder(builder_name): + query = QueueEntry.all() + query = query.filter('builder =', builder_name) + return query + + @staticmethod + def builder_names(): + query = QueueEntry.all() + builder_names = set() + for entry in query: + builder_names.add(entry.builder) + return builder_names diff --git a/Tools/RebaselineQueueServer/static/builder-frame-empty.html b/Tools/RebaselineQueueServer/static/builder-frame-empty.html new file mode 100644 index 0000000..31b91bb --- /dev/null +++ b/Tools/RebaselineQueueServer/static/builder-frame-empty.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> + <title>Rebaseline Queue</title> + <link rel="stylesheet" href="/static/styles.css" type="text/css"> +</head> +<body> +Select a group and then a builder to see tests that are currently failing on it (if any). +</body> +</html> diff --git a/Tools/RebaselineQueueServer/static/styles.css b/Tools/RebaselineQueueServer/static/styles.css new file mode 100644 index 0000000..a36ff35 --- /dev/null +++ b/Tools/RebaselineQueueServer/static/styles.css @@ -0,0 +1,71 @@ +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; +} + +h1 { + border-bottom: solid 1px #ccc; +} + +#builder-picker body, +#add-form body { + margin: 0; +} + +#builder-picker, +#builder-picker body { + height: 100%; +} + +#builder-picker body { + display: -webkit-box; + -webkit-box-orient: vertical; +} + +#builder-picker-controls { + padding: 0.5em; + border-bottom: solid 1px black; +} + +#builder-picker-controls select { + min-width: 10em; +} + +#builder-frame { + border: 0; + -webkit-box-flex: 1; + display: block; +} + +.status { + font-size: 16px; + text-align: center; + padding: 1em; +} + +.test-table { + border-collapse: collapse; +} + +.test-table caption { + font-size: 16px; + font-weight: bold; + background: #eee; + padding: .5em; +} + +.test-table th { + text-align: left; + border-bottom: solid 1px #ccc; + background: #eee; + min-width: 8em; +} + +.test-table tbody tr:hover { + background: #ffa; +} + +.test-table .submit-row { + text-align: right; + padding: 1em 0; +} diff --git a/Tools/RebaselineQueueServer/templates/builder-picker.html b/Tools/RebaselineQueueServer/templates/builder-picker.html new file mode 100644 index 0000000..1068c04 --- /dev/null +++ b/Tools/RebaselineQueueServer/templates/builder-picker.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<html id="builder-picker"> +<head> + <title>Rebaseline Queue: Builders</title> + <script src="http://test-results.appspot.com/dashboards/builders.js"></script> + <link rel="stylesheet" href="/static/styles.css" type="text/css"> +</head> +<body> + +<div id="builder-picker-controls"> + <label for="builder-group">Group:</label> + <select id="builder-group"> + <option disabled></option> + </select> + + <label for="builder">Builder:</label> + <select id="builder"> + <option disabled></option> + </select> +</div> + +<iframe src="/static/builder-frame-empty.html" id="builder-frame"></iframe> + +<script> +function init() +{ + var builderGroupMenu = document.getElementById('builder-group'); + builderGroupMenu.addEventListener( + 'change', handleBuilderGroupSelected, false); + + var builderMenu = document.getElementById('builder'); + builderMenu.addEventListener( + 'change', handleBuilderSelected, false); + + for (var builderGroupName in LAYOUT_TESTS_BUILDER_GROUPS) { + var builderGroupOption = document.createElement('option'); + builderGroupOption.textContent = builderGroupOption.value = + builderGroupName; + builderGroupMenu.appendChild(builderGroupOption); + } +} + +function handleBuilderGroupSelected() +{ + var builderGroupMenu = document.getElementById('builder-group'); + var builderGroupName = + builderGroupMenu.options[builderGroupMenu.selectedIndex].value; + var builderGroup = LAYOUT_TESTS_BUILDER_GROUPS[builderGroupName]; + + var builderMenu = document.getElementById('builder'); + while (builderMenu.options[1]) { + builderMenu.removeChild(builderMenu.options[1]); + } + + for (var builderName in builderGroup.builders) { + var builderOption = document.createElement('option'); + builderOption.textContent = builderOption.value = builderName; + builderMenu.appendChild(builderOption); + } +} + +function handleBuilderSelected() +{ + var builderMenu = document.getElementById('builder'); + var builderName = builderMenu.options[builderMenu.selectedIndex].value; + document.getElementById('builder-frame').src = + '/builder/' + builderName + '/queue/edit'; +} + +init(); +</script> + +</body> +</html> diff --git a/Tools/RebaselineQueueServer/templates/builder-queue-edit.html b/Tools/RebaselineQueueServer/templates/builder-queue-edit.html new file mode 100644 index 0000000..21a0f02 --- /dev/null +++ b/Tools/RebaselineQueueServer/templates/builder-queue-edit.html @@ -0,0 +1,176 @@ +<!DOCTYPE html> +<html id="add-form"> +<head> + <title>Rebaseline Queue: Edit</title> + <script src="http://test-results.appspot.com/dashboards/builders.js"></script> + <link rel="stylesheet" href="/static/styles.css" type="text/css"> +</head> +<body"> + +<div id="loading-indicator" class="status">Loading...</div> + +<form method="POST" id="form-template" style="display: none"> + <table class="test-table"> + <caption></caption> + <thead> + <th>Test</th> + <th>Expected</th> + <th>Actual</th> + <th>Results</th> + </thead> + <tbody></tbody> + <tbody> + <tr> + <td colspan="4" class="submit-row"> + <input type="submit" value=""> + </td> + </tr> + </tbody> + </table> +</form> + +<script> +var TEST_RESULTS_SERVER = 'http://test-results.appspot.com/'; +var BUILDER_TO_GROUP = {}; +for (var builderGroupName in LAYOUT_TESTS_BUILDER_GROUPS) { + for (var builderName in LAYOUT_TESTS_BUILDER_GROUPS[builderGroupName]) { + BUILDER_TO_GROUP[builderName] = builderGroupName; + } +} + +// Extract template parameters +var builderName = '{{ builder_name|escapejs }}'; +var queuedTestNames = {{ queued_test_names|safe }}; + +function init() +{ + var builderMaster = BUILDER_TO_MASTER[builderName]; + var resultsUrl = TEST_RESULTS_SERVER + 'testfile?builder=' + builderName + + '&master=' + builderMaster.name + + '&testtype=layout-tests&name=full_results.json'; + + var script = document.createElement('script'); + script.src = resultsUrl; + document.getElementsByTagName('head')[0].appendChild(script); +} + +function ADD_RESULTS(results) +{ + var builderGroupName = BUILDER_TO_GROUP[builderName]; + + var tests = results.tests; + var failingTests = []; + var queuedTests = []; + for (var test in tests) { + var testResults = tests[test]; + if (testResults.actual == testResults.expected || + testResults.expected.split(' ').indexOf(testResults.actual) != -1 || + testResults.actual == 'SKIP' || + testResults.actual.indexOf('PASS') != -1 || + (testResults.actual != 'PASS' && testResults.expected.indexOf('FAIL') != -1)) { + continue; + } + + testResults.name = test; + + if (queuedTestNames.indexOf(test) != -1) { + queuedTests.push(testResults); + queuedTestNames.splice(queuedTestNames.indexOf(test), 1); + } else { + failingTests.push(testResults); + } + } + + // If we have remaining queued tests that are currently not failing, + // synthesize results for them. + queuedTestNames.forEach(function(queuedTestName) { + queuedTests.push({ + name: queuedTestName, + actual: 'UNKNOWN', + expected: 'UNKNOWN' + }); + }); + + document.getElementById('loading-indicator').style.display = 'none'; + + renderTestResults( + failingTests, + 'add', + 'Failing tests', + 'Add to rebaseline queue', + 'No failing tests.'); + renderTestResults( + queuedTests, + 'remove', + 'Queued tests', + 'Remove from rebaseline queue', + 'No queued tests.'); +} + +function renderTestResults(testResults, formAction, title, submitLabel, emptyMessage) +{ + if (testResults.length == 0) { + var emptyNode = document.createElement('div'); + emptyNode.className = 'status'; + emptyNode.textContent = emptyMessage; + document.body.appendChild(emptyNode); + return; + } + + var form = document.getElementById('form-template').cloneNode(true); + form.action = '/builder/' + builderName + '/queue/' + formAction; + form.style.display = ''; + document.body.appendChild(form); + + var testsTable = form.querySelector('.test-table'); + testsTable.querySelector('caption').textContent = title; + testsTable.querySelector('input[type=submit]').value = submitLabel; + + testResults.sort(function(a, b) { + return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0); + }); + + testResults.forEach(function(result) { + var testRow = document.createElement('tr'); + + var testCell = document.createElement('td'); + testRow.appendChild(testCell); + var testCheckbox = document.createElement('input'); + testCheckbox.type = 'checkbox'; + testCheckbox.name = 'test'; + testCheckbox.value = result.name; + testCheckbox.id = result.name; + testCell.appendChild(testCheckbox); + + var testName = document.createElement('label'); + testName.textContent = result.name; + testName.setAttribute('for', result.name); + testCell.appendChild(testName); + + var expectedCell = document.createElement('td'); + testRow.appendChild(expectedCell); + expectedCell.textContent = result.expected; + + var actualCell = document.createElement('td'); + testRow.appendChild(actualCell); + actualCell.textContent = result.actual; + + var resultsCell = document.createElement('td'); + testRow.appendChild(resultsCell); + var resultsLink = document.createElement('a'); + resultsLink.target = '_blank'; + resultsLink.href = TEST_RESULTS_SERVER + + 'dashboards/flakiness_dashboard.html#tests=' + result.name + + '&group=' + builderGroupName; + resultsLink.textContent = 'Flakiness dashboard'; + resultsCell.appendChild(resultsLink); + + testsTable.tBodies[0].appendChild(testRow); + }); +} + +init(); +</script> + +</body> +</html> diff --git a/Tools/RebaselineQueueServer/templates/builder-queue-list.html b/Tools/RebaselineQueueServer/templates/builder-queue-list.html new file mode 100644 index 0000000..79fa02a --- /dev/null +++ b/Tools/RebaselineQueueServer/templates/builder-queue-list.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> + <title>{{ builder_name|escape }} Queue</title> + <link rel="stylesheet" href="/static/styles.css" type="text/css"> +</head> +<body> + +<h1>Queue: {{ builder_name|escape }}</h1> + +<ol> +{% for entry in entries %} + <li> + {{ entry.test|escape }} + </li> +{% empty %} + No tests found in queue. +{% endfor %} +</ol> + +<a href="/builder/{{ builder_name|escape }}/queue/edit">Edit queue</a> +</body> +</html> diff --git a/Tools/RebaselineQueueServer/templates/home.html b/Tools/RebaselineQueueServer/templates/home.html new file mode 100644 index 0000000..c6a16ff --- /dev/null +++ b/Tools/RebaselineQueueServer/templates/home.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> +<head> + <title>Rebaseline Queue</title> + <link rel="stylesheet" href="/static/styles.css" type="text/css"> +</head> +<body> + +<h1>Rebaseline Queue</h1> + +<ul> + <li><a href="/builder/picker">Browse and enqueue failing tests on builders</a></li> + <li> + Builders with enqueued tests: + <ul> +{% for builder_name in builder_names %} + <li> + <a href="/builder/{{ builder_name|escape }}/queue">{{ builder_name|escape }}</a> + </li> +{% empty %} + None +{% endfor %} + </ul> + </li> +</ul> + +</body> +</html> |