213 lines
7 KiB
JavaScript
213 lines
7 KiB
JavaScript
import { BenchmarkRunner } from "./benchmark-runner.mjs";
|
|
import { params } from "./params.mjs";
|
|
import { Suites } from "./tests.mjs";
|
|
|
|
class InteractiveBenchmarkRunner extends BenchmarkRunner {
|
|
_stepPromise = undefined;
|
|
_stepPromiseResolve = undefined;
|
|
_isRunning = false;
|
|
_isStepping = false;
|
|
|
|
constructor(suites, iterationCount) {
|
|
super(suites);
|
|
this._client = this._createClient();
|
|
if (!Number.isInteger(iterationCount) || iterationCount <= 0)
|
|
throw Error("iterationCount must be a positive integer.");
|
|
this._iterationCount = iterationCount;
|
|
}
|
|
|
|
_createClient() {
|
|
return {
|
|
willStartFirstIteration: this._start.bind(this),
|
|
willRunTest: this._testStart.bind(this),
|
|
didRunTest: this._testDone.bind(this),
|
|
didRunSuites: this._iterationDone.bind(this),
|
|
didFinishLastIteration: this._done.bind(this),
|
|
};
|
|
}
|
|
|
|
_start() {
|
|
if (this._isRunning)
|
|
throw Error("Runner was not stopped before starting;");
|
|
this._isRunning = true;
|
|
if (this._isStepping)
|
|
this._stepPromise = this._newStepPromise();
|
|
}
|
|
|
|
_step() {
|
|
if (!this._stepPromise) {
|
|
// Allow switching to stepping mid-run.
|
|
this._stepPromise = this._newStepPromise();
|
|
} else {
|
|
const resolve = this._stepPromiseResolve;
|
|
this._stepPromise = this._newStepPromise();
|
|
resolve();
|
|
}
|
|
}
|
|
|
|
_newStepPromise() {
|
|
return new Promise((resolve) => {
|
|
this._stepPromiseResolve = resolve;
|
|
});
|
|
}
|
|
|
|
_testStart(suite, test) {
|
|
test.anchor.classList.add("running");
|
|
}
|
|
|
|
async _testDone(suite, test) {
|
|
const classList = test.anchor.classList;
|
|
classList.remove("running");
|
|
classList.add("ran");
|
|
if (this._isStepping)
|
|
await this._stepPromise;
|
|
}
|
|
|
|
_iterationDone(measuredValues) {
|
|
let results = "";
|
|
for (const suiteName in measuredValues.tests) {
|
|
let suiteResults = measuredValues.tests[suiteName];
|
|
for (const testName in suiteResults.tests) {
|
|
let testResults = suiteResults.tests[testName];
|
|
for (const subtestName in testResults.tests)
|
|
results += `${suiteName} : ${testName} : ${subtestName}: ${testResults.tests[subtestName]} ms\n`;
|
|
}
|
|
results += `${suiteName} : ${suiteResults.total} ms\n`;
|
|
}
|
|
results += `Arithmetic Mean : ${measuredValues.mean} ms\n`;
|
|
results += `Geometric Mean : ${measuredValues.geomean} ms\n`;
|
|
results += `Total : ${measuredValues.total} ms\n`;
|
|
results += `Score : ${measuredValues.score} rpm\n`;
|
|
|
|
if (!results)
|
|
return;
|
|
|
|
const pre = document.createElement("pre");
|
|
document.body.appendChild(pre);
|
|
pre.textContent = results;
|
|
}
|
|
|
|
_done() {
|
|
this.isRunning = false;
|
|
}
|
|
|
|
runStep() {
|
|
this._isStepping = true;
|
|
if (!this._isRunning)
|
|
this.runMultipleIterations(this._iterationCount);
|
|
else
|
|
this._step();
|
|
}
|
|
|
|
runSuites() {
|
|
if (this._isRunning) {
|
|
if (this._isStepping) {
|
|
// Switch to continuous running only if we've been stepping.
|
|
this._isStepping = false;
|
|
this._step();
|
|
}
|
|
} else {
|
|
this._isStepping = false;
|
|
this.runMultipleIterations(this._iterationCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Expose Suites/BenchmarkRunner for backwards compatibility
|
|
globalThis.BenchmarkRunner = InteractiveBenchmarkRunner;
|
|
|
|
function formatTestName(suiteName, testName) {
|
|
return suiteName + (testName ? `/${testName}` : "");
|
|
}
|
|
|
|
function createUIForSuites(suites, onStep, onRunSuites) {
|
|
const control = document.createElement("nav");
|
|
const ol = document.createElement("ol");
|
|
const checkboxes = [];
|
|
for (let suiteIndex = 0; suiteIndex < suites.length; suiteIndex++) {
|
|
const suite = suites[suiteIndex];
|
|
const li = document.createElement("li");
|
|
const checkbox = document.createElement("input");
|
|
checkbox.id = suite.name;
|
|
checkbox.type = "checkbox";
|
|
checkbox.checked = !suite.disabled;
|
|
checkbox.onchange = () => {
|
|
suite.disabled = !checkbox.checked;
|
|
};
|
|
checkbox.onchange();
|
|
checkboxes.push(checkbox);
|
|
|
|
li.appendChild(checkbox);
|
|
var label = document.createElement("label");
|
|
label.appendChild(document.createTextNode(formatTestName(suite.name)));
|
|
li.appendChild(label);
|
|
label.htmlFor = checkbox.id;
|
|
|
|
const testList = document.createElement("ol");
|
|
for (let testIndex = 0; testIndex < suite.tests.length; testIndex++) {
|
|
const testItem = document.createElement("li");
|
|
const test = suite.tests[testIndex];
|
|
const anchor = document.createElement("a");
|
|
anchor.id = `${suite.name}-${test.name}`;
|
|
test.anchor = anchor;
|
|
anchor.appendChild(document.createTextNode(formatTestName(suite.name, test.name)));
|
|
testItem.appendChild(anchor);
|
|
testList.appendChild(testItem);
|
|
}
|
|
li.appendChild(testList);
|
|
|
|
ol.appendChild(li);
|
|
}
|
|
|
|
control.appendChild(ol);
|
|
|
|
let button = document.createElement("button");
|
|
button.textContent = "Step";
|
|
button.onclick = onStep;
|
|
control.appendChild(button);
|
|
|
|
button = document.createElement("button");
|
|
button.textContent = "Run";
|
|
button.id = "runSuites";
|
|
button.onclick = onRunSuites;
|
|
control.appendChild(button);
|
|
|
|
button = document.createElement("button");
|
|
button.textContent = "Select all";
|
|
button.onclick = () => {
|
|
for (var suiteIndex = 0; suiteIndex < suites.length; suiteIndex++) {
|
|
suites[suiteIndex].disabled = false;
|
|
checkboxes[suiteIndex].checked = true;
|
|
}
|
|
};
|
|
control.appendChild(button);
|
|
|
|
button = document.createElement("button");
|
|
button.textContent = "Unselect all";
|
|
button.onclick = () => {
|
|
for (var suiteIndex = 0; suiteIndex < suites.length; suiteIndex++) {
|
|
suites[suiteIndex].disabled = true;
|
|
checkboxes[suiteIndex].checked = false;
|
|
}
|
|
};
|
|
control.appendChild(button);
|
|
|
|
return control;
|
|
}
|
|
|
|
function startTest() {
|
|
if (params.suites.length > 0 || params.tags.length > 0)
|
|
Suites.enable(params.suites, params.tags);
|
|
|
|
const interactiveRunner = new window.BenchmarkRunner(Suites, params.iterationCount);
|
|
if (!(interactiveRunner instanceof InteractiveBenchmarkRunner))
|
|
throw Error("window.BenchmarkRunner must be a subclass of InteractiveBenchmarkRunner");
|
|
|
|
// Don't call step while step is already executing.
|
|
document.body.appendChild(createUIForSuites(Suites, interactiveRunner.runStep.bind(interactiveRunner), interactiveRunner.runSuites.bind(interactiveRunner)));
|
|
|
|
if (params.startAutomatically)
|
|
document.getElementById("runSuites").click();
|
|
}
|
|
|
|
window.addEventListener("load", startTest);
|