2 * Copyright (C) 2013, 2014 Apple 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 BuildbotTesterQueueView = function(debugQueues, releaseQueues)
28 BuildbotQueueView.call(this, debugQueues, releaseQueues);
33 BaseObject.addConstructorFunctions(BuildbotTesterQueueView);
35 BuildbotTesterQueueView.prototype = {
36 constructor: BuildbotTesterQueueView,
37 __proto__: BuildbotQueueView.prototype,
41 BuildbotQueueView.prototype.update.call(this);
43 this.element.removeChildren();
45 function appendBuilderQueueStatus(queue)
47 if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated) {
48 this._appendUnauthorizedLineView(queue);
52 this._appendPendingRevisionCount(queue);
54 var appendedStatus = false;
57 for (var i = 0; i < queue.iterations.length && limit > 0; ++i) {
58 var iteration = queue.iterations[i];
59 if (!iteration.loaded || !iteration.finished)
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);
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};
75 if (iteration.successful) {
76 var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
77 var status = new StatusLineView(messageElement, StatusLineView.Status.Good, "all tests passed", undefined, url);
79 } else if (!iteration.productive) {
80 var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
81 var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
82 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
83 // Something wrong happened, but it was not a test failure.
84 var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
85 var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
86 } else if (layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
87 var url = iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration);
88 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);
89 new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
90 } else if (!layoutTestResults.failureCount && javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
91 var url = iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration);
92 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, javascriptTestResults.failureCount === 1 ? "javascript test failure" : "javascript test failures", javascriptTestResults.failureCount, url);
93 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
94 var url = iteration.queue.buildbot.apiTestResultsURLForIteration(iteration);
95 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, apiTestResults.failureCount === 1 ? "api test failure" : "api test failures", apiTestResults.failureCount, url);
96 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
97 var url = iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration);
98 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, platformAPITestResults.failureCount === 1 ? "platform api test failure" : "api test failures", platformAPITestResults.failureCount, url);
99 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
100 var url = iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration);
101 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, pythonTestResults.failureCount === 1 ? "webkitpy test failure" : "webkitpy test failures", pythonTestResults.failureCount, url);
102 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
103 var url = iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration);
104 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, "webkitperl test failed", undefined, url);
105 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && bindingTestResults.errorOccurred) {
106 var url = iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration);
107 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, "bindings tests failed", undefined, url);
109 var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
110 var totalFailures = layoutTestResults.failureCount + javascriptTestResults.failureCount + apiTestResults.failureCount + platformAPITestResults.failureCount + pythonTestResults.failureCount + perlTestResults.errorOccurred + bindingTestResults.errorOccurred;
111 var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, totalFailures === 1 ? "test failure" : "test failures", totalFailures, url);
112 new PopoverTracker(status.statusBubbleElement, this._presentPopoverForMultipleFailureKinds.bind(this), iteration);
115 this.element.appendChild(status.element);
116 appendedStatus = true;
119 if (!appendedStatus) {
120 var status = new StatusLineView("unknown", StatusLineView.Status.Neutral, "last passing build");
121 this.element.appendChild(status.element);
125 function appendBuild(queues, label)
127 queues.forEach(function(queue) {
128 var releaseLabel = document.createElement("a");
129 releaseLabel.classList.add("queueLabel");
130 releaseLabel.textContent = label;
131 releaseLabel.href = queue.overviewURL;
132 releaseLabel.target = "_blank";
133 this.element.appendChild(releaseLabel);
135 appendBuilderQueueStatus.call(this, queue);
139 appendBuild.call(this, this.releaseQueues, "Release");
140 appendBuild.call(this, this.debugQueues, "Debug");
143 _popoverContentForLayoutTestRegressions: function(iteration)
145 var hasTestHistory = typeof testHistory !== "undefined";
147 var content = document.createElement("div");
148 content.className = "test-results-popover";
150 this._addIterationHeadingToPopover(iteration, content, "layout test failures");
151 this._addDividerToPopover(content);
153 if (!iteration.layoutTestResults.regressions) {
154 var message = document.createElement("div");
155 message.className = "loading-failure";
156 message.textContent = "Test results couldn\u2019t be loaded";
157 content.appendChild(message);
161 function addFailureInfoLink(rowElement, className, text, url)
163 var linkElement = document.createElement("a");
164 linkElement.className = className;
165 linkElement.textContent = text;
166 linkElement.href = url;
167 linkElement.target = "_blank";
168 rowElement.appendChild(linkElement);
171 function addFailureInfoText(rowElement, className, text)
173 var spanElement = document.createElement("span");
174 spanElement.className = className;
175 spanElement.textContent = text;
176 rowElement.appendChild(spanElement);
179 var sortedRegressions = iteration.layoutTestResults.regressions.slice().sort(function(a, b) { return (a.path === b.path) ? 0 : (a.path > b.path) ? 1 : -1; });
181 for (var i = 0, end = sortedRegressions.length; i != end; ++i) {
182 var test = sortedRegressions[i];
184 var rowElement = document.createElement("div");
186 var testPathElement = document.createElement("span");
187 testPathElement.className = "test-path";
188 testPathElement.textContent = test.path;
189 rowElement.appendChild(testPathElement);
192 addFailureInfoLink(rowElement, "failure-kind-indicator", "crash", iteration.queue.buildbot.layoutTestCrashLogURLForIteration(iteration, test.path));
195 addFailureInfoText(rowElement, "failure-kind-indicator", "timeout");
198 addFailureInfoLink(rowElement, "additional-link", "diff", iteration.queue.buildbot.layoutTestDiffURLForIteration(iteration, test.path));
200 if (iteration.hasPrettyPatch)
201 addFailureInfoLink(rowElement, "additional-link", "pretty\xa0diff", iteration.queue.buildbot.layoutTestPrettyDiffURLForIteration(iteration, test.path));
204 if (test.has_image_diff) {
205 addFailureInfoLink(rowElement, "additional-link", "images", iteration.queue.buildbot.layoutTestImagesURLForIteration(iteration, test.path));
206 addFailureInfoLink(rowElement, "additional-link", "image\xa0diff", iteration.queue.buildbot.layoutTestImageDiffURLForIteration(iteration, test.path));
210 addFailureInfoLink(rowElement, "additional-link", "stderr", iteration.queue.buildbot.layoutTestStderrURLForIteration(iteration, test.path));
213 addFailureInfoLink(rowElement, "test-history-link", "history", testHistory.historyPageURLForTest(test.path));
215 content.appendChild(rowElement);
218 // Work around bug 80159: -webkit-user-select:none not respected when copying content.
219 // We set clipboard data manually, temporarily making non-selectable content hidden
220 // to easily get accurate selection text.
221 content.oncopy = function(event) {
222 var iterator = document.createNodeIterator(
224 NodeFilter.SHOW_ELEMENT,
226 acceptNode: function(element) {
227 if (window.getComputedStyle(element).webkitUserSelect !== "none")
228 return NodeFilter.FILTER_ACCEPT;
229 return NodeFilter.FILTER_SKIP;
234 while ((node = iterator.nextNode()))
235 node.style.visibility = "visible";
237 event.currentTarget.style.visibility = "hidden";
238 event.clipboardData.setData('text', window.getSelection());
239 event.currentTarget.style.visibility = "";
246 _presentPopoverForLayoutTestRegressions: function(element, popover, iteration)
248 if (iteration.layoutTestResults.regressions)
249 var content = this._popoverContentForLayoutTestRegressions(iteration);
251 var content = document.createElement("div");
252 content.className = "test-results-popover";
254 this._addIterationHeadingToPopover(iteration, content, "layout test failures");
255 this._addDividerToPopover(content);
257 var loadingIndicator = document.createElement("div");
258 loadingIndicator.className = "loading-indicator";
259 loadingIndicator.textContent = "Loading\u2026";
260 content.appendChild(loadingIndicator);
262 iteration.loadLayoutTestResults(function() {
263 popover.content = this._popoverContentForLayoutTestRegressions(iteration);
266 var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
267 popover.content = content;
268 popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
272 _presentPopoverForMultipleFailureKinds: function(element, popover, iteration)
274 function addResultKind(message, url) {
275 var line = document.createElement("a");
276 line.className = "failing-test-kind-summary";
278 line.textContent = message;
279 line.target = "_blank";
280 content.appendChild(line);
283 var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
284 var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
285 var apiTestResults = iteration.apiTestResults || {failureCount: 0};
286 var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
287 var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
288 var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
289 var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
291 var content = document.createElement("div");
292 content.className = "test-results-popover";
294 this._addIterationHeadingToPopover(iteration, content);
295 this._addDividerToPopover(content);
297 if (layoutTestResults.failureCount) {
298 var message = (layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + "\uff0b" : layoutTestResults.failureCount) + "\u00a0" +
299 (layoutTestResults.failureCount === 1 ? "layout test failure" : "layout test failures");
300 addResultKind(message, iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
303 if (javascriptTestResults.failureCount) {
304 var message = javascriptTestResults.failureCount + "\u00a0" + (javascriptTestResults.failureCount === 1 ? "javascript test failure" : "javascript test failures");
305 addResultKind(message, iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration));
308 if (apiTestResults.failureCount) {
309 var message = apiTestResults.failureCount + "\u00a0" + (apiTestResults.failureCount === 1 ? "api test failure" : "api test failures");
310 addResultKind(message, iteration.queue.buildbot.apiTestResultsURLForIteration(iteration));
313 if (platformAPITestResults.failureCount) {
314 var message = platformAPITestResults.failureCount + "\u00a0" + (platformAPITestResults.failureCount === 1 ? "platform api test failure" : "platform api test failures");
315 addResultKind(message, iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration));
318 if (pythonTestResults.failureCount) {
319 var message = pythonTestResults.failureCount + "\u00a0" + (pythonTestResults.failureCount === 1 ? "webkitpy test failure" : "webkitpy test failures");
320 addResultKind(message, iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration));
323 if (perlTestResults.errorOccurred)
324 addResultKind("webkitperl tests failed", iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration));
326 if (bindingTestResults.errorOccurred)
327 addResultKind("bindings tests failed", iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration));
329 var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
330 popover.content = content;
331 popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);