window.benchmarkClient = {
displayUnit: 'runs/min',
iterationCount: 10,
stepCount: null,
suitesCount: null,
_measuredValuesList: [],
_finishedTestCount: 0,
_progressCompleted: null,
willAddTestFrame: function (frame) {
var main = document.querySelector('main');
var style = getComputedStyle(main);
frame.style.left = main.offsetLeft + parseInt(style.borderLeftWidth) + parseInt(style.paddingLeft) + 'px';
frame.style.top = main.offsetTop + parseInt(style.borderTopWidth) + parseInt(style.paddingTop) + 'px';
},
willRunTest: function (suite, test) {
document.getElementById('info').textContent = suite.name + ' ( ' + this._finishedTestCount + ' / ' + this.stepCount + ' )';
},
didRunTest: function () {
this._finishedTestCount++;
this._progressCompleted.style.width = (this._finishedTestCount * 100 / this.stepCount) + '%';
},
didRunSuites: function (measuredValues) {
this._measuredValuesList.push(measuredValues);
},
willStartFirstIteration: function () {
this._measuredValuesList = [];
this._finishedTestCount = 0;
this._progressCompleted = document.getElementById('progress-completed');
document.getElementById('logo-link').onclick = function (event) { event.preventDefault(); return false; }
},
didFinishLastIteration: function () {
document.getElementById('logo-link').onclick = null;
var results = this._computeResults(this._measuredValuesList, this.displayUnit);
this._updateGaugeNeedle(results.mean);
document.getElementById('result-number').textContent = results.formattedMean;
if (results.formattedDelta)
document.getElementById('confidence-number').textContent = '\u00b1 ' + results.formattedDelta;
this._populateDetailedResults(results.formattedValues);
document.getElementById('results-with-statistics').textContent = results.formattedMeanAndDelta;
if (this.displayUnit == 'ms') {
document.getElementById('show-summary').style.display = 'none';
showResultDetails();
} else
showResultsSummary();
},
_computeResults: function (measuredValuesList, displayUnit) {
var suitesCount = this.suitesCount;
function valueForUnit(measuredValues) {
if (displayUnit == 'ms')
return measuredValues.geomean;
return measuredValues.score;
}
function sigFigFromPercentDelta(percentDelta) {
return Math.ceil(-Math.log(percentDelta)/Math.log(10)) + 3;
}
function toSigFigPrecision(number, sigFig) {
var nonDecimalDigitCount = number < 1 ? 0 : (Math.floor(Math.log(number)/Math.log(10)) + 1);
return number.toPrecision(Math.max(nonDecimalDigitCount, Math.min(6, sigFig)));
}
var values = measuredValuesList.map(valueForUnit);
var sum = values.reduce(function (a, b) { return a + b; }, 0);
var arithmeticMean = sum / values.length;
var meanSigFig = 4;
var formattedDelta;
var formattedPercentDelta;
if (window.Statistics) {
var delta = Statistics.confidenceIntervalDelta(0.95, values.length, sum, Statistics.squareSum(values));
if (!isNaN(delta)) {
var percentDelta = delta * 100 / arithmeticMean;
meanSigFig = sigFigFromPercentDelta(percentDelta);
formattedDelta = toSigFigPrecision(delta, 2);
formattedPercentDelta = toSigFigPrecision(percentDelta, 2) + '%';
}
}
var formattedMean = toSigFigPrecision(arithmeticMean, Math.max(meanSigFig, 3));
return {
formattedValues: values.map(function (value) {
return toSigFigPrecision(value, 4) + ' ' + displayUnit;
}),
mean: arithmeticMean,
formattedMean: formattedMean,
formattedDelta: formattedDelta,
formattedMeanAndDelta: formattedMean + (formattedDelta ? ' \xb1 ' + formattedDelta + ' (' + formattedPercentDelta + ')' : ''),
};
},
_addDetailedResultsRow: function (table, iterationNumber, value) {
var row = document.createElement('tr');
var th = document.createElement('th');
th.textContent = 'Iteration ' + (iterationNumber + 1);
var td = document.createElement('td');
td.textContent = value;
row.appendChild(th);
row.appendChild(td);
table.appendChild(row);
},
_updateGaugeNeedle: function (rpm) {
var needleAngle = Math.max(0, Math.min(rpm, 140)) - 70;
var needleRotationValue = 'rotate(' + needleAngle + 'deg)';
var gaugeNeedleElement = document.querySelector('#summarized-results > .gauge .needle');
gaugeNeedleElement.style.setProperty('-webkit-transform', needleRotationValue);
gaugeNeedleElement.style.setProperty('-moz-transform', needleRotationValue);
gaugeNeedleElement.style.setProperty('-ms-transform', needleRotationValue);
gaugeNeedleElement.style.setProperty('transform', needleRotationValue);
},
_populateDetailedResults: function (formattedValues) {
var resultsTables = document.querySelectorAll('.results-table');
var i = 0;
resultsTables[0].innerHTML = '';
for (; i < Math.ceil(formattedValues.length / 2); i++)
this._addDetailedResultsRow(resultsTables[0], i, formattedValues[i]);
resultsTables[1].innerHTML = '';
for (; i < formattedValues.length; i++)
this._addDetailedResultsRow(resultsTables[1], i, formattedValues[i]);
},
prepareUI: function () {
window.addEventListener('popstate', function (event) {
if (event.state) {
var sectionToShow = event.state.section;
if (sectionToShow) {
var sections = document.querySelectorAll('main > section');
for (var i = 0; i < sections.length; i++) {
if (sections[i].id === sectionToShow)
return showSection(sectionToShow, false);
}
}
}
return showSection('home', false);
}, false);
function updateScreenSize() {
// FIXME: Detect when the window size changes during the test.
var screenIsTooSmall = window.innerWidth < 850 || window.innerHeight < 650;
document.getElementById('screen-size').textContent = window.innerWidth + 'px by ' + window.innerHeight + 'px';
document.getElementById('screen-size-warning').style.display = screenIsTooSmall ? null : 'none';
}
window.addEventListener('resize', updateScreenSize);
updateScreenSize();
}
}
function enableOneSuite(suites, suiteToEnable)
{
suiteToEnable = suiteToEnable.toLowerCase();
var found = false;
for (var i = 0; i < suites.length; i++) {
var currentSuite = suites[i];
if (currentSuite.name.toLowerCase() == suiteToEnable) {
currentSuite.disabled = false;
found = true;
} else
currentSuite.disabled = true;
}
return found;
}
function startBenchmark() {
if (location.search.length > 1) {
var parts = location.search.substring(1).split('&');
for (var i = 0; i < parts.length; i++) {
var keyValue = parts[i].split('=');
var key = keyValue[0];
var value = keyValue[1];
switch (key) {
case 'unit':
if (value == 'ms')
benchmarkClient.displayUnit = 'ms';
else
console.error('Invalid unit: ' + value);
break;
case 'iterationCount':
var parsedValue = parseInt(value);
if (!isNaN(parsedValue))
benchmarkClient.iterationCount = parsedValue;
else
console.error('Invalid iteration count: ' + value);
break;
case 'suite':
if (!enableOneSuite(Suites, value)) {
alert('Suite "' + value + '" does not exist. No tests to run.');
return false;
}
break;
}
}
}
var enabledSuites = Suites.filter(function (suite) { return !suite.disabled; });
var totalSubtestsCount = enabledSuites.reduce(function (testsCount, suite) { return testsCount + suite.tests.length; }, 0);
benchmarkClient.stepCount = benchmarkClient.iterationCount * totalSubtestsCount;
benchmarkClient.suitesCount = enabledSuites.length;
var runner = new BenchmarkRunner(Suites, benchmarkClient);
runner.runMultipleIterations(benchmarkClient.iterationCount);
return true;
}
function showSection(sectionIdentifier, pushState) {
var currentSectionElement = document.querySelector('section.selected');
console.assert(currentSectionElement);
var newSectionElement = document.getElementById(sectionIdentifier);
console.assert(newSectionElement);
currentSectionElement.classList.remove('selected');
newSectionElement.classList.add('selected');
if (pushState)
history.pushState({section: sectionIdentifier}, document.title);
}
function showHome() {
showSection('home', true);
}
function startTest() {
if (startBenchmark())
showSection('running');
}
function showResultsSummary() {
showSection('summarized-results', true);
}
function showResultDetails() {
showSection('detailed-results', true);
}
function showAbout() {
showSection('about', true);
}
window.addEventListener('DOMContentLoaded', function () {
if (benchmarkClient.prepareUI)
benchmarkClient.prepareUI();
});