Commit 1e9fc4a6 authored by timothy@apple.com's avatar timothy@apple.com
Browse files

Add initial version of a new Buildbot dashboard view.

https://bugs.webkit.org/show_bug.cgi?id=122131

Reviewed by Alexey Proskuryakov.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Lion.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Lion@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Mavericks.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Mavericks@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/MountainLion.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/MountainLion@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/PlatformRing.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/PlatformRing@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Windows7.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Windows7@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Windows8.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/Windows8@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/WindowsXP.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Images/WindowsXP@2x.png: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BaseObject.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueue.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Main.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/WebKitBuildbot.js: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/BuildbotBuilderQueueView.css: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/BuildbotQueueView.css: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/BuildbotTesterQueueView.css: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Main.css: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css: Added.
* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@156685 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 507879a1
/*
* Copyright (C) 2008, 2013 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
*/
BaseObject = function()
{
};
BaseObject.addConstructorFunctions = function(subclassConstructor)
{
// Copies the relevant functions the subclass constructor.
for (var property in BaseObject) {
var value = BaseObject[property];
if (typeof value !== "function")
continue;
if (value === arguments.callee)
continue;
subclassConstructor[property] = value;
}
};
BaseObject.addEventListener = function(eventType, listener, thisObject)
{
thisObject = thisObject || null;
console.assert(eventType);
if (!eventType)
return;
console.assert(listener);
if (!listener)
return;
if (!this._listeners)
this._listeners = {};
var listeners = this._listeners[eventType];
if (!listeners)
listeners = this._listeners[eventType] = [];
// Prevent registering multiple times.
for (var i = 0; i < listeners.length; ++i) {
if (listeners[i].listener === listener && listeners[i].thisObject === thisObject)
return;
}
listeners.push({thisObject: thisObject, listener: listener});
};
BaseObject.removeEventListener = function(eventType, listener, thisObject)
{
eventType = eventType || null;
listener = listener || null;
thisObject = thisObject || null;
if (!this._listeners)
return;
if (!eventType) {
for (eventType in this._listeners)
this.removeEventListener(eventType, listener, thisObject);
return;
}
var listeners = this._listeners[eventType];
if (!listeners)
return;
for (var i = listeners.length - 1; i >= 0; --i) {
if (listener && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
listeners.splice(i, 1);
else if (!listener && thisObject && listeners[i].thisObject === thisObject)
listeners.splice(i, 1);
}
if (!listeners.length)
delete this._listeners[eventType];
if (!Object.keys(this._listeners).length)
delete this._listeners;
};
BaseObject.removeAllListeners = function()
{
delete this._listeners;
};
BaseObject.hasEventListeners = function(eventType)
{
if (!this._listeners || !this._listeners[eventType])
return false;
return true;
};
BaseObject.prototype = {
constructor: BaseObject,
addEventListener: BaseObject.addEventListener,
removeEventListener: BaseObject.removeEventListener,
removeAllListeners: BaseObject.removeAllListeners,
hasEventListeners: BaseObject.hasEventListeners,
dispatchEventToListeners: function(eventType, eventData)
{
var event = new BaseObjectEvent(this, eventType, eventData);
function dispatch(object)
{
if (!object || !object._listeners || !object._listeners[eventType] || event._stoppedPropagation)
return;
// Make a copy with slice so mutations during the loop doesn't affect us.
var listenersForThisEvent = object._listeners[eventType].slice(0);
// Iterate over the listeners and call them. Stop if stopPropagation is called.
for (var i = 0; i < listenersForThisEvent.length; ++i) {
listenersForThisEvent[i].listener.call(listenersForThisEvent[i].thisObject, event);
if (event._stoppedPropagation)
break;
}
}
// Dispatch to listeners of this specific object.
dispatch(this);
// Allow propagation again so listeners on the constructor always have a crack at the event.
event._stoppedPropagation = false;
// Dispatch to listeners on all constructors up the prototype chain, including the immediate constructor.
var constructor = this.constructor;
while (constructor) {
dispatch(constructor);
if (!constructor.prototype.__proto__)
break;
constructor = constructor.prototype.__proto__.constructor;
}
return event.defaultPrevented;
}
};
BaseObjectEvent = function(target, type, data)
{
this.target = target;
this.type = type;
this.data = data;
this.defaultPrevented = false;
this._stoppedPropagation = false;
};
BaseObjectEvent.prototype = {
constructor: BaseObjectEvent,
stopPropagation: function()
{
this._stoppedPropagation = true;
},
preventDefault: function()
{
this.defaultPrevented = true;
}
};
/*
* Copyright (C) 2013 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
*/
Buildbot = function(baseURL, queuesInfo)
{
BaseObject.call(this);
console.assert(baseURL);
console.assert(queuesInfo);
this.baseURL = baseURL;
this.queues = {};
for (var id in queuesInfo)
this.queues[id] = new BuildbotQueue(this, id, queuesInfo[id]);
};
BaseObject.addConstructorFunctions(Buildbot);
// Ordered importance/recency.
Buildbot.Platform = {
MacOSXMavericks: "mac-os-x-mavericks",
MacOSXMountainLion: "mac-os-x-mountain-lion",
MacOSXLion: "mac-os-x-lion",
Windows8: "windows-8",
Windows7: "windows-7",
WindowsXP: "windows-xp",
LinuxQt: "linux-qt",
LinuxGTK: "linux-gtk",
LinuxEFL: "linux-efl",
};
// Ordered importance.
Buildbot.TestCategory = {
WebKit2: "webkit-2",
WebKit1: "webkit-1"
};
// Ordered importance.
Buildbot.BuildArchitecture = {
Universal: "universal",
SixtyFourBit: "sixty-four-bit",
ThirtyTwoBit: "thirty-two-bit"
};
Buildbot.prototype = {
constructor: Buildbot,
__proto__: BaseObject.prototype,
};
/*
* Copyright (C) 2013 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
*/
BuildbotBuilderQueueView = function(debugQueues, releaseQueues)
{
BuildbotQueueView.call(this, debugQueues, releaseQueues);
function filterQueuesByArchitecture(architecture, queue)
{
return queue.architecture === architecture;
}
this.universalReleaseQueues = this.releaseQueues.filter(filterQueuesByArchitecture.bind(this, Buildbot.BuildArchitecture.Universal));
this.sixtyFourBitReleaseQueues = this.releaseQueues.filter(filterQueuesByArchitecture.bind(this, Buildbot.BuildArchitecture.SixtyFourBit));
this.thirtyTwoBitReleaseQueues = this.releaseQueues.filter(filterQueuesByArchitecture.bind(this, Buildbot.BuildArchitecture.ThirtyTwoBit));
this.universalDebugQueues = this.debugQueues.filter(filterQueuesByArchitecture.bind(this, Buildbot.BuildArchitecture.Universal));
this.sixtyFourBitDebugQueues = this.debugQueues.filter(filterQueuesByArchitecture.bind(this, Buildbot.BuildArchitecture.SixtyFourBit));
this.thirtyTwoBitDebugQueues = this.debugQueues.filter(filterQueuesByArchitecture.bind(this, Buildbot.BuildArchitecture.ThirtyTwoBit));
this.hasMultipleReleaseBuilds = this.releaseQueues.length > 1;
this.hasMultipleDebugBuilds = this.debugQueues.length > 1;
this.update();
};
BaseObject.addConstructorFunctions(BuildbotBuilderQueueView);
BuildbotBuilderQueueView.prototype = {
constructor: BuildbotBuilderQueueView,
__proto__: BuildbotQueueView.prototype,
update: function()
{
BuildbotQueueView.prototype.update.call(this);
this.element.removeChildren();
function createRevisionLink(iteration)
{
var messageLinkElement = document.createElement("a");
messageLinkElement.href = iteration.queue.buildbot.tracRevisionURL(iteration.openSourceRevision);
messageLinkElement.textContent = "r" + iteration.openSourceRevision;
return messageLinkElement;
}
function appendBuilderQueueStatus(queue)
{
var pendingBuildCount = queue.pendingIterationsCount;
if (pendingBuildCount) {
var message = pendingBuildCount === 1 ? "pending build" : "pending builds";
var status = new StatusLineView(message, StatusLineView.Status.Neutral, null, pendingBuildCount);
this.element.appendChild(status.element);
}
var firstRecentFailedIteration = queue.firstRecentFailedIteration;
if (firstRecentFailedIteration && firstRecentFailedIteration.loaded) {
var failureCount = queue.recentFailedIterationCount;
var message = createRevisionLink(firstRecentFailedIteration);
var status = new StatusLineView(message, StatusLineView.Status.Bad, failureCount > 1 ? "failed builds since" : "failed build", failureCount > 1 ? failureCount : null);
this.element.appendChild(status.element);
}
var mostRecentSuccessfulIteration = queue.mostRecentSuccessfulIteration;
if (mostRecentSuccessfulIteration && mostRecentSuccessfulIteration.loaded) {
var message = createRevisionLink(mostRecentSuccessfulIteration);
var status = new StatusLineView(message, StatusLineView.Status.Good, firstRecentFailedIteration ? "last successful build" : "latest build");
this.element.appendChild(status.element);
} else {
var status = new StatusLineView("unknown", StatusLineView.Status.Neutral, firstRecentFailedIteration ? "last successful build" : "latest build");
this.element.appendChild(status.element);
if (firstRecentFailedIteration) {
// We have a failed iteration but no successful. It might be further back in time.
// Update all the iterations so we get more history.
queue.iterations.forEach(function(iteration) { iteration.update(); });
}
}
}
function appendBuildArchitecture(queues, label)
{
if (!queues.length)
return;
var releaseLabel = document.createElement("label");
releaseLabel.textContent = label;
this.element.appendChild(releaseLabel);
queues.forEach(appendBuilderQueueStatus.bind(this));
}
appendBuildArchitecture.call(this, this.universalReleaseQueues, this.hasMultipleReleaseBuilds ? "Release (Universal)" : "Release");
appendBuildArchitecture.call(this, this.sixtyFourBitReleaseQueues, this.hasMultipleReleaseBuilds ? "Release (64-bit)" : "Release");
appendBuildArchitecture.call(this, this.thirtyTwoBitReleaseQueues, this.hasMultipleReleaseBuilds ? "Release (32-bit)" : "Release");
appendBuildArchitecture.call(this, this.universalDebugQueues, this.hasMultipleDebugBuilds ? "Debug (Universal)" : "Debug");
appendBuildArchitecture.call(this, this.sixtyFourBitDebugQueues, this.hasMultipleDebugBuilds ? "Debug (64-bit)" : "Debug");
appendBuildArchitecture.call(this, this.thirtyTwoBitDebugQueues, this.hasMultipleDebugBuilds ? "Debug (32-bit)" : "Debug");
}
};
/*
* Copyright (C) 2013 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
*/
BuildbotIteration = function(queue, id, finished)
{
BaseObject.call(this);
console.assert(queue);
console.assert(id);
this.queue = queue;
this.id = id;
this.loaded = false;
this.layoutTestResults = null;
this.javascriptTestResults = null;
this.pythonTestResults = null;
this.perlTestResults = null;
this.bindingTestResults = null;
this._finished = finished;
};
BaseObject.addConstructorFunctions(BuildbotIteration);
BuildbotIteration.Event = {
Updated: "updated"
};
BuildbotIteration.prototype = {
constructor: BuildbotIteration,
__proto__: BaseObject.prototype,
get finished()
{
return this._finished;
},
set finished(x)
{
this._finished = x;
},
update: function()
{
if (this.loaded && this._finished)
return;
function collectTestResults(data, stepName)
{
var testStep = data.steps.findFirst(function(step) { return step.name === stepName; });
if (!testStep)
return null;
if (testStep.results[0] === 4) {
// This build step was interrupted (perhaps due to the build slave restarting).
return null;
}
var testResults = {};
if (!("isFinished" in testStep)) {
// The step never even ran, or hasn't finish running.
testResults.finished = false;
return testResults;
}
testResults.finished = true;
if (!("results" in testStep) || !testStep.results[0]) {
// All tests passed.
testResults.allPassed = true;
return testResults;
}
if (/Exiting early/.test(testStep.results[1][0]))
testResults.tooManyFailures = true;
function resultSummarizer(matchString, sum, outputLine)
{
var match = /^(\d+)\s/.exec(outputLine);
if (!match)
return sum;
if (!outputLine.contains(matchString))
return sum;
if (!sum || sum === -1)
sum = 0;
return sum + parseInt(match[1], 10);
}
testResults.failureCount = testStep.results[1].reduce(resultSummarizer.bind(null, "fail"), undefined);
testResults.flakeyCount = testStep.results[1].reduce(resultSummarizer.bind(null, "flake"), undefined);
testResults.totalLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, "total leak"), undefined);
testResults.uniqueLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, "unique leak"), undefined);
testResults.newPassesCount = testStep.results[1].reduce(resultSummarizer.bind(null, "new pass"), undefined);
testResults.missingCount = testStep.results[1].reduce(resultSummarizer.bind(null, "missing"), undefined);
if (!testResults.failureCount && !testResults.flakyCount && !testResults.totalLeakCount && !testResults.uniqueLeakCount && !testResults.newPassesCount && !testResults.missingCount) {
// This step exited with a non-zero exit status, but we didn't find any output about the number of failed tests.
// Something must have gone wrong (e.g., timed out and was killed by buildbot).
testResults.errorOccurred = true;
}
return testResults;
}
JSON.load(this.queue.baseURL + "/builds/" + this.id, function(data) {
if (!data || !data.properties)
return;
var openSourceRevisionProperty = data.