Remove interactive runner (#432)
- To simplify future runner refactorings we remove the interactive runner. - Stepping functionality is added to the developer-menu and integrated directly into the default MainBenchmarkClient.
This commit is contained in:
parent
c2e8d4f6bc
commit
2a4d041656
4 changed files with 50 additions and 262 deletions
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=850" />
|
||||
<title>Speedometer 3.0 Interactive Runner</title>
|
||||
<script src="resources/interactive.mjs" type="module"></script>
|
||||
<link rel="icon" href="resources/favicon.png" />
|
||||
<style>
|
||||
iframe {
|
||||
border: 1px solid black;
|
||||
}
|
||||
ol {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ol ol {
|
||||
margin-left: 2em;
|
||||
list-style-position: outside;
|
||||
}
|
||||
.running {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ran {
|
||||
color: grey;
|
||||
}
|
||||
nav {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
height: 600px;
|
||||
}
|
||||
nav > ol {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
|
@ -222,14 +222,20 @@ function createUIForSuites() {
|
|||
}
|
||||
|
||||
function createUIForRun() {
|
||||
let button = document.createElement("button");
|
||||
button.textContent = "Start Test";
|
||||
button.onclick = (event) => {
|
||||
const stepTestButton = document.createElement("button");
|
||||
stepTestButton.textContent = "Step Test \u23EF";
|
||||
stepTestButton.onclick = (event) => {
|
||||
globalThis.benchmarkClient.step();
|
||||
};
|
||||
const startTestButton = document.createElement("button");
|
||||
startTestButton.textContent = "Start Test \u23F5";
|
||||
startTestButton.onclick = (event) => {
|
||||
globalThis.benchmarkClient.start();
|
||||
};
|
||||
let buttons = document.createElement("div");
|
||||
const buttons = document.createElement("div");
|
||||
buttons.className = "button-bar";
|
||||
buttons.appendChild(button);
|
||||
buttons.appendChild(stepTestButton);
|
||||
buttons.appendChild(startTestButton);
|
||||
return buttons;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,213 +0,0 @@
|
|||
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);
|
||||
|
|
@ -17,6 +17,8 @@ class MainBenchmarkClient {
|
|||
_hasResults = false;
|
||||
_developerModeContainer = null;
|
||||
_metrics = Object.create(null);
|
||||
_steppingPromise = null;
|
||||
_steppingResolver = null;
|
||||
|
||||
constructor() {
|
||||
window.addEventListener("DOMContentLoaded", () => this.prepareUI());
|
||||
|
|
@ -24,10 +26,41 @@ class MainBenchmarkClient {
|
|||
}
|
||||
|
||||
start() {
|
||||
if (this._startBenchmark())
|
||||
if (this._isStepping())
|
||||
this._clearStepping();
|
||||
else if (this._startBenchmark())
|
||||
this._showSection("#running");
|
||||
}
|
||||
|
||||
step() {
|
||||
const currentSteppingResolver = this._steppingResolver;
|
||||
this._steppingPromise = new Promise((resolve) => {
|
||||
this._steppingResolver = resolve;
|
||||
});
|
||||
if (this._isStepping())
|
||||
currentSteppingResolver();
|
||||
if (!this._isRunning) {
|
||||
this._startBenchmark();
|
||||
this._showSection("#running");
|
||||
}
|
||||
}
|
||||
|
||||
_clearStepping() {
|
||||
const currentSteppingResolver = this._steppingResolver;
|
||||
this._steppingPromise = null;
|
||||
this._steppingResolver = null;
|
||||
currentSteppingResolver();
|
||||
}
|
||||
|
||||
async _awaitNextStep(suite, test) {
|
||||
console.log(`Next Step: ${suite.name} ${test.name}`, { suite, test });
|
||||
await this._steppingPromise;
|
||||
}
|
||||
|
||||
_isStepping() {
|
||||
return this._steppingResolver !== null;
|
||||
}
|
||||
|
||||
_startBenchmark() {
|
||||
if (this._isRunning)
|
||||
return false;
|
||||
|
|
@ -44,8 +77,8 @@ class MainBenchmarkClient {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._developerModeContainer?.remove();
|
||||
if (!this._isStepping())
|
||||
this._developerModeContainer?.remove();
|
||||
this._progressCompleted = document.getElementById("progress-completed");
|
||||
if (params.iterationCount < 50) {
|
||||
const progressNode = document.getElementById("progress");
|
||||
|
|
@ -81,9 +114,11 @@ class MainBenchmarkClient {
|
|||
frame.style.transform = "translate(-50%, -50%)";
|
||||
}
|
||||
|
||||
willRunTest(suite, test) {
|
||||
async willRunTest(suite, test) {
|
||||
document.getElementById("info-label").textContent = suite.name;
|
||||
document.getElementById("info-progress").textContent = `${this._finishedTestCount} / ${this.stepCount}`;
|
||||
if (this._steppingPromise)
|
||||
await this._awaitNextStep(suite, test);
|
||||
}
|
||||
|
||||
didRunTest() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue