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 |