2 Copyright 2014 The Chromium Authors. All rights reserved.
3 Use of this source code is governed by a BSD-style license that can be
4 found in the LICENSE file.
7 <link rel="import" href="../lib/net.html">
8 <link rel="import" href="../lib/update-util.html">
9 <link rel="import" href="ct-builder-failure.html">
10 <link rel="import" href="ct-step-failure.html">
11 <link rel="import" href="ct-failure-group.html">
12 <link rel="import" href="ct-builder-failure-group-data.html">
13 <link rel="import" href="ct-step-failure-group-data.html">
14 <link rel="import" href="ct-trooper-failure-group-data.html">
15 <link rel="import" href="ct-commit-list.html">
18 function CTFailures(commitLog) {
19 this.commitLog = commitLog;
20 // Maps a tree id to an array of CTFailureGroups within that tree.
22 this.lastUpdateDate = null;
28 // FIXME: This could potentially move onto CTStepFailureGroupData as it isn't relevant to
30 // Reverse sorting order, if a > b, return a negative number.
31 CTFailures.prototype._failureByTreeListComparator = function(tree, a, b) {
32 if (tree === undefined)
35 var rev_a = a.data.commitList.revisions;
36 var rev_b = b.data.commitList.revisions;
38 if (!rev_a || !Object.keys(rev_a).length) {
39 if (!rev_b || !Object.keys(rev_b).length)
42 } else if (!rev_b || !Object.keys(rev_b).length) {
46 // Prioritize the tree's revision, if they are unequal (else, fallback below)
47 if (rev_a[tree] && rev_b[tree] &&
48 rev_a[tree].last() != rev_b[tree].last()) {
49 return rev_b[tree].last() - rev_a[tree].last();
52 // Compare other revisions in alphabetical order.
53 var keys = Object.keys(rev_a).sort();
54 for (var i = 0; i < keys.length; i++) {
55 if (keys[i] == tree) // Already taken care of, above.
58 var a_list = rev_a[keys[i]];
59 var b_list = rev_b[keys[i]];
63 if (a_list.last() != b_list.last())
64 return b_list.last() - a_list.last();
69 // Updates the format of the alerts array to be easier to parse.
70 CTFailures.prototype._mungeAlerts = function(alerts) {
71 alerts.forEach(function(failure) {
72 // FIXME: Have the server store the actual failure type in a different
73 // field instead of smashing it into the reason.
74 if (failure.failureType) {
75 // The server has been fixed.
78 var parts = failure.reason.split(':');
79 failure.reason = parts[0];
80 failure.failureType = parts[1] || 'FAIL';
82 failure.failureType = 'UNKNOWN';
88 CTFailures.prototype.update = function() {
89 var annotationPromise = CTFailureGroup.fetchAnnotations();
90 return Promise.all([annotationPromise, net.json('https://sheriff-o-matic.appspot.com/alerts'),
91 net.json('https://trooper-o-matic.appspot.com/alerts')]).then(function(data_array) {
92 var annotations = data_array[0];
93 var sheriff_data = data_array[1];
94 var trooper_data = data_array[2];
97 this.lastUpdateDate = new Date(sheriff_data.date * 1000);
98 this._mungeAlerts(sheriff_data.alerts);
99 // FIXME: Change builder_alerts to expose the alerts as a map instead of an array.
101 sheriff_data.alerts.forEach(function(alert) {
102 alertsByKey[alert.key] = alert;
104 // Update |failures| with the appropriate CTFailureGroup's for each
106 sheriff_data.range_groups.forEach(function(rangeGroup) {
107 this._processFailuresForRangeGroup(newFailures, rangeGroup, alertsByKey, annotations);
110 // Sort failure groups so that newer failures are shown at the top
112 Object.keys(newFailures, function (tree, failuresByTree) {
113 failuresByTree.sort(this._failureByTreeListComparator.bind(this, tree));
116 sheriff_data.stale_builder_alerts.forEach(function(alert) {
117 this._processBuilderFailure(newFailures, alert);
120 this.failures = updateUtil.updateLeft(this.failures, newFailures);
121 this._processTrooperFailures(trooper_data);
125 CTFailures.prototype._failureComparator = function(a, b) {
130 if (a.testName > b.testName)
132 if (a.testName < b.testName)
137 CTFailures.prototype._processTrooperFailures = function(data) {
138 var trooper_failures = [];
139 Object.keys(data, function(failureType, failuresByTree) {
140 Object.keys(failuresByTree, function(tree, failure) {
141 if (failure.should_alert) {
142 trooper_failures.push(new CTFailureGroup('',
143 new CTTrooperFailureGroupData(failure.details, failure.url, failure, failureType, tree)));
147 this.failures['trooper'] = trooper_failures;
150 CTFailures.prototype._groupFailuresByTreeAndReason = function(failures, annotations) {
151 var failuresByTree = {};
152 failures.forEach(function(failure) {
153 // Establish the key to uniquely identify a failure by reason.
154 var failureKey = CTStepFailure.createKey(failure);
156 var reasonKey = JSON.stringify({
157 step: failure.step_name,
158 reason: failure.reason,
161 // FIXME: Use a model class instead of a dumb object.
162 if (!failuresByTree[failure.tree])
163 failuresByTree[failure.tree] = {};
164 if (!failuresByTree[failure.tree][reasonKey])
165 failuresByTree[failure.tree][reasonKey] = {};
166 failuresByTree[failure.tree][reasonKey][failure.builder_name] = {
168 // FIXME: Rename to failureType.
169 actual: failure.failureType,
170 lastFailingBuild: failure.last_failing_build,
171 earliestFailingBuild: failure.failing_build,
172 masterUrl: failure.master_url,
173 failingBuildCount: (1 + failure.last_failing_build - failure.failing_build),
174 annotation: annotations[failureKey],
177 return failuresByTree
180 CTFailures.prototype._processFailuresForRangeGroup = function(newFailures, rangeGroup, alerts, annotations) {
181 // A rangeGroup may be related to multiple alerts (via |failure_keys|). Categorize
182 // these failures by reason (cause of failure), so that they can be grouped in the UI
183 // (via a CTFailureGroup).
184 var failures = rangeGroup.failure_keys.map(function(failure_key) {
185 return alerts[failure_key];
187 var failuresByTree = this._groupFailuresByTreeAndReason(failures, annotations);
189 if (Object.isEmpty(failuresByTree))
192 Object.keys(failuresByTree, function(tree, resultsByReason) {
193 var treeFailures = [];
194 Object.keys(resultsByReason, function(reasonKey, resultsByBuilder) {
195 var failure = JSON.parse(reasonKey);
197 new CTStepFailure(failure.step, failure.reason, resultsByBuilder));
199 treeFailures.sort(this._failureComparator);
201 if (!newFailures[tree])
202 newFailures[tree] = [];
203 var commitList = new CTCommitList(this.commitLog, rangeGroup.likely_revisions);
204 newFailures[tree].push(new CTFailureGroup(rangeGroup.sort_key, new CTStepFailureGroupData(treeFailures, commitList)));
208 CTFailures.prototype._processBuilderFailure = function(newFailures, alert) {
209 var timeSinceLastUpdate = (this.lastUpdateDate.valueOf() / 1000) - alert.last_update_time;
210 var failure = new CTBuilderFailure(alert.tree, alert.master_url, alert.builder_name, alert.state,
211 timeSinceLastUpdate, alert.pending_builds);
212 var data = new CTBuilderFailureGroupData(failure, alert.builder_name,
213 alert.master_url + "/builders/" + alert.builder_name);
214 if (!newFailures[alert.tree])
215 newFailures[alert.tree] = [];
216 newFailures[alert.tree].push(new CTFailureGroup(failure.key, data, 'builders'));