Merge the latest version of Speedometer 2.0 to browserbench.org against at r222534.
[WebKit-https.git] / Websites / browserbench.org / Speedometer2.0 / resources / main.js
1 window.benchmarkClient = {
2     displayUnit: 'runs/min',
3     iterationCount: 10,
4     stepCount: null,
5     suitesCount: null,
6     _measuredValuesList: [],
7     _finishedTestCount: 0,
8     _progressCompleted: null,
9     willAddTestFrame: function (frame) {
10         var main = document.querySelector('main');
11         var style = getComputedStyle(main);
12         frame.style.left = main.offsetLeft + parseInt(style.borderLeftWidth) + parseInt(style.paddingLeft) + 'px';
13         frame.style.top = main.offsetTop + parseInt(style.borderTopWidth) + parseInt(style.paddingTop) + 'px';
14     },
15     willRunTest: function (suite, test) {
16         document.getElementById('info').textContent = suite.name + ' ( ' + this._finishedTestCount + ' / ' + this.stepCount + ' )';
17     },
18     didRunTest: function () {
19         this._finishedTestCount++;
20         this._progressCompleted.style.width = (this._finishedTestCount * 100 / this.stepCount) + '%';
21     },
22     didRunSuites: function (measuredValues) {
23         this._measuredValuesList.push(measuredValues);
24     },
25     willStartFirstIteration: function () {
26         this._measuredValuesList = [];
27         this._finishedTestCount = 0;
28         this._progressCompleted = document.getElementById('progress-completed');
29         document.getElementById('logo-link').onclick = function (event) { event.preventDefault(); return false; }
30     },
31     didFinishLastIteration: function () {
32         document.getElementById('logo-link').onclick = null;
33
34         var results = this._computeResults(this._measuredValuesList, this.displayUnit);
35
36         this._updateGaugeNeedle(results.mean);
37         document.getElementById('result-number').textContent = results.formattedMean;
38         if (results.formattedDelta)
39             document.getElementById('confidence-number').textContent = '\u00b1 ' + results.formattedDelta;
40
41         this._populateDetailedResults(results.formattedValues);
42         document.getElementById('results-with-statistics').textContent = results.formattedMeanAndDelta;
43
44         if (this.displayUnit == 'ms') {
45             document.getElementById('show-summary').style.display = 'none';
46             showResultDetails();
47         } else
48             showResultsSummary();
49     },
50     _computeResults: function (measuredValuesList, displayUnit) {
51         var suitesCount = this.suitesCount;
52         function valueForUnit(measuredValues) {
53             if (displayUnit == 'ms')
54                 return measuredValues.geomean;
55             return measuredValues.score;
56         }
57
58         function sigFigFromPercentDelta(percentDelta) {
59             return Math.ceil(-Math.log(percentDelta)/Math.log(10)) + 3;
60         }
61
62         function toSigFigPrecision(number, sigFig) {
63             var nonDecimalDigitCount = number < 1 ? 0 : (Math.floor(Math.log(number)/Math.log(10)) + 1);
64             return number.toPrecision(Math.max(nonDecimalDigitCount, Math.min(6, sigFig)));
65         }
66
67         var values = measuredValuesList.map(valueForUnit);
68         var sum = values.reduce(function (a, b) { return a + b; }, 0);
69         var arithmeticMean = sum / values.length;
70         var meanSigFig = 4;
71         var formattedDelta;
72         var formattedPercentDelta;
73         if (window.Statistics) {
74             var delta = Statistics.confidenceIntervalDelta(0.95, values.length, sum, Statistics.squareSum(values));
75             if (!isNaN(delta)) {
76                 var percentDelta = delta * 100 / arithmeticMean;
77                 meanSigFig = sigFigFromPercentDelta(percentDelta);
78                 formattedDelta = toSigFigPrecision(delta, 2);
79                 formattedPercentDelta = toSigFigPrecision(percentDelta, 2) + '%';
80             }
81         }
82
83         var formattedMean = toSigFigPrecision(arithmeticMean, Math.max(meanSigFig, 3));
84
85         return {
86             formattedValues: values.map(function (value) {
87                 return toSigFigPrecision(value, 4) + ' ' + displayUnit;
88             }),
89             mean: arithmeticMean,
90             formattedMean: formattedMean,
91             formattedDelta: formattedDelta,
92             formattedMeanAndDelta: formattedMean + (formattedDelta ? ' \xb1 ' + formattedDelta + ' (' + formattedPercentDelta + ')' : ''),
93         };
94     },
95     _addDetailedResultsRow: function (table, iterationNumber, value) {
96         var row = document.createElement('tr');
97         var th = document.createElement('th');
98         th.textContent = 'Iteration ' + (iterationNumber + 1);
99         var td = document.createElement('td');
100         td.textContent = value;
101         row.appendChild(th);
102         row.appendChild(td);
103         table.appendChild(row);
104     },
105     _updateGaugeNeedle: function (rpm) {
106         var needleAngle = Math.max(0, Math.min(rpm, 140)) - 70;
107         var needleRotationValue = 'rotate(' + needleAngle + 'deg)';
108
109         var gaugeNeedleElement = document.querySelector('#summarized-results > .gauge .needle');
110         gaugeNeedleElement.style.setProperty('-webkit-transform', needleRotationValue);
111         gaugeNeedleElement.style.setProperty('-moz-transform', needleRotationValue);
112         gaugeNeedleElement.style.setProperty('-ms-transform', needleRotationValue);
113         gaugeNeedleElement.style.setProperty('transform', needleRotationValue);
114     },
115     _populateDetailedResults: function (formattedValues) {
116         var resultsTables = document.querySelectorAll('.results-table');
117         var i = 0;
118         resultsTables[0].innerHTML = '';
119         for (; i < Math.ceil(formattedValues.length / 2); i++)
120             this._addDetailedResultsRow(resultsTables[0], i, formattedValues[i]);
121         resultsTables[1].innerHTML = '';
122         for (; i < formattedValues.length; i++)
123             this._addDetailedResultsRow(resultsTables[1], i, formattedValues[i]);
124     },
125     prepareUI: function () {
126         window.addEventListener('popstate', function (event) {
127             if (event.state) {
128                 var sectionToShow = event.state.section;
129                 if (sectionToShow) {
130                     var sections = document.querySelectorAll('main > section');
131                     for (var i = 0; i < sections.length; i++) {
132                         if (sections[i].id === sectionToShow)
133                             return showSection(sectionToShow, false);
134                     }
135                 }
136             }
137             return showSection('home', false);
138         }, false);
139
140         function updateScreenSize() {
141             // FIXME: Detect when the window size changes during the test.
142             var screenIsTooSmall = window.innerWidth < 850 || window.innerHeight < 650;
143             document.getElementById('screen-size').textContent = window.innerWidth + 'px by ' + window.innerHeight + 'px';
144             document.getElementById('screen-size-warning').style.display = screenIsTooSmall ? null : 'none';
145         }
146
147         window.addEventListener('resize', updateScreenSize);
148         updateScreenSize();
149     }
150 }
151
152 function enableOneSuite(suites, suiteToEnable)
153 {
154     suiteToEnable = suiteToEnable.toLowerCase();
155     var found = false;
156     for (var i = 0; i < suites.length; i++) {
157         var currentSuite = suites[i];
158         if (currentSuite.name.toLowerCase() == suiteToEnable) {
159             currentSuite.disabled = false;
160             found = true;
161         } else
162             currentSuite.disabled = true;
163     }
164     return found;
165 }
166
167 function startBenchmark() {
168     if (location.search.length > 1) {
169         var parts = location.search.substring(1).split('&');
170         for (var i = 0; i < parts.length; i++) {
171             var keyValue = parts[i].split('=');
172             var key = keyValue[0];
173             var value = keyValue[1];
174             switch (key) {
175             case 'unit':
176                 if (value == 'ms')
177                     benchmarkClient.displayUnit = 'ms';
178                 else
179                     console.error('Invalid unit: ' + value);
180                 break;
181             case 'iterationCount':
182                 var parsedValue = parseInt(value);
183                 if (!isNaN(parsedValue))
184                     benchmarkClient.iterationCount = parsedValue;
185                 else
186                     console.error('Invalid iteration count: ' + value);
187                 break;
188             case 'suite':
189                 if (!enableOneSuite(Suites, value)) {
190                     alert('Suite "' + value + '" does not exist. No tests to run.');
191                     return false;
192                 }
193                 break;
194             }
195         }
196     }
197
198     var enabledSuites = Suites.filter(function (suite) { return !suite.disabled; });
199     var totalSubtestsCount = enabledSuites.reduce(function (testsCount, suite) { return testsCount + suite.tests.length; }, 0);
200     benchmarkClient.stepCount = benchmarkClient.iterationCount * totalSubtestsCount;
201     benchmarkClient.suitesCount = enabledSuites.length;
202     var runner = new BenchmarkRunner(Suites, benchmarkClient);
203     runner.runMultipleIterations(benchmarkClient.iterationCount);
204
205     return true;
206 }
207
208 function showSection(sectionIdentifier, pushState) {
209     var currentSectionElement = document.querySelector('section.selected');
210     console.assert(currentSectionElement);
211
212     var newSectionElement = document.getElementById(sectionIdentifier);
213     console.assert(newSectionElement);
214
215     currentSectionElement.classList.remove('selected');
216     newSectionElement.classList.add('selected');
217
218     if (pushState)
219         history.pushState({section: sectionIdentifier}, document.title);
220 }
221
222 function showHome() {
223     showSection('home', true);
224 }
225
226 function startTest() {
227     if (startBenchmark())
228         showSection('running');
229 }
230
231 function showResultsSummary() {
232     showSection('summarized-results', true);
233 }
234
235 function showResultDetails() {
236     showSection('detailed-results', true);
237 }
238
239 function showAbout() {
240     showSection('about', true);
241 }
242
243 window.addEventListener('DOMContentLoaded', function () {
244     if (benchmarkClient.prepareUI)
245         benchmarkClient.prepareUI();
246 });