| 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 |