Skip to content

Commit 49c87ef

Browse files
panvanodejs-github-bot
authored andcommitted
test: support inspecting WPTs in child processes
Add WPT_INSPECT to launch one generated main-thread test with --inspect-brk on an available port. Forward debugger stderr so an inspector client can attach while the child is paused. Require an exact generated test path and reject worker variants, whose test code runs in a nested Worker. Cover backend precedence, selector errors, inspector attachment, and clean shutdown. Refs: #51854 Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65510 Reviewed-By: Aviv Keller <me@aviv.sh> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent a15da05 commit 49c87ef

4 files changed

Lines changed: 162 additions & 3 deletions

File tree

test/common/wpt.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,8 @@ function runSpecOnThread(execArgv, workerData, handlers) {
754754
* @returns {SpecHandle}
755755
*/
756756
function runSpecInProcess(execArgv, workerData, handlers) {
757+
const forwardStderr = execArgv.some(
758+
(flag) => flag === '--inspect-brk' || flag.startsWith('--inspect-brk='));
757759
const child = fork(workerPath, {
758760
execArgv,
759761
// Status files may skip subtests by regular expression, which JSON
@@ -767,6 +769,9 @@ function runSpecInProcess(execArgv, workerData, handlers) {
767769
child.stderr.setEncoding('utf8');
768770
child.stderr.on('data', (chunk) => {
769771
stderr += chunk;
772+
if (forwardStderr) {
773+
process.stderr.write(chunk);
774+
}
770775
});
771776

772777
child.on('message', (message) => {
@@ -789,7 +794,8 @@ function runSpecInProcess(execArgv, workerData, handlers) {
789794
const name = signal ?
790795
`Test process was killed by signal ${signal}` :
791796
`Test process exited with code ${code}`;
792-
if (!handlers.failure({ name, message: name, stack: stderr }) && stderr) {
797+
if (!handlers.failure({ name, message: name, stack: stderr }) &&
798+
stderr && !forwardStderr) {
793799
process.stderr.write(stderr);
794800
}
795801
});
@@ -823,9 +829,15 @@ class WPTRunner {
823829
concurrency = Math.min(10, concurrency);
824830
}
825831

832+
this.inspectBrk = process.env.WPT_INSPECT !== undefined;
833+
826834
// The override exists so that every suite can be run either way without
827835
// editing the drivers, which is how the two backends are kept compatible.
828-
backend = process.env.WPT_BACKEND || backend;
836+
if (this.inspectBrk) {
837+
backend = 'process';
838+
} else {
839+
backend = process.env.WPT_BACKEND || backend;
840+
}
829841
this.runSpec = backends[backend];
830842
if (this.runSpec === undefined) {
831843
throw new Error(`Invalid WPT backend ${backend}, expected one of ` +
@@ -841,6 +853,9 @@ class WPTRunner {
841853
// we enable the API globally. This has no practical
842854
// effect on the non-web-worker tests, however.
843855
this.flags = ['--experimental-web-worker'];
856+
if (this.inspectBrk) {
857+
this.flags.push('--inspect-brk=0');
858+
}
844859
this.globalThisInitScripts = [];
845860
this.initScript = null;
846861

@@ -1299,6 +1314,9 @@ class WPTRunner {
12991314
const queue = [];
13001315
this.skippedSpecCount = 0;
13011316
const arg = process.argv[2];
1317+
if (this.inspectBrk && !arg) {
1318+
throw new Error('WPT_INSPECT requires a WPT test path');
1319+
}
13021320
for (const spec of this.specs) {
13031321
if (arg) {
13041322
if (spec.isSelectedBy(arg)) {
@@ -1330,6 +1348,18 @@ class WPTRunner {
13301348
if (arg && queue.length === 0) {
13311349
throw new Error(`${arg} not found!`);
13321350
}
1351+
if (this.inspectBrk && queue.length !== 1) {
1352+
const matches = queue.map((spec) => spec.getTestPath()).join('\n');
1353+
throw new Error(
1354+
`WPT_INSPECT requires exactly one generated WPT test path; ` +
1355+
`${arg} matched ${queue.length}:\n${matches}`,
1356+
);
1357+
}
1358+
if (this.inspectBrk && queue[0].isWebWorkerTest()) {
1359+
throw new Error(
1360+
`WPT_INSPECT does not support worker tests: ${queue[0].getTestPath()}`,
1361+
);
1362+
}
13331363

13341364
return queue;
13351365
}

test/parallel/test-common-wpt-backends.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const { spawnSync } = require('child_process');
1313
const { backends, WPTRunner } = require('../common/wpt');
1414

1515
const queueProbe = process.env.NODE_TEST_WPT_QUEUE_PROBE === '1';
16+
const backendProbe = process.env.NODE_TEST_WPT_BACKEND_PROBE;
1617

1718
const harnessPath = fixtures.path('wpt', 'resources', 'testharness.js');
1819
const specPath = fixtures.path('wpt-backends-spec.js');
@@ -156,7 +157,74 @@ function checkQueuedSpecsKeepRunnerAlive() {
156157
assert.strictEqual(status, 0, `Queued WPT probe failed:\n${stdout}${stderr}`);
157158
}
158159

160+
function runDefaultBackendProbe() {
161+
const runner = new WPTRunner('compression');
162+
assert.strictEqual(runner.runSpec, backends[backendProbe]);
163+
}
164+
165+
function checkBackendSelection() {
166+
const env = { ...process.env };
167+
delete env.WPT_BACKEND;
168+
delete env.WPT_INSPECT;
169+
170+
for (const [name, expected, args, overrides] of [
171+
['default', 'thread', [__filename]],
172+
['environment override', 'process', [__filename], { WPT_BACKEND: 'process' }],
173+
['inspect override', 'process', [__filename], {
174+
WPT_BACKEND: 'thread',
175+
WPT_INSPECT: '1',
176+
}],
177+
]) {
178+
const result = spawnSync(process.execPath, args, {
179+
env: {
180+
...env,
181+
...overrides,
182+
NODE_TEST_WPT_BACKEND_PROBE: expected,
183+
},
184+
encoding: 'utf8',
185+
timeout: common.platformTimeout(10_000),
186+
});
187+
const { error, status, stdout, stderr } = result;
188+
assert.ifError(error);
189+
assert.strictEqual(
190+
status,
191+
0,
192+
`${name} WPT backend probe failed:\n${stdout}${stderr}`,
193+
);
194+
}
195+
}
196+
197+
function checkInspectSelection() {
198+
const driver = path.join(__dirname, '../wpt/test-compression.js');
199+
const env = { ...process.env, WPT_INSPECT: '1' };
200+
delete env.WPT_BACKEND;
201+
202+
const runFailure = common.mustCall((args, expected) => {
203+
const result = spawnSync(process.execPath, [driver, ...args], {
204+
env,
205+
encoding: 'utf8',
206+
timeout: common.platformTimeout(10_000),
207+
});
208+
const { error, status, stdout, stderr } = result;
209+
assert.ifError(error);
210+
assert.strictEqual(status, 1, `WPT inspect probe passed:\n${stdout}${stderr}`);
211+
assert.match(stderr, expected);
212+
}, 3);
213+
214+
runFailure([], /WPT_INSPECT requires a WPT test path/);
215+
runFailure(
216+
['compression-bad-chunks.any.js'],
217+
/WPT_INSPECT requires exactly one generated WPT test path; .* matched 2:\r?\ncompression\/compression-bad-chunks\.any\.html\r?\ncompression\/compression-bad-chunks\.any\.worker\.html/,
218+
);
219+
runFailure(
220+
['compression/compression-bad-chunks.any.worker.html'],
221+
/WPT_INSPECT does not support worker tests: compression\/compression-bad-chunks\.any\.worker\.html/,
222+
);
223+
}
224+
159225
async function main() {
226+
checkBackendSelection();
227+
checkInspectSelection();
160228
checkQueuedSpecsKeepRunnerAlive();
161229

162230
const completed = await compare(false);
@@ -197,7 +265,9 @@ async function main() {
197265
assert.deepStrictEqual(workerResults, windowResults);
198266
}
199267

200-
if (queueProbe) {
268+
if (backendProbe) {
269+
runDefaultBackendProbe();
270+
} else if (queueProbe) {
201271
runQueueProbe();
202272
} else {
203273
main().then(common.mustCall());
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
common.skipIfInspectorDisabled();
5+
6+
const assert = require('assert');
7+
const path = require('path');
8+
const { NodeInstance } = require('../common/inspector-helper');
9+
10+
const driver = path.join(__dirname, '../wpt/test-compression.js');
11+
12+
async function main() {
13+
const parent = new NodeInstance([], `
14+
delete process.env.WPT_BACKEND;
15+
process.env.WPT_INSPECT = '1';
16+
process.argv[2] = 'compression/compression-bad-chunks.any.html';
17+
require(${JSON.stringify(driver)});
18+
`, '', {
19+
log() {},
20+
error() {},
21+
});
22+
const stderr = [];
23+
parent.on('stderr', (line) => stderr.push(line));
24+
25+
const session = await parent.connectInspectorSession();
26+
await session.send([
27+
{ method: 'Runtime.enable' },
28+
{ method: 'Debugger.enable' },
29+
{ method: 'Runtime.runIfWaitingForDebugger' },
30+
]);
31+
await session.waitForNotification('Debugger.paused');
32+
await session.send({ method: 'Debugger.resume' });
33+
await session.disconnect();
34+
35+
const { exitCode, signal } = await parent.expectShutdown();
36+
assert.strictEqual(signal, null);
37+
assert.strictEqual(exitCode, 0);
38+
assert.strictEqual(
39+
stderr.filter((line) => line.startsWith('Debugger listening on ')).length,
40+
1,
41+
);
42+
}
43+
44+
main().then(common.mustCall());

test/wpt/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ tests in child processes instead:
4242
WPT_BACKEND=process tools/test.py wpt
4343
```
4444

45+
### Debugging a test
46+
47+
Set `WPT_INSPECT=1` to run one generated main-thread test in a child process
48+
with `--inspect-brk` on an available port:
49+
50+
```bash
51+
WPT_INSPECT=1 out/Release/node test/wpt/test-compression.js \
52+
'compression/compression-bad-chunks.any.html'
53+
```
54+
55+
Connect an inspector client to the URL printed on stderr. A source file that
56+
generates multiple tests is rejected with the exact paths to choose from.
57+
Worker tests are not supported by inspect mode.
58+
4559
<a id="add-tests"></a>
4660

4761
## How to add tests for a new module
@@ -96,6 +110,7 @@ selected backend.
96110
that require sequential execution (e.g. web-locks, webstorage).
97111
* `backend` {string} Test execution backend. Must be either `'thread'` or
98112
`'process'`. Defaults to `'thread'`. `WPT_BACKEND` overrides this option.
113+
`WPT_INSPECT` always uses `'process'`.
99114

100115
#### `runner.setFlags(flags)`
101116

0 commit comments

Comments
 (0)