35673bf410843eb466079c50c21c7493d10f98a6
[WebKit-https.git] / PerformanceTests / Animometer / resources / extensions.js
1 function Point(x, y)
2 {
3     this.x = x;
4     this.y = y;
5 }
6
7 Point.pointOnCircle = function(angle, radius)
8 {
9     return new Point(radius * Math.cos(angle), radius * Math.sin(angle));
10 }
11
12 Point.pointOnEllipse = function(angle, radiuses)
13 {
14     return new Point(radiuses.x * Math.cos(angle), radiuses.y * Math.sin(angle));
15 }
16
17 Point.elementClientSize = function(element)
18 {
19     return new Point(element.clientWidth, element.clientHeight);
20 }
21
22 Point.prototype =
23 {
24     // Used when the point object is used as a size object.
25     get width()
26     {
27         return this.x;
28     },
29     
30     // Used when the point object is used as a size object.
31     get height()
32     {
33         return this.y;
34     },
35     
36     // Used when the point object is used as a size object.
37     get center()
38     {
39         return new Point(this.x / 2, this.y / 2);
40     },
41     
42     add: function(other)
43     {
44         return new Point(this.x + other.x, this.y + other.y);
45     },
46     
47     subtract: function(other)
48     {
49         return new Point(this.x - other.x, this.y - other.y);
50     },
51     
52     multiply: function(other)
53     {
54         return new Point(this.x * other.x, this.y * other.y);
55     },
56     
57     move: function(angle, velocity, timeDelta)
58     {
59         return this.add(Point.pointOnCircle(angle, velocity * (timeDelta / 1000)));
60     }
61 }
62
63 function Insets(top, right, bottom, left)
64 {
65     this.top = top;
66     this.right = right;
67     this.bottom = bottom;
68     this.left = left;
69 }
70
71 Insets.prototype =
72 {
73     get width()
74     {
75         return this.left + this.right;
76     },
77
78     get height()
79     {
80         return this.top + this.bottom;
81     },
82     
83     get size()
84     {
85         return new Point(this.width, this.height);
86     }
87 }
88
89 function SimplePromise()
90 {
91     this._chainedPromise = null;
92     this._callback = null;
93 }
94
95 SimplePromise.prototype.then = function (callback)
96 {
97     if (this._callback)
98         throw "SimplePromise doesn't support multiple calls to then";
99         
100     this._callback = callback;
101     this._chainedPromise = new SimplePromise;
102     
103     if (this._resolved)
104         this.resolve(this._resolvedValue);
105
106     return this._chainedPromise;
107 }
108
109 SimplePromise.prototype.resolve = function (value)
110 {
111     if (!this._callback) {
112         this._resolved = true;
113         this._resolvedValue = value;
114         return;
115     }
116
117     var result = this._callback(value);
118     if (result instanceof SimplePromise) {
119         var chainedPromise = this._chainedPromise;
120         result.then(function (result) { chainedPromise.resolve(result); });
121     } else
122         this._chainedPromise.resolve(result);
123 }
124
125 window.DocumentExtension =
126 {
127     createElement : function(name, attrs, parentElement)
128     {
129         var element = document.createElement(name);
130
131         for (var key in attrs)
132             element.setAttribute(key, attrs[key]);
133
134         parentElement.appendChild(element);
135         return element;
136     },
137
138     createSvgElement: function(name, attrs, xlinkAttrs, parentElement)
139     {
140         const svgNamespace = "http://www.w3.org/2000/svg";
141         const xlinkNamespace = "http://www.w3.org/1999/xlink";
142
143         var element = document.createElementNS(svgNamespace, name);
144         
145         for (var key in attrs)
146             element.setAttribute(key, attrs[key]);
147             
148         for (var key in xlinkAttrs)
149             element.setAttributeNS(xlinkNamespace, key, xlinkAttrs[key]);
150             
151         parentElement.appendChild(element);
152         return element;
153     },
154     
155     insertCssRuleAfter: function(newRule, referenceRule)
156     {
157         var styleSheets = document.styleSheets;
158
159         for (var i = 0; i < styleSheets.length; ++i) {       
160             for (var j = 0; j < styleSheets[i].cssRules.length; ++j) {
161                 if (styleSheets[i].cssRules[j].selectorText == referenceRule) {
162                     styleSheets[i].insertRule(newRule, j + 1);
163                     return true;
164                 }
165             }
166         }
167         
168         return false;
169     }
170 }
171
172 function ProgressBar(element, ranges)
173 {
174     this.element = element;
175     this.ranges = ranges;
176     this.currentRange = 0;
177 }
178
179 ProgressBar.prototype =
180 {
181     _progressToPercent: function(progress)
182     {
183         return progress * (100 / this.ranges);
184     },
185     
186     incRange: function()
187     {
188         ++this.currentRange;
189     },
190     
191     setPos: function(progress)
192     {
193         this.element.style.width = this._progressToPercent(this.currentRange + progress) + "%";
194     }
195 }
196
197 function ResultsDashboard()
198 {
199     this._iterationsSamplers = [];
200 }
201
202 ResultsDashboard.prototype =
203 {
204     push: function(suitesSamplers)
205     {
206         this._iterationsSamplers.push(suitesSamplers);        
207     },
208     
209     toJSON: function(statistics, graph)
210     {
211         var iterationsResults = [];
212         var iterationsScores = [];
213         
214         this._iterationsSamplers.forEach(function(iterationSamplers, index) {
215             var suitesResults = {};
216             var suitesScores = [];
217         
218             for (var suiteName in iterationSamplers) {
219                 var suite = suiteFromName(suiteName);
220                 var suiteSamplers = iterationSamplers[suiteName];
221
222                 var testsResults = {};
223                 var testsScores = [];
224                 
225                 for (var testName in suiteSamplers) {
226                     var sampler = suiteSamplers[testName];
227                     testsResults[testName] = sampler.toJSON(statistics, graph);
228                     testsScores.push(testsResults[testName][Strings["JSON_SCORE"]]);
229                 }
230
231                 suitesResults[suiteName] =  {};
232                 suitesResults[suiteName][Strings["JSON_SCORE"]] = Statistics.geometricMean(testsScores);
233                 suitesResults[suiteName][Strings["JSON_RESULTS"][2]] = testsResults;
234                 suitesScores.push(suitesResults[suiteName][Strings["JSON_SCORE"]]);
235             }
236             
237             iterationsResults[index] = {};
238             iterationsResults[index][Strings["JSON_SCORE"]] = Statistics.geometricMean(suitesScores);
239             iterationsResults[index][Strings["JSON_RESULTS"][1]] = suitesResults;
240             iterationsScores.push(iterationsResults[index][Strings["JSON_SCORE"]]);
241         });
242
243         var json = {};
244         json[Strings["JSON_SCORE"]] = Statistics.sampleMean(iterationsScores.length, iterationsScores.reduce(function(a, b) { return a * b; }));
245         json[Strings["JSON_RESULTS"][0]] = iterationsResults;
246         return json;
247     }
248 }
249
250 function ResultsTable(element, headers)
251 {
252     this.element = element;
253     this._headers = headers;
254     this.clear();
255 }
256
257 ResultsTable.prototype =
258 {
259     clear: function()
260     {
261         this.element.innerHTML = "";
262     },
263
264     _showHeaderRow: function(row, queue, headers, message)
265     {
266         headers.forEach(function (header) {
267             var th = document.createElement("th");
268             th.textContent = header.text;
269             if (typeof message != "undefined" && message.length) {
270                 th.appendChild(document.createElement('br'));
271                 th.appendChild(document.createTextNode('[' + message +']'));
272                 message = "";
273             }
274             if ("width" in header)
275                 th.width = header.width + "%";
276             row.appendChild(th);
277             queue.push({element: th, headers: header.children });
278         });
279     },
280
281     _showHeader: function(message)
282     {
283         var thead = DocumentExtension.createElement("thead", {}, this.element);
284         var row = DocumentExtension.createElement("tr", {}, thead);
285
286         var queue = [];
287         this._showHeaderRow(row, queue, this._headers, message);
288
289         while (queue.length) {
290             var row = null;
291             var entries = [];
292
293             for (var i = 0, len = queue.length; i < len; ++i) {
294                 var entry = queue.shift();
295
296                 if (!entry.headers.length) {
297                     entries.push(entry.element);
298                     continue;
299                 }
300
301                 if (!row)
302                     row = DocumentExtension.createElement("tr", {}, thead);
303
304                 this._showHeaderRow(row, queue, entry.headers, "");
305                 entry.element.colSpan = entry.headers.length;
306             }
307
308             if (row) {
309                 entries.forEach(function(entry) {
310                     ++entry.rowSpan;
311                 });
312             }
313         }
314     },
315     
316     _showEmpty: function(row)
317     {
318         var td = document.createElement("td");
319         row.appendChild(td);
320     },
321
322     _showText: function(row, text)
323     {
324         var td = document.createElement("td");
325         td.textContent = text;
326         row.appendChild(td);
327     },
328
329     _showFixedNumber: function(row, value, digits)
330     {
331         var td = document.createElement("td");
332         td.textContent = value.toFixed(digits || 2);
333         row.appendChild(td);
334     },
335     
336     _showGraph: function(row, testName, testResults)
337     {
338         var data = testResults[Strings["JSON_SAMPLES"][0]];
339         if (!data) {
340             this._showEmpty(row);
341             return;
342         }
343         
344         var td = document.createElement("td");
345         var button = document.createElement("button");
346         button.className = "small-button";
347
348         button.addEventListener("click", function() {
349             var samples = data[Strings["JSON_GRAPH"][0]];
350             var samplingTimeOffset = data[Strings["JSON_GRAPH"][1]];
351             var axes = Strings["TEXT_EXPERIMENTS"];
352             benchmarkController.showTestGraph(testName, axes, samples, samplingTimeOffset);
353         });
354             
355         button.textContent = Strings["TEXT_RESULTS"][1] + "...";
356         td.appendChild(button);
357         row.appendChild(td);
358     },
359
360     _showJSON: function(row, testName, testResults)
361     {
362         var data = testResults[Strings["JSON_SAMPLES"][0]];
363         if (!data) {
364             this._showEmpty(row);
365             return;
366         }
367         
368         var td = document.createElement("td");
369         var button = document.createElement("button");
370         button.className = "small-button";
371
372         button.addEventListener("click", function() {
373             benchmarkController.showTestJSON(testName, testResults);
374         });
375             
376         button.textContent = Strings["TEXT_RESULTS"][2] + "...";
377         td.appendChild(button);
378         row.appendChild(td);
379     },
380
381     _showTest: function(testName, testResults)
382     {
383         var row = document.createElement("tr");
384         
385         for (var index = 0; index < this._headers.length; ++index) {
386
387             switch (index) {
388             case 0:
389                 this._showText(row, testName);
390                 break;
391
392             case 1:
393                 var data = testResults[Strings["JSON_SCORE"][0]];
394                 this._showFixedNumber(row, data, 2);
395                 break;
396
397             case 2:
398             case 3:
399                 var data = testResults[Strings["JSON_EXPERIMENTS"][index - 2]];
400                 for (var measurement in data)
401                     this._showFixedNumber(row, data[measurement], 2);
402                 break;
403                 
404             case 4:
405                 this._showGraph(row, testName, testResults);
406                 this._showJSON(row, testName, testResults);
407                 break;
408             }
409         }
410         
411         this.element.appendChild(row);
412     },
413
414     _showSuite: function(suiteName, suiteResults)
415     {
416         for (var testName in suiteResults[Strings["JSON_RESULTS"][2]]) {
417             this._showTest(testName, suiteResults[Strings["JSON_RESULTS"][2]][testName]);
418         }
419     },
420     
421     _showIteration : function(iterationResults)
422     {
423         for (var suiteName in iterationResults[Strings["JSON_RESULTS"][1]]) {
424             this._showSuite(suiteName, iterationResults[Strings["JSON_RESULTS"][1]][suiteName]);
425         }
426     },
427     
428     showRecord: function(testName, message, testResults)
429     {
430         this.clear();
431         this._showHeader(message);
432         this._showTest(testName, testResults);
433     },
434
435     showIterations: function(iterationsResults)
436     {
437         this.clear();
438         this._showHeader("");
439         
440         iterationsResults.forEach(function(iterationResults) {
441             this._showIteration(iterationResults);
442         }, this);
443     }
444 }
445