OLD | NEW |
| (Empty) |
1 <!-- | |
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. | |
5 --> | |
6 | |
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-master-failure.html"> | |
14 <link rel="import" href="ct-master-failure-group-data.html"> | |
15 <link rel="import" href="ct-step-failure-group-data.html"> | |
16 <link rel="import" href="ct-trooper-failure-group-data.html"> | |
17 <link rel="import" href="ct-commit-list.html"> | |
18 | |
19 <script> | |
20 function CTFailures(commitLog) { | |
21 this.commitLog = commitLog; | |
22 // Maps a tree id to an array of CTFailureGroups within that tree. | |
23 this.failures = null; | |
24 this.lastUpdateDate = null; | |
25 } | |
26 | |
27 (function() { | |
28 'use strict'; | |
29 | |
30 // FIXME: This could potentially move onto CTStepFailureGroupData as it isn't re
levant to | |
31 // trooper failures. | |
32 // Reverse sorting order, if a > b, return a negative number. | |
33 CTFailures.prototype._failureByTreeListComparator = function(tree, a, b) { | |
34 if (tree === undefined) | |
35 tree = 'chromium'; | |
36 | |
37 // Always bubble master alerts to the top | |
38 if (a.category =='master') { | |
39 if (b.category == 'master') | |
40 return 0; | |
41 return -1; | |
42 } else if (b.category == 'master') { | |
43 return 1; | |
44 } | |
45 | |
46 if (!a.data.commitList) | |
47 return 1; | |
48 if (!b.data.commitList) | |
49 return -1; | |
50 | |
51 var rev_a = a.data.commitList.revisions; | |
52 var rev_b = b.data.commitList.revisions; | |
53 | |
54 if (!rev_a || !Object.keys(rev_a).length) { | |
55 if (!rev_b || !Object.keys(rev_b).length) | |
56 return 0; | |
57 return 1; | |
58 } else if (!rev_b || !Object.keys(rev_b).length) { | |
59 return -1; | |
60 } | |
61 | |
62 // Prioritize the tree's revision, if they are unequal (else, fallback below) | |
63 if (rev_a[tree] && rev_b[tree] && | |
64 rev_a[tree].last() != rev_b[tree].last()) { | |
65 return rev_b[tree].last() - rev_a[tree].last(); | |
66 } | |
67 | |
68 // Compare other revisions in alphabetical order. | |
69 var keys = Object.keys(rev_a).sort(); | |
70 for (var i = 0; i < keys.length; i++) { | |
71 if (keys[i] == tree) // Already taken care of, above. | |
72 continue; | |
73 | |
74 var a_list = rev_a[keys[i]]; | |
75 var b_list = rev_b[keys[i]]; | |
76 if (!b_list) | |
77 return -1; | |
78 | |
79 if (a_list.last() != b_list.last()) | |
80 return b_list.last() - a_list.last(); | |
81 } | |
82 return 0; | |
83 }; | |
84 | |
85 // Updates the format of the alerts array to be easier to parse. | |
86 CTFailures.prototype._mungeAlerts = function(alerts) { | |
87 alerts.forEach(function(failure) { | |
88 // FIXME: Have the server store the actual failure type in a different | |
89 // field instead of smashing it into the reason. | |
90 if (failure.failureType) { | |
91 // The server has been fixed. | |
92 } else { | |
93 if (failure.reason) { | |
94 var parts = failure.reason.split(':'); | |
95 failure.reason = parts[0]; | |
96 failure.failureType = parts[1] || 'FAIL'; | |
97 } else { | |
98 failure.failureType = 'UNKNOWN'; | |
99 } | |
100 } | |
101 }); | |
102 }; | |
103 | |
104 CTFailures.prototype.update = function() { | |
105 var annotationPromise = CTFailureGroup.fetchAnnotations(); | |
106 return Promise.all([annotationPromise, net.json('https://sheriff-o-matic.appsp
ot.com/alerts'), | |
107 net.json('https://trooper-o-matic.appspot.com/alerts')]).then(function(dat
a_array) { | |
108 var annotations = data_array[0]; | |
109 var sheriff_data = data_array[1]; | |
110 var trooper_data = data_array[2]; | |
111 | |
112 var newFailures = {}; | |
113 this.lastUpdateDate = new Date(sheriff_data.date * 1000); | |
114 this._mungeAlerts(sheriff_data.alerts); | |
115 // FIXME: Change builder_alerts to expose the alerts as a map instead of an
array. | |
116 var alertsByKey = {} | |
117 sheriff_data.alerts.forEach(function(alert) { | |
118 alertsByKey[alert.key] = alert; | |
119 }); | |
120 // Update |failures| with the appropriate CTFailureGroup's for each | |
121 // tree. | |
122 sheriff_data.range_groups.forEach(function(rangeGroup) { | |
123 this._processFailuresForRangeGroup(newFailures, rangeGroup, alertsByKey, a
nnotations); | |
124 }.bind(this)); | |
125 | |
126 sheriff_data.stale_builder_alerts.forEach(function(alert) { | |
127 this._processBuilderFailure(newFailures, alert); | |
128 }.bind(this)); | |
129 | |
130 // Sort failure groups so that newer failures are shown at the top | |
131 // of the UI. | |
132 Object.keys(newFailures, function (tree, failuresByTree) { | |
133 failuresByTree.sort(this._failureByTreeListComparator.bind(this, tree)); | |
134 }.bind(this)); | |
135 | |
136 this.failures = updateUtil.updateLeft(this.failures, newFailures); | |
137 this._processTrooperFailures(newFailures, trooper_data); | |
138 }.bind(this)); | |
139 }; | |
140 | |
141 CTFailures.prototype._failureComparator = function(a, b) { | |
142 if (a.step > b.step) | |
143 return 1; | |
144 if (a.step < b.step) | |
145 return -1 | |
146 if (a.testName > b.testName) | |
147 return 1; | |
148 if (a.testName < b.testName) | |
149 return -1 | |
150 return 0; | |
151 }; | |
152 | |
153 CTFailures.prototype._processTrooperFailures = function(all_failures, | |
154 trooper_data) { | |
155 var trooper_failures = []; | |
156 Object.keys(trooper_data, function(failureType, failuresByTree) { | |
157 Object.keys(failuresByTree, function(tree, failure) { | |
158 if (failure.should_alert) { | |
159 trooper_failures.push(new CTFailureGroup('trooper', | |
160 new CTTrooperFailureGroupData(failure.details, failure.url, failure, | |
161 failureType, tree))); | |
162 } | |
163 }); | |
164 }); | |
165 | |
166 Object.keys(all_failures, function(tree, failuresByTree) { | |
167 failuresByTree.forEach(function(failure) { | |
168 if (failure.category == 'builder' || failure.category == 'master') { | |
169 trooper_failures.push(failure); | |
170 } | |
171 }); | |
172 }); | |
173 this.failures['trooper'] = trooper_failures; | |
174 }; | |
175 | |
176 CTFailures.prototype._groupFailuresByTreeAndReason = function(failures, annotati
ons) { | |
177 var failuresByTree = {}; | |
178 failures.forEach(function(failure) { | |
179 // Establish the key to uniquely identify a failure by reason. | |
180 var failureKey = CTStepFailure.createKey(failure); | |
181 | |
182 var reasonKey = JSON.stringify({ | |
183 step: failure.step_name, | |
184 reason: failure.reason, | |
185 }); | |
186 | |
187 // FIXME: Use a model class instead of a dumb object. | |
188 if (!failuresByTree[failure.tree]) | |
189 failuresByTree[failure.tree] = {}; | |
190 if (!failuresByTree[failure.tree][reasonKey]) | |
191 failuresByTree[failure.tree][reasonKey] = {}; | |
192 failuresByTree[failure.tree][reasonKey][failure.builder_name] = { | |
193 key: failureKey, | |
194 // FIXME: Rename to failureType. | |
195 actual: failure.failureType, | |
196 lastFailingBuild: failure.last_failing_build, | |
197 masterUrl: failure.master_url, | |
198 failingBuildCount: (1 + failure.last_failing_build - failure.failing_build
), | |
199 annotation: annotations[failureKey], | |
200 isTreeCloser: failure.would_close_tree, | |
201 }; | |
202 }); | |
203 return failuresByTree | |
204 }; | |
205 | |
206 CTFailures.prototype._processFailuresForRangeGroup = function(newFailures, range
Group, alerts, annotations) { | |
207 // A rangeGroup may be related to multiple alerts (via |failure_keys|). Catego
rize | |
208 // these failures by reason (cause of failure), so that they can be grouped in
the UI | |
209 // (via a CTFailureGroup). | |
210 var failures = rangeGroup.failure_keys.map(function(failure_key) { | |
211 return alerts[failure_key]; | |
212 }); | |
213 var failuresByTree = this._groupFailuresByTreeAndReason(failures, annotations)
; | |
214 | |
215 if (Object.isEmpty(failuresByTree)) | |
216 return; | |
217 | |
218 Object.keys(failuresByTree, function(tree, resultsByReason) { | |
219 var treeFailures = []; | |
220 Object.keys(resultsByReason, function(reasonKey, resultsByBuilder) { | |
221 var failure = JSON.parse(reasonKey); | |
222 treeFailures.push( | |
223 new CTStepFailure(failure.step, failure.reason, resultsByBuilder)); | |
224 }) | |
225 treeFailures.sort(this._failureComparator); | |
226 | |
227 if (!newFailures[tree]) | |
228 newFailures[tree] = []; | |
229 var commitList = new CTCommitList(this.commitLog, rangeGroup.likely_revision
s); | |
230 newFailures[tree].push(new CTFailureGroup(tree, new CTStepFailureGroupData(t
reeFailures, commitList))); | |
231 }.bind(this)); | |
232 }; | |
233 | |
234 CTFailures.prototype._processBuilderFailure = function(newFailures, alert) { | |
235 var timeSinceLastUpdate = (this.lastUpdateDate.valueOf() / 1000) - alert.last_
update_time; | |
236 | |
237 var failure; | |
238 var data; | |
239 var category; | |
240 if (alert.hasOwnProperty('builder_name')) { | |
241 category = 'builder'; | |
242 failure = new CTBuilderFailure(alert.tree, alert.master_url, alert.builder_n
ame, alert.state, | |
243 timeSinceLastUpdate, alert.pending_builds, alert.hasOwnProperty('isMaste
r')); | |
244 data = new CTBuilderFailureGroupData(failure, alert.builder_name, | |
245 alert.master_url + "/builders/" + alert.builder_name); | |
246 } else { | |
247 category = 'master'; | |
248 failure = new CTMasterFailure(alert.tree, alert.master_url, timeSinceLastUpd
ate); | |
249 data = new CTMasterFailureGroupData(failure, alert.master_url); | |
250 } | |
251 if (!newFailures[alert.tree]) | |
252 newFailures[alert.tree] = []; | |
253 newFailures[alert.tree].push(new CTFailureGroup(alert.tree, data, category)); | |
254 }; | |
255 | |
256 })(); | |
257 | |
258 </script> | |
OLD | NEW |