blob: 016725e30d1e9a053660575f1cfaa534130666a4 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/** Information about a particular build. */
function BuildInfo(json) {
// Parse out the status message for the build.
var statusText;
if (json.currentStep) {
statusText = 'running ' + json.currentStep.name;
} else {
statusText = json.text.join(' ');
}
// Determine what state the build is in.
var state;
if (statusText.indexOf('exception') != -1) {
state = 'exception';
} else if (statusText.indexOf('running') != -1) {
state = 'running';
} else if (statusText.indexOf('successful') != -1) {
state = 'success';
} else if (statusText.indexOf('failed') != -1) {
state = 'failed';
} else if (statusText.indexOf('offline') != -1) {
state = 'offline';
} else if (statusText.indexOf('warnings') != -1) {
state = 'warnings';
} else {
state = 'unknown';
}
var failures = (state == 'failed') ? this.parseFailures(json) : null;
this.number = json.number;
this.state = state;
this.failures = failures;
this.statusText = statusText;
this.truncatedStatusText = truncateStatusText(statusText);
}
/** Save data about failed tests to perform blamelist intersections. */
BuildInfo.prototype.parseFailures = function(json) {
var revisionRange = this.getRevisionRange(json);
if (revisionRange == null) return null;
var failures = [];
var botName = json.builderName;
for (var i = 0; i < json.steps.length; ++i) {
var step = json.steps[i];
var binaryName = step.name;
if (step.results[0] != 0) { // Failed.
for (var j = 0; j < step.logs.length; ++j) {
var log = step.logs[j];
if (log[0] == 'stdio')
continue;
var testName = log[0];
failures.push([botName, binaryName, testName, revisionRange]);
}
}
}
return failures;
};
/**
* Get the revisions involved in a build. Sadly, this only works on Chromium's
* main builders because downstream trees provide git revision SHA1s through
* JSON instead of SVN numbers.
*/
BuildInfo.prototype.getRevisionRange = function(json) {
if (json.sourceStamp.changes.length == 0) {
return null;
}
var lowest = parseInt(json.sourceStamp.changes[0].revision, 10);
var highest = parseInt(json.sourceStamp.changes[0].revision, 10);
for (var i = 1; i < json.sourceStamp.changes.length; ++i) {
var rev = parseInt(json.sourceStamp.changes[i].revision, 10);
if (rev < lowest)
lowest = rev;
if (rev > highest)
highest = rev;
}
return [lowest, highest];
};
/** Creates HTML to display info about this build. */
BuildInfo.prototype.createHtml = function(buildNumberCell,
botUrl,
showFullInfo) {
var fullStatusText = 'Build ' + this.number + ':\n' + this.statusText;
createBuildHtml(buildNumberCell,
botUrl + '/builds/' + this.number,
showFullInfo ? this.number : null,
fullStatusText,
showFullInfo ? this.truncatedStatusText : null,
this.state);
};
/** Creates a table cell for a particular build number. */
function createBuildHtml(cellElement,
url,
buildNumber,
fullStatusText,
truncatedStatusText,
buildState) {
// Create a link to the build results.
var linkElement = document.createElement('a');
linkElement.href = url;
// Display either the build number (for the last completed build), or show the
// status of the step.
var buildIdentifierElement = document.createElement('span');
if (buildNumber) {
buildIdentifierElement.className = 'build-identifier';
buildIdentifierElement.innerHTML = buildNumber;
} else {
buildIdentifierElement.className = 'build-letter';
buildIdentifierElement.innerHTML = buildState.toUpperCase()[0];
}
linkElement.appendChild(buildIdentifierElement);
// Show the status of the build in truncated form so it doesn't take up the
// whole screen.
if (truncatedStatusText) {
var statusElement = document.createElement('span');
statusElement.className = 'build-status';
statusElement.innerHTML = truncatedStatusText;
linkElement.appendChild(statusElement);
}
// Tack the cell onto the end of the row.
cellElement.className = buildState;
cellElement.title = fullStatusText;
cellElement.appendChild(linkElement);
}