Dashboard code restructuring
[WebKit-https.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / dashboard / Scripts / BuildbotBuilderQueueView.js
1 /*
2  * Copyright (C) 2013 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 BuildbotBuilderQueueView = function(queues)
27 {
28     BuildbotQueueView.call(this, queues);
29
30     function filterQueues(architecture, debug, queue)
31     {
32         return queue.architecture === architecture && queue.debug === debug;
33     }
34
35     this.universalReleaseQueues = this.queues.filter(filterQueues.bind(this, Buildbot.BuildArchitecture.Universal, false));
36     this.sixtyFourBitReleaseQueues = this.queues.filter(filterQueues.bind(this, Buildbot.BuildArchitecture.SixtyFourBit, false));
37     this.thirtyTwoBitReleaseQueues = this.queues.filter(filterQueues.bind(this, Buildbot.BuildArchitecture.ThirtyTwoBit, false));
38
39     this.universalDebugQueues = this.queues.filter(filterQueues.bind(this, Buildbot.BuildArchitecture.Universal, true));
40     this.sixtyFourBitDebugQueues = this.queues.filter(filterQueues.bind(this, Buildbot.BuildArchitecture.SixtyFourBit, true));
41     this.thirtyTwoBitDebugQueues = this.queues.filter(filterQueues.bind(this, Buildbot.BuildArchitecture.ThirtyTwoBit, true));
42
43     this.hasMultipleReleaseBuilds = this.universalReleaseQueues.length + this.sixtyFourBitReleaseQueues.length + this.thirtyTwoBitReleaseQueues.length > 1;
44     this.hasMultipleDebugBuilds = this.universalDebugQueues.length + this.sixtyFourBitDebugQueues.length + this.thirtyTwoBitDebugQueues.length > 1;
45
46     this.update();
47 };
48
49 BaseObject.addConstructorFunctions(BuildbotBuilderQueueView);
50
51 BuildbotBuilderQueueView.prototype = {
52     constructor: BuildbotBuilderQueueView,
53     __proto__: BuildbotQueueView.prototype,
54
55     update: function()
56     {
57         BuildbotQueueView.prototype.update.call(this);
58
59         this.element.removeChildren();
60
61         function appendBuilderQueueStatus(queue)
62         {
63             if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated) {
64                 this._appendUnauthorizedLineView(queue);
65                 return;
66             }
67
68             this._appendPendingRevisionCount(queue, this._latestProductiveIteration.bind(this, queue));
69
70             var firstRecentUnsuccessfulIteration = queue.firstRecentUnsuccessfulIteration;
71             var mostRecentFinishedIteration = queue.mostRecentFinishedIteration;
72             var mostRecentSuccessfulIteration = queue.mostRecentSuccessfulIteration;
73
74             if (firstRecentUnsuccessfulIteration && firstRecentUnsuccessfulIteration.loaded
75                 && mostRecentFinishedIteration && mostRecentFinishedIteration.loaded) {
76                 console.assert(!mostRecentFinishedIteration.successful);
77                 var message = this.revisionContentForIteration(mostRecentFinishedIteration, mostRecentFinishedIteration.productive ? mostRecentSuccessfulIteration : null);
78                 if (mostRecentFinishedIteration.failed && mostRecentFinishedIteration.productive) {
79                     // Assume it was a build step that failed, and link directly to output.
80                     var url = mostRecentFinishedIteration.failureLogURL("build log");
81                     if (!url)
82                         url = mostRecentFinishedIteration.failureLogURL("stdio");
83                     var status = StatusLineView.Status.Bad;
84                 } else
85                     var status = StatusLineView.Status.Danger;
86
87                 // Show a popover when the URL is not a main build page one, because there are usually multiple logs, and it's good to provide a choice.
88                 var needsPopover = !!url;
89
90                 // Some other step failed, link to main buildbot page for the iteration.
91                 if (!url)
92                     url = queue.buildbot.buildPageURLForIteration(mostRecentFinishedIteration);
93                 var status = new StatusLineView(message, status, mostRecentFinishedIteration.text, null, url);
94                 this.element.appendChild(status.element);
95
96                 if (needsPopover)
97                     new PopoverTracker(status.statusBubbleElement, this._presentPopoverFailureLogs.bind(this), mostRecentFinishedIteration);
98             }
99
100             if (mostRecentSuccessfulIteration && mostRecentSuccessfulIteration.loaded) {
101                 var message = this.revisionContentForIteration(mostRecentSuccessfulIteration);
102                 var url = queue.buildbot.buildPageURLForIteration(mostRecentSuccessfulIteration);
103                 var status = new StatusLineView(message, StatusLineView.Status.Good, firstRecentUnsuccessfulIteration ? "last successful build" : "latest build", null, url);
104                 this.element.appendChild(status.element);
105             } else {
106                 var status = new StatusLineView("unknown", StatusLineView.Status.Neutral, firstRecentUnsuccessfulIteration ? "last successful build" : "latest build");
107                 this.element.appendChild(status.element);
108
109                 if (firstRecentUnsuccessfulIteration) {
110                     // We have a failed iteration but no successful. It might be further back in time.
111                     queue.loadMoreHistoricalIterations();
112                 }
113             }
114         }
115
116         this.appendBuildStyle.call(this, this.universalReleaseQueues, this.hasMultipleReleaseBuilds ? "Release (Universal)" : "Release", appendBuilderQueueStatus);
117         this.appendBuildStyle.call(this, this.sixtyFourBitReleaseQueues, this.hasMultipleReleaseBuilds ? "Release (64-bit)" : "Release", appendBuilderQueueStatus);
118         this.appendBuildStyle.call(this, this.thirtyTwoBitReleaseQueues, this.hasMultipleReleaseBuilds ? "Release (32-bit)" : "Release", appendBuilderQueueStatus);
119
120         this.appendBuildStyle.call(this, this.universalDebugQueues, this.hasMultipleDebugBuilds ? "Debug (Universal)" : "Debug", appendBuilderQueueStatus);
121         this.appendBuildStyle.call(this, this.sixtyFourBitDebugQueues, this.hasMultipleDebugBuilds ? "Debug (64-bit)" : "Debug", appendBuilderQueueStatus);
122         this.appendBuildStyle.call(this, this.thirtyTwoBitDebugQueues, this.hasMultipleDebugBuilds ? "Debug (32-bit)" : "Debug", appendBuilderQueueStatus);
123     },
124
125     _presentPopoverFailureLogs: function(element, popover, iteration)
126     {
127         var content = document.createElement("div");
128         content.className = "build-logs-popover";
129
130         function addLog(name, url) {
131             var line = document.createElement("a");
132             line.className = "build-log-link";
133             line.href = url;
134             line.textContent = name;
135             line.target = "_blank";
136             content.appendChild(line);
137         }
138
139         this._addIterationHeadingToPopover(iteration, content);
140         this._addDividerToPopover(content);
141         
142         var logsHeadingLine = document.createElement("div");
143         logsHeadingLine.className = "build-logs-heading";
144         logsHeadingLine.textContent = iteration.firstFailedStepName + " failed";
145         content.appendChild(logsHeadingLine);
146
147         for (var i = 0, end = iteration.failureLogs.length; i < end; ++i)
148             addLog(iteration.failureLogs[i][0], iteration.failureLogs[i][1]);
149
150         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
151         popover.content = content;
152         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
153         return true;
154     }
155 };