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.
6 * Return the range of intersection between the two lists. Ranges are sets of
7 * CLs, so it's inclusive on both ends.
9 function rangeIntersection(a
, b
) {
14 // We know they intersect, result is the larger lower bound to the smaller
16 return [Math
.max(a
[0], b
[0]), Math
.min(a
[1], b
[1])];
20 * Finds the intersections between the blamelists.
21 * Input is a object mapping botname => [low, high].
25 * [ low0, high0 ], [ bot1, bot2, bot3 ],
26 * [ low1, high1 ], [ bot4, ... ],
30 function findIntersections(testData
) {
31 var keys
= Object
.keys(testData
);
32 var intersections
= [];
33 var botName
= keys
[0];
34 var range
= testData
[botName
];
35 intersections
.push([range
, [botName
]]);
36 for (var i
= 1; i
< keys
.length
; ++i
) {
38 range
= testData
[botName
];
39 var intersectedSome
= false;
40 for (var j
= 0; j
< intersections
.length
; ++j
) {
41 var intersect
= rangeIntersection(intersections
[j
][0], range
);
43 intersections
[j
][0] = intersect
;
44 intersections
[j
][1].push(botName
);
45 intersectedSome
= true;
49 if (!intersectedSome
) {
50 intersections
.push([range
, [botName
]]);
57 /** Flatten out the list of tests and sort them by the binary/test name. */
58 function flattenAndSortTests(rangesByTest) {
59 var rangesByTestNames = Object.keys(rangesByTest);
61 for (var i = 0; i < rangesByTestNames.length; ++i) {
62 var name = rangesByTestNames[i];
63 flat.push([name, rangesByTest[name]]);
66 flat.sort(function(a, b) {
67 if (a[0] < b[0]) return -1;
68 if (a[0] > b[0]) return 1;
76 * Build the HTML table row for a test failure. |test| is [ name, testData ].
77 * |testData| contains ranges suitable for input to |findIntersections|.
79 function buildTestFailureTableRowHTML(test
) {
80 var row
= document
.createElement('tr');
81 var binaryCell
= row
.insertCell(-1);
82 var nameParts
= test
[0].split('-');
83 binaryCell
.innerHTML
= nameParts
[0];
84 binaryCell
.className
= 'category';
85 var flakinessLink
= document
.createElement('a');
87 'http://test-results.appspot.com/dashboards/' +
88 'flakiness_dashboard.html#testType=' +
89 nameParts
[0] + '&tests=' + nameParts
[1];
90 flakinessLink
.innerHTML
= nameParts
[1];
91 row
.appendChild(flakinessLink
);
93 var intersections
= findIntersections(test
[1]);
94 for (var j
= 0; j
< intersections
.length
; ++j
) {
95 var intersection
= intersections
[j
];
96 var range
= row
.insertCell(-1);
97 range
.className
= 'failure-range';
98 var low
= intersection
[0][0];
99 var high
= intersection
[0][1];
101 'http://build.chromium.org/f/chromium/perf/dashboard/ui/' +
102 'changelog.html?url=%2Ftrunk%2Fsrc&range=' +
103 low
+ '%3A' + high
+ '&mode=html';
104 range
.innerHTML
= '<a href="' + url
+ '">' + low
+ ' - ' + high
+ '</a>: ' +
105 truncateStatusText(intersection
[1].join(', '));
111 /** Updates the correlations contents. */
112 function updateCorrelationsHTML() {
113 // The logic here is to try to narrow blamelists by using information across
114 // bots. If a particular test has failed on multiple different
115 // configurations, there's a good chance that it has the same root cause, so
116 // calculate the intersection of blamelists of the first time it failed on
119 var allFailures
= [];
120 for (var i
= 0; i
< gWaterfallData
.length
; ++i
) {
121 var waterfallInfo
= gWaterfallData
[i
];
122 var botInfo
= waterfallInfo
.botInfo
;
123 var allBotNames
= Object
.keys(botInfo
);
124 for (var j
= 0; j
< allBotNames
.length
; ++j
) {
125 var botName
= allBotNames
[j
];
126 if (botInfo
[botName
].isSteadyGreen
)
128 var builds
= botInfo
[botName
].builds
;
129 var buildNames
= Object
.keys(builds
);
130 for (var k
= 0; k
< buildNames
.length
; ++k
) {
131 var build
= builds
[buildNames
[k
]];
132 if (build
.failures
) {
133 for (var l
= 0; l
< build
.failures
.length
; ++l
) {
134 allFailures
.push(build
.failures
[l
]);
141 // allFailures is now a list of lists, each containing:
142 // [ botname, binaryname, testname, [low_rev, high_rev] ].
144 var rangesByTest
= {};
145 for (var i
= 0; i
< allFailures
.length
; ++i
) {
146 var failure
= allFailures
[i
];
147 var botName
= failure
[0];
148 var binaryName
= failure
[1];
149 var testName
= failure
[2];
150 var range
= failure
[3];
151 if (binaryName
.indexOf('steps') != -1)
153 if (testName
.indexOf('preamble') != -1)
155 var key
= binaryName
+ '-' + testName
;
157 if (!rangesByTest
.hasOwnProperty(key
))
158 rangesByTest
[key
] = {};
159 // If there's no range, that's all we know.
160 if (!rangesByTest
[key
].hasOwnProperty([botName
])) {
161 rangesByTest
[key
][botName
] = range
;
163 // Otherwise, track only the lowest range for this bot (we only want
164 // when it turned red, not the whole range that it's been red for).
165 if (range
[0] < rangesByTest
[key
][botName
][0]) {
166 rangesByTest
[key
][botName
] = range
;
171 var table
= document
.getElementById('failure-info');
172 while (table
.rows
.length
> 0) {
176 var headerCell
= document
.createElement('td');
177 headerCell
.colSpan
= 15;
178 headerCell
.innerHTML
=
179 'test failures (ranges are blamelist intersections of first failure on ' +
180 'each bot of retrieved data. so, if a test has been failing for a ' +
181 'while, the range may be incorrect)';
182 headerCell
.className
= 'section-header';
183 var headerRow
= table
.insertRow(-1);
184 headerRow
.appendChild(headerCell
);
186 var flat
= flattenAndSortTests(rangesByTest
);
188 for (var i
= 0; i
< flat
.length
; ++i
) {
189 table
.appendChild(buildTestFailureTableRowHTML(flat
[i
]));