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()
114 model.state.recentCommits.forEach(function(commitData) {
115 g_commitIndex[commitData.revision] = commitData;
119 model.commitDataListForRevisionRange = function(fromRevision, toRevision)
122 for (var revision = fromRevision; revision <= toRevision; ++revision) {
123 var commitData = g_commitIndex[revision];
125 result.push(commitData);
130 model.buildersInFlightForRevision = function(revision)
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' };
141 model.latestRevision = function()
143 return model.state.recentCommits[0].revision;
146 model.latestRevisionWithNoBuildersInFlight = function()
149 Object.keys(model.state.resultsByBuilder).forEach(function(builderName) {
150 var results = model.state.resultsByBuilder[builderName];
151 if (!results.blink_revision)
153 var testedRevision = parseInt(results.blink_revision);
154 revision = revision ? Math.min(revision, testedRevision) : testedRevision;
159 model.latestRevisionByBuilder = function()
162 Object.keys(model.state.resultsByBuilder).forEach(function(builderName) {
163 revision[builderName] = model.state.resultsByBuilder[builderName].blink_revision;
168 model.updateResultsByBuilder = function(callback)
170 results.fetchResultsByBuilder(Object.keys(config.builders), function(resultsByBuilder) {
171 model.state.resultsByBuilder = resultsByBuilder;
176 model.analyzeUnexpectedFailures = function(callback, completionCallback)
178 var unexpectedFailures = results.unexpectedFailuresByTest(model.state.resultsByBuilder);
180 $.each(model.state.failureAnalysisByTest, function(testName, failureAnalysis) {
181 if (!(testName in unexpectedFailures))
182 delete model.state.failureAnalysisByTest[testName];
185 var tracker = new base.RequestTracker(Object.keys(unexpectedFailures).length, completionCallback);
186 $.each(unexpectedFailures, function(testName, resultNodesByBuilder) {
187 var builderNameList = Object.keys(resultNodesByBuilder);
188 results.unifyRegressionRanges(builderNameList, testName, function(oldestFailingRevision, newestPassingRevision) {
189 var failureAnalysis = {
190 'testName': testName,
191 'resultNodesByBuilder': resultNodesByBuilder,
192 'oldestFailingRevision': oldestFailingRevision,
193 'newestPassingRevision': newestPassingRevision,
196 heuristicallyNarrowRegressionRange(failureAnalysis);
198 var previousFailureAnalysis = model.state.failureAnalysisByTest[testName];
199 if (previousFailureAnalysis
200 && previousFailureAnalysis.oldestFailingRevision <= failureAnalysis.oldestFailingRevision
201 && previousFailureAnalysis.newestPassingRevision >= failureAnalysis.newestPassingRevision) {
202 failureAnalysis.oldestFailingRevision = previousFailureAnalysis.oldestFailingRevision;
203 failureAnalysis.newestPassingRevision = previousFailureAnalysis.newestPassingRevision;
206 model.state.failureAnalysisByTest[testName] = failureAnalysis;
208 callback(failureAnalysis);
209 tracker.requestComplete();
214 model.unexpectedFailureInfoForTestName = function(testName)
216 var resultsByTest = results.unexpectedFailuresByTest(model.state.resultsByBuilder);
218 return Object.keys(resultsByTest[testName]).map(function(builderName) {
219 return results.failureInfoForTestAndBuilder(resultsByTest, testName, builderName);
223 model.analyzeexpectedFailures = function(callback)
225 var expectedFailures = results.expectedFailuresByTest(model.state.resultsByBuilder);
226 $.each(expectedFailures, function(testName, resultNodesByBuilder) {
227 var failureAnalysis = {
228 'testName': testName,
229 'resultNodesByBuilder': resultNodesByBuilder,
232 // FIXME: Consider looking at the history to see how long this test
235 callback(failureAnalysis);