2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 var model = model || {};
30 var kCommitLogLength = 50;
33 model.state.failureAnalysisByTest = {};
34 model.state.rebaselineQueue = [];
35 model.state.expectationsUpdateQueue = [];
37 function findAndMarkRevertedRevisions(commitDataList)
39 var revertedRevisions = {};
40 $.each(commitDataList, function(index, commitData) {
41 if (commitData.revertedRevision)
42 revertedRevisions[commitData.revertedRevision] = true;
44 $.each(commitDataList, function(index, commitData) {
45 if (commitData.revision in revertedRevisions)
46 commitData.wasReverted = true;
50 function fuzzyFind(testName, commitData)
52 var indexOfLastDot = testName.lastIndexOf('.');
53 var stem = indexOfLastDot == -1 ? testName : testName.substr(0, indexOfLastDot);
54 return commitData.message.indexOf(stem) != -1;
57 function heuristicallyNarrowRegressionRange(failureAnalysis)
59 var commitDataList = model.state.recentCommits;
60 var commitDataIndex = commitDataList.length - 1;
62 for(var revision = failureAnalysis.newestPassingRevision + 1; revision <= failureAnalysis.oldestFailingRevision; ++revision) {
63 while (commitDataIndex >= 0 && commitDataList[commitDataIndex].revision < revision)
65 var commitData = commitDataList[commitDataIndex];
66 if (commitData.revision != revision)
68 if (fuzzyFind(failureAnalysis.testName, commitData)) {
69 failureAnalysis.oldestFailingRevision = revision;
70 failureAnalysis.newestPassingRevision = revision - 1;
76 model.queueForRebaseline = function(failureInfo)
78 model.state.rebaselineQueue.push(failureInfo);
81 model.takeRebaselineQueue = function()
83 var queue = model.state.rebaselineQueue;
84 model.state.rebaselineQueue = [];
88 model.queueForExpectationUpdate = function(failureInfo)
90 model.state.expectationsUpdateQueue.push(failureInfo);
93 model.takeExpectationUpdateQueue = function()
95 var queue = model.state.expectationsUpdateQueue;
96 model.state.expectationsUpdateQueue = [];
100 var g_commitIndex = {};
102 model.updateRecentCommits = function(callback)
104 trac.recentCommitData('trunk', kCommitLogLength, function(commitDataList) {
105 model.state.recentCommits = commitDataList;
107 findAndMarkRevertedRevisions(model.state.recentCommits);
112 function updateCommitIndex() {
113 model.state.recentCommits.forEach(function(commitData) {
114 g_commitIndex[commitData.revision] = commitData;
118 model.commitDataListForRevisionRange = function(fromRevision, toRevision)
121 for (var revision = fromRevision; revision <= toRevision; ++revision) {
122 var commitData = g_commitIndex[revision];
124 result.push(commitData);
129 model.buildersInFlightForRevision = function(revision)
132 Object.keys(model.state.resultsByBuilder).forEach(function(builderName) {
133 var results = model.state.resultsByBuilder[builderName];
134 if (parseInt(results.revision) < revision)
135 builders[builderName] = { actual: 'BUILDING' };
140 model.updateResultsByBuilder = function(callback)
142 results.fetchResultsByBuilder(Object.keys(config.kBuilders), function(resultsByBuilder) {
143 model.state.resultsByBuilder = resultsByBuilder;
148 model.analyzeUnexpectedFailures = function(callback, completionCallback)
150 var unexpectedFailures = results.unexpectedFailuresByTest(model.state.resultsByBuilder);
152 $.each(model.state.failureAnalysisByTest, function(testName, failureAnalysis) {
153 if (!(testName in unexpectedFailures))
154 delete model.state.failureAnalysisByTest[testName];
157 var tracker = new base.RequestTracker(Object.keys(unexpectedFailures).length, completionCallback);
158 $.each(unexpectedFailures, function(testName, resultNodesByBuilder) {
159 var builderNameList = Object.keys(resultNodesByBuilder);
160 results.unifyRegressionRanges(builderNameList, testName, function(oldestFailingRevision, newestPassingRevision) {
161 var failureAnalysis = {
162 'testName': testName,
163 'resultNodesByBuilder': resultNodesByBuilder,
164 'oldestFailingRevision': oldestFailingRevision,
165 'newestPassingRevision': newestPassingRevision,
168 heuristicallyNarrowRegressionRange(failureAnalysis);
170 var previousFailureAnalysis = model.state.failureAnalysisByTest[testName];
171 if (previousFailureAnalysis
172 && previousFailureAnalysis.oldestFailingRevision <= failureAnalysis.oldestFailingRevision
173 && previousFailureAnalysis.newestPassingRevision >= failureAnalysis.newestPassingRevision) {
174 failureAnalysis.oldestFailingRevision = previousFailureAnalysis.oldestFailingRevision;
175 failureAnalysis.newestPassingRevision = previousFailureAnalysis.newestPassingRevision;
178 model.state.failureAnalysisByTest[testName] = failureAnalysis;
179 callback(failureAnalysis);
180 tracker.requestComplete();
185 model.unexpectedFailureInfoForTestName = function(testName)
187 var resultsByTest = results.unexpectedFailuresByTest(model.state.resultsByBuilder);
189 return Object.keys(resultsByTest[testName]).map(function(builderName) {
190 return results.failureInfoForTestAndBuilder(resultsByTest, testName, builderName);
194 model.analyzeUnexpectedSuccesses = function(callback)
196 var unexpectedSuccesses = results.unexpectedSuccessesByTest(model.state.resultsByBuilder);
197 $.each(unexpectedSuccesses, function(testName, resultNodesByBuilder) {
198 var successAnalysis = {
199 'testName': testName,
200 'resultNodesByBuilder': resultNodesByBuilder,
203 // FIXME: Consider looking at the history to see how long this test
204 // has been unexpectedly passing.
206 callback(successAnalysis);
210 model.analyzeExpectedOrUnexpectedFailures = function(callback)
212 var expectedFailures = results.expectedOrUnexpectedFailuresByTest(model.state.resultsByBuilder);
213 $.each(expectedFailures, function(testName, resultNodesByBuilder) {
214 var failureAnalysis = {
215 'testName': testName,
216 'resultNodesByBuilder': resultNodesByBuilder,
219 // FIXME: Consider looking at the history to see how long this test
222 callback(failureAnalysis);