From 2e6cbd9a4dbe9bdf0a41526ab40165bfe75aebad Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Fri, 7 Mar 2025 00:32:23 -0800 Subject: [PATCH] node: update test/common (#17786) --- test/js/node/test/common/assertSnapshot.js | 8 +- test/js/node/test/common/benchmark.js | 27 +++-- test/js/node/test/common/child_process.js | 24 +++-- test/js/node/test/common/crypto.js | 18 ---- test/js/node/test/common/dns.js | 11 +- test/js/node/test/common/gc.js | 4 +- test/js/node/test/common/http2.js | 19 ---- test/js/node/test/common/index.js | 70 +------------ test/js/node/test/common/index.mjs | 4 +- test/js/node/test/common/inspector-helper.js | 16 ++- test/js/node/test/common/net.js | 23 +++++ test/js/node/test/common/ongc.js | 32 ------ test/js/node/test/common/report.js | 27 +++-- test/js/node/test/common/shared-lib-util.js | 2 +- test/js/node/test/common/tick.js | 1 - test/js/node/test/common/tmpdir.js | 2 - test/js/node/test/common/udppair.js | 101 ------------------- test/js/node/test/common/wpt.js | 24 +++-- test/js/node/test/common/wpt/worker.js | 70 +++++++++++++ 19 files changed, 192 insertions(+), 291 deletions(-) create mode 100644 test/js/node/test/common/net.js delete mode 100644 test/js/node/test/common/ongc.js delete mode 100644 test/js/node/test/common/udppair.js create mode 100644 test/js/node/test/common/wpt/worker.js diff --git a/test/js/node/test/common/assertSnapshot.js b/test/js/node/test/common/assertSnapshot.js index a22455160b..dbd4a60798 100644 --- a/test/js/node/test/common/assertSnapshot.js +++ b/test/js/node/test/common/assertSnapshot.js @@ -25,7 +25,7 @@ function replaceWindowsPaths(str) { } function replaceFullPaths(str) { - return str.replaceAll(path.resolve(__dirname, '../..'), ''); + return str.replaceAll('\\\'', "'").replaceAll(path.resolve(__dirname, '../..'), ''); } function transform(...args) { @@ -77,7 +77,11 @@ async function spawnAndAssert(filename, transform = (x) => x, { tty = false, ... test({ skip: 'Skipping pseudo-tty tests, as pseudo terminals are not available on Windows.' }); return; } - const flags = common.parseTestFlags(filename); + let flags = common.parseTestFlags(filename); + if (options.flags) { + flags = [...options.flags, ...flags]; + } + const executable = tty ? (process.env.PYTHON || 'python3') : process.execPath; const args = tty ? diff --git a/test/js/node/test/common/benchmark.js b/test/js/node/test/common/benchmark.js index d9c1cdc627..7211ff8703 100644 --- a/test/js/node/test/common/benchmark.js +++ b/test/js/node/test/common/benchmark.js @@ -27,16 +27,27 @@ function runBenchmark(name, env) { child.on('exit', (code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); + // This bit makes sure that each benchmark file is being sent settings such // that the benchmark file runs just one set of options. This helps keep the - // benchmark tests from taking a long time to run. Therefore, each benchmark - // file should result in three lines of output: a blank line, a line with - // the name of the benchmark file, and a line with the only results that we - // get from testing the benchmark file. - assert.ok( - /^(?:\n.+?\n.+?\n)+$/.test(stdout), - `benchmark file not running exactly one configuration in test: ${stdout}`, - ); + // benchmark tests from taking a long time to run. Therefore, stdout should be composed as follows: + // The first and last lines should be empty. + // Each test should be separated by a blank line. + // The first line of each test should contain the test's name. + // The second line of each test should contain the configuration for the test. + // If the test configuration is not a group, there should be exactly two lines. + // Otherwise, it is possible to have more than two lines. + + const splitTests = stdout.split(/\n\s*\n/); + + for (let testIdx = 1; testIdx < splitTests.length - 1; testIdx++) { + const lines = splitTests[testIdx].split('\n'); + assert.ok(/.+/.test(lines[0])); + + if (!lines[1].includes('group="')) { + assert.strictEqual(lines.length, 2, `benchmark file not running exactly one configuration in test: ${stdout}`); + } + } }); } diff --git a/test/js/node/test/common/child_process.js b/test/js/node/test/common/child_process.js index d555d09a94..6c2bc6c961 100644 --- a/test/js/node/test/common/child_process.js +++ b/test/js/node/test/common/child_process.js @@ -60,13 +60,14 @@ function checkOutput(str, check) { return { passed: true }; } -function expectSyncExit(child, { +function expectSyncExit(caller, spawnArgs, { status, signal, stderr: stderrCheck, stdout: stdoutCheck, trim = false, }) { + const child = spawnSync(...spawnArgs); const failures = []; let stderrStr, stdoutStr; if (status !== undefined && child.status !== status) { @@ -83,7 +84,18 @@ function expectSyncExit(child, { console.error(`${tag} --- stdout ---`); console.error(stdoutStr === undefined ? child.stdout.toString() : stdoutStr); console.error(`${tag} status = ${child.status}, signal = ${child.signal}`); - throw new Error(`${failures.join('\n')}`); + + const error = new Error(`${failures.join('\n')}`); + if (spawnArgs[2]) { + error.options = spawnArgs[2]; + } + let command = spawnArgs[0]; + if (Array.isArray(spawnArgs[1])) { + command += ' ' + spawnArgs[1].join(' '); + } + error.command = command; + Error.captureStackTrace(error, caller); + throw error; } // If status and signal are not matching expectations, fail early. @@ -114,12 +126,11 @@ function expectSyncExit(child, { function spawnSyncAndExit(...args) { const spawnArgs = args.slice(0, args.length - 1); const expectations = args[args.length - 1]; - const child = spawnSync(...spawnArgs); - return expectSyncExit(child, expectations); + return expectSyncExit(spawnSyncAndExit, spawnArgs, expectations); } function spawnSyncAndExitWithoutError(...args) { - return expectSyncExit(spawnSync(...args), { + return expectSyncExit(spawnSyncAndExitWithoutError, [...args], { status: 0, signal: null, }); @@ -127,8 +138,7 @@ function spawnSyncAndExitWithoutError(...args) { function spawnSyncAndAssert(...args) { const expectations = args.pop(); - const child = spawnSync(...args); - return expectSyncExit(child, { + return expectSyncExit(spawnSyncAndAssert, [...args], { status: 0, signal: null, ...expectations, diff --git a/test/js/node/test/common/crypto.js b/test/js/node/test/common/crypto.js index 19dfb1388d..e592e63cc5 100644 --- a/test/js/node/test/common/crypto.js +++ b/test/js/node/test/common/crypto.js @@ -34,23 +34,6 @@ const modp2buf = Buffer.from([ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]); -function testDH({ publicKey: alicePublicKey, privateKey: alicePrivateKey }, - { publicKey: bobPublicKey, privateKey: bobPrivateKey }, - expectedValue) { - const buf1 = crypto.diffieHellman({ - privateKey: alicePrivateKey, - publicKey: bobPublicKey, - }); - const buf2 = crypto.diffieHellman({ - privateKey: bobPrivateKey, - publicKey: alicePublicKey, - }); - assert.deepStrictEqual(buf1, buf2); - - if (expectedValue !== undefined) - assert.deepStrictEqual(buf1, expectedValue); -} - // Asserts that the size of the given key (in chars or bytes) is within 10% of // the expected size. function assertApproximateSize(key, expectedSize) { @@ -139,7 +122,6 @@ let opensslCli = null; module.exports = { modp2buf, - testDH, assertApproximateSize, testEncryptDecrypt, testSignVerify, diff --git a/test/js/node/test/common/dns.js b/test/js/node/test/common/dns.js index 8fa264dc2c..738f2299dd 100644 --- a/test/js/node/test/common/dns.js +++ b/test/js/node/test/common/dns.js @@ -15,7 +15,6 @@ const types = { TXT: 16, ANY: 255, CAA: 257, - SRV: 33, }; const classes = { @@ -197,11 +196,11 @@ function writeDNSPacket(parsed) { buffers.push(new Uint16Array([ parsed.id, - parsed.flags === undefined ? kStandardResponseFlags : parsed.flags, - parsed.questions && parsed.questions.length, - parsed.answers && parsed.answers.length, - parsed.authorityAnswers && parsed.authorityAnswers.length, - parsed.additionalRecords && parsed.additionalRecords.length, + parsed.flags ?? kStandardResponseFlags, + parsed.questions?.length, + parsed.answers?.length, + parsed.authorityAnswers?.length, + parsed.additionalRecords?.length, ])); for (const q of parsed.questions) { diff --git a/test/js/node/test/common/gc.js b/test/js/node/test/common/gc.js index 3774f81cc8..056db4cdcf 100644 --- a/test/js/node/test/common/gc.js +++ b/test/js/node/test/common/gc.js @@ -126,7 +126,7 @@ var finalizationRegistry = new FinalizationRegistry(heldValue => { function onGC(value, holder) { if (holder?.ongc) { - + finalizationRegistry.register(value, { ongc: holder.ongc }); } } @@ -137,5 +137,3 @@ module.exports = { checkIfCollectableByCounting, onGC, }; - - diff --git a/test/js/node/test/common/http2.js b/test/js/node/test/common/http2.js index 6df1c29c09..1968b5bcfd 100644 --- a/test/js/node/test/common/http2.js +++ b/test/js/node/test/common/http2.js @@ -81,24 +81,6 @@ class SettingsFrame extends Frame { } } -class DataFrame extends Frame { - constructor(id, payload, padlen = 0, final = false) { - let len = payload.length; - let flags = 0; - if (final) flags |= FLAG_EOS; - const buffers = [payload]; - if (padlen > 0) { - buffers.unshift(Buffer.from([padlen])); - buffers.push(PADDING.slice(0, padlen)); - len += padlen + 1; - flags |= FLAG_PADDED; - } - super(len, 0, flags, id); - buffers.unshift(this[kFrameData]); - this[kFrameData] = Buffer.concat(buffers); - } -} - class HeadersFrame extends Frame { constructor(id, payload, padlen = 0, final = false) { let len = payload.length; @@ -138,7 +120,6 @@ class AltSvcFrame extends Frame { module.exports = { Frame, AltSvcFrame, - DataFrame, HeadersFrame, SettingsFrame, PingFrame, diff --git a/test/js/node/test/common/index.js b/test/js/node/test/common/index.js index ef04b184e3..56c1f22d8a 100644 --- a/test/js/node/test/common/index.js +++ b/test/js/node/test/common/index.js @@ -110,7 +110,7 @@ function parseTestFlags(filename = process.argv[1]) { // `worker_threads`) and child processes. // If the binary was built without-ssl then the crypto flags are // invalid (bad option). The test itself should handle this case. -if ((process.argv.length === 2 || process.argv.length === 3) && +if (process.argv.length === 2 && !process.env.NODE_SKIP_FLAG_CHECK && isMainThread && hasCrypto && @@ -259,15 +259,13 @@ const PIPE = (() => { // `$node --abort-on-uncaught-exception $file child` // the process aborts. function childShouldThrowAndAbort() { - let testCmd = ''; + const escapedArgs = escapePOSIXShell`"${process.argv[0]}" --abort-on-uncaught-exception "${process.argv[1]}" child`; if (!isWindows) { // Do not create core files, as it can take a lot of disk space on // continuous testing and developers' machines - testCmd += 'ulimit -c 0 && '; + escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0]; } - testCmd += `"${process.argv[0]}" --abort-on-uncaught-exception `; - testCmd += `"${process.argv[1]}" child`; - const child = exec(testCmd); + const child = exec(...escapedArgs); child.on('exit', function onExit(exitCode, signal) { const errMsg = 'Test should have aborted ' + `but instead exited with exit code ${exitCode}` + @@ -400,57 +398,6 @@ if (global.Storage) { ); } -if (global.Bun) { - knownGlobals.push( - global.addEventListener, - global.alert, - global.confirm, - global.dispatchEvent, - global.postMessage, - global.prompt, - global.removeEventListener, - global.reportError, - global.Bun, - global.File, - global.process, - global.Blob, - global.Buffer, - global.BuildError, - global.BuildMessage, - global.HTMLRewriter, - global.Request, - global.ResolveError, - global.ResolveMessage, - global.Response, - global.TextDecoder, - global.AbortSignal, - global.BroadcastChannel, - global.CloseEvent, - global.DOMException, - global.ErrorEvent, - global.Event, - global.EventTarget, - global.FormData, - global.Headers, - global.MessageChannel, - global.MessageEvent, - global.MessagePort, - global.PerformanceEntry, - global.PerformanceObserver, - global.PerformanceObserverEntryList, - global.PerformanceResourceTiming, - global.PerformanceServerTiming, - global.PerformanceTiming, - global.TextEncoder, - global.URL, - global.URLSearchParams, - global.WebSocket, - global.Worker, - global.onmessage, - global.onerror - ); -} - function allowGlobals(...allowlist) { knownGlobals = knownGlobals.concat(allowlist); } @@ -1252,15 +1199,6 @@ const common = { get checkoutEOL() { return fs.readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; }, - - get isInsideDirWithUnusualChars() { - return __dirname.includes('%') || - (!isWindows && __dirname.includes('\\')) || - __dirname.includes('$') || - __dirname.includes('\n') || - __dirname.includes('\r') || - __dirname.includes('\t'); - }, }; const validProperties = new Set(Object.keys(common)); diff --git a/test/js/node/test/common/index.mjs b/test/js/node/test/common/index.mjs index 007ce233fb..748977b85f 100644 --- a/test/js/node/test/common/index.mjs +++ b/test/js/node/test/common/index.mjs @@ -11,11 +11,11 @@ const { childShouldThrowAndAbort, createZeroFilledFile, enoughTestMem, + escapePOSIXShell, expectsError, expectWarning, getArrayBufferViews, getBufferSources, - getCallSite, getTTYfd, hasCrypto, hasIntl, @@ -65,11 +65,11 @@ export { createRequire, createZeroFilledFile, enoughTestMem, + escapePOSIXShell, expectsError, expectWarning, getArrayBufferViews, getBufferSources, - getCallSite, getPort, getTTYfd, hasCrypto, diff --git a/test/js/node/test/common/inspector-helper.js b/test/js/node/test/common/inspector-helper.js index 2c4d4af6de..864538f245 100644 --- a/test/js/node/test/common/inspector-helper.js +++ b/test/js/node/test/common/inspector-helper.js @@ -255,8 +255,8 @@ class InspectorSession { const callFrame = message.params.callFrames[0]; const location = callFrame.location; const scriptPath = this._scriptsIdsByUrl.get(location.scriptId); - assert.strictEqual(scriptPath.toString(), - expectedScriptPath.toString(), + assert.strictEqual(decodeURIComponent(scriptPath), + decodeURIComponent(expectedScriptPath), `${scriptPath} !== ${expectedScriptPath}`); assert.strictEqual(location.lineNumber, line); return true; @@ -271,6 +271,14 @@ class InspectorSession { `break on ${url}:${line}`); } + waitForPauseOnStart() { + return this + .waitForNotification( + (notification) => + notification.method === 'Debugger.paused' && notification.params.reason === 'Break on start', + 'break on start'); + } + pausedDetails() { return this._pausedDetails; } @@ -309,6 +317,10 @@ class InspectorSession { return notification.method === 'Runtime.executionContextDestroyed' && notification.params.executionContextId === 1; }); + await this.waitForDisconnect(); + } + + async waitForDisconnect() { while ((await this._instance.nextStderrString()) !== 'Waiting for the debugger to disconnect...'); await this.disconnect(); diff --git a/test/js/node/test/common/net.js b/test/js/node/test/common/net.js new file mode 100644 index 0000000000..84eddd0966 --- /dev/null +++ b/test/js/node/test/common/net.js @@ -0,0 +1,23 @@ +'use strict'; +const net = require('net'); + +const options = { port: 0, reusePort: true }; + +function checkSupportReusePort() { + return new Promise((resolve, reject) => { + const server = net.createServer().listen(options); + server.on('listening', () => { + server.close(resolve); + }); + server.on('error', (err) => { + console.log('The `reusePort` option is not supported:', err.message); + server.close(); + reject(err); + }); + }); +} + +module.exports = { + checkSupportReusePort, + options, +}; diff --git a/test/js/node/test/common/ongc.js b/test/js/node/test/common/ongc.js deleted file mode 100644 index d361c55b51..0000000000 --- a/test/js/node/test/common/ongc.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const gcTrackerMap = new WeakMap(); -const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER'; - -function onGC(obj, gcListener) { - const async_hooks = require('async_hooks'); - - const onGcAsyncHook = async_hooks.createHook({ - init: common.mustCallAtLeast(function(id, type) { - if (this.trackedId === undefined) { - assert.strictEqual(type, gcTrackerTag); - this.trackedId = id; - } - }), - destroy(id) { - assert.notStrictEqual(this.trackedId, -1); - if (id === this.trackedId) { - this.gcListener.ongc(); - onGcAsyncHook.disable(); - } - }, - }).enable(); - onGcAsyncHook.gcListener = gcListener; - - gcTrackerMap.set(obj, new async_hooks.AsyncResource(gcTrackerTag)); - obj = null; -} - -module.exports = onGC; diff --git a/test/js/node/test/common/report.js b/test/js/node/test/common/report.js index 6e41561186..ade0f9aa94 100644 --- a/test/js/node/test/common/report.js +++ b/test/js/node/test/common/report.js @@ -59,7 +59,12 @@ function _validateContent(report, fields = []) { // Verify that all sections are present as own properties of the report. const sections = ['header', 'nativeStack', 'javascriptStack', 'libuv', - 'environmentVariables', 'sharedObjects', 'resourceUsage', 'workers']; + 'sharedObjects', 'resourceUsage', 'workers']; + + if (!process.report.excludeEnv) { + sections.push('environmentVariables'); + } + if (!isWindows) sections.push('userLimits'); @@ -105,7 +110,7 @@ function _validateContent(report, fields = []) { 'glibcVersionRuntime', 'glibcVersionCompiler', 'cwd', 'reportVersion', 'networkInterfaces', 'threadId']; checkForUnknownFields(header, headerFields); - assert.strictEqual(header.reportVersion, 3); // Increment as needed. + assert.strictEqual(header.reportVersion, 5); // Increment as needed. assert.strictEqual(typeof header.event, 'string'); assert.strictEqual(typeof header.trigger, 'string'); assert(typeof header.filename === 'string' || header.filename === null); @@ -251,7 +256,7 @@ function _validateContent(report, fields = []) { assert(typeof usage.free_memory, 'string'); assert(typeof usage.total_memory, 'string'); assert(typeof usage.available_memory, 'string'); - // This field may not exsit + // This field may not exist if (report.resourceUsage.constrained_memory) { assert(typeof report.resourceUsage.constrained_memory, 'string'); } @@ -294,19 +299,21 @@ function _validateContent(report, fields = []) { resource.type === 'loop' ? 'undefined' : 'boolean'); }); - // Verify the format of the environmentVariables section. - for (const [key, value] of Object.entries(report.environmentVariables)) { - assert.strictEqual(typeof key, 'string'); - assert.strictEqual(typeof value, 'string'); + if (!process.report.excludeEnv) { + // Verify the format of the environmentVariables section. + for (const [key, value] of Object.entries(report.environmentVariables)) { + assert.strictEqual(typeof key, 'string'); + assert.strictEqual(typeof value, 'string'); + } } // Verify the format of the userLimits section on non-Windows platforms. if (!isWindows) { - const userLimitsFields = ['core_file_size_blocks', 'data_seg_size_kbytes', + const userLimitsFields = ['core_file_size_blocks', 'data_seg_size_bytes', 'file_size_blocks', 'max_locked_memory_bytes', - 'max_memory_size_kbytes', 'open_files', + 'max_memory_size_bytes', 'open_files', 'stack_size_bytes', 'cpu_time_seconds', - 'max_user_processes', 'virtual_memory_kbytes']; + 'max_user_processes', 'virtual_memory_bytes']; checkForUnknownFields(report.userLimits, userLimitsFields); for (const [type, limits] of Object.entries(report.userLimits)) { assert.strictEqual(typeof type, 'string'); diff --git a/test/js/node/test/common/shared-lib-util.js b/test/js/node/test/common/shared-lib-util.js index b5d947a266..5c2832cda9 100644 --- a/test/js/node/test/common/shared-lib-util.js +++ b/test/js/node/test/common/shared-lib-util.js @@ -13,7 +13,7 @@ function addLibraryPath(env) { return; } - env = env || process.env; + env ||= process.env; env.LD_LIBRARY_PATH = (env.LD_LIBRARY_PATH ? env.LD_LIBRARY_PATH + path.delimiter : '') + diff --git a/test/js/node/test/common/tick.js b/test/js/node/test/common/tick.js index b02315b10c..7bb86a81d5 100644 --- a/test/js/node/test/common/tick.js +++ b/test/js/node/test/common/tick.js @@ -1,5 +1,4 @@ 'use strict'; -require('../common'); module.exports = function tick(x, cb) { function ontick() { diff --git a/test/js/node/test/common/tmpdir.js b/test/js/node/test/common/tmpdir.js index 089e4d03a9..f1f06818dc 100644 --- a/test/js/node/test/common/tmpdir.js +++ b/test/js/node/test/common/tmpdir.js @@ -46,8 +46,6 @@ function refresh(useSpawn = false) { } function onexit(useSpawn) { - if (process.env.KEEP_TEMP) return; - // Change directory to avoid possible EBUSY if (isMainThread) process.chdir(testRoot); diff --git a/test/js/node/test/common/udppair.js b/test/js/node/test/common/udppair.js deleted file mode 100644 index 3f2b0aed78..0000000000 --- a/test/js/node/test/common/udppair.js +++ /dev/null @@ -1,101 +0,0 @@ -'use strict'; -const { internalBinding } = require('internal/test/binding'); -const { JSUDPWrap } = internalBinding('js_udp_wrap'); -const EventEmitter = require('events'); - -// FakeUDPWrap is a testing utility that emulates a UDP connection -// for the sake of making UDP tests more deterministic. -class FakeUDPWrap extends EventEmitter { - constructor() { - super(); - - this._handle = new JSUDPWrap(); - - this._handle.onreadstart = () => this._startReading(); - this._handle.onreadstop = () => this._stopReading(); - this._handle.onwrite = - (wrap, buffers, addr) => this._write(wrap, buffers, addr); - this._handle.getsockname = (obj) => { - Object.assign(obj, { address: '127.0.0.1', family: 'IPv4', port: 1337 }); - return 0; - }; - - this.reading = false; - this.bufferedReceived = []; - this.emitBufferedImmediate = null; - } - - _emitBuffered = () => { - if (!this.reading) return; - if (this.bufferedReceived.length > 0) { - this.emitReceived(this.bufferedReceived.shift()); - this.emitBufferedImmediate = setImmediate(this._emitBuffered); - } else { - this.emit('wantRead'); - } - }; - - _startReading() { - this.reading = true; - this.emitBufferedImmediate = setImmediate(this._emitBuffered); - } - - _stopReading() { - this.reading = false; - clearImmediate(this.emitBufferedImmediate); - } - - _write(wrap, buffers, addr) { - this.emit('send', { buffers, addr }); - setImmediate(() => this._handle.onSendDone(wrap, 0)); - } - - afterBind() { - this._handle.onAfterBind(); - } - - emitReceived(info) { - if (!this.reading) { - this.bufferedReceived.push(info); - return; - } - - const { - buffers, - addr: { - family = 4, - address = '127.0.0.1', - port = 1337, - }, - flags = 0, - } = info; - - let familyInt; - switch (family) { - case 'IPv4': familyInt = 4; break; - case 'IPv6': familyInt = 6; break; - default: throw new Error('bad family'); - } - - for (const buffer of buffers) { - this._handle.emitReceived(buffer, familyInt, address, port, flags); - } - } -} - -function makeUDPPair() { - const serverSide = new FakeUDPWrap(); - const clientSide = new FakeUDPWrap(); - - serverSide.on('send', - (chk) => setImmediate(() => clientSide.emitReceived(chk))); - clientSide.on('send', - (chk) => setImmediate(() => serverSide.emitReceived(chk))); - - return { serverSide, clientSide }; -} - -module.exports = { - FakeUDPWrap, - makeUDPPair, -}; diff --git a/test/js/node/test/common/wpt.js b/test/js/node/test/common/wpt.js index 7917e17729..3ead9cb23b 100644 --- a/test/js/node/test/common/wpt.js +++ b/test/js/node/test/common/wpt.js @@ -458,8 +458,16 @@ class StatusLoader { load() { const dir = path.join(__dirname, '..', 'wpt'); - const statusFile = path.join(dir, 'status', `${this.path}.json`); - const result = JSON.parse(fs.readFileSync(statusFile, 'utf8')); + let statusFile = path.join(dir, 'status', `${this.path}.json`); + let result; + + if (fs.existsSync(statusFile)) { + result = JSON.parse(fs.readFileSync(statusFile, 'utf8')); + } else { + statusFile = path.join(dir, 'status', `${this.path}.cjs`); + result = require(statusFile); + } + this.rules.addRules(result); const subDir = fixtures.path('wpt', this.path); @@ -870,22 +878,16 @@ class WPTRunner { addTestResult(spec, item) { let result = this.results[spec.filename]; - if (!result) { - result = this.results[spec.filename] = {}; - } + result ||= this.results[spec.filename] = {}; if (item.status === kSkip) { // { filename: { skip: 'reason' } } result[kSkip] = item.reason; } else { // { filename: { fail: { expected: [ ... ], // unexpected: [ ... ] } }} - if (!result[item.status]) { - result[item.status] = {}; - } + result[item.status] ||= {}; const key = item.expected ? 'expected' : 'unexpected'; - if (!result[item.status][key]) { - result[item.status][key] = []; - } + result[item.status][key] ||= []; const hasName = result[item.status][key].includes(item.name); if (!hasName) { result[item.status][key].push(item.name); diff --git a/test/js/node/test/common/wpt/worker.js b/test/js/node/test/common/wpt/worker.js new file mode 100644 index 0000000000..855ec7e91c --- /dev/null +++ b/test/js/node/test/common/wpt/worker.js @@ -0,0 +1,70 @@ +'use strict'; + +const { runInNewContext, runInThisContext } = require('vm'); +const { setFlagsFromString } = require('v8'); +const { parentPort, workerData } = require('worker_threads'); + +const { ResourceLoader } = require(workerData.wptRunner); +const resource = new ResourceLoader(workerData.wptPath); + +if (workerData.needsGc) { + // See https://github.com/nodejs/node/issues/16595#issuecomment-340288680 + setFlagsFromString('--expose-gc'); + globalThis.gc = runInNewContext('gc'); +} + +globalThis.self = global; +globalThis.GLOBAL = { + isWindow() { return false; }, + isShadowRealm() { return false; }, +}; +globalThis.require = require; + +// This is a mock for non-fetch tests that use fetch to resolve +// a relative fixture file. +// Actual Fetch API WPTs are executed in nodejs/undici. +globalThis.fetch = function fetch(file) { + return resource.readAsFetch(workerData.testRelativePath, file); +}; + +if (workerData.initScript) { + runInThisContext(workerData.initScript); +} + +runInThisContext(workerData.harness.code, { + filename: workerData.harness.filename, +}); + +// eslint-disable-next-line no-undef +add_result_callback((result) => { + parentPort.postMessage({ + type: 'result', + result: { + status: result.status, + name: result.name, + message: result.message, + stack: result.stack, + }, + }); +}); + +// Keep the event loop alive +const timeout = setTimeout(() => { + parentPort.postMessage({ + type: 'completion', + status: { status: 2 }, + }); +}, 2 ** 31 - 1); // Max timeout is 2^31-1, when overflown the timeout is set to 1. + +// eslint-disable-next-line no-undef +add_completion_callback((_, status) => { + clearTimeout(timeout); + parentPort.postMessage({ + type: 'completion', + status, + }); +}); + +for (const scriptToRun of workerData.scriptsToRun) { + runInThisContext(scriptToRun.code, { filename: scriptToRun.filename }); +}