8c53586b5df4cc43f490650d735c8f136bd39aa0
[WebKit-https.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._buildRequests = [];
11         this._requestsAreInOrder = false;
12         this._repositories = null;
13         this._requestedRootSets = null;
14         this._rootSetToLabel = new Map;
15         this._allRootSets = null;
16         console.assert(!object.platform || object.platform instanceof Platform);
17         this._platform = object.platform;
18     }
19
20     createdAt() { return this._createdAt; }
21     buildRequests() { return this._buildRequests; }
22     addBuildRequest(request)
23     {
24         this._buildRequests.push(request);
25         this._requestsAreInOrder = false;
26         this._requestedRootSets = null;
27         this._rootSetToLabel = null;
28     }
29
30     repetitionCount()
31     {
32         if (!this._buildRequests.length)
33             return 0;
34         var rootSet = this._buildRequests[0].rootSet();
35         var count = 0;
36         for (var request of this._buildRequests) {
37             if (request.rootSet() == rootSet)
38                 count++;
39         }
40         return count;
41     }
42
43     requestedRootSets()
44     {
45         if (!this._requestedRootSets) {
46             this._orderBuildRequests();
47             this._requestedRootSets = [];
48             for (var request of this._buildRequests) {
49                 var set = request.rootSet();
50                 if (!this._requestedRootSets.includes(set))
51                     this._requestedRootSets.push(set);
52             }
53             this._requestedRootSets.sort(function (a, b) { return a.latestCommitTime() - b.latestCommitTime(); });
54             var setIndex = 0;
55             for (var set of this._requestedRootSets) {
56                 this._rootSetToLabel.set(set, String.fromCharCode('A'.charCodeAt(0) + setIndex));
57                 setIndex++;
58             }
59
60         }
61         return this._requestedRootSets;
62     }
63
64     requestsForRootSet(rootSet)
65     {
66         this._orderBuildRequests();
67         return this._buildRequests.filter(function (request) { return request.rootSet() == rootSet; });
68     }
69
70     labelForRootSet(rootSet)
71     {
72         console.assert(this._requestedRootSets);
73         return this._rootSetToLabel.get(rootSet);
74     }
75
76     _orderBuildRequests()
77     {
78         if (this._requestsAreInOrder)
79             return;
80         this._buildRequests = this._buildRequests.sort(function (a, b) { return a.order() - b.order(); });
81         this._requestsAreInOrder = true;
82     }
83
84     didSetResult(request)
85     {
86         this._allRootSets = null;
87     }
88
89     hasCompleted()
90     {
91         return this._buildRequests.every(function (request) { return request.hasCompleted(); });
92     }
93
94     hasStarted()
95     {
96         return this._buildRequests.some(function (request) { return request.hasStarted(); });
97     }
98
99     compareTestResults(rootSetA, rootSetB)
100     {
101         var beforeValues = this._valuesForRootSet(rootSetA);
102         var afterValues = this._valuesForRootSet(rootSetB);
103         var beforeMean = Statistics.sum(beforeValues) / beforeValues.length;
104         var afterMean = Statistics.sum(afterValues) / afterValues.length;
105
106         var metric = AnalysisTask.findById(this._taskId).metric();
107         console.assert(metric);
108
109         var result = {changeType: null, status: 'failed', label: 'Failed', fullLabel: 'Failed', isStatisticallySignificant: false};
110         if (beforeValues.length && afterValues.length) {
111             var diff = afterMean - beforeMean;
112             var smallerIsBetter = metric.isSmallerBetter();
113             result.changeType = diff < 0 == smallerIsBetter ? 'better' : 'worse';
114             result.label = Math.abs(diff / beforeMean * 100).toFixed(2) + '% ' + result.changeType;
115             result.isStatisticallySignificant = Statistics.testWelchsT(beforeValues, afterValues);
116             result.status = result.isStatisticallySignificant ? result.changeType : 'unchanged';
117         }
118
119         if (!this.hasCompleted()) {
120             if (this.hasStarted()) {
121                 result.status = 'running';
122                 result.label = 'Running';
123                 result.fullLabel = 'Running';
124             } else {
125                 result.status = 'pending';
126                 result.label = 'Pending';
127                 result.fullLabel = 'Pending';
128             }
129         } else if (result.changeType) {
130             var significance = result.isStatisticallySignificant ? 'significant' : 'insignificant';
131             result.fullLabel = `${result.label} (statistically ${significance})`;
132         }
133
134         return result;
135     }
136
137     _valuesForRootSet(rootSet)
138     {
139         var requests = this.requestsForRootSet(rootSet);
140         var values = [];
141         for (var request of requests) {
142             if (request.result())
143                 values.push(request.result().value);
144         }
145         return values;
146     }
147
148     static createAndRefetchTestGroups(task, name, repetitionCount, rootSets)
149     {
150         var self = this;
151         return PrivilegedAPI.sendRequest('create-test-group', {
152             task: task.id(),
153             name: name,
154             repetitionCount: repetitionCount,
155             rootSets: rootSets,
156         }).then(function (data) {
157             return self.cachedFetch('../api/test-groups', {task: task.id()}, true).then(self._createModelsFromFetchedTestGroups.bind(self));
158         });
159     }
160
161     static fetchByTask(taskId)
162     {
163         return this.cachedFetch('../api/test-groups', {task: taskId}).then(this._createModelsFromFetchedTestGroups.bind(this));
164     }
165
166     static _createModelsFromFetchedTestGroups(data)
167     {
168         var testGroups = data['testGroups'].map(function (row) {
169             row.platform = Platform.findById(row.platform);
170             return TestGroup.ensureSingleton(row.id, row);
171         });
172
173         var rootIdMap = {};
174         for (var root of data['roots'])
175             rootIdMap[root.id] = root;
176
177         var rootSets = data['rootSets'].map(function (row) {
178             row.roots = row.roots.map(function (rootId) { return rootIdMap[rootId]; });
179             row.testGroup = RootSet.findById(row.testGroup);
180             return RootSet.ensureSingleton(row.id, row);
181         });
182
183         var buildRequests = data['buildRequests'].map(function (rawData) {
184             rawData.testGroup = TestGroup.findById(rawData.testGroup);
185             rawData.rootSet = RootSet.findById(rawData.rootSet);
186             return BuildRequest.ensureSingleton(rawData.id, rawData);
187         });
188
189         return testGroups;
190     }
191 }