OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2011 Apple 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 function Builder(name, buildbot) { | |
27 this.name = name; | |
28 this.buildbot = buildbot; | |
29 this._cache = {}; | |
30 } | |
31 | |
32 Builder.prototype = { | |
33 buildURL: function(buildName) { | |
34 return this.buildbot.buildURL(this.name, buildName); | |
35 }, | |
36 | |
37 failureDiagnosisTextAndURL: function(buildName, testName, testResult) { | |
38 var urlStem = this.resultsDirectoryURL(buildName) + testName.replace(/\.
[^.]+$/, ''); | |
39 var diagnosticInfo = { | |
40 fail: { | |
41 text: 'pretty diff', | |
42 url: urlStem + '-pretty-diff.html', | |
43 }, | |
44 flaky: { | |
45 text: 'pretty diff (flaky)', | |
46 url: urlStem + '-pretty-diff.html', | |
47 }, | |
48 timeout: { | |
49 text: 'timed out', | |
50 }, | |
51 crash: { | |
52 text: 'crash log', | |
53 url: urlStem + '-crash-log.txt', | |
54 }, | |
55 'webprocess crash': { | |
56 text: 'web process crash log', | |
57 url: urlStem + '-crash-log.txt', | |
58 }, | |
59 }; | |
60 | |
61 return diagnosticInfo[testResult.failureType]; | |
62 }, | |
63 | |
64 getBuildNames: function(callback) { | |
65 this._getBuildNamesFromResultsDirectory(this.buildbot.baseURL + 'results
/' + this.name, callback); | |
66 }, | |
67 | |
68 getMostRecentCompletedBuildNumber: function(callback) { | |
69 var cacheKey = 'getMostRecentCompletedBuildNumber'; | |
70 if (cacheKey in this._cache) { | |
71 callback(this._cache[cacheKey]); | |
72 return; | |
73 } | |
74 | |
75 var self = this; | |
76 getResource(self.buildbot.baseURL + 'json/builders/' + self.name, functi
on(xhr) { | |
77 var data = JSON.parse(xhr.responseText); | |
78 | |
79 var currentBuilds = {}; | |
80 if ('currentBuilds' in data) | |
81 data.currentBuilds.forEach(function(buildNumber) { currentBuilds
[buildNumber] = true }); | |
82 | |
83 for (var i = data.cachedBuilds.length - 1; i >= 0; --i) { | |
84 if (data.cachedBuilds[i] in currentBuilds) | |
85 continue; | |
86 | |
87 self._cache[cacheKey] = data.cachedBuilds[i]; | |
88 callback(data.cachedBuilds[i]); | |
89 return; | |
90 } | |
91 | |
92 self._cache[cacheKey] = -1; | |
93 callback(self._cache[cacheKey]); | |
94 }, | |
95 function(xhr) { | |
96 self._cache[cacheKey] = -1; | |
97 callback(self._cache[cacheKey]); | |
98 }); | |
99 }, | |
100 | |
101 getNumberOfFailingTests: function(buildNumber, callback) { | |
102 var cacheKey = this.name + '_getNumberOfFailingTests_' + buildNumber; | |
103 const currentCachedDataVersion = 3; | |
104 if (PersistentCache.contains(cacheKey)) { | |
105 var cachedData = PersistentCache.get(cacheKey); | |
106 if (cachedData.version === currentCachedDataVersion) { | |
107 callback(cachedData.failureCount, cachedData.tooManyFailures); | |
108 return; | |
109 } | |
110 } | |
111 | |
112 var result = { failureCount: -1, tooManyFailures: false, version: curren
tCachedDataVersion }; | |
113 | |
114 var self = this; | |
115 self._getBuildJSON(buildNumber, function(data) { | |
116 var layoutTestStep = data.steps.findFirst(function(step) { return st
ep.name === 'layout-test'; }); | |
117 if (!layoutTestStep) { | |
118 PersistentCache.set(cacheKey, result); | |
119 callback(result.failureCount, result.tooManyFailures); | |
120 return; | |
121 } | |
122 | |
123 if (!('isFinished' in layoutTestStep)) { | |
124 // run-webkit-tests never even ran, or didn't finish running. | |
125 PersistentCache.set(cacheKey, result); | |
126 callback(result.failureCount, result.tooManyFailures); | |
127 return; | |
128 } | |
129 | |
130 if (!('results' in layoutTestStep) || layoutTestStep.results[0] ===
0) { | |
131 if (!('times' in layoutTestStep) || layoutTestStep.times.length
< 2 || layoutTestStep.times[1] - layoutTestStep.times[0] < self._minimumSuccessf
ulLayoutTestStepRunTime) { | |
132 // Either something caused the start/stop times not to be re
corded, or | |
133 // run-webkit-tests ran so quickly that we can't believe the
re wasn't an error | |
134 // (e.g., a bug in the script that made it not find any test
s to run). | |
135 PersistentCache.set(cacheKey, result); | |
136 callback(result.failureCount, result.tooManyFailures); | |
137 return; | |
138 } | |
139 | |
140 // All tests passed. | |
141 result.failureCount = 0; | |
142 PersistentCache.set(cacheKey, result); | |
143 callback(result.failureCount, result.tooManyFailures); | |
144 return; | |
145 } | |
146 | |
147 if (layoutTestStep.results[0] === 4) { | |
148 // This build step was interrupted (perhaps due to the build sla
ve restarting). | |
149 PersistentCache.set(cacheKey, result); | |
150 callback(result.failureCount, result.tooManyFailures); | |
151 return; | |
152 } | |
153 | |
154 if (/Exiting early/.test(layoutTestStep.results[1][0])) | |
155 result.tooManyFailures = true; | |
156 | |
157 result.failureCount = layoutTestStep.results[1].reduce(function(sum,
outputLine) { | |
158 var match = /^(\d+)\s/.exec(outputLine); | |
159 if (!match) | |
160 return sum; | |
161 // Don't count leaks, new tests, or passes as failures. | |
162 if (outputLine.contains('leak') || outputLine.contains('were new
') || outputLine.contains('new passes')) | |
163 return sum; | |
164 return sum + parseInt(match[1], 10); | |
165 }, 0); | |
166 | |
167 if (!result.failureCount) { | |
168 // run-webkit-tests exited with a non-zero exit status, but we | |
169 // didn't find any output about the number of failed tests. | |
170 // Something must have gone wrong (e.g., run-webkit-tests timed | |
171 // out and was killed by buildbot). | |
172 result.failureCount = -1; | |
173 } | |
174 | |
175 PersistentCache.set(cacheKey, result); | |
176 callback(result.failureCount, result.tooManyFailures); | |
177 }); | |
178 }, | |
179 | |
180 getOldBuildNames: function(callback) { | |
181 this._getBuildNamesFromResultsDirectory(this.buildbot.baseURL + 'old-res
ults/' + this.name, callback); | |
182 }, | |
183 | |
184 resultsDirectoryURL: function(buildName) { | |
185 return this.buildbot.resultsDirectoryURL(this.name, buildName); | |
186 }, | |
187 | |
188 resultsPageURL: function(buildName) { | |
189 return this.resultsDirectoryURL(buildName) + 'results.html'; | |
190 }, | |
191 | |
192 _getBuildJSON: function(buildNumber, callback) { | |
193 var cacheKey = 'getBuildJSON_' + buildNumber; | |
194 if (cacheKey in this._cache) { | |
195 callback(this._cache[cacheKey]); | |
196 return; | |
197 } | |
198 | |
199 var self = this; | |
200 getResource(self.buildbot.baseURL + 'json/builders/' + self.name + '/bui
lds/' + buildNumber, function(xhr) { | |
201 var data = JSON.parse(xhr.responseText); | |
202 self._cache[cacheKey] = data; | |
203 callback(data); | |
204 }); | |
205 }, | |
206 | |
207 _getBuildNamesFromResultsDirectory: function(directoryURL, callback) { | |
208 var cacheKey = '_getBuildNamesFromResultsDirectory.' + directoryURL; | |
209 if (cacheKey in this._cache) { | |
210 callback(this._cache[cacheKey]); | |
211 return; | |
212 } | |
213 | |
214 var self = this; | |
215 | |
216 function buildNamesFromDirectoryXHR(xhr) { | |
217 var root = document.createElement('html'); | |
218 root.innerHTML = xhr.responseText; | |
219 | |
220 var buildNames = Array.prototype.map.call(root.querySelectorAll('td:
first-child > a > b'), function(elem) { | |
221 return elem.innerText.replace(/\/$/, ''); | |
222 }).filter(function(filename) { | |
223 return self.buildbot.parseBuildName(filename); | |
224 }); | |
225 buildNames.reverse(); | |
226 | |
227 return buildNames; | |
228 } | |
229 | |
230 getResource(directoryURL, function(xhr) { | |
231 var buildNames = buildNamesFromDirectoryXHR(xhr); | |
232 self._cache[cacheKey] = buildNames; | |
233 callback(buildNames); | |
234 }); | |
235 }, | |
236 | |
237 // Any successful runs of run-webkit-tests that took less than this number o
f seconds are | |
238 // assumed to be errors. | |
239 _minimumSuccessfulLayoutTestStepRunTime: 20, | |
240 }; | |
OLD | NEW |