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