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