9a131b40240089e5c24d1b554c6bfa8a27fcb18c
[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._requestedRootSets = null;
16         this._rootSetToLabel = new Map;
17         this._allRootSets = null;
18         console.assert(!object.platform || object.platform instanceof Platform);
19         this._platform = object.platform;
20     }
21
22     updateSingleton(object)
23     {
24         super.updateSingleton(object);
25
26         console.assert(this._taskId == object.task);
27         console.assert(+this._createdAt == +object.createdAt);
28         console.assert(this._platform == object.platform);
29
30         this._isHidden = object.hidden;
31     }
32
33     createdAt() { return this._createdAt; }
34     isHidden() { return this._isHidden; }
35     buildRequests() { return this._buildRequests; }
36     addBuildRequest(request)
37     {
38         this._buildRequests.push(request);
39         this._requestsAreInOrder = false;
40         this._requestedRootSets = null;
41         this._rootSetToLabel.clear();
42     }
43
44     repetitionCount()
45     {
46         if (!this._buildRequests.length)
47             return 0;
48         var rootSet = this._buildRequests[0].rootSet();
49         var count = 0;
50         for (var request of this._buildRequests) {
51             if (request.rootSet() == rootSet)
52                 count++;
53         }
54         return count;
55     }
56
57     requestedRootSets()
58     {
59         if (!this._requestedRootSets) {
60             this._orderBuildRequests();
61             this._requestedRootSets = [];
62             for (var request of this._buildRequests) {
63                 var set = request.rootSet();
64                 if (!this._requestedRootSets.includes(set))
65                     this._requestedRootSets.push(set);
66             }
67             this._requestedRootSets.sort(function (a, b) { return a.latestCommitTime() - b.latestCommitTime(); });
68             var setIndex = 0;
69             for (var set of this._requestedRootSets) {
70                 this._rootSetToLabel.set(set, String.fromCharCode('A'.charCodeAt(0) + setIndex));
71                 setIndex++;
72             }
73
74         }
75         return this._requestedRootSets;
76     }
77
78     requestsForRootSet(rootSet)
79     {
80         this._orderBuildRequests();
81         return this._buildRequests.filter(function (request) { return request.rootSet() == rootSet; });
82     }
83
84     labelForRootSet(rootSet)
85     {
86         console.assert(this._requestedRootSets);
87         return this._rootSetToLabel.get(rootSet);
88     }
89
90     _orderBuildRequests()
91     {
92         if (this._requestsAreInOrder)
93             return;
94         this._buildRequests = this._buildRequests.sort(function (a, b) { return a.order() - b.order(); });
95         this._requestsAreInOrder = true;
96     }
97
98     didSetResult(request)
99     {
100         this._allRootSets = null;
101     }
102
103     hasFinished()
104     {
105         return this._buildRequests.every(function (request) { return request.hasFinished(); });
106     }
107
108     hasStarted()
109     {
110         return this._buildRequests.some(function (request) { return request.hasStarted(); });
111     }
112
113     hasPending()
114     {
115         return this._buildRequests.some(function (request) { return request.hasPending(); });
116     }
117
118     compareTestResults(rootSetA, rootSetB)
119     {
120         var beforeValues = this._valuesForRootSet(rootSetA);
121         var afterValues = this._valuesForRootSet(rootSetB);
122         var beforeMean = Statistics.sum(beforeValues) / beforeValues.length;
123         var afterMean = Statistics.sum(afterValues) / afterValues.length;
124
125         var metric = AnalysisTask.findById(this._taskId).metric();
126         console.assert(metric);
127
128         var result = {changeType: null, status: 'failed', label: 'Failed', fullLabel: 'Failed', isStatisticallySignificant: false};
129
130         var hasCompleted = this.hasFinished();
131         if (!hasCompleted) {
132             if (this.hasStarted()) {
133                 result.status = 'running';
134                 result.label = 'Running';
135                 result.fullLabel = 'Running';
136             } else {
137                 console.assert(result.changeType === null);
138                 result.status = 'pending';
139                 result.label = 'Pending';
140                 result.fullLabel = 'Pending';
141             }
142         }
143
144         if (beforeValues.length && afterValues.length) {
145             var diff = afterMean - beforeMean;
146             var smallerIsBetter = metric.isSmallerBetter();
147             var changeType = diff < 0 == smallerIsBetter ? 'better' : 'worse';
148             var changeLabel = Math.abs(diff / beforeMean * 100).toFixed(2) + '% ' + changeType;
149             var isSignificant = Statistics.testWelchsT(beforeValues, afterValues);
150             var significanceLabel = isSignificant ? 'significant' : 'insignificant';
151
152             result.changeType = changeType;
153             result.label = changeLabel;
154             if (hasCompleted)
155                 result.status = isSignificant ? result.changeType : 'unchanged';
156             result.fullLabel = `${result.label} (statistically ${significanceLabel})`;
157             result.isStatisticallySignificant = isSignificant;
158         }
159
160         return result;
161     }
162
163     _valuesForRootSet(rootSet)
164     {
165         var requests = this.requestsForRootSet(rootSet);
166         var values = [];
167         for (var request of requests) {
168             if (request.result())
169                 values.push(request.result().value);
170         }
171         return values;
172     }
173
174     updateName(newName)
175     {
176         var self = this;
177         var id = this.id();
178         return PrivilegedAPI.sendRequest('update-test-group', {
179             group: id,
180             name: newName,
181         }).then(function (data) {
182             return TestGroup.cachedFetch(`../api/test-groups/${id}`, {}, true)
183                 .then(TestGroup._createModelsFromFetchedTestGroups.bind(TestGroup));
184         });
185     }
186
187     updateHiddenFlag(hidden)
188     {
189         var self = this;
190         var id = this.id();
191         return PrivilegedAPI.sendRequest('update-test-group', {
192             group: id,
193             hidden: !!hidden,
194         }).then(function (data) {
195             return TestGroup.cachedFetch(`../api/test-groups/${id}`, {}, true)
196                 .then(TestGroup._createModelsFromFetchedTestGroups.bind(TestGroup));
197         });
198     }
199
200     static createAndRefetchTestGroups(task, name, repetitionCount, rootSets)
201     {
202         var self = this;
203         return PrivilegedAPI.sendRequest('create-test-group', {
204             task: task.id(),
205             name: name,
206             repetitionCount: repetitionCount,
207             rootSets: rootSets,
208         }).then(function (data) {
209             return self.cachedFetch('../api/test-groups', {task: task.id()}, true).then(self._createModelsFromFetchedTestGroups.bind(self));
210         });
211     }
212
213     static fetchByTask(taskId)
214     {
215         return this.cachedFetch('../api/test-groups', {task: taskId}).then(this._createModelsFromFetchedTestGroups.bind(this));
216     }
217
218     static _createModelsFromFetchedTestGroups(data)
219     {
220         var testGroups = data['testGroups'].map(function (row) {
221             row.platform = Platform.findById(row.platform);
222             return TestGroup.ensureSingleton(row.id, row);
223         });
224
225         var rootIdMap = {};
226         for (var root of data['roots']) {
227             rootIdMap[root.id] = root;
228             root.repository = Repository.findById(root.repository);
229         }
230
231         var rootSets = data['rootSets'].map(function (row) {
232             row.roots = row.roots.map(function (rootId) { return rootIdMap[rootId]; });
233             return RootSet.ensureSingleton(row.id, row);
234         });
235
236         var buildRequests = data['buildRequests'].map(function (rawData) {
237             rawData.testGroup = TestGroup.findById(rawData.testGroup);
238             rawData.rootSet = RootSet.findById(rawData.rootSet);
239             return BuildRequest.ensureSingleton(rawData.id, rawData);
240         });
241
242         return testGroups;
243     }
244 }
245
246 if (typeof module != 'undefined')
247     module.exports.TestGroup = TestGroup;