|  | 
| 1 | 1 | import { Metric } from "./metric.mjs"; | 
| 2 | 2 | import { params } from "./params.mjs"; | 
| 3 |  | -import { TEST_INVOKER_LOOKUP } from "./test-invoker.mjs"; | 
|  | 3 | +import { SUITE_RUNNER_LOOKUP } from "./suite-runner.mjs"; | 
| 4 | 4 | 
 | 
| 5 | 5 | const performance = globalThis.performance; | 
| 6 | 6 | 
 | 
| @@ -223,7 +223,7 @@ function geomeanToScore(geomean) { | 
| 223 | 223 | // The WarmupSuite is used to make sure all runner helper functions and | 
| 224 | 224 | // classes are compiled, to avoid unnecessary pauses due to delayed | 
| 225 | 225 | // compilation of runner methods in the middle of the measuring cycle. | 
| 226 |  | -const WarmupSuite = { | 
|  | 226 | +export const WarmupSuite = { | 
| 227 | 227 |     name: "Warmup", | 
| 228 | 228 |     url: "warmup/index.html", | 
| 229 | 229 |     async prepare(page) { | 
| @@ -410,7 +410,7 @@ export class BenchmarkRunner { | 
| 410 | 410 |         // FIXME: Encapsulate more state in the SuiteRunner. | 
| 411 | 411 |         // FIXME: Return and use measured values from SuiteRunner. | 
| 412 | 412 |         const suiteRunnerClass = SUITE_RUNNER_LOOKUP[suite.type ?? "default"]; | 
| 413 |  | -        const suiteRunner = new suiteRunnerClass(this._measuredValues, this._frame, this._page, this._client, suite); | 
|  | 413 | +        const suiteRunner = new suiteRunnerClass(this._frame, this._page, params, suite, this._client, this._measuredValues); | 
| 414 | 414 |         await suiteRunner.run(); | 
| 415 | 415 |     } | 
| 416 | 416 | 
 | 
| @@ -484,151 +484,3 @@ export class BenchmarkRunner { | 
| 484 | 484 |             metric.computeAggregatedMetrics(); | 
| 485 | 485 |     } | 
| 486 | 486 | } | 
| 487 |  | - | 
| 488 |  | -// FIXME: Create AsyncSuiteRunner subclass. | 
| 489 |  | -// FIXME: Create RemoteSuiteRunner subclass. | 
| 490 |  | -export class SuiteRunner { | 
| 491 |  | -    constructor(measuredValues, frame, page, client, suite) { | 
| 492 |  | -        // FIXME: Create SuiteRunner-local measuredValues. | 
| 493 |  | -        this._suiteResults = measuredValues.tests[suite.name]; | 
| 494 |  | -        if (!this._suiteResults) { | 
| 495 |  | -            this._suiteResults = { tests: {}, total: 0 }; | 
| 496 |  | -            measuredValues.tests[suite.name] = this._suiteResults; | 
| 497 |  | -        } | 
| 498 |  | -        this._measuredValues = measuredValues; | 
| 499 |  | -        this._frame = frame; | 
| 500 |  | -        this._page = page; | 
| 501 |  | -        this._client = client; | 
| 502 |  | -        this._suite = suite; | 
| 503 |  | -    } | 
| 504 |  | - | 
| 505 |  | -    async run() { | 
| 506 |  | -        await this._prepareSuite(); | 
| 507 |  | -        await this._runSuite(); | 
| 508 |  | -    } | 
| 509 |  | - | 
| 510 |  | -    async _prepareSuite() { | 
| 511 |  | -        const suiteName = this._suite.name; | 
| 512 |  | -        const suitePrepareStartLabel = `suite-${suiteName}-prepare-start`; | 
| 513 |  | -        const suitePrepareEndLabel = `suite-${suiteName}-prepare-end`; | 
| 514 |  | - | 
| 515 |  | -        performance.mark(suitePrepareStartLabel); | 
| 516 |  | -        await this._loadFrame(); | 
| 517 |  | -        await this._suite.prepare(this._page); | 
| 518 |  | -        performance.mark(suitePrepareEndLabel); | 
| 519 |  | - | 
| 520 |  | -        performance.measure(`suite-${suiteName}-prepare`, suitePrepareStartLabel, suitePrepareEndLabel); | 
| 521 |  | -    } | 
| 522 |  | - | 
| 523 |  | -    async _runSuite() { | 
| 524 |  | -        const suiteName = this._suite.name; | 
| 525 |  | -        const suiteStartLabel = `suite-${suiteName}-start`; | 
| 526 |  | -        const suiteEndLabel = `suite-${suiteName}-end`; | 
| 527 |  | - | 
| 528 |  | -        performance.mark(suiteStartLabel); | 
| 529 |  | -        for (const test of this._suite.tests) | 
| 530 |  | -            await this._runTestAndRecordResults(test); | 
| 531 |  | -        performance.mark(suiteEndLabel); | 
| 532 |  | - | 
| 533 |  | -        performance.measure(`suite-${suiteName}`, suiteStartLabel, suiteEndLabel); | 
| 534 |  | -        this._validateSuiteTotal(); | 
| 535 |  | -    } | 
| 536 |  | - | 
| 537 |  | -    _validateSuiteTotal() { | 
| 538 |  | -        // When the test is fast and the precision is low (for example with Firefox' | 
| 539 |  | -        // privacy.resistFingerprinting preference), it's possible that the measured | 
| 540 |  | -        // total duration for an entire is 0. | 
| 541 |  | -        const suiteTotal = this._suiteResults.total; | 
| 542 |  | -        if (suiteTotal === 0) | 
| 543 |  | -            throw new Error(`Got invalid 0-time total for suite ${this._suite.name}: ${suiteTotal}`); | 
| 544 |  | -    } | 
| 545 |  | - | 
| 546 |  | -    async _loadFrame() { | 
| 547 |  | -        return new Promise((resolve, reject) => { | 
| 548 |  | -            const frame = this._page._frame; | 
| 549 |  | -            frame.onload = () => resolve(); | 
| 550 |  | -            frame.onerror = () => reject(); | 
| 551 |  | -            frame.src = this._suite.url; | 
| 552 |  | -        }); | 
| 553 |  | -    } | 
| 554 |  | - | 
| 555 |  | -    async _runTestAndRecordResults(test) { | 
| 556 |  | -        if (this._client?.willRunTest) | 
| 557 |  | -            await this._client.willRunTest(this._suite, test); | 
| 558 |  | - | 
| 559 |  | -        // Prepare all mark labels outside the measuring loop. | 
| 560 |  | -        const suiteName = this._suite.name; | 
| 561 |  | -        const testName = test.name; | 
| 562 |  | -        const startLabel = `${suiteName}.${testName}-start`; | 
| 563 |  | -        const syncEndLabel = `${suiteName}.${testName}-sync-end`; | 
| 564 |  | -        const asyncStartLabel = `${suiteName}.${testName}-async-start`; | 
| 565 |  | -        const asyncEndLabel = `${suiteName}.${testName}-async-end`; | 
| 566 |  | - | 
| 567 |  | -        let syncTime; | 
| 568 |  | -        let asyncStartTime; | 
| 569 |  | -        let asyncTime; | 
| 570 |  | -        const runSync = () => { | 
| 571 |  | -            if (params.warmupBeforeSync) { | 
| 572 |  | -                performance.mark("warmup-start"); | 
| 573 |  | -                const startTime = performance.now(); | 
| 574 |  | -                // Infinite loop for the specified ms. | 
| 575 |  | -                while (performance.now() - startTime < params.warmupBeforeSync) | 
| 576 |  | -                    continue; | 
| 577 |  | -                performance.mark("warmup-end"); | 
| 578 |  | -            } | 
| 579 |  | -            performance.mark(startLabel); | 
| 580 |  | -            const syncStartTime = performance.now(); | 
| 581 |  | -            test.run(this._page); | 
| 582 |  | -            const syncEndTime = performance.now(); | 
| 583 |  | -            performance.mark(syncEndLabel); | 
| 584 |  | - | 
| 585 |  | -            syncTime = syncEndTime - syncStartTime; | 
| 586 |  | - | 
| 587 |  | -            performance.mark(asyncStartLabel); | 
| 588 |  | -            asyncStartTime = performance.now(); | 
| 589 |  | -        }; | 
| 590 |  | -        const measureAsync = () => { | 
| 591 |  | -            // Some browsers don't immediately update the layout for paint. | 
| 592 |  | -            // Force the layout here to ensure we're measuring the layout time. | 
| 593 |  | -            const height = this._frame.contentDocument.body.getBoundingClientRect().height; | 
| 594 |  | -            const asyncEndTime = performance.now(); | 
| 595 |  | -            asyncTime = asyncEndTime - asyncStartTime; | 
| 596 |  | -            this._frame.contentWindow._unusedHeightValue = height; // Prevent dead code elimination. | 
| 597 |  | -            performance.mark(asyncEndLabel); | 
| 598 |  | -            if (params.warmupBeforeSync) | 
| 599 |  | -                performance.measure("warmup", "warmup-start", "warmup-end"); | 
| 600 |  | -            const suiteName = this._suite.name; | 
| 601 |  | -            const testName = test.name; | 
| 602 |  | -            performance.measure(`${suiteName}.${testName}-sync`, startLabel, syncEndLabel); | 
| 603 |  | -            performance.measure(`${suiteName}.${testName}-async`, asyncStartLabel, asyncEndLabel); | 
| 604 |  | -        }; | 
| 605 |  | - | 
| 606 |  | -        const report = () => this._recordTestResults(test, syncTime, asyncTime); | 
| 607 |  | -        const invokerClass = TEST_INVOKER_LOOKUP[params.measurementMethod]; | 
| 608 |  | -        const invoker = new invokerClass(runSync, measureAsync, report, params); | 
| 609 |  | - | 
| 610 |  | -        return invoker.start(); | 
| 611 |  | -    } | 
| 612 |  | - | 
| 613 |  | -    async _recordTestResults(test, syncTime, asyncTime) { | 
| 614 |  | -        // Skip reporting updates for the warmup suite. | 
| 615 |  | -        if (this._suite === WarmupSuite) | 
| 616 |  | -            return; | 
| 617 |  | - | 
| 618 |  | -        const total = syncTime + asyncTime; | 
| 619 |  | -        this._suiteResults.tests[test.name] = { tests: { Sync: syncTime, Async: asyncTime }, total: total }; | 
| 620 |  | -        this._suiteResults.total += total; | 
| 621 |  | - | 
| 622 |  | -        if (this._client?.didRunTest) | 
| 623 |  | -            await this._client.didRunTest(this._suite, test); | 
| 624 |  | -    } | 
| 625 |  | -} | 
| 626 |  | - | 
| 627 |  | -// FIXME: implement remote steps | 
| 628 |  | -class RemoteSuiteRunner extends SuiteRunner {} | 
| 629 |  | - | 
| 630 |  | -const SUITE_RUNNER_LOOKUP = { | 
| 631 |  | -    __proto__: null, | 
| 632 |  | -    default: SuiteRunner, | 
| 633 |  | -    remote: RemoteSuiteRunner, | 
| 634 |  | -}; | 
0 commit comments