Dashboard code restructuring
[WebKit-https.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / dashboard / Scripts / BuildbotQueueView.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 BuildbotQueueView = function(queues)
27 {
28     QueueView.call(this);
29
30     this.queues = queues || [];
31
32     this.queues.forEach(function(queue) {
33         if (this.platform && this.platform != queue.platform)
34             throw "A buildbot view may not contain queues for multiple platforms."
35         else
36             this.platform = queue.platform;
37         queue.addEventListener(BuildbotQueue.Event.IterationsAdded, this._queueIterationsAdded, this);
38         queue.addEventListener(BuildbotQueue.Event.UnauthorizedAccess, this._unauthorizedAccess, this);
39     }.bind(this));
40
41     var sortedRepositories = Dashboard.sortedRepositories;
42     for (var i = 0; i < sortedRepositories.length; i++) {
43         var trac = sortedRepositories[i].trac;
44         if (trac)
45             trac.addEventListener(Trac.Event.CommitsUpdated, this._newCommitsRecorded, this);
46     }
47 };
48
49 BaseObject.addConstructorFunctions(BuildbotQueueView);
50
51 BuildbotQueueView.UpdateInterval = 45000; // 45 seconds
52 BuildbotQueueView.UpdateSoonTimeout = 1000; // 1 second
53
54 BuildbotQueueView.prototype = {
55     constructor: BuildbotQueueView,
56     __proto__: QueueView.prototype,
57
58     _latestProductiveIteration: function(queue)
59     {
60         if (!queue.iterations.length)
61             return null;
62         if (queue.iterations[0].productive)
63             return queue.iterations[0];
64         return queue.iterations[0].previousProductiveIteration;
65     },
66
67     _appendUnauthorizedLineView: function(queue)
68     {
69         console.assert(queue.buildbot.needsAuthentication);
70         console.assert(!queue.buildbot.isAuthenticated);
71         var javascriptURL = "javascript:buildbots[" + buildbots.indexOf(queue.buildbot) + "].updateQueues(Buildbot.UpdateReason.Reauthenticate)";
72         var status = new StatusLineView("unauthorized", StatusLineView.Status.Unauthorized, "", null, javascriptURL);
73         this.element.appendChild(status.element);
74     },
75
76     _presentPopoverForRevisionRange: function(element, popover, context)
77     {
78         var content = document.createElement("div");
79         content.className = "commit-history-popover";
80
81         // FIXME: Nothing guarantees that Trac has historical data for these revisions.
82         var linesForCommits = this._popoverLinesForCommitRange(context.trac, context.branch, context.firstRevision, context.lastRevision);
83
84         var line = document.createElement("div");
85         line.className = "title";
86
87         if (linesForCommits.length) {
88             line.textContent = "commits since previous result";
89             content.appendChild(line);
90             this._addDividerToPopover(content);
91         } else {
92             line.textContent = "no commits to " + context.branch.name + " since previous result";
93             content.appendChild(line);
94         }
95
96         for (var i = 0; i != linesForCommits.length; ++i)
97             content.appendChild(linesForCommits[i]);
98
99         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
100         popover.content = content;
101         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
102
103         return true;
104     },
105
106     _revisionContentWithPopoverForIteration: function(iteration, previousIteration, branch)
107     {
108         var repository = branch.repository;
109         var repositoryName = repository.name;
110         console.assert(iteration.revision[repositoryName]);
111         var trac = repository.trac;
112         var content = document.createElement("span");
113         content.textContent = this._formatRevisionForDisplay(iteration.revision[repositoryName], repository);
114         content.classList.add("revision-number");
115
116         if (!previousIteration)
117             return content;
118
119         var previousIterationRevision = previousIteration.revision[repositoryName];
120         console.assert(previousIterationRevision);
121         var previousIterationRevisionIndex = trac.indexOfRevision(previousIterationRevision);
122         if (previousIterationRevisionIndex === -1)
123             return content;
124
125         var firstRevision = trac.nextRevision(branch.name, previousIterationRevision);
126         if (firstRevision === Trac.NO_MORE_REVISIONS)
127             return content;
128         var firstRevisionIndex = trac.indexOfRevision(firstRevision);
129         console.assert(firstRevisionIndex !== -1);
130
131         var lastRevision = iteration.revision[repositoryName];
132         var lastRevisionIndex = trac.indexOfRevision(lastRevision);
133         if (lastRevisionIndex === -1)
134             return content;
135
136         if (firstRevisionIndex > lastRevisionIndex)
137             return content;
138
139         var context = {
140             trac: trac,
141             branch: branch,
142             firstRevision: firstRevision,
143             lastRevision: lastRevision,
144         };
145         new PopoverTracker(content, this._presentPopoverForRevisionRange.bind(this), context);
146
147         return content;
148     },
149
150     _addIterationHeadingToPopover: function(iteration, content, additionalText)
151     {
152         var title = document.createElement("div");
153         title.className = "popover-iteration-heading";
154
155         var queueName = document.createElement("span");
156         queueName.className = "queue-name";
157         queueName.textContent = iteration.queue.id;
158         title.appendChild(queueName);
159
160         var buildbotLink = document.createElement("a");
161         buildbotLink.className = "buildbot-link";
162         buildbotLink.textContent = "build #" + iteration.id;
163         buildbotLink.href = iteration.queue.buildbot.buildPageURLForIteration(iteration);
164         buildbotLink.target = "_blank";
165         title.appendChild(buildbotLink);
166
167         if (additionalText) {
168             var additionalTextElement = document.createElement("span");
169             additionalTextElement.className = "additional-text";
170             additionalTextElement.textContent = additionalText;
171             title.appendChild(additionalTextElement);
172         }
173
174         content.appendChild(title);
175     },
176
177     revisionContentForIteration: function(iteration, previousDisplayedIteration)
178     {
179         var fragment = document.createDocumentFragment();
180         var shouldAddPlusSign = false;
181         var branches = iteration.queue.branches;
182         for (var i = 0; i < branches.length; ++i) {
183             var branch = branches[i];
184             if (!iteration.revision[branch.repository.name])
185                 continue;
186             var content = this._revisionContentWithPopoverForIteration(iteration, previousDisplayedIteration, branch);
187             if (shouldAddPlusSign)
188                 fragment.appendChild(document.createTextNode(" \uff0b "));
189             fragment.appendChild(content);
190             shouldAddPlusSign = true;
191         }
192         return fragment;
193     },
194
195     appendBuildStyle: function(queues, defaultLabel, appendFunction)
196     {
197         queues.forEach(function(queue) {
198             var releaseLabel = document.createElement("a");
199             releaseLabel.classList.add("queueLabel");
200             releaseLabel.textContent = queue.heading ? queue.heading : defaultLabel;
201             releaseLabel.href = queue.overviewURL;
202             releaseLabel.target = "_blank";
203             this.element.appendChild(releaseLabel);
204
205             appendFunction.call(this, queue);
206         }.bind(this));
207     },
208
209     _updateQueues: function()
210     {
211         this.queues.forEach(function(queue) { queue.update(); });
212     },
213
214     _queueIterationsAdded: function(event)
215     {
216         this.updateSoon();
217
218         event.data.addedIterations.forEach(function(iteration) {
219             iteration.addEventListener(BuildbotIteration.Event.Updated, this._iterationUpdated, this);
220             iteration.addEventListener(BuildbotIteration.Event.UnauthorizedAccess, this._unauthorizedAccess, this);
221         }.bind(this));
222     },
223
224     _iterationUpdated: function(event)
225     {
226         this.updateSoon();
227     },
228     
229     _newCommitsRecorded: function(event)
230     {
231         this.updateSoon();
232     },
233
234     _unauthorizedAccess: function(event)
235     {
236         this.updateSoon();
237     }
238 };