video-object-fit tests are flaky
[WebKit-https.git] / PerformanceTests / DoYouEvenBench / benchmark.js
1
2 // FIXME: Use the real promise if available.
3 // FIXME: Make sure this interface is compatible with the real Promise.
4 function SimplePromise() {
5     this._chainedPromise = null;
6     this._callback = null;
7 }
8
9 SimplePromise.prototype.then = function (callback) {
10     if (this._callback)
11     throw "SimplePromise doesn't support multiple calls to then";
12     this._callback = callback;
13     this._chainedPromise = new SimplePromise;
14     
15     if (this._resolved)
16         this.resolve(this._resolvedValue);
17
18     return this._chainedPromise;
19 }
20
21 SimplePromise.prototype.resolve = function (value) {
22     if (!this._callback) {
23         this._resolved = true;
24         this._resolvedValue = value;
25         return;
26     }
27
28     var result = this._callback(value);
29     if (result instanceof SimplePromise) {
30         var chainedPromise = this._chainedPromise;
31         result.then(function (result) { chainedPromise.resolve(result); });
32     } else
33         this._chainedPromise.resolve(result);
34 }
35
36 var BenchmarkRunner = {_suites: [], _prepareReturnValue: null, _measuredValues: {}};
37
38 BenchmarkRunner.suite = function (suite) {
39     var self = BenchmarkRunner;
40     self._suites.push(suite);
41 }
42
43 BenchmarkRunner.waitForElement = function (selector) {
44     var self = BenchmarkRunner;
45     var promise = new SimplePromise;
46     var contentDocument = self._frame.contentDocument;
47
48     function resolveIfReady() {
49         var element = contentDocument.querySelector(selector);
50         if (element)
51             return promise.resolve(element);
52         setTimeout(resolveIfReady, 50);
53     }
54
55     resolveIfReady();
56     return promise;
57 }
58
59 BenchmarkRunner._removeFrame = function () {
60     var self = BenchmarkRunner;
61     if (self._frame) {
62         self._frame.parentNode.removeChild(self._frame);
63         self._frame = null;
64     }
65 }
66
67 BenchmarkRunner._appendFrame = function (src) {
68     var self = BenchmarkRunner;
69     var frame = document.createElement('iframe');
70     document.body.appendChild(frame);
71     self._frame = frame;
72     return frame;
73 }
74
75 BenchmarkRunner._waitAndWarmUp = function () {
76     var startTime = Date.now();
77
78     function Fibonacci(n) {
79         if (Date.now() - startTime > 100)
80             return;
81         if (n <= 0)
82             return 0;
83         else if (n == 1)
84             return 1;
85         return Fibonacci(n - 2) + Fibonacci(n - 1);
86     }
87
88     var promise = new SimplePromise;
89     setTimeout(function () {
90         Fibonacci(100);
91         promise.resolve();
92     }, 200);
93     return promise;
94 }
95
96 // This function ought be as simple as possible. Don't even use SimplePromise.
97 BenchmarkRunner._runTest = function(suite, testFunction, prepareReturnValue, callback)
98 {
99     var self = BenchmarkRunner;
100     var now = window.performance && window.performance.now ? function () { return window.performance.now(); } : Date.now;
101
102     var contentWindow = self._frame.contentWindow;
103     var contentDocument = self._frame.contentDocument;
104
105     var startTime = now();
106     testFunction(prepareReturnValue, contentWindow, contentDocument);
107     var endTime = now();
108     var syncTime = endTime - startTime;
109
110     var startTime = now();
111     setTimeout(function () {
112         var endTime = now();
113         callback(syncTime, endTime - startTime);
114     }, 0);
115 }
116
117 BenchmarkRunner._testName = function (suite, testName, metric) {
118     if (!testName)
119         return suite.name;
120     return suite.name + '/' + testName + (metric ? '/' + metric : '');
121 }
122
123 BenchmarkRunner._testItemId = function (suite, testName) {
124     return suite.name + '-' + testName;
125 }
126
127 BenchmarkRunner.listSuites = function () {
128     var self = BenchmarkRunner;
129
130     var control = document.createElement('nav');
131
132     var suites = self._suites;
133     var ol = document.createElement('ol');
134     var checkboxes = [];
135     for (var suiteIndex = 0; suiteIndex < suites.length; suiteIndex++) {
136         var suite = suites[suiteIndex];
137         var li = document.createElement('li');
138         var checkbox = document.createElement('input');
139         checkbox.id = suite.name;
140         checkbox.type = 'checkbox';
141         checkbox.checked = true;
142         checkboxes.push(checkbox);
143
144         li.appendChild(checkbox);
145         var label = document.createElement('label');
146         label.appendChild(document.createTextNode(self._testName(suite)));
147         li.appendChild(label);
148         label.htmlFor = checkbox.id;
149
150         var testList = document.createElement('ol');
151         for (var testIndex = 0; testIndex < suite.tests.length; testIndex++) {
152             var testItem = document.createElement('li');
153             var test = suite.tests[testIndex];
154             var anchor = document.createElement('a');
155             anchor.id = self._testItemId(suite, test[0]);
156             anchor.appendChild(document.createTextNode(self._testName(suite, test[0])));
157             testItem.appendChild(anchor);
158             testList.appendChild(testItem);
159         }
160         li.appendChild(testList);
161
162         ol.appendChild(li);
163     }
164
165     control.appendChild(ol);
166
167     var currentState = null;
168
169     // Don't call step while step is already executing.
170     var button = document.createElement('button');
171     button.textContent = 'Step';
172     button.onclick = function () {
173         self.step(currentState).then(function (state) { currentState = state; });
174     }
175     control.appendChild(button);
176
177     function callNextStep(state) {
178         self.step(state).then(function (newState) {
179             currentState = newState;
180             if (newState)
181                 callNextStep(newState);
182         });
183     }
184
185     var button = document.createElement('button');
186     button.textContent = 'Run';
187     button.onclick = function () { callNextStep(currentState); }
188     control.appendChild(button);
189
190     document.body.appendChild(control);
191 }
192
193 function BenchmarkState(suites) {
194     this._suites = suites;
195     this._suiteIndex = -1;
196     this._testIndex = 0;
197     this.next();
198 }
199
200 BenchmarkState.prototype.currentSuite = function() {
201     return this._suites[this._suiteIndex];
202 }
203
204 BenchmarkState.prototype.currentTest = function () {
205     var suite = this.currentSuite();
206     return suite ? suite.tests[this._testIndex] : null;
207 }
208
209 BenchmarkState.prototype.next = function () {
210     this._testIndex++;
211
212     var suite = this._suites[this._suiteIndex];
213     if (suite && this._testIndex < suite.tests.length)
214         return this;
215
216     this._testIndex = 0;
217     do {
218         this._suiteIndex++;
219     } while (this._suiteIndex < this._suites.length && !document.getElementById(this._suites[this._suiteIndex].name).checked);
220
221     return this;
222 }
223
224 BenchmarkState.prototype.isFirstTest = function () {
225     return !this._testIndex;
226 }
227
228 BenchmarkState.prototype.prepareCurrentSuite = function (frame) {
229     var self = this;
230     var suite = this.currentSuite();
231     var promise = new SimplePromise;
232     frame.onload = function () {
233         suite.prepare(frame.contentWindow, frame.contentDocument).then(function (result) { promise.resolve(result); });
234     }
235     frame.src = suite.url;
236     return promise;
237 }
238
239 BenchmarkRunner.step = function (state) {
240     var self = BenchmarkRunner;
241
242     if (!state)
243         state = new BenchmarkState(self._suites);
244
245     var suite = state.currentSuite();
246     if (!suite) {
247         self._finalize();
248         var promise = new SimplePromise;
249         promise.resolve();
250         return promise;
251     }
252
253     if (state.isFirstTest()) {
254         self._masuredValuesForCurrentSuite = {};
255         return state.prepareCurrentSuite(self._appendFrame()).then(function (prepareReturnValue) {
256             self._prepareReturnValue = prepareReturnValue;
257             return self._runTestAndRecordResults(state);
258         });
259     }
260
261     return self._runTestAndRecordResults(state);
262 }
263
264 BenchmarkRunner._runTestAndRecordResults = function (state) {
265     var self = BenchmarkRunner;
266     var promise = new SimplePromise;
267     var suite = state.currentSuite();
268     var test = state.currentTest();
269
270     var testName = test[0];
271     var testItem = document.getElementById(self._testItemId(suite, testName));
272     testItem.classList.add('running');
273     setTimeout(function () {
274         self._runTest(suite, test[1], self._prepareReturnValue, function (syncTime, asyncTime) {
275             self._masuredValuesForCurrentSuite[self._testName(suite, testName, 'Sync')] = syncTime;
276             self._masuredValuesForCurrentSuite[self._testName(suite, testName, 'Async')] = asyncTime;
277             testItem.classList.remove('running');
278             testItem.classList.add('ran');
279             state.next();
280             if (state.currentSuite() != suite) {
281                 var total = 0;
282                 for (var title in self._masuredValuesForCurrentSuite) {
283                     var value = self._masuredValuesForCurrentSuite[title];
284                     total += value;
285                     self._measuredValues[title] = value;
286                 }
287                 self._measuredValues[self._testName(suite)] = total;
288                 self._removeFrame();
289             }
290             promise.resolve(state);
291         });
292     }, 0);
293     return promise;
294 }
295
296 BenchmarkRunner._finalize = function () {
297     var self = BenchmarkRunner;
298
299     var results = '';
300     var total = 0; // FIXME: Compute the total properly.
301     for (var title in self._measuredValues) {
302         results += title + ' : ' + self._measuredValues[title] + ' ms\n';
303         total += self._measuredValues[title];
304     }
305     results += 'Total : ' + (total / 2) + ' ms\n';
306     self._measuredValues = {};
307
308     self._removeFrame();
309
310     if (!results)
311         return;
312
313     var pre = document.createElement('pre');
314     document.body.appendChild(pre);
315     pre.textContent = results;
316 }
317
318 window.addEventListener('load', function () { BenchmarkRunner.listSuites(); });
319
320 (function () {
321     var style = document.createElement('style');
322     style.appendChild(document.createTextNode('iframe { width: 700px; height: 500px; border: 2px solid black; }'
323         + 'ol { list-style: none; margin: 0; padding: 0; }'
324         + 'ol ol { margin-left: 2em; list-position: outside; }'
325         + '.running { text-decoration: underline; }'
326         + '.ran {color: grey}'
327         + 'nav { position: absolute; right: 10px; }'));
328     document.head.appendChild(style);
329 })();