32be0a635227df020c45c3acf9868a904fc1a465
[WebKit-https.git] / Websites / perf.webkit.org / public / v3 / models / test-group.js
1 'use strict';
2
3 class TestGroup extends LabeledObject {
4
5     constructor(id, object)
6     {
7         super(id, object);
8         this._taskId = object.task;
9         this._authorName = object.author;
10         this._createdAt = new Date(object.createdAt);
11         this._isHidden = object.hidden;
12         this._buildRequests = [];
13         this._requestsAreInOrder = false;
14         this._repositories = null;
15         this._requestedCommitSets = null;
16         this._commitSetToLabel = new Map;
17         console.assert(!object.platform || object.platform instanceof Platform);
18         this._platform = object.platform;
19     }
20
21     updateSingleton(object)
22     {
23         super.updateSingleton(object);
24
25         console.assert(this._taskId == object.task);
26         console.assert(+this._createdAt == +object.createdAt);
27         console.assert(this._platform == object.platform);
28
29         this._isHidden = object.hidden;
30     }
31
32     createdAt() { return this._createdAt; }
33     isHidden() { return this._isHidden; }
34     buildRequests() { return this._buildRequests; }
35     addBuildRequest(request)
36     {
37         this._buildRequests.push(request);
38         this._requestsAreInOrder = false;
39         this._requestedCommitSets = null;
40         this._commitSetToLabel.clear();
41     }
42
43     test()
44     {
45         if (!this._buildRequests.length)
46             return null;
47         return this._buildRequests[0].test();
48     }
49
50     platform()
51     {
52         if (!this._buildRequests.length)
53             return null;
54         return this._buildRequests[0].platform();
55     }
56
57     repetitionCount()
58     {
59         if (!this._buildRequests.length)
60             return 0;
61         var commitSet = this._buildRequests[0].commitSet();
62         var count = 0;
63         for (var request of this._buildRequests) {
64             if (request.commitSet() == commitSet)
65                 count++;
66         }
67         return count;
68     }
69
70     requestedCommitSets()
71     {
72         if (!this._requestedCommitSets) {
73             this._orderBuildRequests();
74             this._requestedCommitSets = [];
75             for (var request of this._buildRequests) {
76                 var set = request.commitSet();
77                 if (!this._requestedCommitSets.includes(set))
78                     this._requestedCommitSets.push(set);
79             }
80             this._requestedCommitSets.sort(function (a, b) { return a.latestCommitTime() - b.latestCommitTime(); });
81             var setIndex = 0;
82             for (var set of this._requestedCommitSets) {
83                 this._commitSetToLabel.set(set, String.fromCharCode('A'.charCodeAt(0) + setIndex));
84                 setIndex++;
85             }
86
87         }
88         return this._requestedCommitSets;
89     }
90
91     requestsForCommitSet(commitSet)
92     {
93         this._orderBuildRequests();
94         return this._buildRequests.filter(function (request) { return request.commitSet() == commitSet; });
95     }
96
97     labelForCommitSet(commitSet)
98     {
99         console.assert(this._requestedCommitSets);
100         return this._commitSetToLabel.get(commitSet);
101     }
102
103     _orderBuildRequests()
104     {
105         if (this._requestsAreInOrder)
106             return;
107         this._buildRequests = this._buildRequests.sort(function (a, b) { return a.order() - b.order(); });
108         this._requestsAreInOrder = true;
109     }
110
111     hasFinished()
112     {
113         return this._buildRequests.every(function (request) { return request.hasFinished(); });
114     }
115
116     hasStarted()
117     {
118         return this._buildRequests.some(function (request) { return request.hasStarted(); });
119     }
120
121     hasPending()
122     {
123         return this._buildRequests.some(function (request) { return request.isPending(); });
124     }
125
126     compareTestResults(metric, beforeValues, afterValues)
127     {
128         console.assert(metric);
129         const beforeMean = Statistics.sum(beforeValues) / beforeValues.length;
130         const afterMean = Statistics.sum(afterValues) / afterValues.length;
131
132         var result = {changeType: null, status: 'failed', label: 'Failed', fullLabel: 'Failed', isStatisticallySignificant: false};
133
134         var hasCompleted = this.hasFinished();
135         if (!hasCompleted) {
136             if (this.hasStarted()) {
137                 result.status = 'running';
138                 result.label = 'Running';
139                 result.fullLabel = 'Running';
140             } else {
141                 console.assert(result.changeType === null);
142                 result.status = 'pending';
143                 result.label = 'Pending';
144                 result.fullLabel = 'Pending';
145             }
146         }
147
148         if (beforeValues.length && afterValues.length) {
149             var diff = afterMean - beforeMean;
150             var smallerIsBetter = metric.isSmallerBetter();
151             var changeType = diff < 0 == smallerIsBetter ? 'better' : 'worse';
152             var changeLabel = Math.abs(diff / beforeMean * 100).toFixed(2) + '% ' + changeType;
153             var isSignificant = Statistics.testWelchsT(beforeValues, afterValues);
154             var significanceLabel = isSignificant ? 'significant' : 'insignificant';
155
156             result.changeType = changeType;
157             result.label = changeLabel;
158             if (hasCompleted)
159                 result.status = isSignificant ? result.changeType : 'unchanged';
160             result.fullLabel = `${result.label} (statistically ${significanceLabel})`;
161             result.isStatisticallySignificant = isSignificant;
162         }
163
164         return result;
165     }
166
167     updateName(newName)
168     {
169         var self = this;
170         var id = this.id();
171         return PrivilegedAPI.sendRequest('update-test-group', {
172             group: id,
173             name: newName,
174         }).then(function (data) {
175             return TestGroup.cachedFetch(`/api/test-groups/${id}`, {}, true)
176                 .then(TestGroup._createModelsFromFetchedTestGroups.bind(TestGroup));
177         });
178     }
179
180     updateHiddenFlag(hidden)
181     {
182         var self = this;
183         var id = this.id();
184         return PrivilegedAPI.sendRequest('update-test-group', {
185             group: id,
186             hidden: !!hidden,
187         }).then(function (data) {
188             return TestGroup.cachedFetch(`/api/test-groups/${id}`, {}, true)
189                 .then(TestGroup._createModelsFromFetchedTestGroups.bind(TestGroup));
190         });
191     }
192
193     static createWithTask(taskName, platform, test, groupName, repetitionCount, commitSets)
194     {
195         console.assert(commitSets.length == 2);
196         const revisionSets = this._revisionSetsFromCommitSets(commitSets);
197         const params = {taskName, name: groupName, platform: platform.id(), test: test.id(), repetitionCount, revisionSets};
198         return PrivilegedAPI.sendRequest('create-test-group', params).then((data) => {
199             return AnalysisTask.fetchById(data['taskId']);
200         }).then((task) => {
201             return this._fetchTestGroupsForTask(task.id()).then(() => task);
202         });
203     }
204
205     static createWithCustomConfiguration(task, platform, test, groupName, repetitionCount, commitSets)
206     {
207         console.assert(commitSets.length == 2);
208         const revisionSets = this._revisionSetsFromCommitSets(commitSets);
209         const params = {task: task.id(), name: groupName, platform: platform.id(), test: test.id(), repetitionCount, revisionSets};
210         return PrivilegedAPI.sendRequest('create-test-group', params).then((data) => {
211             return this._fetchTestGroupsForTask(task.id());
212         });
213     }
214
215     static createAndRefetchTestGroups(task, name, repetitionCount, commitSets)
216     {
217         console.assert(commitSets.length == 2);
218         const revisionSets = this._revisionSetsFromCommitSets(commitSets);
219         return PrivilegedAPI.sendRequest('create-test-group', {
220             task: task.id(),
221             name: name,
222             repetitionCount: repetitionCount,
223             revisionSets: revisionSets,
224         }).then((data) => this._fetchTestGroupsForTask(task.id()));
225     }
226
227     static _revisionSetsFromCommitSets(commitSets)
228     {
229         return commitSets.map((commitSet) => {
230             console.assert(commitSet instanceof CustomCommitSet || commitSet instanceof CommitSet);
231             const revisionSet = {};
232             for (let repository of commitSet.repositories())
233                 revisionSet[repository.id()] = commitSet.revisionForRepository(repository);
234             const customRoots = commitSet.customRoots();
235             if (customRoots && customRoots.length)
236                 revisionSet['customRoots'] = customRoots.map((uploadedFile) => uploadedFile.id());
237             return revisionSet;
238         });
239     }
240
241     static _fetchTestGroupsForTask(taskId)
242     {
243         return this.cachedFetch('/api/test-groups', {task: taskId}, true).then((data) => this._createModelsFromFetchedTestGroups(data));
244     }
245
246     static fetchByTask(taskId)
247     {
248         return this.cachedFetch('/api/test-groups', {task: taskId}).then(this._createModelsFromFetchedTestGroups.bind(this));
249     }
250
251     static _createModelsFromFetchedTestGroups(data)
252     {
253         var testGroups = data['testGroups'].map(function (row) {
254             row.platform = Platform.findById(row.platform);
255             return TestGroup.ensureSingleton(row.id, row);
256         });
257
258         BuildRequest.constructBuildRequestsFromData(data);
259
260         return testGroups;
261     }
262 }
263
264 if (typeof module != 'undefined')
265     module.exports.TestGroup = TestGroup;