1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 var model = model || {}; 27 28 (function () { 29 30 var kCommitLogLength = 50; 31 32 model.state = {}; 33 model.state.failureAnalysisByTest = {}; 34 model.state.rebaselineQueue = []; 35 model.state.expectationsUpdateQueue = []; 36 37 function findAndMarkRevertedRevisions(commitDataList) 38 { 39 var revertedRevisions = {}; 40 $.each(commitDataList, function(index, commitData) { 41 if (commitData.revertedRevision) 42 revertedRevisions[commitData.revertedRevision] = true; 43 }); 44 $.each(commitDataList, function(index, commitData) { 45 if (commitData.revision in revertedRevisions) 46 commitData.wasReverted = true; 47 }); 48 } 49 50 function fuzzyFind(testName, commitData) 51 { 52 var indexOfLastDot = testName.lastIndexOf('.'); 53 var stem = indexOfLastDot == -1 ? testName : testName.substr(0, indexOfLastDot); 54 return commitData.message.indexOf(stem) != -1; 55 } 56 57 function heuristicallyNarrowRegressionRange(failureAnalysis) 58 { 59 var commitDataList = model.state.recentCommits; 60 var commitDataIndex = commitDataList.length - 1; 61 62 for(var revision = failureAnalysis.newestPassingRevision + 1; revision <= failureAnalysis.oldestFailingRevision; ++revision) { 63 while (commitDataIndex >= 0 && commitDataList[commitDataIndex].revision < revision) 64 --commitDataIndex; 65 var commitData = commitDataList[commitDataIndex]; 66 if (commitData.revision != revision) 67 continue; 68 if (fuzzyFind(failureAnalysis.testName, commitData)) { 69 failureAnalysis.oldestFailingRevision = revision; 70 failureAnalysis.newestPassingRevision = revision - 1; 71 return; 72 } 73 } 74 } 75 76 model.queueForRebaseline = function(failureInfo) 77 { 78 model.state.rebaselineQueue.push(failureInfo); 79 }; 80 81 model.takeRebaselineQueue = function() 82 { 83 var queue = model.state.rebaselineQueue; 84 model.state.rebaselineQueue = []; 85 return queue; 86 }; 87 88 model.queueForExpectationUpdate = function(failureInfo) 89 { 90 model.state.expectationsUpdateQueue.push(failureInfo); 91 }; 92 93 model.takeExpectationUpdateQueue = function() 94 { 95 var queue = model.state.expectationsUpdateQueue; 96 model.state.expectationsUpdateQueue = []; 97 return queue; 98 }; 99 100 var g_commitIndex = {}; 101 102 model.updateRecentCommits = function(callback) 103 { 104 trac.recentCommitData('trunk', kCommitLogLength, function(commitDataList) { 105 model.state.recentCommits = commitDataList; 106 updateCommitIndex(); 107 findAndMarkRevertedRevisions(model.state.recentCommits); 108 callback(); 109 }); 110 }; 111 112 function updateCommitIndex() 113 { 114 model.state.recentCommits.forEach(function(commitData) { 115 g_commitIndex[commitData.revision] = commitData; 116 }); 117 } 118 119 model.commitDataListForRevisionRange = function(fromRevision, toRevision) 120 { 121 var result = []; 122 for (var revision = fromRevision; revision <= toRevision; ++revision) { 123 var commitData = g_commitIndex[revision]; 124 if (commitData) 125 result.push(commitData); 126 } 127 return result; 128 }; 129 130 model.buildersInFlightForRevision = function(revision) 131 { 132 var builders = {}; 133 Object.keys(model.state.resultsByBuilder).forEach(function(builderName) { 134 var results = model.state.resultsByBuilder[builderName]; 135 if (parseInt(results.blink_revision) < revision) 136 builders[builderName] = { actual: 'BUILDING' }; 137 }); 138 return builders; 139 }; 140 141 model.latestRevision = function() 142 { 143 return model.state.recentCommits[0].revision; 144 } 145 146 model.latestRevisionWithNoBuildersInFlight = function() 147 { 148 var revision = 0; 149 Object.keys(model.state.resultsByBuilder).forEach(function(builderName) { 150 var results = model.state.resultsByBuilder[builderName]; 151 if (!results.blink_revision) 152 return; 153 var testedRevision = parseInt(results.blink_revision); 154 revision = revision ? Math.min(revision, testedRevision) : testedRevision; 155 }); 156 return revision; 157 } 158 159 model.latestRevisionByBuilder = function() 160 { 161 var revision = {}; 162 Object.keys(model.state.resultsByBuilder).forEach(function(builderName) { 163 revision[builderName] = model.state.resultsByBuilder[builderName].blink_revision; 164 }); 165 return revision; 166 } 167 168 model.updateResultsByBuilder = function(callback) 169 { 170 var platformBuilders = config.currentBuilders(); 171 results.fetchResultsByBuilder(Object.keys(platformBuilders), function(resultsByBuilder) { 172 model.state.resultsByBuilder = resultsByBuilder; 173 callback(); 174 }); 175 }; 176 177 model.analyzeUnexpectedFailures = function(callback, completionCallback) 178 { 179 var unexpectedFailures = results.unexpectedFailuresByTest(model.state.resultsByBuilder); 180 181 $.each(model.state.failureAnalysisByTest, function(testName, failureAnalysis) { 182 if (!(testName in unexpectedFailures)) 183 delete model.state.failureAnalysisByTest[testName]; 184 }); 185 186 var tracker = new base.RequestTracker(Object.keys(unexpectedFailures).length, completionCallback); 187 $.each(unexpectedFailures, function(testName, resultNodesByBuilder) { 188 var builderNameList = Object.keys(resultNodesByBuilder); 189 results.unifyRegressionRanges(builderNameList, testName, function(oldestFailingRevision, newestPassingRevision) { 190 var failureAnalysis = { 191 'testName': testName, 192 'resultNodesByBuilder': resultNodesByBuilder, 193 'oldestFailingRevision': oldestFailingRevision, 194 'newestPassingRevision': newestPassingRevision, 195 }; 196 197 heuristicallyNarrowRegressionRange(failureAnalysis); 198 199 var previousFailureAnalysis = model.state.failureAnalysisByTest[testName]; 200 if (previousFailureAnalysis 201 && previousFailureAnalysis.oldestFailingRevision <= failureAnalysis.oldestFailingRevision 202 && previousFailureAnalysis.newestPassingRevision >= failureAnalysis.newestPassingRevision) { 203 failureAnalysis.oldestFailingRevision = previousFailureAnalysis.oldestFailingRevision; 204 failureAnalysis.newestPassingRevision = previousFailureAnalysis.newestPassingRevision; 205 } 206 207 model.state.failureAnalysisByTest[testName] = failureAnalysis; 208 209 callback(failureAnalysis); 210 tracker.requestComplete(); 211 }); 212 }); 213 }; 214 215 model.unexpectedFailureInfoForTestName = function(testName) 216 { 217 var resultsByTest = results.unexpectedFailuresByTest(model.state.resultsByBuilder); 218 219 return Object.keys(resultsByTest[testName]).map(function(builderName) { 220 return results.failureInfoForTestAndBuilder(resultsByTest, testName, builderName); 221 }); 222 }; 223 224 model.analyzeexpectedFailures = function(callback) 225 { 226 var expectedFailures = results.expectedFailuresByTest(model.state.resultsByBuilder); 227 $.each(expectedFailures, function(testName, resultNodesByBuilder) { 228 var failureAnalysis = { 229 'testName': testName, 230 'resultNodesByBuilder': resultNodesByBuilder, 231 }; 232 233 // FIXME: Consider looking at the history to see how long this test 234 // has been failing. 235 236 callback(failureAnalysis); 237 }); 238 }; 239 240 })(); 241