blob: 1b03d270e6e58468f8390a404cf34395b354b974 [file] [edit]
import { STEP_SCHEDULER_LOOKUP } from "./step-scheduler.mjs";
import { forceLayout } from "./helpers.mjs";
export class StepRunner {
#frame;
#page;
#params;
#suite;
#step;
#type;
constructor(frame, page, params, suite, step, type) {
this.#suite = suite;
this.#step = step;
this.#params = params;
this.#page = page;
this.#frame = frame;
this.#type = type;
}
get page() {
return this.#page;
}
get step() {
return this.#step;
}
_runSyncStep(step, page) {
step.run(page);
}
async runStep() {
// Prepare all mark labels outside the measuring loop.
const suiteName = this.#suite.name;
const stepName = this.#step.name;
const syncStartLabel = `${suiteName}.${stepName}-start`;
const syncEndLabel = `${suiteName}.${stepName}-sync-end`;
const asyncEndLabel = `${suiteName}.${stepName}-async-end`;
let syncTime;
let asyncStartTime;
let asyncTime;
const runSync = async () => {
if (this.#params.warmupBeforeSync) {
performance.mark("warmup-start");
const startTime = performance.now();
// Infinite loop for the specified ms.
while (performance.now() - startTime < this.#params.warmupBeforeSync)
continue;
performance.mark("warmup-end");
}
performance.mark(syncStartLabel);
const syncStartTime = performance.now();
if (this.#type === "async")
await this._runSyncStep(this.step, this.page);
else
this._runSyncStep(this.step, this.page);
const mark = performance.mark(syncEndLabel);
const syncEndTime = mark.startTime;
syncTime = syncEndTime - syncStartTime;
asyncStartTime = syncEndTime;
};
const measureAsync = () => {
// Some browsers don't immediately update the layout for paint.
// Force the layout here to ensure we're measuring the layout time.
//
// Note: This matches the behavior of Page.layout() in benchmark-runner.mjs.
// Since shared code cannot depend on Page, we duplicate the logic here,
// falling back to document.body for remote workloads that don't have a frame.
const body = this.#frame?.contentDocument?.body ?? document.body;
const value = forceLayout(body, this.#params.layoutMode);
body._leakedLayoutValue = value; // Prevent dead code elimination.
const asyncEndTime = performance.now();
performance.mark(asyncEndLabel);
asyncTime = asyncEndTime - asyncStartTime;
if (this.#params.warmupBeforeSync)
performance.measure("warmup", "warmup-start", "warmup-end");
performance.measure(`${suiteName}.${stepName}-sync`, syncStartLabel, syncEndLabel);
performance.measure(`${suiteName}.${stepName}-async`, syncEndLabel, asyncEndLabel);
};
const schedulerType = this.#suite.type === "async" || this.#params.useAsyncSteps ? "async" : this.#params.measurementMethod;
const schedulerClass = STEP_SCHEDULER_LOOKUP[schedulerType];
const scheduler = new schedulerClass(runSync, measureAsync, this.#params);
await scheduler.start();
return { syncTime, asyncTime };
}
}
export class AsyncStepRunner extends StepRunner {
async _runSyncStep(step, page) {
await step.run(page);
}
}
export const STEP_RUNNER_LOOKUP = {
__proto__: null,
default: StepRunner,
async: AsyncStepRunner,
remote: StepRunner,
};