1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/** Information about a particular build. */
6function BuildInfo(json) {
7  // Parse out the status message for the build.
8  var statusText;
9  if (json.currentStep) {
10    statusText = 'running ' + json.currentStep.name;
11  } else {
12    statusText = json.text.join(' ');
13  }
14
15  // Determine what state the build is in.
16  var state;
17  if (statusText.indexOf('exception') != -1) {
18    state = 'exception';
19  } else if (statusText.indexOf('running') != -1) {
20    state = 'running';
21  } else if (statusText.indexOf('successful') != -1) {
22    state = 'success';
23  } else if (statusText.indexOf('failed') != -1) {
24    state = 'failed';
25  } else if (statusText.indexOf('offline') != -1) {
26    state = 'offline';
27  } else if (statusText.indexOf('warnings') != -1) {
28    state = 'warnings';
29  } else {
30    state = 'unknown';
31  }
32
33  var failures = (state == 'failed') ? this.parseFailures(json) : null;
34
35  this.number = json.number;
36  this.state = state;
37  this.failures = failures;
38  this.statusText = statusText;
39  this.truncatedStatusText = truncateStatusText(statusText);
40}
41
42/** Save data about failed tests to perform blamelist intersections. */
43BuildInfo.prototype.parseFailures = function(json) {
44  var revisionRange = this.getRevisionRange(json);
45  if (revisionRange == null) return null;
46
47  var failures = [];
48  var botName = json.builderName;
49  for (var i = 0; i < json.steps.length; ++i) {
50    var step = json.steps[i];
51    var binaryName = step.name;
52    if (step.results[0] != 0) {  // Failed.
53      for (var j = 0; j < step.logs.length; ++j) {
54        var log = step.logs[j];
55        if (log[0] == 'stdio')
56          continue;
57        var testName = log[0];
58        failures.push([botName, binaryName, testName, revisionRange]);
59      }
60    }
61  }
62
63  return failures;
64};
65
66/**
67 * Get the revisions involved in a build.  Sadly, this only works on Chromium's
68 * main builders because downstream trees provide git revision SHA1s through
69 * JSON instead of SVN numbers.
70 */
71BuildInfo.prototype.getRevisionRange = function(json) {
72  if (json.sourceStamp.changes.length == 0) {
73    return null;
74  }
75
76  var lowest = parseInt(json.sourceStamp.changes[0].revision, 10);
77  var highest = parseInt(json.sourceStamp.changes[0].revision, 10);
78  for (var i = 1; i < json.sourceStamp.changes.length; ++i) {
79    var rev = parseInt(json.sourceStamp.changes[i].revision, 10);
80    if (rev < lowest)
81      lowest = rev;
82    if (rev > highest)
83      highest = rev;
84  }
85  return [lowest, highest];
86};
87
88/** Creates HTML to display info about this build. */
89BuildInfo.prototype.createHtml = function(buildNumberCell,
90                                          botUrl,
91                                          showFullInfo) {
92  var fullStatusText = 'Build ' + this.number + ':\n' + this.statusText;
93  createBuildHtml(buildNumberCell,
94                  botUrl + '/builds/' + this.number,
95                  showFullInfo ? this.number : null,
96                  fullStatusText,
97                  showFullInfo ? this.truncatedStatusText : null,
98                  this.state);
99};
100
101/** Creates a table cell for a particular build number. */
102function createBuildHtml(cellElement,
103                         url,
104                         buildNumber,
105                         fullStatusText,
106                         truncatedStatusText,
107                         buildState) {
108  // Create a link to the build results.
109  var linkElement = document.createElement('a');
110  linkElement.href = url;
111
112  // Display either the build number (for the last completed build), or show the
113  // status of the step.
114  var buildIdentifierElement = document.createElement('span');
115  if (buildNumber) {
116    buildIdentifierElement.className = 'build-identifier';
117    buildIdentifierElement.innerHTML = buildNumber;
118  } else {
119    buildIdentifierElement.className = 'build-letter';
120    buildIdentifierElement.innerHTML = buildState.toUpperCase()[0];
121  }
122  linkElement.appendChild(buildIdentifierElement);
123
124  // Show the status of the build in truncated form so it doesn't take up the
125  // whole screen.
126  if (truncatedStatusText) {
127    var statusElement = document.createElement('span');
128    statusElement.className = 'build-status';
129    statusElement.innerHTML = truncatedStatusText;
130    linkElement.appendChild(statusElement);
131  }
132
133  // Tack the cell onto the end of the row.
134  cellElement.className = buildState;
135  cellElement.title = fullStatusText;
136  cellElement.appendChild(linkElement);
137}
138