Skip to content

Commit

Permalink
Reapply "Add shared worker support (#3345)" (#3428) (#3435)
Browse files Browse the repository at this point in the history
This reverts commit 55e6eea.

Attempting to land this again to see if it causes any infra issues when
rolled into Chromium.
  • Loading branch information
dj2 authored Feb 27, 2024
1 parent c20056d commit e9cadab
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 224 deletions.
5 changes: 3 additions & 2 deletions docs/intro/developing.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,16 @@ You can use this to preview how your test plan will appear.
You can view different suites (webgpu, unittests, stress, etc.) or different subtrees of
the test suite.

- `http://localhost:8080/standalone/` (defaults to `?runnow=0&worker=0&debug=0&q=webgpu:*`)
- `http://localhost:8080/standalone/` (defaults to `?runnow=0&debug=0&q=webgpu:*`)
- `http://localhost:8080/standalone/?q=unittests:*`
- `http://localhost:8080/standalone/?q=unittests:basic:*`

The following url parameters change how the harness runs:

- `runnow=1` runs all matching tests on page load.
- `debug=1` enables verbose debug logging from tests.
- `worker=1` runs the tests on a Web Worker instead of the main thread.
- `worker=dedicated` runs the tests on a dedicated worker instead of the main thread.
- `worker=shared` runs the tests on a shared worker instead of the main thread.
- `power_preference=low-power` runs most tests passing `powerPreference: low-power` to `requestAdapter`
- `power_preference=high-performance` runs most tests passing `powerPreference: high-performance` to `requestAdapter`

Expand Down
6 changes: 3 additions & 3 deletions src/common/internal/query/query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TestParams } from '../../framework/fixture.js';
import { optionEnabled } from '../../runtime/helper/options.js';
import { optionString } from '../../runtime/helper/options.js';
import { assert, unreachable } from '../../util/util.js';
import { Expectation } from '../logging/result.js';

