From 2a4d041656b62e6447385e80dc81853b045ba1ac Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Fri, 11 Oct 2024 08:26:53 -0400 Subject: [PATCH] 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. --- InteractiveRunner.html | 40 ------- resources/developer-mode.mjs | 16 ++- resources/interactive.mjs | 213 ----------------------------------- resources/main.mjs | 43 ++++++- 4 files changed, 50 insertions(+), 262 deletions(-) delete mode 100644 InteractiveRunner.html delete mode 100644 resources/interactive.mjs diff --git a/InteractiveRunner.html b/InteractiveRunner.html deleted file mode 100644 index 26bed2bfa..000000000 --- a/InteractiveRunner.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - Speedometer 3.0 Interactive Runner - - - - - - diff --git a/resources/developer-mode.mjs b/resources/developer-mode.mjs index 166aea977..642486912 100644 --- a/resources/developer-mode.mjs +++ b/resources/developer-mode.mjs @@ -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; } diff --git a/resources/interactive.mjs b/resources/interactive.mjs deleted file mode 100644 index bdb2bc438..000000000 --- a/resources/interactive.mjs +++ /dev/null @@ -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); diff --git a/resources/main.mjs b/resources/main.mjs index c406205cf..76b756449 100644 --- a/resources/main.mjs +++ b/resources/main.mjs @@ -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() {