<html>
<head>
<title>WebKit Test Results</title>
-<link rel="stylesheet" href="/common.css">
+<link rel="stylesheet" href="common.css">
+<link rel="stylesheet" href="main.css">
<script src="js/autocompleter.js"></script>
<script src="js/build.js"></script>
<script src="js/dom.js"></script>
</ul>
</header>
-<form id="navigationBar" onsubmit="TestResultsView.fetchTest(this['testName'].value);
+<form onsubmit="TestResultsView.fetchTest(this['testName'].value);
TestResultsView.updateLocationHash();
return false;">
<input id="testName" type="text" size="150" onpaste="pasteHelper(this, event)"
placeholder="Type in a test name, or copy and paste test names on results.html or NRWT stdout (including junks)"></form>
+<div id="testView"></div>
-<div id="container"></div>
-<div id="tooltipContainer"></div>
-
-<style>
-
-#testName {
- width: 99%;
- font-size: 1em;
- outline: none;
- border: 1px solid #ccc;
- border-radius: 5px;
- padding: 5px;
-}
-
-.testResults {
- border: 1px solid #ccc;
- border-radius: 5px;
- padding: 5px;
- margin: 10px 0px;
- position: relative;
-}
-
-.closeButton {
- position: absolute;
- right: 5px;
- top: 5px;
- width: 1em;
- height: 1em;
- stroke: #999;
-}
-
-.resultsTable {
- font-size: small;
- border-collapse: collapse;
- border: 2px solid #fff;
- padding: 0;
- margin: 0;
-}
-
-.resultsTable caption {
- font-size: large;
- font-weight: normal;
- text-align: left;
- margin-bottom: 0.3em;
- white-space: pre;
-}
-
-.resultsTable td,
-.resultsTable th {
- border: 2px solid #fff;
- padding: 0;
- margin: 0;
-}
-
-.resultsTable th,
-.resultsTable .passingRate {
- font-weight: normal;
- padding-right: 10px;
-}
-
-.resultsTable th {
- width: 15em;
-}
-
-.resultsTable .passingRate {
- width: 3em;
-}
-
-.resultsTable .resultCell {
- display: inline-block;
- padding: 0.2em 0.2em;
-}
-
-.resultsTable a {
- display: block;
- width: 1em;
- height: 1.5em;
- border-radius: 3px;
-}
-
-.resultsTable .PASS a {
- background-color: #0c3;
-}
-
-.resultsTable .TEXT a {
- background-color: #c33;
-}
-
-.resultsTable .IMAGE a {
- background-color: #3cf;
-}
-
-.resultsTable .TEXT.PASS a {
- background-color: #cf3;
-}
-
-.resultsTable .CRASH a {
- background-color: #f00;
-}
-
-.candidateWindow {
- z-index: 999;
- position: absolute;
- background: white;
- color: black;
- border: 1px solid #ccc;
- border-radius: 5px;
- margin: 5px 0 0 0;
- padding: 5px;
- font-size: 1em;
- list-style: none;
-}
-
-.candidateWindow em {
- background-color: #ccc;
- font-style: normal;
-}
-
-.candidateWindow .selected {
- background-color: #0cf;
- color: white;
-}
-
-#tooltipContainer {
- position: absolute;
-}
-
-.tooltip {
- position: relative;
- border-radius: 5px;
- padding: 5px;
- opacity: 0.9;
- background: #333;
- color: #eee;
- font-size: small;
- line-height: 130%;
-}
-
-.tooltip:after {
- position: absolute;
- width: 0;
- height: 0;
- left: 50%;
- margin-left: -9px;
- bottom: -19px;
- content: "";
- display: block;
- border-style: solid;
- border-width: 10px;
- border-color: #333 transparent transparent transparent;
-}
+<form>
+<label>Show failing tests for</label>
+<select id="builderListView"><option value="">Select builder</option></select>
+<select id="builderDaysView"><option>5</option><option>15</option><option>30</option></select>
+</form>
+<div id="builderFailingTestsView"></div>
-.tooltip ul,
-.tooltip li {
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
-.tooltip a {
- color: white;
- text-shadow: none;
- text-decoration: underline;
-}
+<div id="tooltipContainer"></div>
-</style>
<script>
var TestResultsView = new (function () {
this._tooltipContainer = tooltipContainer;
this._tests = [];
+ this._currentBuilder = null;
this._oldHash = null;
this._builders = [];
this._slaves = [];
this._repositories = [];
});
+TestResultsView.setAvailableTests = function (availableTests) {
+ this._availableTests = availableTests;
+}
+
TestResultsView.setBuilders = function (builders) {
this._builders = builders;
}
tooltipContainer.removeChild(tooltipContainer.firstChild);
tooltipContainer.appendChild(contentElement);
- var rect = anchor.getBoundingClientRect();
- tooltipContainer.style.left = (rect.left - contentElement.offsetWidth / 2 + anchor.offsetWidth / 2) + 'px';
- tooltipContainer.style.top = (rect.top - contentElement.clientHeight - 5) + 'px';
+ var position = {x: 0, y: 0};
+ var currentNode = anchor;
+ while (currentNode) {
+ position.x += currentNode.offsetLeft;
+ position.y += currentNode.offsetTop;
+ currentNode = currentNode.offsetParent;
+ }
+ tooltipContainer.style.left = (position.x - contentElement.offsetWidth / 2 + anchor.offsetWidth / 2) + 'px';
+ tooltipContainer.style.top = (position.y - contentElement.clientHeight - 5) + 'px';
}
TestResultsView._urlFromBuilder = function (urlType, master, builder, revision, build) {
return cell;
}
-TestResultsView._populatePane = function(testName, results, section) {
+TestResultsView._populateTestPane = function(testName, results, section) {
var table = element('table', {'class': 'resultsTable'}, [element('caption', [testName])]);
var resultsByBuilder = results['builders'];
for (var builderId in resultsByBuilder) {
- var results = resultsByBuilder[builderId];
- for (var i = 0; i < results.length; i++) {
- results[i].build = new TestBuild(this._repositories, this._builders, results[i]);
- }
+ var resultsByTest = resultsByBuilder[builderId];
+ var results;
+ for (var testId in resultsByTest)
+ results = resultsByTest[testId];
+ if (!results)
+ continue;
- var sortedResults = results.sort(function (result1, result2) { return result1.build.time() - result2.build.time(); });
- var cells = new Array(sortedResults.length);
var builder = this._builders[builderId];
- for (var i = 0; i < sortedResults.length; i++)
- cells[i] = this._createResultCell(builder.master, builder.name, sortedResults[i], sortedResults[i - 1]);
-
- var passCount = cells.filter(function (cell) { return cell.className == 'PASS'; }).length;
- var passingRate = Math.round(passCount / cells.length * 100) + '%';
- table.appendChild(element('tr', [element('th', [builder.name]), element('td', {'class': 'passingRate'}, [passingRate]),
- element('td', cells)]));
// FIXME: Add a master name if there is more than one.
+ table.appendChild(this._createTestResultRow(builder.name, results, builder));
}
section.appendChild(table);
}
+TestResultsView._createTestResultRow = function (title, results, builder) {
+ for (var i = 0; i < results.length; i++)
+ results[i].build = new TestBuild(this._repositories, this._builders, results[i]);
+
+ var sortedResults = results.sort(function (result1, result2) { return result1.build.time() - result2.build.time(); });
+ var cells = new Array(sortedResults.length);
+ for (var i = 0; i < sortedResults.length; i++)
+ cells[i] = this._createResultCell(builder.master, builder.name, sortedResults[i], sortedResults[i - 1]);
+
+ var passCount = cells.filter(function (cell) { return cell.className == 'PASS'; }).length;
+ var passingRate = Math.round(passCount / cells.length * 100) + '%';
+ return element('tr', [element('th', ['' + title]), element('td', {'class': 'passingRate'}, [passingRate]), element('td', cells)]);
+}
+
TestResultsView.fetchTest = function (testName) {
if (this._tests.indexOf(testName) >= 0)
return;
section.parentNode.removeChild(section);
event.preventDefault();
});
- var section = element('section', {'id': testName, 'class': 'testResults'}, [closeButton]);
+ var section = element('section', {'id': testName, 'class': 'testResults'}, [closeButton, 'Loading...']);
- document.getElementById('container').appendChild(section);
+ document.getElementById('testView').appendChild(section);
var xhr = new XMLHttpRequest();
xhr.open("GET", 'api/results.php?test=' + testName, true);
xhr.onload = function(event) {
var response = JSON.parse(xhr.response);
+ section.removeChild(section.lastChild); // Remove Loading...
if (response['status'] != 'OK') {
section.appendChild(text('Failed to load results for ' + testName + ': ' + response['status']));
return;
}
-
- self._populatePane(testName, response, section);
+ self._populateTestPane(testName, response, section);
}
xhr.send();
this._tests.push(testName);
TestResultsView.fetchTests = function (testNames, doNotUpdateHash) {
for (var i = 0; i < testNames.length; i++)
this.fetchTest(testNames[i], doNotUpdateHash);
- this.updateLocationHash();
+ if (!doNotUpdateHash)
+ this.updateLocationHash();
+}
+
+TestResultsView._populateBuilderPane = function(builderName, results, section) {
+ var table = element('table', {'class': 'resultsTable'}, [element('caption', [builderName])]);
+ var resultsByBuilder = results['builders'];
+ var resultsByTests;
+ var builderId;
+ for (var currentBuilderId in resultsByBuilder) {
+ builderId = currentBuilderId;
+ resultsByTests = resultsByBuilder[builderId];
+ }
+ if (!resultsByTests)
+ return;
+
+ var builder = this._builders[builderId];
+ for (var testId in resultsByTests)
+ table.appendChild(this._createTestResultRow(this._availableTests[testId].name, resultsByTests[testId], builder));
+ section.appendChild(table);
+}
+
+TestResultsView.fetchFailingTestsForBuilder = function (builderName, numberOfDays, doNotUpdateHash) {
+ var section = element('section', {'class': 'testResults'}, ['Loading...']);
+
+ var container = document.getElementById('builderFailingTestsView');
+ container.innerHTML = '';
+ container.appendChild(section);
+
+ var self = this;
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", 'api/failing-tests.php?builder=' + escape(builderName) + '&' + 'days=' + numberOfDays, true);
+ xhr.onload = function(event) {
+ var response = JSON.parse(xhr.response);
+ section.innerHTML = '';
+ if (response['status'] != 'OK') {
+ section.appendChild(text('Failed to load results for ' + builderName + ': ' + response['status']));
+ return;
+ }
+ self._currentBuilder = builderName;
+ self._currentBuilderDays = numberOfDays;
+ self._populateBuilderPane(builderName, response, section);
+ if (!doNotUpdateHash)
+ self.updateLocationHash();
+ }
+ xhr.send();
}
TestResultsView.updateLocationHash = function () {
var params = {
- 'tests': this._tests.join(',')
+ 'tests': this._tests.join(','),
+ 'builder': this._currentBuilder,
+ 'builderDays': this._currentBuilderDays,
};
var hash = '';
- for (var key in params)
- hash += decodeURIComponent(key) + '=' + decodeURIComponent(params[key]);
+ for (var key in params) {
+ var value = params[key];
+ if (value === null || value === undefined)
+ continue;
+ hash += '&' + decodeURIComponent(key) + '=' + decodeURIComponent(value);
+ }
location.hash = hash;
this._oldHash = location.hash;
}
return;
this._oldHash = newHash;
this._tests = [];
- document.getElementById('container').innerHTML = '';
this.loadTestsFromLocationHash();
}
var name = decodeURIComponent(component.substr(0, equalPosition));
parsed[name] = decodeURIComponent(component.substr(equalPosition + 1));
});
- if (!parsed['tests'])
- return;
var doNotUpdateHash = true;
- this.fetchTests(parsed['tests'].split(','), doNotUpdateHash);
+ if (parsed['tests']) {
+ document.getElementById('testView').innerHTML = '';
+ this.fetchTests(parsed['tests'].split(','), doNotUpdateHash);
+ }
+ if (parsed['builder'])
+ this.fetchFailingTestsForBuilder(parsed['builder'], parsed['builderDays'] || 5, doNotUpdateHash);
}
function fetchManifest(callback) {
var input = document.getElementById('testName');
input.autocompleter = new Autocompleter(input, testNames);
- TestResultsView.setBuilders(response['builders']);
- TestResultsView.setSlaves(response['slaves']);
- TestResultsView.setRepositories(response['repositories']);
+ var builderListView = document.getElementById('builderListView');
+ for (var builderId in response['builders'])
+ builderListView.appendChild(element('option', [text(response['builders'][builderId].name)]));
+
+ var builderDaysView = document.getElementById('builderDaysView');
+
+ function updateBuilderView() {
+ if (builderListView.value)
+ TestResultsView.fetchFailingTestsForBuilder(builderListView.value, builderDaysView.value);
+ }
+
+ builderListView.addEventListener('change', updateBuilderView);
+ builderDaysView.addEventListener('change', updateBuilderView);
+
+ function mapById(items) {
+ var results = {};
+ for (var i = 0; i < items.length; i++)
+ results[items[i].id] = items[i];
+ return results;
+ }
+
+ TestResultsView.setAvailableTests(mapById(response['tests']));
+ TestResultsView.setBuilders(mapById(response['builders']));
+ TestResultsView.setSlaves(mapById(response['slaves']));
+ TestResultsView.setRepositories(mapById(response['repositories']));
TestResultsView.loadTestsFromLocationHash();
});