Index: Tools/GardeningServer/ui/ct-failure-analyzer.html |
diff --git a/Tools/GardeningServer/ui/ct-failure-analyzer.html b/Tools/GardeningServer/ui/ct-failure-analyzer.html |
index a4c6a26e22a98a7bd97f1f072441c9742b0d5b82..36db63217f2fdb8a8ee6c9a932fe451f720d97c1 100644 |
--- a/Tools/GardeningServer/ui/ct-failure-analyzer.html |
+++ b/Tools/GardeningServer/ui/ct-failure-analyzer.html |
@@ -9,11 +9,12 @@ found in the LICENSE file. |
<link rel="import" href="../model/ct-failure.html"> |
<link rel="import" href="../model/ct-failure-group.html"> |
-<polymer-element name="ct-failure-analyzer" attributes="failures builderLatestRevisions lastUpdateDate"> |
+<polymer-element name="ct-failure-analyzer" attributes="commitLog failures builderLatestRevisions lastUpdateDate"> |
<script> |
// FIXME: Don't use a polymer component for this. Instead use a Failures model |
// object that knows how to do the XHR and process the data appropriately. |
Polymer({ |
+ commitLog: null, |
builderLatestRevisions: null, |
failures: null, |
lastUpdateDate: null, |
@@ -35,32 +36,42 @@ found in the LICENSE file. |
], |
}, |
- _failureListComparator: function(tree, a, b) { |
+ // Reverse sorting order, if a > b, return a negative number. |
+ _failureByTreeListComparator: function(tree, a, b) { |
if (tree === undefined) |
tree = 'chromium'; |
- var rev_a = a.failures[0].firstFailingRevisions; |
- var rev_b = b.failures[0].firstFailingRevisions; |
+ var rev_a = a.commitList.revisions; |
+ var rev_b = b.commitList.revisions; |
- // Handle missing revision. |
- if (!rev_a) { |
- if (!rev_b) { |
+ if (!rev_a || !Object.keys(rev_a).length) { |
+ if (!rev_b || !Object.keys(rev_b).length) |
return 0; |
- } |
- return -1; |
- } else if (!rev_b) { |
return 1; |
+ } else if (!rev_b || !Object.keys(rev_b).length) { |
+ return -1; |
} |
- // Prioritize the tree's revision. |
- if (rev_a[tree] != rev_b[tree]) |
- return rev_b[tree] - rev_a[tree]; |
+ // Prioritize the tree's revision, if they are unequal (else, fallback |
+ // below). |
+ if (rev_a[tree] && rev_b[tree] && |
+ rev_a[tree].last() != rev_b[tree].last()) { |
+ return rev_b[tree].last() - rev_a[tree].last(); |
+ } |
// Compare other revisions in alphabetical order. |
var keys = Object.keys(rev_a).sort(); |
for (var i = 0; i < keys.length; i++) { |
- if (rev_a[keys[i]] != rev_b[keys[i]]) |
- return rev_b[keys[i]] - rev_a[keys[i]]; |
+ if (keys[i] == tree) // Already taken care of, above. |
+ continue; |
+ |
+ var a_list = rev_a[keys[i]]; |
+ var b_list = rev_b[keys[i]]; |
+ if (!b_list) |
+ return -1; |
+ |
+ if (a_list.last() != b_list.last()) |
+ return b_list.last() - a_list.last(); |
} |
return 0; |
}, |
@@ -73,11 +84,13 @@ found in the LICENSE file. |
this.builderLatestRevisions = new CTBuilderRevisions(data.latest_builder_info['chromium.webkit']); |
this.failures = {}; |
this.lastUpdateDate = new Date(data.date * 1000); |
- data.range_groups.forEach(function(group) { |
- this._processFailuresForGroup(group, data.alerts, annotations); |
+ // Update |failures| with the appropriate CTFailureGroup's for each tree. |
+ data.range_groups.forEach(function(rangeGroup) { |
+ this._processFailuresForRangeGroup(rangeGroup, data.alerts, annotations); |
}.bind(this)); |
+ // Sort failure groups so that newer failures are shown at the top of the UI. |
Object.keys(this.failures, function (tree, failuresByTree) { |
- this.failures[tree].sort(this._failureListComparator.bind(this, tree)); |
+ this.failures[tree].sort(this._failureByTreeListComparator.bind(this, tree)); |
}.bind(this)); |
}.bind(this)); |
}.bind(this)); |
@@ -95,9 +108,7 @@ found in the LICENSE file. |
return 0; |
}, |
- _processFailuresForGroup: function(group, failures, annotations) { |
- var failuresByReason = {}; |
- |
+ _processFailuresForRangeGroup: function(rangeGroup, alerts, annotations) { |
var masterToTree = {}; |
Object.keys(this._trees, function(tree, masters) { |
masters.forEach(function(master) { |
@@ -105,8 +116,13 @@ found in the LICENSE file. |
}); |
}); |
- group.failure_keys.forEach(function(failure_key) { |
- var failure = failures.find(function(item) { return item.key == failure_key; }); |
+ // A rangeGroup may be related to multiple alerts (via |failure_keys|). Categorize |
+ // these failures by reason (cause of failure), so that they can be grouped in the UI |
+ // (via a CTFailureGroup). Failures will be grouped in |failuresByReason|. |
+ var failuresByReason = {}; |
+ rangeGroup.failure_keys.forEach(function(failure_key) { |
+ var failure = alerts.find(function(item) { return item.key == failure_key; }); |
+ // Establish the key to uniquely identify a failure by reason. |
var reason, failureType; |
if (failure.reason) { |
// FIXME: Store the actual failure type in a different field instead of smashing it into the reason. |
@@ -125,6 +141,8 @@ found in the LICENSE file. |
reason: reason, |
}); |
+ // FIXME: Figure out what tree masters that aren't in masterToTree |
+ // we should have. |
var tree = masterToTree[failure.master_url]; |
// FIXME: Use a model class instead of a dumb object. |
@@ -144,13 +162,17 @@ found in the LICENSE file. |
}; |
}.bind(this)); |
+ if (!Object.keys(failuresByReason).length) |
+ return; |
+ |
var groupedFailures = {}; |
Object.keys(failuresByReason, function(reasonKey, resultsByTree) { |
var failure = JSON.parse(reasonKey); |
Object.keys(resultsByTree, function(tree, resultsByBuilder) { |
if (!groupedFailures[tree]) |
groupedFailures[tree] = []; |
- groupedFailures[tree].push(new CTFailure(failure.step, failure.reason, resultsByBuilder, group.merged_first_failing, group.merged_last_passing)); |
+ groupedFailures[tree].push( |
+ new CTFailure(failure.step, failure.reason, resultsByBuilder)); |
}) |
}); |
@@ -159,7 +181,8 @@ found in the LICENSE file. |
if (!this.failures[tree]) |
this.failures[tree] = []; |
- this.failures[tree].push(new CTFailureGroup(failures)); |
+ var commitList = new CTCommitList(this.commitLog, rangeGroup.likely_revisions); |
+ this.failures[tree].push(new CTFailureGroup(failures, commitList)); |
}.bind(this)); |
}, |
}); |