6eb6e9481a7d02de95537832ff2f985f40b909cc
[WebKit-https.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / dashboard / Scripts / BuildbotTesterQueueView.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 BuildbotTesterQueueView = function(debugQueues, releaseQueues)
27 {
28     BuildbotQueueView.call(this, debugQueues, releaseQueues);
29
30     this.update();
31 };
32
33 BaseObject.addConstructorFunctions(BuildbotTesterQueueView);
34
35 BuildbotTesterQueueView.prototype = {
36     constructor: BuildbotTesterQueueView,
37     __proto__: BuildbotQueueView.prototype,
38
39     update: function()
40     {
41         BuildbotQueueView.prototype.update.call(this);
42
43         this.element.removeChildren();
44
45         function appendBuilderQueueStatus(queue)
46         {
47             if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated) {
48                 this._appendUnauthorizedLineView(queue);
49                 return;
50             }
51
52             this._appendPendingRevisionCount(queue);
53
54             var appendedStatus = false;
55
56             var limit = 2;
57             for (var i = 0; i < queue.iterations.length && limit > 0; ++i) {
58                 var iteration = queue.iterations[i];
59                 if (!iteration.loaded || !iteration.finished)
60                     continue;
61
62                 --limit;
63
64                 var willHaveAnotherStatusLine = i + 1 < queue.iterations.length && limit > 0 && !iteration.successful; // This is not 100% correct, as the remaining iterations may not be finished or loaded yet, but close enough.
65                 var messageElement = this.revisionContentForIteration(iteration, (iteration.productive && willHaveAnotherStatusLine) ? iteration.previousProductiveIteration : null);
66
67                 var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
68                 var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
69                 var apiTestResults = iteration.apiTestResults || {failureCount: 0};
70                 var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
71                 var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
72                 var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
73                 var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
74
75                 if (iteration.successful) {
76                     var status = new StatusLineView(messageElement, StatusLineView.Status.Good, "all tests passed");
77                     limit = 0;
78                 } else if (!iteration.productive) {
79                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
80                     var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
81                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
82                     // Something wrong happened, but it was not a test failure.
83                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
84                     var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
85                 } else if (layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
86                     var url = iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration);
87                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, layoutTestResults.failureCount === 1 ? "layout test failure" : "layout test failures", layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + "\uff0b" : layoutTestResults.failureCount, url);
88                     new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
89                 } else if (!layoutTestResults.failureCount && javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
90                     var url = iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration);
91                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, javascriptTestResults.failureCount === 1 ? "javascript test failure" : "javascript test failures", javascriptTestResults.failureCount, url);
92                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
93                     var url = iteration.queue.buildbot.apiTestResultsURLForIteration(iteration);
94                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, apiTestResults.failureCount === 1 ? "api test failure" : "api test failures", apiTestResults.failureCount, url);
95                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
96                     var url = iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration);
97                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, platformAPITestResults.failureCount === 1 ? "platform api test failure" : "api test failures", platformAPITestResults.failureCount, url);
98                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
99                     var url = iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration);
100                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, pythonTestResults.failureCount === 1 ? "webkitpy test failure" : "webkitpy test failures", pythonTestResults.failureCount, url);
101                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
102                     var url = iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration);
103                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, "webkitperl test failed", undefined, url);
104                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && bindingTestResults.errorOccurred) {
105                     var url = iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration);
106                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, "bindings tests failed", undefined, url);
107                 } else {
108                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
109                     var totalFailures = layoutTestResults.failureCount + javascriptTestResults.failureCount + apiTestResults.failureCount + platformAPITestResults.failureCount + pythonTestResults.failureCount + perlTestResults.errorOccurred + bindingTestResults.errorOccurred;
110                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, totalFailures === 1 ? "test failure" : "test failures", totalFailures, url);
111                     new PopoverTracker(status.statusBubbleElement, this._presentPopoverForMultipleFailureKinds.bind(this), iteration);
112                 }
113
114                 this.element.appendChild(status.element);
115                 appendedStatus = true;
116             }
117
118             if (!appendedStatus) {
119                 var status = new StatusLineView("unknown", StatusLineView.Status.Neutral, "last passing build");
120                 this.element.appendChild(status.element);
121             }
122         }
123
124         function appendBuild(queues, label)
125         {
126             queues.forEach(function(queue) {
127                 var releaseLabel = document.createElement("a");
128                 releaseLabel.classList.add("queueLabel");
129                 releaseLabel.textContent = label;
130                 releaseLabel.href = queue.overviewURL;
131                 releaseLabel.target = "_blank";
132                 this.element.appendChild(releaseLabel);
133
134                 appendBuilderQueueStatus.call(this, queue);
135             }.bind(this));
136         }
137
138         appendBuild.call(this, this.releaseQueues, "Release");
139         appendBuild.call(this, this.debugQueues, "Debug");
140     },
141
142     _popoverContentForLayoutTestRegressions: function(iteration)
143     {
144         var hasTestHistory = typeof testHistory !== "undefined";
145
146         var content = document.createElement("div");
147         content.className = "test-results-popover";
148
149         this._addIterationHeadingToPopover(iteration, content, "layout test failures");
150         this._addDividerToPopover(content);
151
152         if (!iteration.layoutTestResults.regressions) {
153             var message = document.createElement("div");
154             message.className = "loading-failure";
155             message.textContent = "Test results couldn\u2019t be loaded";
156             content.appendChild(message);
157             return content;
158         }
159
160         function addFailureInfoLink(rowElement, className, text, url)
161         {
162             var linkElement = document.createElement("a");
163             linkElement.className = className;
164             linkElement.textContent = text;
165             linkElement.href = url;
166             linkElement.target = "_blank";
167             rowElement.appendChild(linkElement);
168         }
169
170         function addFailureInfoText(rowElement, className, text)
171         {
172             var spanElement = document.createElement("span");
173             spanElement.className = className;
174             spanElement.textContent = text;
175             rowElement.appendChild(spanElement);
176         }
177
178         var sortedRegressions = iteration.layoutTestResults.regressions.slice().sort(function(a, b) { return (a.path === b.path) ? 0 : (a.path > b.path) ? 1 : -1; });
179
180         for (var i = 0, end = sortedRegressions.length; i != end; ++i) {
181             var test = sortedRegressions[i];
182
183             var rowElement = document.createElement("div");
184
185             var testPathElement = document.createElement("span");
186             testPathElement.className = "test-path";
187             testPathElement.textContent = test.path;
188             rowElement.appendChild(testPathElement);
189
190             if (test.crash)
191                 addFailureInfoLink(rowElement, "failure-kind-indicator", "crash", iteration.queue.buildbot.layoutTestCrashLogURLForIteration(iteration, test.path));
192
193             if (test.timeout)
194                 addFailureInfoText(rowElement, "failure-kind-indicator", "timeout");
195
196             if (test.has_diff) {
197                 addFailureInfoLink(rowElement, "additional-link", "diff", iteration.queue.buildbot.layoutTestDiffURLForIteration(iteration, test.path));
198
199                 if (iteration.hasPrettyPatch)
200                     addFailureInfoLink(rowElement, "additional-link", "pretty\xa0diff", iteration.queue.buildbot.layoutTestPrettyDiffURLForIteration(iteration, test.path));
201             }
202
203             if (test.has_image_diff) {
204                 addFailureInfoLink(rowElement, "additional-link", "images", iteration.queue.buildbot.layoutTestImagesURLForIteration(iteration, test.path));
205                 addFailureInfoLink(rowElement, "additional-link", "image\xa0diff", iteration.queue.buildbot.layoutTestImageDiffURLForIteration(iteration, test.path));
206             }
207
208             if (test.has_stderr)
209                 addFailureInfoLink(rowElement, "additional-link", "stderr", iteration.queue.buildbot.layoutTestStderrURLForIteration(iteration, test.path));
210
211             if (hasTestHistory)
212                 addFailureInfoLink(rowElement, "test-history-link", "history", testHistory.historyPageURLForTest(test.path));
213
214             content.appendChild(rowElement);
215         }
216
217         // Work around bug 80159: -webkit-user-select:none not respected when copying content.
218         // We set clipboard data manually, temporarily making non-selectable content hidden
219         // to easily get accurate selection text.
220         content.oncopy = function(event) {
221             var iterator = document.createNodeIterator(
222                 event.currentTarget,
223                 NodeFilter.SHOW_ELEMENT,
224                 {
225                     acceptNode: function(element) {
226                         if (window.getComputedStyle(element).webkitUserSelect !== "none")
227                             return NodeFilter.FILTER_ACCEPT;
228                         return NodeFilter.FILTER_SKIP;
229                     }
230                 }
231             );
232
233             while ((node = iterator.nextNode()))
234                 node.style.visibility = "visible";
235
236             event.currentTarget.style.visibility = "hidden";
237             event.clipboardData.setData('text', window.getSelection());
238             event.currentTarget.style.visibility = "";
239             return false;
240         }
241
242         return content;
243     },
244
245     _presentPopoverForLayoutTestRegressions: function(element, popover, iteration)
246     {
247         if (iteration.layoutTestResults.regressions)
248             var content = this._popoverContentForLayoutTestRegressions(iteration);
249         else {
250             var content = document.createElement("div");
251             content.className = "test-results-popover";
252
253             this._addIterationHeadingToPopover(iteration, content, "layout test failures");
254             this._addDividerToPopover(content);
255
256             var loadingIndicator = document.createElement("div");
257             loadingIndicator.className = "loading-indicator";
258             loadingIndicator.textContent = "Loading\u2026";
259             content.appendChild(loadingIndicator);
260
261             iteration.loadLayoutTestResults(function() {
262                 popover.content = this._popoverContentForLayoutTestRegressions(iteration);
263             }.bind(this));
264         }
265         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
266         popover.content = content;
267         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
268         return true;
269     },
270
271     _presentPopoverForMultipleFailureKinds: function(element, popover, iteration)
272     {
273         function addResultKind(message, url) {
274             var line = document.createElement("a");
275             line.className = "failing-test-kind-summary";
276             line.href = url;
277             line.textContent = message;
278             line.target = "_blank";
279             content.appendChild(line);            
280         }
281
282         var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
283         var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
284         var apiTestResults = iteration.apiTestResults || {failureCount: 0};
285         var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
286         var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
287         var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
288         var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
289
290         var content = document.createElement("div");
291         content.className = "test-results-popover";
292
293         this._addIterationHeadingToPopover(iteration, content);
294         this._addDividerToPopover(content);
295
296         if (layoutTestResults.failureCount) {
297             var message = (layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + "\uff0b" : layoutTestResults.failureCount) + "\u00a0" +
298                 (layoutTestResults.failureCount === 1 ? "layout test failure" : "layout test failures");
299             addResultKind(message, iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
300         }
301
302         if (javascriptTestResults.failureCount) {
303             var message = javascriptTestResults.failureCount + "\u00a0" + (javascriptTestResults.failureCount === 1 ? "javascript test failure" : "javascript test failures");
304             addResultKind(message, iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration));
305         }
306
307         if (apiTestResults.failureCount) {
308             var message = apiTestResults.failureCount + "\u00a0" + (apiTestResults.failureCount === 1 ? "api test failure" : "api test failures");
309             addResultKind(message, iteration.queue.buildbot.apiTestResultsURLForIteration(iteration));
310         }
311
312         if (platformAPITestResults.failureCount) {
313             var message = platformAPITestResults.failureCount + "\u00a0" + (platformAPITestResults.failureCount === 1 ? "platform api test failure" : "platform api test failures");
314             addResultKind(message, iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration));
315         }
316
317         if (pythonTestResults.failureCount) {
318             var message = pythonTestResults.failureCount + "\u00a0" + (pythonTestResults.failureCount === 1 ? "webkitpy test failure" : "webkitpy test failures");
319             addResultKind(message, iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration));
320         }
321
322         if (perlTestResults.errorOccurred)
323             addResultKind("webkitperl tests failed", iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration));
324
325         if (bindingTestResults.errorOccurred)
326             addResultKind("bindings tests failed", iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration));
327
328         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
329         popover.content = content;
330         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
331         return true;
332     }
333 };