OLD | NEW |
| (Empty) |
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 /** | |
6 * Return the range of intersection between the two lists. Ranges are sets of | |
7 * CLs, so it's inclusive on both ends. | |
8 */ | |
9 function rangeIntersection(a, b) { | |
10 if (a[0] > b[1]) | |
11 return null; | |
12 if (a[1] < b[0]) | |
13 return null; | |
14 // We know they intersect, result is the larger lower bound to the smaller | |
15 // upper bound. | |
16 return [Math.max(a[0], b[0]), Math.min(a[1], b[1])]; | |
17 } | |
18 | |
19 /** | |
20 * Finds the intersections between the blamelists. | |
21 * Input is a object mapping botname => [low, high]. | |
22 * | |
23 * Result: | |
24 * [ | |
25 * [ low0, high0 ], [ bot1, bot2, bot3 ], | |
26 * [ low1, high1 ], [ bot4, ... ], | |
27 * ..., | |
28 * ] | |
29 */ | |
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) { | |
37 botName = keys[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); | |
42 if (intersect) { | |
43 intersections[j][0] = intersect; | |
44 intersections[j][1].push(botName); | |
45 intersectedSome = true; | |
46 break; | |
47 } | |
48 } | |
49 if (!intersectedSome) { | |
50 intersections.push([range, [botName]]); | |
51 } | |
52 } | |
53 | |
54 return intersections; | |
55 } | |
56 | |
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); | |
60 var flat = []; | |
61 for (var i = 0; i < rangesByTestNames.length; ++i) { | |
62 var name = rangesByTestNames[i]; | |
63 flat.push([name, rangesByTest[name]]); | |
64 } | |
65 | |
66 flat.sort(function(a, b) { | |
67 if (a[0] < b[0]) return -1; | |
68 if (a[0] > b[0]) return 1; | |
69 return 0; | |
70 }); | |
71 | |
72 return flat; | |
73 } | |
74 | |
75 /** | |
76 * Build the HTML table row for a test failure. |test| is [ name, testData ]. | |
77 * |testData| contains ranges suitable for input to |findIntersections|. | |
78 */ | |
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'); | |
86 flakinessLink.href = | |
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); | |
92 | |
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]; | |
100 var url = | |
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(', ')); | |
106 } | |
107 return row; | |
108 } | |
109 | |
110 | |
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 | |
117 // each builder. | |
118 | |
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) | |
127 continue; | |
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]); | |
135 } | |
136 } | |
137 } | |
138 } | |
139 } | |
140 | |
141 // allFailures is now a list of lists, each containing: | |
142 // [ botname, binaryname, testname, [low_rev, high_rev] ]. | |
143 | |
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) | |
152 continue; | |
153 if (testName.indexOf('preamble') != -1) | |
154 continue; | |
155 var key = binaryName + '-' + testName; | |
156 | |
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; | |
162 } else { | |
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; | |
167 } | |
168 } | |
169 } | |
170 | |
171 var table = document.getElementById('failure-info'); | |
172 while (table.rows.length > 0) { | |
173 table.deleteRow(-1); | |
174 } | |
175 | |
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); | |
185 | |
186 var flat = flattenAndSortTests(rangesByTest); | |
187 | |
188 for (var i = 0; i < flat.length; ++i) { | |
189 table.appendChild(buildTestFailureTableRowHTML(flat[i])); | |
190 } | |
191 } | |
OLD | NEW |