blob: c53344e287fd4384a097f751e4356eb0ddf2e4c0 [file] [log] [blame]
/*
* Copyright (C) 2013, 2015-2016 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.
*/
BuildbotCombinedQueueView = function(queue)
{
console.assert(queue.branches === undefined);
var indicesOfFirstQueueWithRepository = {};
var combinedQueues = queue.combinedQueues;
var buildbot = combinedQueues[0].buildbot;
for (var i = 0; i < combinedQueues.length; ++i) {
var subQueue = combinedQueues[i];
console.assert(buildbot === subQueue.buildbot);
var branches = subQueue.branches;
for (var j = 0; j < branches.length; ++j) {
var branch = branches[j];
var repositoryName = branch.repository.name;
var expected = indicesOfFirstQueueWithRepository[repositoryName];
if (expected === undefined) {
indicesOfFirstQueueWithRepository[repositoryName] = { queueIndex: i, branchIndex: j };
continue;
}
var expectedBranch = combinedQueues[expected.queueIndex].branches[expected.branchIndex];
var message = queue.id + ": " + branch.name + " === combinedQueues[" + i + "].branch[" + j + "] ";
message += "=== combinedQueues[" + expected.queueIndex + "].branch[" + expected.branchIndex + "] === " + expectedBranch.name;
console.assert(branch.name === expectedBranch.name, message);
}
}
BuildbotQueueView.call(this, queue.combinedQueues);
this._alwaysExpand = false;
this.combinedQueue = queue;
this.update();
};
BaseObject.addConstructorFunctions(BuildbotCombinedQueueView);
BuildbotCombinedQueueView.prototype = {
constructor: BuildbotCombinedQueueView,
__proto__: BuildbotQueueView.prototype,
update: function()
{
BuildbotQueueView.prototype.update.call(this);
this.element.removeChildren();
if (!this._alwaysExpand && this._queuesShouldDisplayCombined()) {
var releaseLabel = document.createElement("a");
releaseLabel.classList.add("queueLabel");
releaseLabel.href = "#";
releaseLabel.textContent = this.combinedQueue.heading;
releaseLabel.onclick = function() { this._alwaysExpand = true; this.update(); return false; }.bind(this);
this.element.appendChild(releaseLabel);
var queue = this.queues[0]; // All queues in the combined queue are from the same buildbot.
if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated) {
this._appendUnauthorizedLineView(queue);
return;
}
// Show the revision for the slowest queue, because we don't know if any newer revisions are green on all queues.
// This can be slightly misleading after fixing a problem, because we can show a known broken revision as green.
var slowestQueue = this.queues.slice().sort(function(a, b) { return BuildbotQueue.prototype.compareIterationsByRevisions(a.mostRecentSuccessfulIteration, b.mostRecentSuccessfulIteration); }).pop();
this._appendPendingRevisionCount(slowestQueue, this._latestProductiveIteration.bind(this, slowestQueue));
var message = this.revisionContentForIteration(slowestQueue.mostRecentSuccessfulIteration);
var statusMessagePassed = "all " + (queue.builder ? "builds succeeded" : "tests passed");
var statusView = new StatusLineView(message, StatusLineView.Status.Good, statusMessagePassed, null, null);
new PopoverTracker(statusView.statusBubbleElement, this._presentPopoverForCombinedGreenBubble.bind(this));
this.element.appendChild(statusView.element);
} else {
this.appendBuildStyle.call(this, this.queues, null, function(queue) {
if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated) {
this._appendUnauthorizedLineView(queue);
return;
}
this._appendPendingRevisionCount(queue, this._latestProductiveIteration.bind(this, queue));
var firstRecentUnsuccessfulIteration = queue.firstRecentUnsuccessfulIteration;
var mostRecentFinishedIteration = queue.mostRecentFinishedIteration;
var mostRecentSuccessfulIteration = queue.mostRecentSuccessfulIteration;
if (firstRecentUnsuccessfulIteration && firstRecentUnsuccessfulIteration.loaded && mostRecentFinishedIteration && mostRecentFinishedIteration.loaded) {
console.assert(!mostRecentFinishedIteration.successful);
var message = this.revisionContentForIteration(mostRecentFinishedIteration, mostRecentFinishedIteration.productive ? mostRecentSuccessfulIteration : null);
if (!mostRecentFinishedIteration.productive) {
var status = StatusLineView.Status.Danger;
} else if (mostRecentFinishedIteration.failedTestSteps.length === 1 && /(?=.*test)(?=.*jsc)/.test(mostRecentFinishedIteration.failedTestSteps[0].name)) {
var failedStep = mostRecentFinishedIteration.failedTestSteps[0];
var URL = mostRecentFinishedIteration.queue.buildbot.javaScriptCoreTestStdioUrlForIteration(mostRecentFinishedIteration, failedStep.name);
var statusView = new StatusLineView(message, StatusLineView.Status.Bad, this._testStepFailureDescription(failedStep), failedStep.failureCount, URL);
this.element.appendChild(statusView.element);
new PopoverTracker(statusView.statusBubbleElement, this._presentPopoverForJavaScriptCoreTestRegressions.bind(this, failedStep.name), mostRecentFinishedIteration);
return;
} else {
// Direct links to some common logs.
var url = mostRecentFinishedIteration.failureLogURL("build log");
if (!url)
url = mostRecentFinishedIteration.failureLogURL("stdio");
var status = StatusLineView.Status.Bad;
}
// Show a popover when the URL is not a main build page one, because there are usually multiple logs, and it's good to provide a choice.
var needsPopover = !url;
// Some other step failed, link to main buildbot page for the iteration.
if (!url)
url = queue.buildbot.buildPageURLForIteration(mostRecentFinishedIteration);
var statusView = new StatusLineView(message, status, mostRecentFinishedIteration.text, null, url);
this.element.appendChild(statusView.element);
if (needsPopover)
new PopoverTracker(statusView.statusBubbleElement, this._presentIndividualQueuePopover.bind(this), mostRecentFinishedIteration);
}
var statusMessagePassed = "all " + (queue.builder ? "builds succeeded" : "tests passed");
if (mostRecentSuccessfulIteration && mostRecentSuccessfulIteration.loaded) {
var message = this.revisionContentForIteration(mostRecentSuccessfulIteration);
var url = queue.buildbot.buildPageURLForIteration(mostRecentSuccessfulIteration);
var label = firstRecentUnsuccessfulIteration ? "last succeeded" : statusMessagePassed;
var statusView = new StatusLineView(message, StatusLineView.Status.Good, label, null, url);
this.element.appendChild(statusView.element);
} else {
var label = firstRecentUnsuccessfulIteration ? "last succeeded" : statusMessagePassed;
var statusView = new StatusLineView("unknown", StatusLineView.Status.Neutral, label);
this.element.appendChild(statusView.element);
if (firstRecentUnsuccessfulIteration) {
// We have a failed iteration but no successful. It might be further back in time.
queue.loadMoreHistoricalIterations();
}
}
});
}
},
// All queues are green, or all are unauthorized (the latter case always applies to all queues, because they are all from the same buildbot).
_queuesShouldDisplayCombined: function()
{
for (var i = 0, end = this.queues.length; i < end; ++i) {
var queue = this.queues[i];
if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated)
return true;
if (!queue.mostRecentFinishedIteration || !queue.mostRecentFinishedIteration.successful)
return false;
}
return true;
},
_presentPopoverForCombinedGreenBubble: function(element, popover)
{
var content = document.createElement("div");
content.className = "combined-queue-popover";
var title = document.createElement("div");
title.className = "popover-iteration-heading";
title.textContent = "latest tested revisions";
content.appendChild(title);
this._addDividerToPopover(content);
function addQueue(queue, view) {
var line = document.createElement("div");
var link = document.createElement("a");
link.className = "queue-link";
link.href = queue.overviewURL;
link.textContent = queue.heading;
link.target = "_blank";
line.appendChild(link);
var revision = document.createElement("span");
revision.className = "revision";
revision.appendChild(view.revisionContentForIteration(queue.mostRecentSuccessfulIteration));
line.appendChild(revision);
content.appendChild(line);
}
for (var i = 0, end = this.queues.length; i < end; ++i)
addQueue(this.queues[i], this);
var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
popover.content = content;
popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
return true;
},
_presentIndividualQueuePopover: function(element, popover, iteration)
{
var content = document.createElement("div");
content.className = "build-logs-popover";
function addLog(name, url) {
var line = document.createElement("a");
line.className = "build-log-link";
line.href = url;
line.textContent = name;
line.target = "_blank";
content.appendChild(line);
}
this._addIterationHeadingToPopover(iteration, content);
this._addDividerToPopover(content);
var logsHeadingLine = document.createElement("div");
logsHeadingLine.className = "build-logs-heading";
logsHeadingLine.textContent = iteration.firstFailedStepName + " failed";
content.appendChild(logsHeadingLine);
for (var i = 0, end = iteration.failureLogs.length; i < end; ++i)
addLog(iteration.failureLogs[i][0], iteration.failureLogs[i][1]);
var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
popover.content = content;
popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
return true;
},
};