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