Remove some unneeded debugger code.
[WebKit-https.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / dashboard / Scripts / BuildbotIteration.js
1 /*
2  * Copyright (C) 2013, 2014 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 BuildbotIteration = function(queue, id, finished)
27 {
28     BaseObject.call(this);
29
30     console.assert(queue);
31
32     this.queue = queue;
33     this.id = id;
34
35     this.loaded = false;
36
37     this.openSourceRevision = null;
38     this.internalRevision = null;
39
40     this.layoutTestResults = null;
41     this.javascriptTestResults = null;
42     this.apiTestResults = null;
43     this.platformAPITestResults = null;
44     this.pythonTestResults = null;
45     this.perlTestResults = null;
46     this.bindingTestResults = null;
47
48     this._finished = finished;
49 };
50
51 BaseObject.addConstructorFunctions(BuildbotIteration);
52
53 // JSON result values for both individual steps and the whole iteration.
54 BuildbotIteration.SUCCESS = 0;
55 BuildbotIteration.WARNINGS = 1;
56 BuildbotIteration.FAILURE = 2;
57 BuildbotIteration.SKIPPED = 3;
58 BuildbotIteration.EXCEPTION = 4;
59 BuildbotIteration.RETRY = 5;
60
61 BuildbotIteration.Event = {
62     Updated: "updated"
63 };
64
65 BuildbotIteration.prototype = {
66     constructor: BuildbotIteration,
67     __proto__: BaseObject.prototype,
68
69     get finished()
70     {
71         return this._finished;
72     },
73
74     set finished(x)
75     {
76         this._finished = x;
77     },
78
79     get successful()
80     {
81         return this._result === BuildbotIteration.SUCCESS;
82     },
83
84     get productive()
85     {
86         return this.loaded && this._finished && this._result !== BuildbotIteration.EXCEPTION && this._result !== BuildbotIteration.RETRY;
87     },
88
89     // It is not a real failure if Buildbot itself failed with codes like EXCEPTION or RETRY.
90     get failed()
91     {
92         return this._result === BuildbotIteration.FAILURE;
93     },
94
95     get firstFailedStepName()
96     {
97         if (!this._firstFailedStep)
98             return undefined;
99         return this._firstFailedStep.name;
100     },
101
102     failureLogURL: function(kind)
103     {
104         if (!this.failed)
105             return undefined;
106
107         console.assert(this._firstFailedStep);
108
109         for (var i = 0; i < this._firstFailedStep.logs.length; ++i) {
110             if (this._firstFailedStep.logs[i][0] == kind)
111                 return this._firstFailedStep.logs[i][1];
112         }
113
114         return undefined;
115     },
116
117     get failureLogs()
118     {
119         if (!this.failed)
120             return undefined;
121
122         console.assert(this._firstFailedStep);
123         return this._firstFailedStep.logs;
124     },
125
126     get previousProductiveIteration()
127     {
128         for (var i = 0; i < this.queue.iterations.length - 1; ++i) {
129             if (this.queue.iterations[i] === this) {
130                 while (++i < this.queue.iterations.length) {
131                     var iteration = this.queue.iterations[i];
132                     if (iteration.productive)
133                         return iteration;
134                 }
135                 break;
136             }
137         }
138         return null;
139     },
140
141     update: function()
142     {
143         if (this.loaded && this._finished)
144             return;
145
146         function collectTestResults(data, stepName)
147         {
148             var testStep = data.steps.findFirst(function(step) { return step.name === stepName; });
149             if (!testStep)
150                 return null;
151
152             var testResults = {};
153
154             if (!testStep.isFinished) {
155                 // The step never even ran, or hasn't finish running.
156                 testResults.finished = false;
157                 return testResults;
158             }
159
160             testResults.finished = true;
161
162             if (!testStep.results || !testStep.results[0]) {
163                 // All tests passed.
164                 testResults.allPassed = true;
165                 return testResults;
166             }
167
168             if (/Exiting early/.test(testStep.results[1][0]))
169                 testResults.tooManyFailures = true;
170
171             function resultSummarizer(matchString, sum, outputLine)
172             {
173                 var match = /^(\d+)\s/.exec(outputLine);
174                 if (!match)
175                     return sum;
176                 if (!outputLine.contains(matchString))
177                     return sum;
178                 if (!sum || sum === -1)
179                     sum = 0;
180                 return sum + parseInt(match[1], 10);
181             }
182
183             testResults.failureCount = testStep.results[1].reduce(resultSummarizer.bind(null, "fail"), undefined);
184             testResults.flakeyCount = testStep.results[1].reduce(resultSummarizer.bind(null, "flake"), undefined);
185             testResults.totalLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, "total leak"), undefined);
186             testResults.uniqueLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, "unique leak"), undefined);
187             testResults.newPassesCount = testStep.results[1].reduce(resultSummarizer.bind(null, "new pass"), undefined);
188             testResults.missingCount = testStep.results[1].reduce(resultSummarizer.bind(null, "missing"), undefined);
189
190             if (!testResults.failureCount && !testResults.flakyCount && !testResults.totalLeakCount && !testResults.uniqueLeakCount && !testResults.newPassesCount && !testResults.missingCount) {
191                 // This step exited with a non-zero exit status, but we didn't find any output about the number of failed tests.
192                 // Something must have gone wrong (e.g., timed out and was killed by buildbot).
193                 testResults.errorOccurred = true;
194             }
195
196             return testResults;
197         }
198
199         JSON.load(this.queue.baseURL + "/builds/" + this.id, function(data) {
200             if (!data || !data.properties)
201                 return;
202
203             var openSourceRevisionProperty = data.properties.findFirst(function(property) { return property[0] === "got_revision" || property[0] === "revision" || property[0] === "opensource_got_revision"; });
204             this.openSourceRevision = openSourceRevisionProperty ? parseInt(openSourceRevisionProperty[1], 10) : null;
205
206             var internalRevisionProperty = data.properties.findFirst(function(property) { return property[0] === "internal_got_revision"; });
207             this.internalRevision = internalRevisionProperty ? parseInt(internalRevisionProperty[1], 10) : null;
208
209             var layoutTestResults = collectTestResults.call(this, data, "layout-test");
210             this.layoutTestResults = layoutTestResults ? new BuildbotTestResults(this, layoutTestResults) : null;
211
212             var javascriptTestResults = collectTestResults.call(this, data, "jscore-test");
213             this.javascriptTestResults = javascriptTestResults ? new BuildbotTestResults(this, javascriptTestResults) : null;
214
215             var apiTestResults = collectTestResults.call(this, data, "run-api-tests");
216             this.apiTestResults = apiTestResults ? new BuildbotTestResults(this, apiTestResults) : null;
217
218             var platformAPITestResults = collectTestResults.call(this, data, "API tests");
219             this.platformAPITestResults = platformAPITestResults ? new BuildbotTestResults(this, platformAPITestResults) : null;
220
221             var pythonTestResults = collectTestResults.call(this, data, "webkitpy-test");
222             this.pythonTestResults = pythonTestResults ? new BuildbotTestResults(this, pythonTestResults) : null;
223
224             var perlTestResults = collectTestResults.call(this, data, "webkitperl-test");
225             this.perlTestResults = perlTestResults ? new BuildbotTestResults(this, perlTestResults) : null;
226
227             var bindingTestResults = collectTestResults.call(this, data, "bindings-generation-tests");
228             this.bindingTestResults = bindingTestResults ? new BuildbotTestResults(this, bindingTestResults) : null;
229
230             this.loaded = true;
231
232             this._firstFailedStep = data.steps.findFirst(function(step) { return step.results[0] === BuildbotIteration.FAILURE; });
233
234             console.assert(data.results === null || typeof data.results === "number");
235             this._result = data.results;
236
237             this.text = data.text.join(" ");
238
239             if (!data.currentStep)
240                 this.finished = true;
241
242             // Update the sorting since it is based on the revisions we just loaded.
243             this.queue.sortIterations();
244
245             this.dispatchEventToListeners(BuildbotIteration.Event.Updated);
246         }.bind(this));
247     },
248
249     loadLayoutTestResults: function(callback)
250     {
251         function collectResults(subtree, predicate)
252         {
253             // Results object is a trie:
254             // directory
255             //   subdirectory
256             //     test1.html
257             //       expected:"PASS"
258             //       actual: "IMAGE"
259             //       report: "REGRESSION"
260             //     test2.html
261             //       expected:"FAIL"
262             //       actual:"TEXT"
263
264             var result = [];
265             for (var key in subtree) {
266                 var value = subtree[key];
267                 console.assert(typeof value === "object");
268                 var isIndividualTest = value.hasOwnProperty("actual") && value.hasOwnProperty("expected");
269                 if (isIndividualTest) {
270                     // Possible values for actual and expected keys: PASS, FAIL, AUDIO, IMAGE, TEXT, IMAGE+TEXT, TIMEOUT, CRASH, MISSING.
271                     // Both actual and expected can be space separated lists. Actual contains two values when retrying a failed test
272                     // gives a different result (retrying may be disabled in tester configuration).
273                     // Possible values for report key (when present): REGRESSION, MISSING, FLAKY.
274
275                     if (predicate(value)) {
276                         var item = {path: key};
277
278                         // FIXME (bug 127186): Crash log URL will be incorrect if crash only happened on retry (e.g. "TEXT CRASH").
279                         // It should point to retries subdirectory, but the information about which attempt failed gets lost here.
280                         if (value.actual.contains("CRASH"))
281                             item.crash = true;
282                         if (value.actual.contains("TIMEOUT"))
283                             item.timeout = true;
284
285                         // FIXME (bug 127186): Similarly, we don't have a good way to present results for something like "TIMEOUT TEXT",
286                         // not even UI wise. For now, only show a diff link if the first attempt has the diff.
287                         if (value.actual.split(" ")[0].contains("TEXT"))
288                             item.has_diff = true;
289
290                         // FIXME (bug 127186): It is particularly unfortunate for image diffs, because we currently only check image results
291                         // on retry (except for reftests), so many times, you will see images on buidbot page, but not on the dashboard.
292                         // FIXME: Find a way to display expected mismatch reftest failures. 
293                         if (value.actual.split(" ")[0].contains("IMAGE") && value.reftest_type != "!=")
294                             item.has_image_diff = true;
295
296                         if (value.has_stderr)
297                             item.has_stderr = true;
298
299                         result.push(item);
300                     }
301
302                 } else {
303                     var nestedTests = collectResults(value, predicate);
304                     for (var i = 0, end = nestedTests.length; i < end; ++i)
305                         nestedTests[i].path = key + "/" + nestedTests[i].path;
306                     result = result.concat(nestedTests);
307                 }
308             }
309
310             return result;
311         }
312
313         JSON.load(this.queue.buildbot.layoutTestFullResultsURLForIteration(this), function(data) {
314             if (data.error) {
315                 console.log(data.error);
316                 callback();
317                 return;
318             }
319
320             this.hasPrettyPatch = data.has_pretty_patch;
321
322             this.layoutTestResults.regressions = collectResults(data.tests, function(info) { return info["report"] === "REGRESSION" });
323             console.assert(data.num_regressions === this.layoutTestResults.regressions.length);
324
325             this.layoutTestResults.flakyTests = collectResults(data.tests, function(info) { return info["report"] === "FLAKY" });
326             console.assert(data.num_flaky === this.layoutTestResults.flakyTests.length);
327
328             this.layoutTestResults.testsWithMissingResults = collectResults(data.tests, function(info) { return info["report"] === "MISSING" });
329             console.assert(data.num_missing === this.layoutTestResults.testsWithMissingResults.length);
330
331             callback();
332         }.bind(this), "ADD_RESULTS");
333     }
334 };