Expand Down Expand Up @@ -188,12 +188,12 @@ export function parseExpectationsForTestQuery(
assert(
expectationURL.pathname === wptURL.pathname,
`Invalid expectation path ${expectationURL.pathname}
Expectation should be of the form path/to/cts.https.html?worker=0&q=suite:test_path:test_name:foo=1;bar=2;...
Expectation should be of the form path/to/cts.https.html?debug=0&q=suite:test_path:test_name:foo=1;bar=2;...
`
);

const params = expectationURL.searchParams;
if (optionEnabled('worker', params) !== optionEnabled('worker', wptURL.searchParams)) {
if (optionString('worker', params) !== optionString('worker', wptURL.searchParams)) {
continue;
}

Expand Down
14 changes: 11 additions & 3 deletions src/common/runtime/helper/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function optionString(
* The possible options for the tests.
*/
export interface CTSOptions {
worker: boolean;
worker?: 'dedicated' | 'shared' | '';
debug: boolean;
compatibility: boolean;
forceFallbackAdapter: boolean;
Expand All @@ -34,7 +34,7 @@ export interface CTSOptions {
}

export const kDefaultCTSOptions: CTSOptions = {
worker: false,
worker: '',
debug: true,
compatibility: false,
forceFallbackAdapter: false,
Expand All @@ -61,7 +61,15 @@ export type OptionsInfos<Type> = Record<keyof Type, OptionInfo>;
* Options to the CTS.
*/
export const kCTSOptionsInfo: OptionsInfos<CTSOptions> = {
worker: { description: 'run in a worker' },
worker: {
description: 'run in a worker',
parser: optionString,
selectValueDescriptions: [
{ value: '', description: 'no worker' },
{ value: 'dedicated', description: 'dedicated worker' },
{ value: 'shared', description: 'shared worker' },
],
},
debug: { description: 'show more info' },
compatibility: { description: 'run in compatibility mode' },
forceFallbackAdapter: { description: 'pass forceFallbackAdapter: true to requestAdapter' },
Expand Down
18 changes: 15 additions & 3 deletions src/common/runtime/helper/test_worker-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { assert } from '../../util/util.js';

import { CTSOptions } from './options.js';

// Should be DedicatedWorkerGlobalScope, but importing lib "webworker" conflicts with lib "dom".
// Should be WorkerGlobalScope, but importing lib "webworker" conflicts with lib "dom".
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
declare const self: any;

const loader = new DefaultTestFileLoader();

setBaseResourcePath('../../../resources');

self.onmessage = async (ev: MessageEvent) => {
async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) {
const query: string = ev.data.query;
const expectations: TestQueryWithExpectation[] = ev.data.expectations;
const ctsOptions: CTSOptions = ev.data.ctsOptions;
Expand All @@ -44,5 +44,17 @@ self.onmessage = async (ev: MessageEvent) => {
const [rec, result] = log.record(testcase.query.toString());
await testcase.run(rec, expectations);

self.postMessage({ query, result });
this.postMessage({ query, result });
}

self.onmessage = (ev: MessageEvent) => {
void reportTestResults.call(self, ev);
};

self.onconnect = (event: MessageEvent) => {
const port = event.ports[0];

port.onmessage = (ev: MessageEvent) => {
void reportTestResults.call(port, ev);
};
};
49 changes: 47 additions & 2 deletions src/common/runtime/helper/test_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { TestQueryWithExpectation } from '../../internal/query/query.js';

import { CTSOptions, kDefaultCTSOptions } from './options.js';

export class TestWorker {
export class TestDedicatedWorker {
private readonly ctsOptions: CTSOptions;
private readonly worker: Worker;
private readonly resolvers = new Map<string, (result: LiveTestCaseResult) => void>();

constructor(ctsOptions?: CTSOptions) {
this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: true } };
this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: 'dedicated' } };
const selfPath = import.meta.url;
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
const workerPath = selfPathDir + '/test_worker-worker.js';
Expand Down Expand Up @@ -47,3 +47,48 @@ export class TestWorker {
rec.injectResult(workerResult);
}
}

export class TestSharedWorker {
private readonly ctsOptions: CTSOptions;
private readonly port: MessagePort;
private readonly resolvers = new Map<string, (result: LiveTestCaseResult) => void>();

constructor(ctsOptions?: CTSOptions) {
this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: 'shared' } };
const selfPath = import.meta.url;
const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
const workerPath = selfPathDir + '/test_worker-worker.js';
const worker = new SharedWorker(workerPath, { type: 'module' });
this.port = worker.port;
this.port.start();
this.port.onmessage = ev => {
const query: string = ev.data.query;
const result: TransferredTestCaseResult = ev.data.result;
if (result.logs) {
for (const l of result.logs) {
Object.setPrototypeOf(l, LogMessageWithStack.prototype);
}
}
this.resolvers.get(query)!(result as LiveTestCaseResult);

// MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and
// update the entire results JSON somehow at some point).
};
}

async run(
rec: TestCaseRecorder,
query: string,
expectations: TestQueryWithExpectation[] = []
): Promise<void> {
this.port.postMessage({
query,
expectations,
ctsOptions: this.ctsOptions,
});
const workerResult = await new Promise<LiveTestCaseResult>(resolve => {
this.resolvers.set(query, resolve);
});
rec.injectResult(workerResult);
}
}
12 changes: 8 additions & 4 deletions src/common/runtime/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
OptionsInfos,
camelCaseToSnakeCase,
} from './helper/options.js';
import { TestWorker } from './helper/test_worker.js';
import { TestDedicatedWorker, TestSharedWorker } from './helper/test_worker.js';

const rootQuerySpec = 'webgpu:*';
let promptBeforeReload = false;
Expand Down Expand Up @@ -63,7 +63,9 @@ const logger = new Logger();

setBaseResourcePath('../out/resources');

const worker = options.worker ? new TestWorker(options) : undefined;
const dedicatedWorker =
options.worker === 'dedicated' ? new TestDedicatedWorker(options) : undefined;
const sharedWorker = options.worker === 'shared' ? new TestSharedWorker(options) : undefined;

const autoCloseOnPass = document.getElementById('autoCloseOnPass') as HTMLInputElement;
const resultsVis = document.getElementById('resultsVis')!;
Expand Down Expand Up @@ -176,8 +178,10 @@ function makeCaseHTML(t: TestTreeLeaf): VisualizedSubtree {

const [rec, res] = logger.record(name);
caseResult = res;
if (worker) {
await worker.run(rec, name);
if (dedicatedWorker) {
await dedicatedWorker.run(rec, name);
} else if (sharedWorker) {
await sharedWorker.run(rec, name);
} else {
await t.run(rec);
}
Expand Down
15 changes: 9 additions & 6 deletions src/common/runtime/wpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { parseQuery } from '../internal/query/parseQuery.js';
import { parseExpectationsForTestQuery, relativeQueryString } from '../internal/query/query.js';
import { assert } from '../util/util.js';

import { optionEnabled } from './helper/options.js';
import { TestWorker } from './helper/test_worker.js';
import { optionEnabled, optionString } from './helper/options.js';
import { TestDedicatedWorker, TestSharedWorker } from './helper/test_worker.js';

// testharness.js API (https://web-platform-tests.org/writing-tests/testharness-api.html)
declare interface WptTestObject {
Expand All @@ -31,8 +31,9 @@ setup({
});

void (async () => {
const workerEnabled = optionEnabled('worker');
const worker = workerEnabled ? new TestWorker() : undefined;
const workerString = optionString('worker');
const dedicatedWorker = workerString === 'dedicated' ? new TestDedicatedWorker() : undefined;
const sharedWorker = workerString === 'shared' ? new TestSharedWorker() : undefined;

globalTestConfig.unrollConstEvalLoops = optionEnabled('unroll_const_eval_loops');

Expand Down Expand Up @@ -63,8 +64,10 @@ void (async () => {

const wpt_fn = async () => {
const [rec, res] = log.record(name);
if (worker) {
await worker.run(rec, name, expectations);
if (dedicatedWorker) {
await dedicatedWorker.run(rec, name, expectations);
} else if (sharedWorker) {
await sharedWorker.run(rec, name, expectations);
} else {
await testcase.run(rec, expectations);
}
Expand Down
12 changes: 6 additions & 6 deletions src/common/tools/gen_wpt_cts_html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ where arguments.txt is a file containing a list of arguments prefixes to both ge
in the expectations. The entire variant list generation runs *once per prefix*, so this
multiplies the size of the variant list.
?worker=0&q=
?worker=1&q=
?debug=0&q=
?debug=1&q=
and myexpectations.txt is a file containing a list of WPT paths to suppress, e.g.:
path/to/cts.https.html?worker=0&q=webgpu:a/foo:bar={"x":1}
path/to/cts.https.html?worker=1&q=webgpu:a/foo:bar={"x":1}
path/to/cts.https.html?debug=0&q=webgpu:a/foo:bar={"x":1}
path/to/cts.https.html?debug=1&q=webgpu:a/foo:bar={"x":1}
path/to/cts.https.html?worker=1&q=webgpu:a/foo:bar={"x":3}
path/to/cts.https.html?debug=1&q=webgpu:a/foo:bar={"x":3}
`);
process.exit(rc);
}
Expand Down Expand Up @@ -224,7 +224,7 @@ ${queryString}`
}

lines.push({
urlQueryString: prefix + query.toString(), // "?worker=0&q=..."
urlQueryString: prefix + query.toString(), // "?debug=0&q=..."
comment: useChunking ? `estimated: ${subtreeCounts?.totalTimeMS.toFixed(3)} ms` : undefined,
});

Expand Down
Loading

0 comments on commit e9cadab

Please sign in to comment.