| OLD | NEW |
| (Empty) |
| 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, indexOfLastD
ot); | |
| 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 <= fa
ilureAnalysis.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.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.revision) | |
| 152 return; | |
| 153 var testedRevision = parseInt(results.revision); | |
| 154 revision = revision ? Math.min(revision, testedRevision) : testedRevisio
n; | |
| 155 }); | |
| 156 return revision; | |
| 157 } | |
| 158 | |
| 159 model.updateResultsByBuilder = function(callback) | |
| 160 { | |
| 161 var platformBuilders = config.currentBuilders(); | |
| 162 results.fetchResultsByBuilder(Object.keys(platformBuilders), function(result
sByBuilder) { | |
| 163 model.state.resultsByBuilder = resultsByBuilder; | |
| 164 callback(); | |
| 165 }); | |
| 166 }; | |
| 167 | |
| 168 model.analyzeUnexpectedFailures = function(callback, completionCallback) | |
| 169 { | |
| 170 var unexpectedFailures = results.unexpectedFailuresByTest(model.state.result
sByBuilder); | |
| 171 | |
| 172 $.each(model.state.failureAnalysisByTest, function(testName, failureAnalysis
) { | |
| 173 if (!(testName in unexpectedFailures)) | |
| 174 delete model.state.failureAnalysisByTest[testName]; | |
| 175 }); | |
| 176 | |
| 177 var tracker = new base.RequestTracker(Object.keys(unexpectedFailures).length
, completionCallback); | |
| 178 $.each(unexpectedFailures, function(testName, resultNodesByBuilder) { | |
| 179 var builderNameList = Object.keys(resultNodesByBuilder); | |
| 180 results.unifyRegressionRanges(builderNameList, testName, function(oldest
FailingRevision, newestPassingRevision) { | |
| 181 var failureAnalysis = { | |
| 182 'testName': testName, | |
| 183 'resultNodesByBuilder': resultNodesByBuilder, | |
| 184 'oldestFailingRevision': oldestFailingRevision, | |
| 185 'newestPassingRevision': newestPassingRevision, | |
| 186 }; | |
| 187 | |
| 188 heuristicallyNarrowRegressionRange(failureAnalysis); | |
| 189 | |
| 190 var previousFailureAnalysis = model.state.failureAnalysisByTest[test
Name]; | |
| 191 if (previousFailureAnalysis | |
| 192 && previousFailureAnalysis.oldestFailingRevision <= failureAnaly
sis.oldestFailingRevision | |
| 193 && previousFailureAnalysis.newestPassingRevision >= failureAnaly
sis.newestPassingRevision) { | |
| 194 failureAnalysis.oldestFailingRevision = previousFailureAnalysis.
oldestFailingRevision; | |
| 195 failureAnalysis.newestPassingRevision = previousFailureAnalysis.
newestPassingRevision; | |
| 196 } | |
| 197 | |
| 198 model.state.failureAnalysisByTest[testName] = failureAnalysis; | |
| 199 | |
| 200 callback(failureAnalysis); | |
| 201 tracker.requestComplete(); | |
| 202 }); | |
| 203 }); | |
| 204 }; | |
| 205 | |
| 206 model.unexpectedFailureInfoForTestName = function(testName) | |
| 207 { | |
| 208 var resultsByTest = results.unexpectedFailuresByTest(model.state.resultsByBu
ilder); | |
| 209 | |
| 210 return Object.keys(resultsByTest[testName]).map(function(builderName) { | |
| 211 return results.failureInfoForTestAndBuilder(resultsByTest, testName, bui
lderName); | |
| 212 }); | |
| 213 }; | |
| 214 | |
| 215 model.analyzeUnexpectedSuccesses = function(callback) | |
| 216 { | |
| 217 var unexpectedSuccesses = results.unexpectedSuccessesByTest(model.state.resu
ltsByBuilder); | |
| 218 $.each(unexpectedSuccesses, function(testName, resultNodesByBuilder) { | |
| 219 var successAnalysis = { | |
| 220 'testName': testName, | |
| 221 'resultNodesByBuilder': resultNodesByBuilder, | |
| 222 }; | |
| 223 | |
| 224 // FIXME: Consider looking at the history to see how long this test | |
| 225 // has been unexpectedly passing. | |
| 226 | |
| 227 callback(successAnalysis); | |
| 228 }); | |
| 229 }; | |
| 230 | |
| 231 model.analyzeexpectedFailures = function(callback) | |
| 232 { | |
| 233 var expectedFailures = results.expectedFailuresByTest(model.state.resultsByB
uilder); | |
| 234 $.each(expectedFailures, function(testName, resultNodesByBuilder) { | |
| 235 var failureAnalysis = { | |
| 236 'testName': testName, | |
| 237 'resultNodesByBuilder': resultNodesByBuilder, | |
| 238 }; | |
| 239 | |
| 240 // FIXME: Consider looking at the history to see how long this test | |
| 241 // has been failing. | |
| 242 | |
| 243 callback(failureAnalysis); | |
| 244 }); | |
| 245 }; | |
| 246 | |
| 247 })(); | |
| OLD | NEW |