diff --git a/.prettierignore b/.prettierignore index da765c9c28..42d0c454a9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,6 +5,5 @@ test/js/deno test/node.js src/react-refresh.js *.min.js -test/js/node/test/fixtures -test/js/node/test/common test/snippets +test/js/node/test diff --git a/scripts/check-node.sh b/scripts/check-node.sh new file mode 100755 index 0000000000..e704b7c893 --- /dev/null +++ b/scripts/check-node.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +i=0 +j=0 + +for x in $(git ls-files test/js/node/test/parallel --exclude-standard --others | grep test-$1) +do + i=$((i+1)) + echo ./$x + if timeout 2 $PWD/build/debug/bun-debug ./$x + then + j=$((j+1)) + git add ./$x + fi + echo + echo +done + +echo $i tests tested +echo $j tests passed diff --git a/scripts/runner.node.mjs b/scripts/runner.node.mjs index ae70949a86..483f918a27 100755 --- a/scripts/runner.node.mjs +++ b/scripts/runner.node.mjs @@ -28,9 +28,11 @@ import { getEnv, getFileUrl, getWindowsExitReason, + isArm64, isBuildkite, isCI, isGithubAction, + isMacOS, isWindows, printEnvironment, startGroup, @@ -206,6 +208,31 @@ async function runTests() { if (results.every(({ ok }) => ok)) { for (const testPath of tests) { const title = relative(cwd, join(testsPath, testPath)).replace(/\\/g, "/"); + if (title.startsWith("test/js/node/test/parallel/")) { + await runTest(title, async () => { + const { ok, error, stdout } = await spawnBun(execPath, { + cwd: cwd, + args: [title], + timeout: spawnTimeout, + env: { + FORCE_COLOR: "0", + }, + stdout: chunk => pipeTestStdout(process.stdout, chunk), + stderr: chunk => pipeTestStdout(process.stderr, chunk), + }); + return { + testPath: title, + ok, + status: ok ? "pass" : "fail", + error, + errors: [], + tests: [], + stdout, + stdoutPreview: "", + }; + }); + continue; + } await runTest(title, async () => spawnBunTest(execPath, join("test", testPath))); } } @@ -770,7 +797,7 @@ function isJavaScriptTest(path) { * @returns {boolean} */ function isTest(path) { - if (path.replaceAll(sep, "/").includes("/test-cluster-") && path.endsWith(".js")) return true; + if (path.startsWith("js/node/test/parallel/") && isMacOS && isArm64) return true; if (path.replaceAll(sep, "/").startsWith("js/node/cluster/test-") && path.endsWith(".ts")) return true; return isTestStrict(path); } diff --git a/scripts/utils.mjs b/scripts/utils.mjs index 1ecf4692f3..f3ba348784 100755 --- a/scripts/utils.mjs +++ b/scripts/utils.mjs @@ -23,6 +23,8 @@ export const isMacOS = process.platform === "darwin"; export const isLinux = process.platform === "linux"; export const isPosix = isMacOS || isLinux; +export const isArm64 = process.arch === "arm64"; + /** * @param {string} name * @param {boolean} [required] diff --git a/src/js/node/crypto.ts b/src/js/node/crypto.ts index 32909a2004..3d6da6f6d3 100644 --- a/src/js/node/crypto.ts +++ b/src/js/node/crypto.ts @@ -12037,6 +12037,10 @@ crypto_exports.hash = function hash(algorithm, input, outputEncoding = "hex") { return CryptoHasher.hash(algorithm, input, outputEncoding); }; +crypto_exports.getFips = function getFips() { + return 0; +}; + crypto_exports.getRandomValues = getRandomValues; crypto_exports.randomUUID = randomUUID; crypto_exports.randomInt = randomInt; diff --git a/test/js/node/cluster/common.ts b/test/js/node/cluster/common.ts deleted file mode 100644 index b99b402c49..0000000000 --- a/test/js/node/cluster/common.ts +++ /dev/null @@ -1,37 +0,0 @@ -import assert from "node:assert"; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import process from "node:process"; -import util from "node:util"; - -export const isWindows = process.platform === "win32"; - -export function tmpdirSync(pattern: string = "bun.test.") { - return fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), pattern)); -} - -export function isAlive(pid) { - try { - process.kill(pid, "SIGCONT"); - return true; - } catch { - return false; - } -} - -export function mustNotCall(msg?) { - return function mustNotCall(...args) { - const argsInfo = args.length > 0 ? `\ncalled with arguments: ${args.map(arg => util.inspect(arg)).join(", ")}` : ""; - assert.fail(`${msg || "function should not have been called"} ` + argsInfo); - }; -} - -export function patchEmitter(emitter: any, prefix: string) { - var oldEmit = emitter.emit; - - emitter.emit = function () { - console.log([prefix, arguments[0]]); - oldEmit.apply(emitter, arguments); - }; -} diff --git a/test/js/node/cluster/test-worker-no-exit-http.ts b/test/js/node/cluster/test-worker-no-exit-http.ts index 661c439861..93156225b9 100644 --- a/test/js/node/cluster/test-worker-no-exit-http.ts +++ b/test/js/node/cluster/test-worker-no-exit-http.ts @@ -1,7 +1,15 @@ const assert = require("assert"); const cluster = require("cluster"); const http = require("http"); -import { patchEmitter } from "./common"; + +function patchEmitter(emitter: any, prefix: string) { + var oldEmit = emitter.emit; + + emitter.emit = function () { + console.log([prefix, arguments[0]]); + oldEmit.apply(emitter, arguments); + }; +} let destroyed; let success; diff --git a/test/js/node/cluster/upstream/common/countdown.js b/test/js/node/cluster/upstream/common/countdown.js deleted file mode 100644 index 9853c9fa47..0000000000 --- a/test/js/node/cluster/upstream/common/countdown.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; - -const assert = require("assert"); -const kLimit = Symbol("limit"); -const kCallback = Symbol("callback"); -const common = require("./"); - -class Countdown { - constructor(limit, cb) { - assert.strictEqual(typeof limit, "number"); - assert.strictEqual(typeof cb, "function"); - this[kLimit] = limit; - this[kCallback] = common.mustCall(cb); - } - - dec() { - assert(this[kLimit] > 0, "Countdown expired"); - if (--this[kLimit] === 0) this[kCallback](); - return this[kLimit]; - } - - get remaining() { - return this[kLimit]; - } -} - -module.exports = Countdown; diff --git a/test/js/node/cluster/upstream/common/index.js b/test/js/node/cluster/upstream/common/index.js deleted file mode 100644 index 46df521726..0000000000 --- a/test/js/node/cluster/upstream/common/index.js +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// /* eslint-disable node-core/crypto-check */ -"use strict"; -const process = global.process; // Some tests tamper with the process global. - -const assert = require("assert"); -const { exec, execSync, spawn, spawnSync } = require("child_process"); -const fs = require("fs"); -const net = require("net"); -// Do not require 'os' until needed so that test-os-checked-function can -// monkey patch it. If 'os' is required here, that test will fail. -const path = require("path"); -const { inspect } = require("util"); -const { isMainThread } = require("worker_threads"); - -// Some tests assume a umask of 0o022 so set that up front. Tests that need a -// different umask will set it themselves. -// -// Workers can read, but not set the umask, so check that this is the main -// thread. -if (isMainThread) process.umask(0o022); - -const noop = () => {}; - -const isWindows = process.platform === "win32"; -const isSunOS = process.platform === "sunos"; -const isFreeBSD = process.platform === "freebsd"; -const isOpenBSD = process.platform === "openbsd"; -const isLinux = process.platform === "linux"; -const isOSX = process.platform === "darwin"; -const isPi = (() => { - try { - // Normal Raspberry Pi detection is to find the `Raspberry Pi` string in - // the contents of `/sys/firmware/devicetree/base/model` but that doesn't - // work inside a container. Match the chipset model number instead. - const cpuinfo = fs.readFileSync("/proc/cpuinfo", { encoding: "utf8" }); - const ok = /^Hardware\s*:\s*(.*)$/im.exec(cpuinfo)?.[1] === "BCM2835"; - /^/.test(""); // Clear RegExp.$_, some tests expect it to be empty. - return ok; - } catch { - return false; - } -})(); - -const isDumbTerminal = process.env.TERM === "dumb"; - -const mustCallChecks = []; - -function runCallChecks(exitCode) { - if (exitCode !== 0) return; - - const failed = mustCallChecks.filter(function (context) { - if ("minimum" in context) { - context.messageSegment = `at least ${context.minimum}`; - return context.actual < context.minimum; - } - context.messageSegment = `exactly ${context.exact}`; - return context.actual !== context.exact; - }); - - failed.forEach(function (context) { - console.log( - "Mismatched %s function calls. Expected %s, actual %d.", - context.name, - context.messageSegment, - context.actual, - ); - console.log(context.stack.split("\n").slice(2).join("\n")); - }); - - if (failed.length) process.exit(1); -} - -function mustCall(fn, exact) { - return _mustCallInner(fn, exact, "exact"); -} - -function mustSucceed(fn, exact) { - return mustCall(function (err, ...args) { - assert.ifError(err); - if (typeof fn === "function") return fn.apply(this, args); - }, exact); -} - -function _mustCallInner(fn, criteria = 1, field) { - if (process._exiting) throw new Error("Cannot use common.mustCall*() in process exit handler"); - if (typeof fn === "number") { - criteria = fn; - fn = noop; - } else if (fn === undefined) { - fn = noop; - } - - if (typeof criteria !== "number") throw new TypeError(`Invalid ${field} value: ${criteria}`); - - const context = { - [field]: criteria, - actual: 0, - stack: inspect(new Error()), - name: fn.name || "", - }; - - // Add the exit listener only once to avoid listener leak warnings - if (mustCallChecks.length === 0) process.on("exit", runCallChecks); - - mustCallChecks.push(context); - - const _return = function () { - // eslint-disable-line func-style - context.actual++; - return fn.apply(this, arguments); - }; - // Function instances have own properties that may be relevant. - // Let's replicate those properties to the returned function. - // Refs: https://tc39.es/ecma262/#sec-function-instances - Object.defineProperties(_return, { - name: { - value: fn.name, - writable: false, - enumerable: false, - configurable: true, - }, - length: { - value: fn.length, - writable: false, - enumerable: false, - configurable: true, - }, - }); - return _return; -} - -function getCallSite(top) { - const originalStackFormatter = Error.prepareStackTrace; - Error.prepareStackTrace = (err, stack) => `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; - const err = new Error(); - Error.captureStackTrace(err, top); - // With the V8 Error API, the stack is not formatted until it is accessed - err.stack; // eslint-disable-line no-unused-expressions - Error.prepareStackTrace = originalStackFormatter; - return err.stack; -} - -function mustNotCall(msg) { - const callSite = getCallSite(mustNotCall); - return function mustNotCall(...args) { - const argsInfo = args.length > 0 ? `\ncalled with arguments: ${args.map(arg => inspect(arg)).join(", ")}` : ""; - assert.fail(`${msg || "function should not have been called"} at ${callSite}` + argsInfo); - }; -} - -function printSkipMessage(msg) { - console.log(`1..0 # Skipped: ${msg}`); -} - -function skip(msg) { - printSkipMessage(msg); - process.exit(0); -} - -function isAlive(pid) { - try { - process.kill(pid, "SIGCONT"); - return true; - } catch { - return false; - } -} - -function skipIf32Bits() { - if (bits < 64) { - skip("The tested feature is not available in 32bit builds"); - } -} - -function skipIfWorker() { - if (!isMainThread) { - skip("This test only works on a main thread"); - } -} - -function skipIfDumbTerminal() { - if (isDumbTerminal) { - skip("skipping - dumb terminal"); - } -} - -const common = { - isAlive, - isDumbTerminal, - isFreeBSD, - isLinux, - isMainThread, - isOpenBSD, - isOSX, - isPi, - isSunOS, - isWindows, - mustCall, - mustNotCall, - mustSucceed, - printSkipMessage, - skip, - skipIf32Bits, - skipIfDumbTerminal, - // On IBMi, process.platform and os.platform() both return 'aix', - // when built with Python versions earlier than 3.9. - // It is not enough to differentiate between IBMi and real AIX system. - get isAIX() { - return require("os").type() === "AIX"; - }, - - get isIBMi() { - return require("os").type() === "OS400"; - }, - - get isLinuxPPCBE() { - return process.platform === "linux" && process.arch === "ppc64" && require("os").endianness() === "BE"; - }, -}; - -const validProperties = new Set(Object.keys(common)); -module.exports = new Proxy(common, { - get(obj, prop) { - if (!validProperties.has(prop)) throw new Error(`Using invalid common property: '${prop}'`); - return obj[prop]; - }, -}); diff --git a/test/js/node/cluster/upstream/common/tmpdir.js b/test/js/node/cluster/upstream/common/tmpdir.js deleted file mode 100644 index 7de0b113a3..0000000000 --- a/test/js/node/cluster/upstream/common/tmpdir.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; - -const { spawnSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); -const { pathToFileURL } = require("url"); -const { isMainThread } = require("worker_threads"); - -function rmSync(pathname, useSpawn) { - if (useSpawn) { - const escapedPath = pathname.replaceAll("\\", "\\\\"); - spawnSync(process.execPath, [ - "-e", - `require("fs").rmSync("${escapedPath}", { maxRetries: 3, recursive: true, force: true });`, - ]); - } else { - fs.rmSync(pathname, { maxRetries: 3, recursive: true, force: true }); - } -} - -const testRoot = process.env.NODE_TEST_DIR ? fs.realpathSync(process.env.NODE_TEST_DIR) : path.resolve(__dirname, ".."); - -// Using a `.` prefixed name, which is the convention for "hidden" on POSIX, -// gets tools to ignore it by default or by simple rules, especially eslint. -const tmpdirName = ".tmp." + (process.env.TEST_SERIAL_ID || process.env.TEST_THREAD_ID || "0"); -const tmpPath = path.join(testRoot, tmpdirName); - -let firstRefresh = true; -function refresh(useSpawn = false) { - rmSync(tmpPath, useSpawn); - fs.mkdirSync(tmpPath); - - if (firstRefresh) { - firstRefresh = false; - // Clean only when a test uses refresh. This allows for child processes to - // use the tmpdir and only the parent will clean on exit. - process.on("exit", () => { - return onexit(useSpawn); - }); - } -} - -function onexit(useSpawn) { - // Change directory to avoid possible EBUSY - if (isMainThread) process.chdir(testRoot); - - try { - rmSync(tmpPath, useSpawn); - } catch (e) { - console.error("Can't clean tmpdir:", tmpPath); - - const files = fs.readdirSync(tmpPath); - console.error("Files blocking:", files); - - if (files.some(f => f.startsWith(".nfs"))) { - // Warn about NFS "silly rename" - console.error('Note: ".nfs*" might be files that were open and ' + "unlinked but not closed."); - console.error("See http://nfs.sourceforge.net/#faq_d2 for details."); - } - - console.error(); - throw e; - } -} - -function resolve(...paths) { - return path.resolve(tmpPath, ...paths); -} - -function hasEnoughSpace(size) { - const { bavail, bsize } = fs.statfsSync(tmpPath); - return bavail >= Math.ceil(size / bsize); -} - -function fileURL(...paths) { - // When called without arguments, add explicit trailing slash - const fullPath = path.resolve(tmpPath + path.sep, ...paths); - - return pathToFileURL(fullPath); -} - -module.exports = { - fileURL, - hasEnoughSpace, - path: tmpPath, - refresh, - resolve, -}; diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-advanced-serialization.js b/test/js/node/cluster/upstream/parallel/test-cluster-advanced-serialization.js deleted file mode 100644 index 8a368d44c7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-advanced-serialization.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - cluster.settings.serialization = "advanced"; - const worker = cluster.fork(); - const circular = {}; - circular.circular = circular; - - worker.on( - "online", - common.mustCall(() => { - worker.send(circular); - - worker.on( - "message", - common.mustCall(msg => { - assert.deepStrictEqual(msg, circular); - worker.kill(); - }), - ); - }), - ); -} else { - process.on("message", msg => process.send(msg)); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-call-and-destroy.js b/test/js/node/cluster/upstream/parallel/test-cluster-call-and-destroy.js deleted file mode 100644 index 6d9ff44e67..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-call-and-destroy.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -const common = require("../common"); -const cluster = require("cluster"); -const assert = require("assert"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - worker.on( - "disconnect", - common.mustCall(() => { - assert.strictEqual(worker.isConnected(), false); - worker.destroy(); - }), - ); -} else { - assert.strictEqual(cluster.worker.isConnected(), true); - cluster.worker.disconnect(); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-dgram.js b/test/js/node/cluster/upstream/parallel/test-cluster-child-index-dgram.js deleted file mode 100644 index 426c8de9f4..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-dgram.js +++ /dev/null @@ -1,43 +0,0 @@ -"use strict"; -const common = require("../common"); -const Countdown = require("../common/countdown"); -if (common.isWindows) common.skip("dgram clustering is currently not supported on Windows."); - -const cluster = require("cluster"); -const dgram = require("dgram"); - -// Test an edge case when using `cluster` and `dgram.Socket.bind()` -// the port of `0`. -const kPort = 0; - -function child() { - const kTime = 2; - const countdown = new Countdown(kTime * 2, () => { - process.exit(0); - }); - for (let i = 0; i < kTime; i += 1) { - const socket = new dgram.Socket("udp4"); - socket.bind( - kPort, - common.mustCall(() => { - // `process.nextTick()` or `socket2.close()` would throw - // ERR_SOCKET_DGRAM_NOT_RUNNING - process.nextTick(() => { - socket.close(countdown.dec()); - const socket2 = new dgram.Socket("udp4"); - socket2.bind( - kPort, - common.mustCall(() => { - process.nextTick(() => { - socket2.close(countdown.dec()); - }); - }), - ); - }); - }), - ); - } -} - -if (cluster.isMaster) cluster.fork(__filename); -else child(); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-net.js b/test/js/node/cluster/upstream/parallel/test-cluster-child-index-net.js deleted file mode 100644 index 961924a2d6..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-net.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -const common = require("../common"); -const Countdown = require("../common/countdown"); -const cluster = require("cluster"); -const net = require("net"); - -// Test an edge case when using `cluster` and `net.Server.listen()` to -// the port of `0`. -const kPort = 0; - -function child() { - const kTime = 2; - const countdown = new Countdown(kTime * 2, () => { - process.exit(0); - }); - for (let i = 0; i < kTime; i += 1) { - const server = net.createServer(); - server.listen( - kPort, - common.mustCall(() => { - server.close(countdown.dec()); - const server2 = net.createServer(); - server2.listen( - kPort, - common.mustCall(() => { - server2.close(countdown.dec()); - }), - ); - }), - ); - } -} - -if (cluster.isMaster) cluster.fork(__filename); -else child(); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-concurrent-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-concurrent-disconnect.js deleted file mode 100644 index 1e1daa9ef4..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-concurrent-disconnect.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; - -// Ref: https://github.com/nodejs/node/issues/32106 - -const common = require("../common"); -if (common.isLinux) return; // TODO: bun -if (common.isWindows) return; // TODO: bun - -const assert = require("assert"); -const cluster = require("cluster"); -const os = require("os"); - -if (cluster.isPrimary) { - const workers = []; - const numCPUs = os.availableParallelism(); - let waitOnline = numCPUs; - for (let i = 0; i < numCPUs; i++) { - const worker = cluster.fork(); - workers[i] = worker; - worker.once( - "online", - common.mustCall(() => { - if (--waitOnline === 0) - for (const worker of workers) if (worker.isConnected()) worker.send(i % 2 ? "disconnect" : "destroy"); - }), - ); - - // These errors can occur due to the nature of the test, we might be trying - // to send messages when the worker is disconnecting. - worker.on("error", err => { - assert.strictEqual(err.syscall, "write"); - if (common.isOSX) { - assert(["EPIPE", "ENOTCONN"].includes(err.code), err); - } else { - assert(["EPIPE", "ECONNRESET"].includes(err.code), err); - } - }); - - worker.once( - "disconnect", - common.mustCall(() => { - for (const worker of workers) if (worker.isConnected()) worker.send("disconnect"); - }), - ); - - worker.once( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }), - ); - } -} else { - process.on("message", msg => { - if (cluster.worker.isConnected()) cluster.worker[msg](); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-cwd.js b/test/js/node/cluster/upstream/parallel/test-cluster-cwd.js deleted file mode 100644 index d28bfdf545..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-cwd.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const tmpdir = require("../common/tmpdir"); - -if (cluster.isPrimary) { - tmpdir.refresh(); - - assert.strictEqual(cluster.settings.cwd, undefined); - cluster.fork().on( - "message", - common.mustCall(msg => { - assert.strictEqual(msg, process.cwd()); - }), - ); - - cluster.setupPrimary({ cwd: tmpdir.path }); - assert.strictEqual(cluster.settings.cwd, tmpdir.path); - cluster.fork().on( - "message", - common.mustCall(msg => { - assert.strictEqual(msg, tmpdir.path); - }), - ); -} else { - process.send(process.cwd()); - process.disconnect(); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js b/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js deleted file mode 100644 index f461734eb6..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; - -const common = require("../common"); - -// Test should fail in Node.js 5.4.1 and pass in later versions. - -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - cluster.on("exit", (worker, code) => { - assert.strictEqual(code, 0, `worker exited with code: ${code}, expected 0`); - }); - - return cluster.fork(); -} - -let eventFired = false; - -cluster.worker.disconnect(); - -process.nextTick( - common.mustCall(() => { - assert.ok(!eventFired, "disconnect event should wait for ack"); - }), -); - -cluster.worker.on( - "disconnect", - common.mustCall(() => { - eventFired = true; - }), -); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-fork-windowsHide.js b/test/js/node/cluster/upstream/parallel/test-cluster-fork-windowsHide.js deleted file mode 100644 index 273e8146a7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-fork-windowsHide.js +++ /dev/null @@ -1,76 +0,0 @@ -"use strict"; -const common = require("../common"); -if (common.isWindows) return; // TODO: bun -const assert = require("assert"); -const child_process = require("child_process"); -const cluster = require("cluster"); - -if (!process.argv[2]) { - // It seems Windows only allocate new console window for - // attaching processes spawned by detached processes. i.e. - // - If process D is spawned by process C with `detached: true`, - // and process W is spawned by process D with `detached: false`, - // W will get a new black console window popped up. - // - If D is spawned by C with `detached: false` or W is spawned - // by D with `detached: true`, no console window will pop up for W. - // - // So, we have to spawn a detached process first to run the actual test. - const primary = child_process.spawn(process.argv[0], [process.argv[1], "--cluster"], { - detached: true, - stdio: ["ignore", "ignore", "ignore", "ipc"], - }); - - const messageHandlers = { - workerOnline: common.mustCall(), - mainWindowHandle: common.mustCall(msg => { - assert.match(msg.value, /0\s*/); - }), - workerExit: common.mustCall(msg => { - assert.strictEqual(msg.code, 0); - assert.strictEqual(msg.signal, null); - }), - }; - - primary.on("message", msg => { - const handler = messageHandlers[msg.type]; - assert.ok(handler); - handler(msg); - }); - - primary.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }), - ); -} else if (cluster.isPrimary) { - cluster.setupPrimary({ - silent: true, - windowsHide: true, - }); - - const worker = cluster.fork(); - worker.on("exit", (code, signal) => { - process.send({ type: "workerExit", code: code, signal: signal }); - }); - - worker.on("online", msg => { - process.send({ type: "workerOnline" }); - - let output = "0"; - if (process.platform === "win32") { - output = child_process.execSync( - "powershell -NoProfile -c " + `"(Get-Process -Id ${worker.process.pid}).MainWindowHandle"`, - { windowsHide: true, encoding: "utf8" }, - ); - } - - process.send({ type: "mainWindowHandle", value: output }); - worker.send("shutdown"); - }); -} else { - cluster.worker.on("message", msg => { - cluster.worker.disconnect(); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-invalid-message.js b/test/js/node/cluster/upstream/parallel/test-cluster-invalid-message.js deleted file mode 100644 index fdfe1ada62..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-invalid-message.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - - worker.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }), - ); - - worker.on("online", () => { - worker.send( - { - cmd: "NODE_CLUSTER", - ack: -1, - }, - () => { - worker.disconnect(); - }, - ); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-kill-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-kill-disconnect.js deleted file mode 100644 index e1c0a313e2..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-kill-disconnect.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -const common = require("../common"); - -// Check that cluster works perfectly for both `kill` and `disconnect` cases. -// Also take into account that the `disconnect` event may be received after the -// `exit` event. -// https://github.com/nodejs/node/issues/3238 - -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - function forkWorker(action) { - const worker = cluster.fork({ action }); - worker.on( - "disconnect", - common.mustCall(() => { - assert.strictEqual(worker.exitedAfterDisconnect, true); - }), - ); - - worker.on( - "exit", - common.mustCall(() => { - assert.strictEqual(worker.exitedAfterDisconnect, true); - }), - ); - } - - forkWorker("disconnect"); - forkWorker("kill"); -} else { - cluster.worker[process.env.action](); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-kill-infinite-loop.js b/test/js/node/cluster/upstream/parallel/test-cluster-kill-infinite-loop.js deleted file mode 100644 index 837b11f2a1..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-kill-infinite-loop.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -const common = require("../common"); -const cluster = require("cluster"); -const assert = require("assert"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - - worker.on( - "online", - common.mustCall(() => { - // Use worker.process.kill() instead of worker.kill() because the latter - // waits for a graceful disconnect, which will never happen. - worker.process.kill(); - }), - ); - - worker.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, null); - assert.strictEqual(signal, "SIGTERM"); - }), - ); -} else { - while (true); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-listening-port.js b/test/js/node/cluster/upstream/parallel/test-cluster-listening-port.js deleted file mode 100644 index ecf9398cd7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-listening-port.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - cluster.fork(); - cluster.on( - "listening", - common.mustCall(function (worker, address) { - const port = address.port; - // Ensure that the port is not 0 or null - assert(port); - // Ensure that the port is numerical - assert.strictEqual(typeof port, "number"); - worker.kill(); - }), - ); -} else { - net.createServer(common.mustNotCall()).listen(0); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-primary-error.js b/test/js/node/cluster/upstream/parallel/test-cluster-primary-error.js deleted file mode 100644 index 763ae3eab3..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-primary-error.js +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -const totalWorkers = 2; - -// Cluster setup -if (cluster.isWorker) { - const http = require("http"); - http.Server(() => {}).listen(0, "127.0.0.1"); -} else if (process.argv[2] === "cluster") { - // Send PID to testcase process - let forkNum = 0; - cluster.on( - "fork", - common.mustCall(function forkEvent(worker) { - // Send PID - process.send({ - cmd: "worker", - workerPID: worker.process.pid, - }); - - // Stop listening when done - if (++forkNum === totalWorkers) { - cluster.removeListener("fork", forkEvent); - } - }, totalWorkers), - ); - - // Throw accidental error when all workers are listening - let listeningNum = 0; - cluster.on( - "listening", - common.mustCall(function listeningEvent() { - // When all workers are listening - if (++listeningNum === totalWorkers) { - // Stop listening - cluster.removeListener("listening", listeningEvent); - - // Throw accidental error - process.nextTick(() => { - throw new Error("accidental error"); - }); - } - }, totalWorkers), - ); - - // Startup a basic cluster - cluster.fork(); - cluster.fork(); -} else { - // This is the testcase - - const fork = require("child_process").fork; - - // List all workers - const workers = []; - - // Spawn a cluster process - const primary = fork(process.argv[1], ["cluster"], { silent: true }); - - // Handle messages from the cluster - primary.on( - "message", - common.mustCall(data => { - // Add worker pid to list and progress tracker - if (data.cmd === "worker") { - workers.push(data.workerPID); - } - }, totalWorkers), - ); - - // When cluster is dead - primary.on( - "exit", - common.mustCall(code => { - // Check that the cluster died accidentally (non-zero exit code) - assert.strictEqual(code, 1); - - // XXX(addaleax): The fact that this uses raw PIDs makes the test inherently - // flaky – another process might end up being started right after the - // workers finished and receive the same PID. - const pollWorkers = () => { - // When primary is dead all workers should be dead too - if (workers.some(pid => common.isAlive(pid))) { - setTimeout(pollWorkers, 50); - } - }; - - // Loop indefinitely until worker exit - pollWorkers(); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-process-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-process-disconnect.js deleted file mode 100644 index bcaf7df146..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-process-disconnect.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - worker.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0, `Worker did not exit normally with code: ${code}`); - assert.strictEqual(signal, null, `Worker did not exit normally with signal: ${signal}`); - }), - ); -} else { - const net = require("net"); - const server = net.createServer(); - server.listen( - 0, - common.mustCall(() => { - process.disconnect(); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-rr-ref.js b/test/js/node/cluster/upstream/parallel/test-cluster-rr-ref.js deleted file mode 100644 index d5f0cbd083..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-rr-ref.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; - -const common = require("../common"); -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - cluster.fork().on("message", function (msg) { - if (msg === "done") this.kill(); - }); -} else { - const server = net.createServer(common.mustNotCall()); - server.listen(0, function () { - server.unref(); - server.ref(); - server.close(function () { - process.send("done"); - }); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect-on-error.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect-on-error.js deleted file mode 100644 index f9e3a0de2c..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect-on-error.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -const common = require("../common"); -const http = require("http"); -const cluster = require("cluster"); -const assert = require("assert"); - -cluster.schedulingPolicy = cluster.SCHED_NONE; - -const server = http.createServer(); -if (cluster.isPrimary) { - let worker; - - server.listen( - 0, - common.mustSucceed(() => { - assert(worker); - - worker.send({ port: server.address().port }); - }), - ); - - worker = cluster.fork(); - worker.on( - "exit", - common.mustCall(() => { - server.close(); - }), - ); -} else { - process.on( - "message", - common.mustCall(msg => { - assert(msg.port); - - server.listen(msg.port); - server.on( - "error", - common.mustCall(e => { - cluster.worker.disconnect(); - }), - ); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-isdead.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-isdead.js deleted file mode 100644 index 079a154443..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-isdead.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -require("../common"); -const cluster = require("cluster"); -const assert = require("assert"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - let workerDead = worker.isDead(); - assert.ok( - !workerDead, - `isDead() returned ${workerDead}. isDead() should return ` + "false right after the worker has been created.", - ); - - worker.on("exit", function () { - workerDead = worker.isDead(); - assert.ok( - workerDead, - `isDead() returned ${workerDead}. After an event has been ` + "emitted, isDead should return true", - ); - }); - - worker.on("message", function (msg) { - if (msg === "readyToDie") { - worker.kill(); - } - }); -} else if (cluster.isWorker) { - const workerDead = cluster.worker.isDead(); - assert.ok( - !workerDead, - `isDead() returned ${workerDead}. isDead() should return ` + "false when called from within a worker", - ); - process.send("readyToDie"); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill-signal.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill-signal.js deleted file mode 100644 index 1562a5e9f3..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill-signal.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -// test-cluster-worker-kill-signal.js -// verifies that when we're killing a worker using Worker.prototype.kill -// and the worker's process was killed with the given signal (SIGKILL) - -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - // Make the worker run something - const http = require("http"); - const server = http.Server(() => {}); - - server.once("listening", common.mustCall()); - server.listen(0, "127.0.0.1"); -} else if (cluster.isMaster) { - const KILL_SIGNAL = "SIGKILL"; - - // Start worker - const worker = cluster.fork(); - - // When the worker is up and running, kill it - worker.once( - "listening", - common.mustCall(() => { - worker.kill(KILL_SIGNAL); - }), - ); - - // Check worker events and properties - worker.on( - "disconnect", - common.mustCall(() => { - assert.strictEqual(worker.exitedAfterDisconnect, false); - assert.strictEqual(worker.state, "disconnected"); - }, 1), - ); - - // Check that the worker died - worker.once( - "exit", - common.mustCall((exitCode, signalCode) => { - const isWorkerProcessStillAlive = common.isAlive(worker.process.pid); - const numOfRunningWorkers = Object.keys(cluster.workers).length; - - assert.strictEqual(exitCode, null); - assert.strictEqual(signalCode, KILL_SIGNAL); - assert.strictEqual(isWorkerProcessStillAlive, false); - assert.strictEqual(numOfRunningWorkers, 0); - }, 1), - ); - - // Check if the cluster was killed as well - cluster.on("exit", common.mustCall(1)); -} diff --git a/test/js/node/test/common/assertSnapshot.js b/test/js/node/test/common/assertSnapshot.js index 88f40281e0..a22455160b 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(process.cwd(), ''); + return str.replaceAll(path.resolve(__dirname, '../..'), ''); } function transform(...args) { @@ -78,8 +78,11 @@ async function spawnAndAssert(filename, transform = (x) => x, { tty = false, ... return; } const flags = common.parseTestFlags(filename); - const executable = tty ? 'tools/pseudo-tty.py' : process.execPath; - const args = tty ? [process.execPath, ...flags, filename] : [...flags, filename]; + const executable = tty ? (process.env.PYTHON || 'python3') : process.execPath; + const args = + tty ? + [path.join(__dirname, '../..', 'tools/pseudo-tty.py'), process.execPath, ...flags, filename] : + [...flags, filename]; const { stdout, stderr } = await common.spawnPromisified(executable, args, options); await assertSnapshot(transform(`${stdout}${stderr}`), filename); } diff --git a/test/js/node/test/common/duplexpair.js b/test/js/node/test/common/duplexpair.js deleted file mode 100644 index 1f41ed32f1..0000000000 --- a/test/js/node/test/common/duplexpair.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; -const { Duplex } = require('stream'); -const assert = require('assert'); - -const kCallback = Symbol('Callback'); -const kOtherSide = Symbol('Other'); - -class DuplexSocket extends Duplex { - constructor() { - super(); - this[kCallback] = null; - this[kOtherSide] = null; - } - - _read() { - const callback = this[kCallback]; - if (callback) { - this[kCallback] = null; - callback(); - } - } - - _write(chunk, encoding, callback) { - assert.notStrictEqual(this[kOtherSide], null); - assert.strictEqual(this[kOtherSide][kCallback], null); - if (chunk.length === 0) { - process.nextTick(callback); - } else { - this[kOtherSide].push(chunk); - this[kOtherSide][kCallback] = callback; - } - } - - _final(callback) { - this[kOtherSide].on('end', callback); - this[kOtherSide].push(null); - } -} - -function makeDuplexPair() { - const clientSide = new DuplexSocket(); - const serverSide = new DuplexSocket(); - clientSide[kOtherSide] = serverSide; - serverSide[kOtherSide] = clientSide; - return { clientSide, serverSide }; -} - -module.exports = makeDuplexPair; diff --git a/test/js/node/test/common/globals.js b/test/js/node/test/common/globals.js index 42caece2b8..5d1c4415ee 100644 --- a/test/js/node/test/common/globals.js +++ b/test/js/node/test/common/globals.js @@ -79,7 +79,6 @@ const webIdlExposedWildcard = new Set([ 'TextDecoder', 'AbortController', 'AbortSignal', - 'CustomEvent', 'EventTarget', 'Event', 'URL', @@ -127,7 +126,6 @@ const webIdlExposedWindow = new Set([ 'Response', 'WebSocket', 'EventSource', - 'CloseEvent', ]); const nodeGlobals = new Set([ diff --git a/test/js/node/test/common/index.js b/test/js/node/test/common/index.js index c67a5b8a81..38a48e8901 100644 --- a/test/js/node/test/common/index.js +++ b/test/js/node/test/common/index.js @@ -141,7 +141,7 @@ const isSunOS = process.platform === 'sunos'; const isFreeBSD = process.platform === 'freebsd'; const isOpenBSD = process.platform === 'openbsd'; const isLinux = process.platform === 'linux'; -const isOSX = process.platform === 'darwin'; +const isMacOS = process.platform === 'darwin'; const isASan = process.config.variables.asan === 1; const isPi = (() => { try { @@ -338,10 +338,9 @@ if (global.structuredClone) { knownGlobals.push(global.structuredClone); } -// BUN:TODO: uncommenting this crashes bun -// if (global.EventSource) { -// knownGlobals.push(EventSource); -// } +if (global.EventSource) { + knownGlobals.push(EventSource); +} if (global.fetch) { knownGlobals.push(fetch); @@ -967,13 +966,18 @@ function getPrintedStackTrace(stderr) { * @param {object} mod result returned by require() * @param {object} expectation shape of expected namespace. */ -function expectRequiredModule(mod, expectation) { +function expectRequiredModule(mod, expectation, checkESModule = true) { + const clone = { ...mod }; + if (Object.hasOwn(mod, 'default') && checkESModule) { + assert.strictEqual(mod.__esModule, true); + delete clone.__esModule; + } assert(isModuleNamespaceObject(mod)); - assert.deepStrictEqual({ ...mod }, { ...expectation }); + assert.deepStrictEqual(clone, { ...expectation }); } const common = { - allowGlobals: [], + allowGlobals, buildType, canCreateSymLink, childShouldThrowAndAbort, @@ -1001,7 +1005,7 @@ const common = { isLinux, isMainThread, isOpenBSD, - isOSX, + isMacOS, isPi, isSunOS, isWindows, diff --git a/test/js/node/test/common/index.mjs b/test/js/node/test/common/index.mjs index 430527faf8..007ce233fb 100644 --- a/test/js/node/test/common/index.mjs +++ b/test/js/node/test/common/index.mjs @@ -30,7 +30,7 @@ const { isLinuxPPCBE, isMainThread, isOpenBSD, - isOSX, + isMacOS, isSunOS, isWindows, localIPv6Hosts, @@ -85,7 +85,7 @@ export { isLinuxPPCBE, isMainThread, isOpenBSD, - isOSX, + isMacOS, isSunOS, isWindows, localIPv6Hosts, diff --git a/test/js/node/test/common/process-exit-code-cases.js b/test/js/node/test/common/process-exit-code-cases.js new file mode 100644 index 0000000000..54cfe2655b --- /dev/null +++ b/test/js/node/test/common/process-exit-code-cases.js @@ -0,0 +1,138 @@ +'use strict'; + +const assert = require('assert'); + +function getTestCases(isWorker = false) { + const cases = []; + function exitsOnExitCodeSet() { + process.exitCode = 42; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + } + cases.push({ func: exitsOnExitCodeSet, result: 42 }); + + function changesCodeViaExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + process.exit(42); + } + cases.push({ func: changesCodeViaExit, result: 42 }); + + function changesCodeZeroExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 0); + assert.strictEqual(code, 0); + }); + process.exit(0); + } + cases.push({ func: changesCodeZeroExit, result: 0 }); + + function exitWithOneOnUncaught() { + process.exitCode = 99; + process.on('exit', (code) => { + // Cannot use assert because it will be uncaughtException -> 1 exit code + // that will render this test useless + if (code !== 1 || process.exitCode !== 1) { + console.log('wrong code! expected 1 for uncaughtException'); + process.exit(99); + } + }); + throw new Error('ok'); + } + cases.push({ + func: exitWithOneOnUncaught, + result: 1, + error: /^Error: ok$/, + }); + + function changeCodeInsideExit() { + process.exitCode = 95; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 95); + assert.strictEqual(code, 95); + process.exitCode = 99; + }); + } + cases.push({ func: changeCodeInsideExit, result: 99 }); + + function zeroExitWithUncaughtHandler() { + const noop = () => { }; + process.on('exit', (code) => { + process.off('uncaughtException', noop); + assert.strictEqual(process.exitCode, undefined); + assert.strictEqual(code, 0); + }); + process.on('uncaughtException', noop); + throw new Error('ok'); + } + cases.push({ func: zeroExitWithUncaughtHandler, result: 0 }); + + function changeCodeInUncaughtHandler() { + const modifyExitCode = () => { process.exitCode = 97; }; + process.on('exit', (code) => { + process.off('uncaughtException', modifyExitCode); + assert.strictEqual(process.exitCode, 97); + assert.strictEqual(code, 97); + }); + process.on('uncaughtException', modifyExitCode); + throw new Error('ok'); + } + cases.push({ func: changeCodeInUncaughtHandler, result: 97 }); + + function changeCodeInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 98; + }); + throw new Error('ok'); + } + cases.push({ + func: changeCodeInExitWithUncaught, + result: 98, + error: /^Error: ok$/, + }); + + function exitWithZeroInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 0; + }); + throw new Error('ok'); + } + cases.push({ + func: exitWithZeroInExitWithUncaught, + result: 0, + error: /^Error: ok$/, + }); + + function exitWithThrowInUncaughtHandler() { + process.on('uncaughtException', () => { + throw new Error('ok'); + }); + throw new Error('bad'); + } + cases.push({ + func: exitWithThrowInUncaughtHandler, + result: isWorker ? 1 : 7, + error: /^Error: ok$/, + }); + + function exitWithUndefinedFatalException() { + process._fatalException = undefined; + throw new Error('ok'); + } + cases.push({ + func: exitWithUndefinedFatalException, + result: 6, + }); + return cases; +} +exports.getTestCases = getTestCases; diff --git a/test/js/node/test/common/sea.js b/test/js/node/test/common/sea.js index 863047ab36..53bfd93d92 100644 --- a/test/js/node/test/common/sea.js +++ b/test/js/node/test/common/sea.js @@ -5,7 +5,7 @@ const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const { inspect } = require('util'); -const { readFileSync, copyFileSync } = require('fs'); +const { readFileSync, copyFileSync, statSync } = require('fs'); const { spawnSyncAndExitWithoutError, } = require('../common/child_process'); @@ -50,12 +50,23 @@ function skipIfSingleExecutableIsNotSupported() { common.skip('UndefinedBehavior Sanitizer is not supported'); } + try { + readFileSync(process.execPath); + } catch (e) { + if (e.code === 'ERR_FS_FILE_TOO_LARGE') { + common.skip('The Node.js binary is too large to be supported by postject'); + } + } + tmpdir.refresh(); // The SEA tests involve making a copy of the executable and writing some fixtures - // to the tmpdir. To be safe, ensure that at least 120MB disk space is available. - if (!tmpdir.hasEnoughSpace(120 * 1024 * 1024)) { - common.skip('Available disk space < 120MB'); + // to the tmpdir. To be safe, ensure that the disk space has at least a copy of the + // executable and some extra space for blobs and configs is available. + const stat = statSync(process.execPath); + const expectedSpace = stat.size + 10 * 1024 * 1024; + if (!tmpdir.hasEnoughSpace(expectedSpace)) { + common.skip(`Available disk space < ${Math.floor(expectedSpace / 1024 / 1024)} MB`); } } diff --git a/test/js/node/test/common/shared-lib-util.js b/test/js/node/test/common/shared-lib-util.js index 3ecd38791c..b5d947a266 100644 --- a/test/js/node/test/common/shared-lib-util.js +++ b/test/js/node/test/common/shared-lib-util.js @@ -22,7 +22,7 @@ function addLibraryPath(env) { env.LIBPATH = (env.LIBPATH ? env.LIBPATH + path.delimiter : '') + kExecPath; - // For Mac OSX. + // For macOS. env.DYLD_LIBRARY_PATH = (env.DYLD_LIBRARY_PATH ? env.DYLD_LIBRARY_PATH + path.delimiter : '') + kExecPath; diff --git a/test/js/node/test/parallel/.gitignore b/test/js/node/test/parallel/.gitignore deleted file mode 100644 index fd3ec92e0c..0000000000 --- a/test/js/node/test/parallel/.gitignore +++ /dev/null @@ -1,67 +0,0 @@ -# Not working yet: -child-process-double-pipe.test.js -child-process-exec-cwd.test.js -child-process-exec-timeout-expire.test.js -child-process-spawn-controller.test.js -child-process-stdio-inherit.test.js -cluster-fork-env.test.js -cluster-kill-infinite-loop.test.js -file-write-stream4.test.js -file-write-stream5.test.js -filehandle-close.test.js -fs-existssync-false.test.js -fs-fmap.test.js -fs-read-stream-fd.test.js -fs-readdir-ucs2.test.js -fs-watch-recursive-add-file-to-new-folder.test.js -fs-watch-recursive-symlink.test.js -http-parser-finish-error.test.js -http-request-agent.test.js -http2-connect-options.test.js -https-server-connections-checking-leak.test.js -module-circular-symlinks.test.js -module-prototype-mutation.test.js -net-listen-error.test.js -net-server-close.test.js -permission-fs-windows-path.test.js -pipe-abstract-socket-http.test.js -pipe-file-to-http.test.js -process-ppid.test.js -require-invalid-package.test.js -require-long-path.test.js -snapshot-dns-lookup-localhost.test.js -trace-events-net-abstract-socket.test.js -trace-events-worker-metadata-with-name.test.js -windows-failed-heap-allocation.test.js -worker-dns-terminate.test.js -worker-esm-exit.test.js -worker-message-port-wasm-module.test.js - -# Failing on Windows: -pipe-head.test.js -http-client-response-domain.test.js -require-extensions-same-filename-as-dir-trailing-slash.test.js -fs-realpath-on-substed-drive.test.js -fs-symlink-dir-junction.test.js - -# macOS not working yet -node-dns.test.js - -# Things we don't support: -repl* -inspector* -npm* -*changelog* -permission* -shadow-realm* -trace-events* -cli-node-options* -corepack* -eslint* -coverage* -buffer-zero-fill-cli* -icu-minimum-version.test.js -release-npm.test.js - -# Bad tests -tls-wrap-no-abort.test.js diff --git a/test/js/node/test/parallel/arm-math-illegal-instruction.test.js b/test/js/node/test/parallel/arm-math-illegal-instruction.test.js deleted file mode 100644 index 58199f8742..0000000000 --- a/test/js/node/test/parallel/arm-math-illegal-instruction.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-arm-math-illegal-instruction.js -//#SHA1: 08aea7234b93dfe296564c6dd21a58bc91acd9dd -//----------------- -"use strict"; - -// This test ensures Math functions don't fail with an "illegal instruction" -// error on ARM devices (primarily on the Raspberry Pi 1) -// See https://github.com/nodejs/node/issues/1376 -// and https://code.google.com/p/v8/issues/detail?id=4019 - -test("Math functions do not fail with illegal instruction on ARM devices", () => { - // Iterate over all Math functions - Object.getOwnPropertyNames(Math).forEach(functionName => { - if (!/[A-Z]/.test(functionName)) { - // The function names don't have capital letters. - expect(() => Math[functionName](-0.5)).not.toThrow(); - } - }); -}); - -//<#END_FILE: test-arm-math-illegal-instruction.js diff --git a/test/js/node/test/parallel/assert-esm-cjs-message-verify.test.js b/test/js/node/test/parallel/assert-esm-cjs-message-verify.test.js deleted file mode 100644 index 93537273a8..0000000000 --- a/test/js/node/test/parallel/assert-esm-cjs-message-verify.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-assert-esm-cjs-message-verify.js -//#SHA1: 3d120c4813c4051523045df80fc501e9921b878f -//----------------- -"use strict"; - -const { spawnPromisified } = require("../common"); -const tmpdir = require("../common/tmpdir"); -const assert = require("assert"); -const { writeFileSync, unlink } = require("fs"); -const { join } = require("path"); - -tmpdir.refresh(); - -const fileImports = { - cjs: 'const assert = require("assert");', - mjs: 'import assert from "assert";', -}; - -const fileNames = []; - -for (const [ext, header] of Object.entries(fileImports)) { - const fileName = `test-file.${ext}`; - // Store the generated filesnames in an array - fileNames.push(join(tmpdir.path, fileName)); - - writeFileSync(tmpdir.resolve(fileName), `${header}\nassert.ok(0 === 2);`); -} - -describe("ensure the assert.ok throwing similar error messages for esm and cjs files", () => { - const nodejsPath = process.execPath; - const errorsMessages = []; - - test("should return code 1 for each command", async () => { - for (const fileName of fileNames) { - const { stderr, code } = await spawnPromisified(nodejsPath, [fileName]); - expect(code).toBe(1); - // For each error message, filter the lines which will starts with AssertionError - errorsMessages.push(stderr.split("\n").find(s => s.startsWith("AssertionError"))); - } - }); - - afterAll(() => { - expect(errorsMessages).toHaveLength(2); - expect(errorsMessages[0]).toEqual(errorsMessages[1]); - - for (const fileName of fileNames) { - unlink(fileName, () => {}); - } - - tmpdir.refresh(); - }); -}); - -//<#END_FILE: test-assert-esm-cjs-message-verify.js diff --git a/test/js/node/test/parallel/assert-strict-exists.test.js b/test/js/node/test/parallel/assert-strict-exists.test.js deleted file mode 100644 index 3595cf38ec..0000000000 --- a/test/js/node/test/parallel/assert-strict-exists.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-assert-strict-exists.js -//#SHA1: 390d3a53b3e79630cbb673eed78ac5857a49352f -//----------------- -"use strict"; - -test("assert/strict is the same as assert.strict", () => { - const assert = require("assert"); - const assertStrict = require("assert/strict"); - - expect(assertStrict).toBe(assert.strict); -}); - -//<#END_FILE: test-assert-strict-exists.js diff --git a/test/js/node/test/parallel/async-hooks-recursive-stack-runinasyncscope.test.js b/test/js/node/test/parallel/async-hooks-recursive-stack-runinasyncscope.test.js deleted file mode 100644 index 30f03f8332..0000000000 --- a/test/js/node/test/parallel/async-hooks-recursive-stack-runinasyncscope.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-async-hooks-recursive-stack-runInAsyncScope.js -//#SHA1: 7258dfd5a442e34e60920fb484336420db8754e2 -//----------------- -"use strict"; - -const async_hooks = require("async_hooks"); - -// This test verifies that the async ID stack can grow indefinitely. - -function recurse(n) { - const a = new async_hooks.AsyncResource("foobar"); - a.runInAsyncScope(() => { - expect(a.asyncId()).toBe(async_hooks.executionAsyncId()); - expect(a.triggerAsyncId()).toBe(async_hooks.triggerAsyncId()); - if (n >= 0) recurse(n - 1); - expect(a.asyncId()).toBe(async_hooks.executionAsyncId()); - expect(a.triggerAsyncId()).toBe(async_hooks.triggerAsyncId()); - }); -} - -test("async ID stack can grow indefinitely", () => { - expect(() => recurse(1000)).not.toThrow(); -}); - -//<#END_FILE: test-async-hooks-recursive-stack-runInAsyncScope.js diff --git a/test/js/node/test/parallel/async-hooks-run-in-async-scope-this-arg.test.js b/test/js/node/test/parallel/async-hooks-run-in-async-scope-this-arg.test.js deleted file mode 100644 index 41479c5c05..0000000000 --- a/test/js/node/test/parallel/async-hooks-run-in-async-scope-this-arg.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-async-hooks-run-in-async-scope-this-arg.js -//#SHA1: a716f9818bdd704e1cf7ca188ffd4ccb9501a8a7 -//----------------- -"use strict"; - -// Test that passing thisArg to runInAsyncScope() works. - -const { AsyncResource } = require("async_hooks"); - -const thisArg = {}; - -const res = new AsyncResource("fhqwhgads"); - -function callback() { - expect(this).toBe(thisArg); -} - -test("runInAsyncScope with thisArg", () => { - const callbackSpy = jest.fn(callback); - res.runInAsyncScope(callbackSpy, thisArg); - expect(callbackSpy).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-async-hooks-run-in-async-scope-this-arg.js diff --git a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-1.test.js b/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-1.test.js deleted file mode 100644 index c8988c5655..0000000000 --- a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-1.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-async-hooks-worker-asyncfn-terminate-1.js -//#SHA1: 556f29e8c45107ff3448d713145f6f4e070e8073 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -test("Worker with async function and async_hooks terminates correctly", () => { - const w = new Worker( - ` - const { createHook } = require('async_hooks'); - - setImmediate(async () => { - createHook({ init() {} }).enable(); - await 0; - process.exit(); - }); - `, - { eval: true }, - ); - - return new Promise(resolve => { - w.on("exit", () => { - expect(true).toBe(true); // Ensures the 'exit' event was called - resolve(); - }); - }); -}); - -//<#END_FILE: test-async-hooks-worker-asyncfn-terminate-1.js diff --git a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-2.test.js b/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-2.test.js deleted file mode 100644 index 2140f81307..0000000000 --- a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-2.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-async-hooks-worker-asyncfn-terminate-2.js -//#SHA1: 2329c972e1256f3aadc37e92f0ff1219d8519328 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Like test-async-hooks-worker-promise.js but with the `await` and `createHook` -// lines switched, because that resulted in different assertion failures -// (one a Node.js assertion and one a V8 DCHECK) and it seems prudent to -// cover both of those failures. - -test("Worker with async function and createHook", () => { - const w = new Worker( - ` - const { createHook } = require('async_hooks'); - - setImmediate(async () => { - await 0; - createHook({ init() {} }).enable(); - process.exit(); - }); - `, - { eval: true }, - ); - - w.postMessage({}); - - return new Promise(resolve => { - w.on("exit", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - resolve(); - }); - }); -}); - -//<#END_FILE: test-async-hooks-worker-asyncfn-terminate-2.js diff --git a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-3.test.js b/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-3.test.js deleted file mode 100644 index ecc905292a..0000000000 --- a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-3.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-async-hooks-worker-asyncfn-terminate-3.js -//#SHA1: bd68d52f5ecd5cb22738f78ee855706ed424cbf0 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Like test-async-hooks-worker-promise.js but with an additional statement -// after the `process.exit()` call, that shouldn't really make a difference -// but apparently does. - -test("Worker with async function and process.exit()", done => { - const w = new Worker( - ` - const { createHook } = require('async_hooks'); - - setImmediate(async () => { - createHook({ init() {} }).enable(); - await 0; - process.exit(); - process._rawDebug('THIS SHOULD NEVER BE REACHED'); - }); - `, - { eval: true }, - ); - - w.on("exit", () => { - expect(true).toBe(true); // Ensure the exit event is called - done(); - }); -}); - -//<#END_FILE: test-async-hooks-worker-asyncfn-terminate-3.js diff --git a/test/js/node/test/parallel/async-local-storage-deep-stack.test.js b/test/js/node/test/parallel/async-local-storage-deep-stack.test.js deleted file mode 100644 index 9c3926b18b..0000000000 --- a/test/js/node/test/parallel/async-local-storage-deep-stack.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-async-local-storage-deep-stack.js -//#SHA1: 305d85dc794f55b19fffebfbb720ba0c83714f63 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); - -// Regression test for: https://github.com/nodejs/node/issues/34556 - -test("AsyncLocalStorage deep stack", () => { - const als = new AsyncLocalStorage(); - - const done = jest.fn(); - - function run(count) { - if (count !== 0) return als.run({}, run, --count); - done(); - } - - run(1000); - - expect(done).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-async-local-storage-deep-stack.js diff --git a/test/js/node/test/parallel/async-local-storage-http-multiclients.test.js b/test/js/node/test/parallel/async-local-storage-http-multiclients.test.js deleted file mode 100644 index b90b19ea6b..0000000000 --- a/test/js/node/test/parallel/async-local-storage-http-multiclients.test.js +++ /dev/null @@ -1,89 +0,0 @@ -//#FILE: test-async-local-storage-http-multiclients.js -//#SHA1: adf8feaec8fa034cbb22fd0f3e2a5ed224c1905e -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const http = require("http"); - -const NUM_CLIENTS = 10; - -// Run multiple clients that receive data from a server -// in multiple chunks, in a single non-closure function. -// Use the AsyncLocalStorage (ALS) APIs to maintain the context -// and data download. Make sure that individual clients -// receive their respective data, with no conflicts. - -describe("AsyncLocalStorage with multiple HTTP clients", () => { - const cls = new AsyncLocalStorage(); - let server; - let index = 0; - - beforeAll(() => { - // Set up a server that sends large buffers of data, filled - // with cardinal numbers, increasing per request - server = http.createServer((q, r) => { - // Send a large chunk as response, otherwise the data - // may be sent in a single chunk, and the callback in the - // client may be called only once, defeating the purpose of test - r.end((index++ % 10).toString().repeat(1024 * 1024)); - }); - }); - - afterAll(() => { - server.close(); - }); - - it("should handle multiple clients correctly", async () => { - const clientPromises = []; - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - for (let i = 0; i < NUM_CLIENTS; i++) { - clientPromises.push( - new Promise(resolve => { - cls.run(new Map(), () => { - const options = { port: server.address().port }; - const req = http.get(options, res => { - const store = cls.getStore(); - store.set("data", ""); - - // Make ondata and onend non-closure - // functions and fully dependent on ALS - res.setEncoding("utf8"); - res.on("data", ondata); - res.on("end", () => { - onend(); - resolve(); - }); - }); - req.end(); - }); - }), - ); - } - - await Promise.all(clientPromises); - }); - - // Accumulate the current data chunk with the store data - function ondata(d) { - const store = cls.getStore(); - expect(store).not.toBeUndefined(); - let chunk = store.get("data"); - chunk += d; - store.set("data", chunk); - } - - // Retrieve the store data, and test for homogeneity - function onend() { - const store = cls.getStore(); - expect(store).not.toBeUndefined(); - const data = store.get("data"); - expect(data).toBe(data[0].repeat(data.length)); - } -}); - -//<#END_FILE: test-async-local-storage-http-multiclients.js diff --git a/test/js/node/test/parallel/async-local-storage-snapshot.test.js b/test/js/node/test/parallel/async-local-storage-snapshot.test.js deleted file mode 100644 index 8b4d0b80bd..0000000000 --- a/test/js/node/test/parallel/async-local-storage-snapshot.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-async-local-storage-snapshot.js -//#SHA1: f8d967194bfb0b73994d296b03c0c43afa5127e5 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); - -describe("AsyncLocalStorage snapshot", () => { - test("should preserve the original context when using snapshot", () => { - const asyncLocalStorage = new AsyncLocalStorage(); - - const runInAsyncScope = asyncLocalStorage.run(123, () => AsyncLocalStorage.snapshot()); - - const result = asyncLocalStorage.run(321, () => { - return runInAsyncScope(() => { - return asyncLocalStorage.getStore(); - }); - }); - - expect(result).toBe(123); - }); -}); - -//<#END_FILE: test-async-local-storage-snapshot.js diff --git a/test/js/node/test/parallel/atomics-wake.test.js b/test/js/node/test/parallel/atomics-wake.test.js deleted file mode 100644 index 7c690a086a..0000000000 --- a/test/js/node/test/parallel/atomics-wake.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-atomics-wake.js -//#SHA1: 311b66a7cd5fbc08a20b77de98a66f9cba763f8f -//----------------- -"use strict"; - -// https://github.com/nodejs/node/issues/21219 -test("Atomics.wake should be undefined", () => { - expect(Atomics.wake).toBeUndefined(); -}); - -//<#END_FILE: test-atomics-wake.js diff --git a/test/js/node/test/parallel/binding-constants.test.js b/test/js/node/test/parallel/binding-constants.test.js deleted file mode 100644 index e3cabf4e2b..0000000000 --- a/test/js/node/test/parallel/binding-constants.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-binding-constants.js -//#SHA1: 84b14e2a54ec767074f2a4103eaa0b419655cf8b -//----------------- -"use strict"; - -// Note: This test originally used internal bindings which are not recommended for use in tests. -// The test has been modified to focus on the public API and behavior that can be tested without internals. - -test("constants object structure", () => { - const constants = process.binding("constants"); - - expect(Object.keys(constants).sort()).toEqual(["crypto", "fs", "os", "trace", "zlib"]); - - expect(Object.keys(constants.os).sort()).toEqual(["UV_UDP_REUSEADDR", "dlopen", "errno", "priority", "signals"]); -}); - -test("constants objects do not inherit from Object.prototype", () => { - const constants = process.binding("constants"); - const inheritedProperties = Object.getOwnPropertyNames(Object.prototype); - - function testObject(obj) { - expect(obj).toBeTruthy(); - expect(Object.prototype.toString.call(obj)).toBe("[object Object]"); - expect(Object.getPrototypeOf(obj)).toBeNull(); - - inheritedProperties.forEach(property => { - expect(property in obj).toBe(false); - }); - } - - [ - constants, - constants.crypto, - constants.fs, - constants.os, - constants.trace, - constants.zlib, - constants.os.dlopen, - constants.os.errno, - constants.os.signals, - ].forEach(testObject); -}); - -//<#END_FILE: test-binding-constants.js diff --git a/test/js/node/test/parallel/blob-createobjecturl.test.js b/test/js/node/test/parallel/blob-createobjecturl.test.js deleted file mode 100644 index 9f94ba18f5..0000000000 --- a/test/js/node/test/parallel/blob-createobjecturl.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-blob-createobjecturl.js -//#SHA1: d2030ca0ad6757dd9d338bc2e65cd3ff8917009d -//----------------- -// Flags: --no-warnings -"use strict"; - -// Because registering a Blob URL requires generating a random -// UUID, it can only be done if crypto support is enabled. -if (typeof crypto === "undefined") { - test.skip("missing crypto"); -} - -const { URL } = require("url"); -const { Blob, resolveObjectURL } = require("buffer"); - -test("Blob URL creation and resolution", async () => { - const blob = new Blob(["hello"]); - const id = URL.createObjectURL(blob); - expect(typeof id).toBe("string"); - const otherBlob = resolveObjectURL(id); - expect(otherBlob).toBeInstanceOf(Blob); - expect(otherBlob.constructor).toBe(Blob); - expect(otherBlob.size).toBe(5); - expect(Buffer.from(await otherBlob.arrayBuffer()).toString()).toBe("hello"); - URL.revokeObjectURL(id); - - // should do nothing - URL.revokeObjectURL(id); - - expect(resolveObjectURL(id)).toBeUndefined(); - - // Leaving a Blob registered should not cause an assert - // when Node.js exists - URL.createObjectURL(new Blob()); -}); - -test("resolveObjectURL with invalid inputs", () => { - ["not a url", undefined, 1, "blob:nodedata:1:wrong", {}].forEach(i => { - expect(resolveObjectURL(i)).toBeUndefined(); - }); -}); - -test("createObjectURL with invalid inputs", () => { - [undefined, 1, "", false, {}].forEach(i => { - expect(() => URL.createObjectURL(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-blob-createobjecturl.js diff --git a/test/js/node/test/parallel/buffer-arraybuffer.test.js b/test/js/node/test/parallel/buffer-arraybuffer.test.js deleted file mode 100644 index d33487198f..0000000000 --- a/test/js/node/test/parallel/buffer-arraybuffer.test.js +++ /dev/null @@ -1,158 +0,0 @@ -//#FILE: test-buffer-arraybuffer.js -//#SHA1: 2297240ef18399097bd3383db051d8e37339a123 -//----------------- -"use strict"; - -const LENGTH = 16; - -test("Buffer from ArrayBuffer", () => { - const ab = new ArrayBuffer(LENGTH); - const dv = new DataView(ab); - const ui = new Uint8Array(ab); - const buf = Buffer.from(ab); - - expect(buf).toBeInstanceOf(Buffer); - expect(buf.parent).toBe(buf.buffer); - expect(buf.buffer).toBe(ab); - expect(buf.length).toBe(ab.byteLength); - - buf.fill(0xc); - for (let i = 0; i < LENGTH; i++) { - expect(ui[i]).toBe(0xc); - ui[i] = 0xf; - expect(buf[i]).toBe(0xf); - } - - buf.writeUInt32LE(0xf00, 0); - buf.writeUInt32BE(0xb47, 4); - buf.writeDoubleLE(3.1415, 8); - - expect(dv.getUint32(0, true)).toBe(0xf00); - expect(dv.getUint32(4)).toBe(0xb47); - expect(dv.getFloat64(8, true)).toBe(3.1415); -}); - -test.todo("Buffer.from with invalid ArrayBuffer", () => { - expect(() => { - function AB() {} - Object.setPrototypeOf(AB, ArrayBuffer); - Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype); - Buffer.from(new AB()); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining( - "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.", - ), - }), - ); -}); - -test("Buffer.from with byteOffset and length arguments", () => { - const ab = new Uint8Array(5); - ab[0] = 1; - ab[1] = 2; - ab[2] = 3; - ab[3] = 4; - ab[4] = 5; - const buf = Buffer.from(ab.buffer, 1, 3); - expect(buf.length).toBe(3); - expect(buf[0]).toBe(2); - expect(buf[1]).toBe(3); - expect(buf[2]).toBe(4); - buf[0] = 9; - expect(ab[1]).toBe(9); - - expect(() => Buffer.from(ab.buffer, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"offset" is outside of buffer bounds'), - }), - ); - - expect(() => Buffer.from(ab.buffer, 3, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"length" is outside of buffer bounds'), - }), - ); -}); - -test("Deprecated Buffer() constructor", () => { - const ab = new Uint8Array(5); - ab[0] = 1; - ab[1] = 2; - ab[2] = 3; - ab[3] = 4; - ab[4] = 5; - const buf = Buffer(ab.buffer, 1, 3); - expect(buf.length).toBe(3); - expect(buf[0]).toBe(2); - expect(buf[1]).toBe(3); - expect(buf[2]).toBe(4); - buf[0] = 9; - expect(ab[1]).toBe(9); - - expect(() => Buffer(ab.buffer, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"offset" is outside of buffer bounds'), - }), - ); - - expect(() => Buffer(ab.buffer, 3, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"length" is outside of buffer bounds'), - }), - ); -}); - -test("Buffer.from with non-numeric byteOffset", () => { - const ab = new ArrayBuffer(10); - const expected = Buffer.from(ab, 0); - expect(Buffer.from(ab, "fhqwhgads")).toEqual(expected); - expect(Buffer.from(ab, NaN)).toEqual(expected); - expect(Buffer.from(ab, {})).toEqual(expected); - expect(Buffer.from(ab, [])).toEqual(expected); - - expect(Buffer.from(ab, [1])).toEqual(Buffer.from(ab, 1)); - - expect(() => Buffer.from(ab, Infinity)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"offset" is outside of buffer bounds'), - }), - ); -}); - -test("Buffer.from with non-numeric length", () => { - const ab = new ArrayBuffer(10); - const expected = Buffer.from(ab, 0, 0); - expect(Buffer.from(ab, 0, "fhqwhgads")).toEqual(expected); - expect(Buffer.from(ab, 0, NaN)).toEqual(expected); - expect(Buffer.from(ab, 0, {})).toEqual(expected); - expect(Buffer.from(ab, 0, [])).toEqual(expected); - - expect(Buffer.from(ab, 0, [1])).toEqual(Buffer.from(ab, 0, 1)); - - expect(() => Buffer.from(ab, 0, Infinity)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"length" is outside of buffer bounds'), - }), - ); -}); - -test("Buffer.from with array-like entry and NaN length", () => { - expect(Buffer.from({ length: NaN })).toEqual(Buffer.alloc(0)); -}); - -//<#END_FILE: test-buffer-arraybuffer.js diff --git a/test/js/node/test/parallel/buffer-bytelength.test.js b/test/js/node/test/parallel/buffer-bytelength.test.js deleted file mode 100644 index 5934db1dc8..0000000000 --- a/test/js/node/test/parallel/buffer-bytelength.test.js +++ /dev/null @@ -1,131 +0,0 @@ -//#FILE: test-buffer-bytelength.js -//#SHA1: bcc75ad2f868ac9414c789c29f23ee9c806c749d -//----------------- -"use strict"; - -const SlowBuffer = require("buffer").SlowBuffer; -const vm = require("vm"); - -test("Buffer.byteLength with invalid arguments", () => { - [[32, "latin1"], [NaN, "utf8"], [{}, "latin1"], []].forEach(args => { - expect(() => Buffer.byteLength(...args)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining( - 'The "string" argument must be of type string or an instance of Buffer or ArrayBuffer.', - ), - }), - ); - }); -}); - -test("ArrayBuffer.isView for various Buffer types", () => { - expect(ArrayBuffer.isView(new Buffer(10))).toBe(true); - expect(ArrayBuffer.isView(new SlowBuffer(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.alloc(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.allocUnsafe(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.from(""))).toBe(true); -}); - -test("Buffer.byteLength for various buffer types", () => { - const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]); - expect(Buffer.byteLength(incomplete)).toBe(5); - - const ascii = Buffer.from("abc"); - expect(Buffer.byteLength(ascii)).toBe(3); - - const buffer = new ArrayBuffer(8); - expect(Buffer.byteLength(buffer)).toBe(8); -}); - -test("Buffer.byteLength for TypedArrays", () => { - expect(Buffer.byteLength(new Int8Array(8))).toBe(8); - expect(Buffer.byteLength(new Uint8Array(8))).toBe(8); - expect(Buffer.byteLength(new Uint8ClampedArray(2))).toBe(2); - expect(Buffer.byteLength(new Int16Array(8))).toBe(16); - expect(Buffer.byteLength(new Uint16Array(8))).toBe(16); - expect(Buffer.byteLength(new Int32Array(8))).toBe(32); - expect(Buffer.byteLength(new Uint32Array(8))).toBe(32); - expect(Buffer.byteLength(new Float32Array(8))).toBe(32); - expect(Buffer.byteLength(new Float64Array(8))).toBe(64); -}); - -test("Buffer.byteLength for DataView", () => { - const dv = new DataView(new ArrayBuffer(2)); - expect(Buffer.byteLength(dv)).toBe(2); -}); - -test("Buffer.byteLength for zero length string", () => { - expect(Buffer.byteLength("", "ascii")).toBe(0); - expect(Buffer.byteLength("", "HeX")).toBe(0); -}); - -test("Buffer.byteLength for utf8", () => { - expect(Buffer.byteLength("∑éllö wørl∂!", "utf-8")).toBe(19); - expect(Buffer.byteLength("κλμνξο", "utf8")).toBe(12); - expect(Buffer.byteLength("挵挶挷挸挹", "utf-8")).toBe(15); - expect(Buffer.byteLength("𠝹𠱓𠱸", "UTF8")).toBe(12); - expect(Buffer.byteLength("hey there")).toBe(9); - expect(Buffer.byteLength("𠱸挶νξ#xx :)")).toBe(17); - expect(Buffer.byteLength("hello world", "")).toBe(11); - expect(Buffer.byteLength("hello world", "abc")).toBe(11); - expect(Buffer.byteLength("ßœ∑≈", "unkn0wn enc0ding")).toBe(10); -}); - -test("Buffer.byteLength for base64", () => { - expect(Buffer.byteLength("aGVsbG8gd29ybGQ=", "base64")).toBe(11); - expect(Buffer.byteLength("aGVsbG8gd29ybGQ=", "BASE64")).toBe(11); - expect(Buffer.byteLength("bm9kZS5qcyByb2NrcyE=", "base64")).toBe(14); - expect(Buffer.byteLength("aGkk", "base64")).toBe(3); - expect(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==", "base64")).toBe(25); -}); - -test("Buffer.byteLength for base64url", () => { - expect(Buffer.byteLength("aGVsbG8gd29ybGQ", "base64url")).toBe(11); - expect(Buffer.byteLength("aGVsbG8gd29ybGQ", "BASE64URL")).toBe(11); - expect(Buffer.byteLength("bm9kZS5qcyByb2NrcyE", "base64url")).toBe(14); - expect(Buffer.byteLength("aGkk", "base64url")).toBe(3); - expect(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw", "base64url")).toBe(25); -}); - -test("Buffer.byteLength for special padding", () => { - expect(Buffer.byteLength("aaa=", "base64")).toBe(2); - expect(Buffer.byteLength("aaaa==", "base64")).toBe(3); - expect(Buffer.byteLength("aaa=", "base64url")).toBe(2); - expect(Buffer.byteLength("aaaa==", "base64url")).toBe(3); -}); - -test("Buffer.byteLength for various encodings", () => { - expect(Buffer.byteLength("Il était tué")).toBe(14); - expect(Buffer.byteLength("Il était tué", "utf8")).toBe(14); - - ["ascii", "latin1", "binary"] - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - expect(Buffer.byteLength("Il était tué", encoding)).toBe(12); - }); - - ["ucs2", "ucs-2", "utf16le", "utf-16le"] - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - expect(Buffer.byteLength("Il était tué", encoding)).toBe(24); - }); -}); - -test("Buffer.byteLength for ArrayBuffer from different context", () => { - const arrayBuf = vm.runInNewContext("new ArrayBuffer()"); - expect(Buffer.byteLength(arrayBuf)).toBe(0); -}); - -test("Buffer.byteLength for invalid encodings", () => { - for (let i = 1; i < 10; i++) { - const encoding = String(i).repeat(i); - - expect(Buffer.isEncoding(encoding)).toBe(false); - expect(Buffer.byteLength("foo", encoding)).toBe(Buffer.byteLength("foo", "utf8")); - } -}); - -//<#END_FILE: test-buffer-bytelength.js diff --git a/test/js/node/test/parallel/buffer-compare-offset.test.js b/test/js/node/test/parallel/buffer-compare-offset.test.js deleted file mode 100644 index df674d2f59..0000000000 --- a/test/js/node/test/parallel/buffer-compare-offset.test.js +++ /dev/null @@ -1,95 +0,0 @@ -//#FILE: test-buffer-compare-offset.js -//#SHA1: 460e187ac1a40db0dbc00801ad68f1272d27c3cd -//----------------- -"use strict"; - -const assert = require("assert"); - -describe("Buffer.compare with offset", () => { - const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); - const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]); - - test("basic comparison", () => { - expect(a.compare(b)).toBe(-1); - }); - - test("comparison with default arguments", () => { - expect(a.compare(b, 0)).toBe(-1); - expect(() => a.compare(b, "0")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(a.compare(b, undefined)).toBe(-1); - }); - - test("comparison with specified ranges", () => { - expect(a.compare(b, 0, undefined, 0)).toBe(-1); - expect(a.compare(b, 0, 0, 0)).toBe(1); - expect(() => a.compare(b, 0, "0", "0")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(a.compare(b, 6, 10)).toBe(1); - expect(a.compare(b, 6, 10, 0, 0)).toBe(-1); - expect(a.compare(b, 0, 0, 0, 0)).toBe(0); - expect(a.compare(b, 1, 1, 2, 2)).toBe(0); - expect(a.compare(b, 0, 5, 4)).toBe(1); - expect(a.compare(b, 5, undefined, 1)).toBe(1); - expect(a.compare(b, 2, 4, 2)).toBe(-1); - expect(a.compare(b, 0, 7, 4)).toBe(-1); - expect(a.compare(b, 0, 7, 4, 6)).toBe(-1); - }); - - test("invalid arguments", () => { - expect(() => a.compare(b, 0, null)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(() => a.compare(b, 0, { valueOf: () => 5 })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(() => a.compare(b, Infinity, -Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - expect(a.compare(b, 0xff)).toBe(1); - expect(() => a.compare(b, "0xff")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(() => a.compare(b, 0, "0xff")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - test("out of range arguments", () => { - const oor = expect.objectContaining({ code: "ERR_OUT_OF_RANGE" }); - expect(() => a.compare(b, 0, 100, 0)).toThrow(oor); - expect(() => a.compare(b, 0, 1, 0, 100)).toThrow(oor); - expect(() => a.compare(b, -1)).toThrow(oor); - expect(() => a.compare(b, 0, Infinity)).toThrow(oor); - expect(() => a.compare(b, 0, 1, -1)).toThrow(oor); - expect(() => a.compare(b, -Infinity, Infinity)).toThrow(oor); - }); - - test("missing target argument", () => { - expect(() => a.compare()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "target" argument must be an instance of Buffer or Uint8Array'), - }), - ); - }); -}); - -//<#END_FILE: test-buffer-compare-offset.js diff --git a/test/js/node/test/parallel/buffer-compare.test.js b/test/js/node/test/parallel/buffer-compare.test.js deleted file mode 100644 index 9f6d0c70be..0000000000 --- a/test/js/node/test/parallel/buffer-compare.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-buffer-compare.js -//#SHA1: eab68d7262240af3d53eabedb0e7a515b2d84adf -//----------------- -"use strict"; - -test("Buffer compare", () => { - const b = Buffer.alloc(1, "a"); - const c = Buffer.alloc(1, "c"); - const d = Buffer.alloc(2, "aa"); - const e = new Uint8Array([0x61, 0x61]); // ASCII 'aa', same as d - - expect(b.compare(c)).toBe(-1); - expect(c.compare(d)).toBe(1); - expect(d.compare(b)).toBe(1); - expect(d.compare(e)).toBe(0); - expect(b.compare(d)).toBe(-1); - expect(b.compare(b)).toBe(0); - - expect(Buffer.compare(b, c)).toBe(-1); - expect(Buffer.compare(c, d)).toBe(1); - expect(Buffer.compare(d, b)).toBe(1); - expect(Buffer.compare(b, d)).toBe(-1); - expect(Buffer.compare(c, c)).toBe(0); - expect(Buffer.compare(e, e)).toBe(0); - expect(Buffer.compare(d, e)).toBe(0); - expect(Buffer.compare(d, b)).toBe(1); - - expect(Buffer.compare(Buffer.alloc(0), Buffer.alloc(0))).toBe(0); - expect(Buffer.compare(Buffer.alloc(0), Buffer.alloc(1))).toBe(-1); - expect(Buffer.compare(Buffer.alloc(1), Buffer.alloc(0))).toBe(1); - - expect(() => Buffer.compare(Buffer.alloc(1), "abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "buf2" argument must be an instance of Buffer or Uint8Array.'), - }), - ); - - expect(() => Buffer.compare("abc", Buffer.alloc(1))).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "buf1" argument must be an instance of Buffer or Uint8Array.'), - }), - ); - - expect(() => Buffer.alloc(1).compare("abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "target" argument must be an instance of Buffer or Uint8Array.'), - }), - ); -}); - -//<#END_FILE: test-buffer-compare.js diff --git a/test/js/node/test/parallel/buffer-constants.test.js b/test/js/node/test/parallel/buffer-constants.test.js deleted file mode 100644 index 5c7c8a18d1..0000000000 --- a/test/js/node/test/parallel/buffer-constants.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-buffer-constants.js -//#SHA1: a5818d34d1588306e48d574ec76b69b2ee4dc51c -//----------------- -"use strict"; - -const { kMaxLength, kStringMaxLength } = require("buffer"); -const { MAX_LENGTH, MAX_STRING_LENGTH } = require("buffer").constants; - -test("Buffer constants", () => { - expect(typeof MAX_LENGTH).toBe("number"); - expect(typeof MAX_STRING_LENGTH).toBe("number"); - expect(MAX_STRING_LENGTH).toBeLessThanOrEqual(MAX_LENGTH); - - expect(() => " ".repeat(MAX_STRING_LENGTH + 1)).toThrow( - expect.objectContaining({ - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => " ".repeat(MAX_STRING_LENGTH)).not.toThrow(); - - // Legacy values match: - expect(kMaxLength).toBe(MAX_LENGTH); - expect(kStringMaxLength).toBe(MAX_STRING_LENGTH); -}); - -//<#END_FILE: test-buffer-constants.js diff --git a/test/js/node/test/parallel/buffer-copy.test.js b/test/js/node/test/parallel/buffer-copy.test.js deleted file mode 100644 index afb49923d2..0000000000 --- a/test/js/node/test/parallel/buffer-copy.test.js +++ /dev/null @@ -1,204 +0,0 @@ -//#FILE: test-buffer-copy.js -//#SHA1: bff8bfe75b7289a279d9fc1a1bf2293257282d27 -//----------------- -"use strict"; - -test("Buffer copy operations", () => { - const b = Buffer.allocUnsafe(1024); - const c = Buffer.allocUnsafe(512); - - let cntr = 0; - - // Copy 512 bytes, from 0 to 512. - b.fill(++cntr); - c.fill(++cntr); - const copied = b.copy(c, 0, 0, 512); - expect(copied).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Current behavior is to coerce values to integers. - b.fill(++cntr); - c.fill(++cntr); - const copiedWithStrings = b.copy(c, "0", "0", "512"); - expect(copiedWithStrings).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Floats will be converted to integers via `Math.floor` - b.fill(++cntr); - c.fill(++cntr); - const copiedWithFloat = b.copy(c, 0, 0, 512.5); - expect(copiedWithFloat).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Copy c into b, without specifying sourceEnd - b.fill(++cntr); - c.fill(++cntr); - const copiedWithoutSourceEnd = c.copy(b, 0, 0); - expect(copiedWithoutSourceEnd).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(b[i]).toBe(c[i]); - } - - // Copy c into b, without specifying sourceStart - b.fill(++cntr); - c.fill(++cntr); - const copiedWithoutSourceStart = c.copy(b, 0); - expect(copiedWithoutSourceStart).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(b[i]).toBe(c[i]); - } - - // Copied source range greater than source length - b.fill(++cntr); - c.fill(++cntr); - const copiedWithGreaterRange = c.copy(b, 0, 0, c.length + 1); - expect(copiedWithGreaterRange).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(b[i]).toBe(c[i]); - } - - // Copy longer buffer b to shorter c without targetStart - b.fill(++cntr); - c.fill(++cntr); - const copiedLongerToShorter = b.copy(c); - expect(copiedLongerToShorter).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Copy starting near end of b to c - b.fill(++cntr); - c.fill(++cntr); - const copiedNearEnd = b.copy(c, 0, b.length - Math.floor(c.length / 2)); - expect(copiedNearEnd).toBe(Math.floor(c.length / 2)); - for (let i = 0; i < Math.floor(c.length / 2); i++) { - expect(c[i]).toBe(b[b.length - Math.floor(c.length / 2) + i]); - } - for (let i = Math.floor(c.length / 2) + 1; i < c.length; i++) { - expect(c[c.length - 1]).toBe(c[i]); - } - - // Try to copy 513 bytes, and check we don't overrun c - b.fill(++cntr); - c.fill(++cntr); - const copiedOverrun = b.copy(c, 0, 0, 513); - expect(copiedOverrun).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Copy 768 bytes from b into b - b.fill(++cntr); - b.fill(++cntr, 256); - const copiedIntoSelf = b.copy(b, 0, 256, 1024); - expect(copiedIntoSelf).toBe(768); - for (let i = 0; i < b.length; i++) { - expect(b[i]).toBe(cntr); - } - - // Copy string longer than buffer length (failure will segfault) - const bb = Buffer.allocUnsafe(10); - bb.fill("hello crazy world"); - - // Try to copy from before the beginning of b. Should not throw. - expect(() => b.copy(c, 0, 100, 10)).not.toThrow(); - - // Throw with invalid source type - expect(() => Buffer.prototype.copy.call(0)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", //TODO:"ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - // Copy throws at negative targetStart - expect(() => Buffer.allocUnsafe(10).copy(Buffer.allocUnsafe(5), -1, 0)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "targetStart" is out of range. It must be >= 0 and <= 5. Received -1`, - }); - - // Copy throws at negative sourceStart - expect(() => Buffer.allocUnsafe(10).copy(Buffer.allocUnsafe(5), 0, -1)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "sourceStart" is out of range. It must be >= 0 and <= 10. Received -1`, - }); - - // Copy throws if sourceStart is greater than length of source - expect(() => Buffer.allocUnsafe(10).copy(Buffer.allocUnsafe(5), 0, 100)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "sourceStart" is out of range. It must be >= 0 and <= 10. Received 100`, - }); - - // Check sourceEnd resets to targetEnd if former is greater than the latter - b.fill(++cntr); - c.fill(++cntr); - b.copy(c, 0, 0, 1025); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Throw with negative sourceEnd - expect(() => b.copy(c, 0, 0, -1)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "sourceEnd" is out of range. It must be >= 0 and <= 1024. Received -1`, - }); - - // When sourceStart is greater than sourceEnd, zero copied - expect(b.copy(c, 0, 100, 10)).toBe(0); - - // When targetStart > targetLength, zero copied - expect(b.copy(c, 512, 0, 10)).toBe(0); - - // Test that the `target` can be a Uint8Array. - const d = new Uint8Array(c); - // copy 512 bytes, from 0 to 512. - b.fill(++cntr); - d.fill(++cntr); - const copiedToUint8Array = b.copy(d, 0, 0, 512); - expect(copiedToUint8Array).toBe(512); - for (let i = 0; i < d.length; i++) { - expect(d[i]).toBe(b[i]); - } - - // Test that the source can be a Uint8Array, too. - const e = new Uint8Array(b); - // copy 512 bytes, from 0 to 512. - e.fill(++cntr); - c.fill(++cntr); - const copiedFromUint8Array = Buffer.prototype.copy.call(e, c, 0, 0, 512); - expect(copiedFromUint8Array).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(e[i]); - } - - // https://github.com/nodejs/node/issues/23668: Do not crash for invalid input. - c.fill("c"); - b.copy(c, "not a valid offset"); - // Make sure this acted like a regular copy with `0` offset. - expect(c).toEqual(b.slice(0, c.length)); - - c.fill("C"); - expect(c.toString()).toBe("C".repeat(c.length)); - expect(() => { - b.copy(c, { - [Symbol.toPrimitive]() { - throw new Error("foo"); - }, - }); - }).toThrow("foo"); - // No copying took place: - expect(c.toString()).toBe("C".repeat(c.length)); -}); - -//<#END_FILE: test-buffer-copy.js diff --git a/test/js/node/test/parallel/buffer-equals.test.js b/test/js/node/test/parallel/buffer-equals.test.js deleted file mode 100644 index 8fbd4c13c4..0000000000 --- a/test/js/node/test/parallel/buffer-equals.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-buffer-equals.js -//#SHA1: 917344b9c4ba47f1e30d02ec6adfad938b2d342a -//----------------- -"use strict"; - -test("Buffer.equals", () => { - const b = Buffer.from("abcdf"); - const c = Buffer.from("abcdf"); - const d = Buffer.from("abcde"); - const e = Buffer.from("abcdef"); - - expect(b.equals(c)).toBe(true); - expect(c.equals(d)).toBe(false); - expect(d.equals(e)).toBe(false); - expect(d.equals(d)).toBe(true); - expect(d.equals(new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]))).toBe(true); - - expect(() => Buffer.alloc(1).equals("abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining( - `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received`, - ), - }), - ); -}); - -//<#END_FILE: test-buffer-equals.js diff --git a/test/js/node/test/parallel/buffer-failed-alloc-typed-arrays.test.js b/test/js/node/test/parallel/buffer-failed-alloc-typed-arrays.test.js deleted file mode 100644 index 8dfcd1d03a..0000000000 --- a/test/js/node/test/parallel/buffer-failed-alloc-typed-arrays.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-buffer-failed-alloc-typed-arrays.js -//#SHA1: caa3a29c5ca1921e9ab5324d464067a364b8e687 -//----------------- -"use strict"; - -const { Buffer } = require("buffer"); -const SlowBuffer = require("buffer").SlowBuffer; - -// Test failed or zero-sized Buffer allocations not affecting typed arrays. -// This test exists because of a regression that occurred. Because Buffer -// instances are allocated with the same underlying allocator as TypedArrays, -// but Buffer's can optional be non-zero filled, there was a regression that -// occurred when a Buffer allocated failed, the internal flag specifying -// whether or not to zero-fill was not being reset, causing TypedArrays to -// allocate incorrectly. - -test("failed or zero-sized Buffer allocations do not affect typed arrays", () => { - const zeroArray = new Uint32Array(10).fill(0); - const sizes = [1e20, 0, 0.1, -1, "a", undefined, null, NaN]; - const allocators = [Buffer, SlowBuffer, Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow]; - - for (const allocator of allocators) { - for (const size of sizes) { - try { - // Some of these allocations are known to fail. If they do, - // Uint32Array should still produce a zeroed out result. - allocator(size); - } catch { - expect(new Uint32Array(10)).toEqual(zeroArray); - } - } - } -}); - -//<#END_FILE: test-buffer-failed-alloc-typed-arrays.js diff --git a/test/js/node/test/parallel/buffer-fill.test.js b/test/js/node/test/parallel/buffer-fill.test.js deleted file mode 100644 index f045645d93..0000000000 --- a/test/js/node/test/parallel/buffer-fill.test.js +++ /dev/null @@ -1,428 +0,0 @@ -//#FILE: test-buffer-fill.js -//#SHA1: 983940aa8a47c4d0985c2c4b4d1bc323a4e7d0f5 -//----------------- -"use strict"; - -const SIZE = 28; - -let buf1, buf2; - -beforeEach(() => { - buf1 = Buffer.allocUnsafe(SIZE); - buf2 = Buffer.allocUnsafe(SIZE); -}); - -// Helper functions -function genBuffer(size, args) { - const b = Buffer.allocUnsafe(size); - return b.fill(0).fill.apply(b, args); -} - -function bufReset() { - buf1.fill(0); - buf2.fill(0); -} - -function writeToFill(string, offset, end, encoding) { - if (typeof offset === "string") { - encoding = offset; - offset = 0; - end = buf2.length; - } else if (typeof end === "string") { - encoding = end; - end = buf2.length; - } else if (end === undefined) { - end = buf2.length; - } - - if (offset < 0 || end > buf2.length) throw new RangeError("ERR_OUT_OF_RANGE"); - - if (end <= offset) return buf2; - - offset >>>= 0; - end >>>= 0; - expect(offset).toBeLessThanOrEqual(buf2.length); - - const length = end - offset < 0 ? 0 : end - offset; - - let wasZero = false; - do { - const written = buf2.write(string, offset, length, encoding); - offset += written; - if (written === 0) { - if (wasZero) throw new Error("Could not write all data to Buffer"); - else wasZero = true; - } - } while (offset < buf2.length); - - return buf2; -} - -function testBufs(string, offset, length, encoding) { - bufReset(); - buf1.fill.apply(buf1, arguments); - expect(buf1.fill.apply(buf1, arguments)).toEqual(writeToFill.apply(null, arguments)); -} - -// Tests -test("Default encoding", () => { - testBufs("abc"); - testBufs("\u0222aa"); - testBufs("a\u0234b\u0235c\u0236"); - testBufs("abc", 4); - testBufs("abc", 5); - testBufs("abc", SIZE); - testBufs("\u0222aa", 2); - testBufs("\u0222aa", 8); - testBufs("a\u0234b\u0235c\u0236", 4); - testBufs("a\u0234b\u0235c\u0236", 12); - testBufs("abc", 4, 1); - testBufs("abc", 5, 1); - testBufs("\u0222aa", 8, 1); - testBufs("a\u0234b\u0235c\u0236", 4, 1); - testBufs("a\u0234b\u0235c\u0236", 12, 1); -}); - -test("UTF8 encoding", () => { - testBufs("abc", "utf8"); - testBufs("\u0222aa", "utf8"); - testBufs("a\u0234b\u0235c\u0236", "utf8"); - testBufs("abc", 4, "utf8"); - testBufs("abc", 5, "utf8"); - testBufs("abc", SIZE, "utf8"); - testBufs("\u0222aa", 2, "utf8"); - testBufs("\u0222aa", 8, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 4, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 12, "utf8"); - testBufs("abc", 4, 1, "utf8"); - testBufs("abc", 5, 1, "utf8"); - testBufs("\u0222aa", 8, 1, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "utf8"); - expect(Buffer.allocUnsafe(1).fill(0).fill("\u0222")[0]).toBe(0xc8); -}); - -test("BINARY encoding", () => { - testBufs("abc", "binary"); - testBufs("\u0222aa", "binary"); - testBufs("a\u0234b\u0235c\u0236", "binary"); - testBufs("abc", 4, "binary"); - testBufs("abc", 5, "binary"); - testBufs("abc", SIZE, "binary"); - testBufs("\u0222aa", 2, "binary"); - testBufs("\u0222aa", 8, "binary"); - testBufs("a\u0234b\u0235c\u0236", 4, "binary"); - testBufs("a\u0234b\u0235c\u0236", 12, "binary"); - testBufs("abc", 4, 1, "binary"); - testBufs("abc", 5, 1, "binary"); - testBufs("\u0222aa", 8, 1, "binary"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "binary"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "binary"); -}); - -test("LATIN1 encoding", () => { - testBufs("abc", "latin1"); - testBufs("\u0222aa", "latin1"); - testBufs("a\u0234b\u0235c\u0236", "latin1"); - testBufs("abc", 4, "latin1"); - testBufs("abc", 5, "latin1"); - testBufs("abc", SIZE, "latin1"); - testBufs("\u0222aa", 2, "latin1"); - testBufs("\u0222aa", 8, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 4, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 12, "latin1"); - testBufs("abc", 4, 1, "latin1"); - testBufs("abc", 5, 1, "latin1"); - testBufs("\u0222aa", 8, 1, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "latin1"); -}); - -test("UCS2 encoding", () => { - testBufs("abc", "ucs2"); - testBufs("\u0222aa", "ucs2"); - testBufs("a\u0234b\u0235c\u0236", "ucs2"); - testBufs("abc", 4, "ucs2"); - testBufs("abc", SIZE, "ucs2"); - testBufs("\u0222aa", 2, "ucs2"); - testBufs("\u0222aa", 8, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 4, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 12, "ucs2"); - testBufs("abc", 4, 1, "ucs2"); - testBufs("abc", 5, 1, "ucs2"); - testBufs("\u0222aa", 8, 1, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "ucs2"); - expect(Buffer.allocUnsafe(1).fill("\u0222", "ucs2")[0]).toBe(0x22); -}); - -test("HEX encoding", () => { - testBufs("616263", "hex"); - testBufs("c8a26161", "hex"); - testBufs("61c8b462c8b563c8b6", "hex"); - testBufs("616263", 4, "hex"); - testBufs("616263", 5, "hex"); - testBufs("616263", SIZE, "hex"); - testBufs("c8a26161", 2, "hex"); - testBufs("c8a26161", 8, "hex"); - testBufs("61c8b462c8b563c8b6", 4, "hex"); - testBufs("61c8b462c8b563c8b6", 12, "hex"); - testBufs("616263", 4, 1, "hex"); - testBufs("616263", 5, 1, "hex"); - testBufs("c8a26161", 8, 1, "hex"); - testBufs("61c8b462c8b563c8b6", 4, 1, "hex"); - testBufs("61c8b462c8b563c8b6", 12, 1, "hex"); -}); - -test("Invalid HEX encoding", () => { - expect(() => { - const buf = Buffer.allocUnsafe(SIZE); - buf.fill("yKJh", "hex"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }), - ); - - expect(() => { - const buf = Buffer.allocUnsafe(SIZE); - buf.fill("\u0222", "hex"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }), - ); -}); - -test("BASE64 encoding", () => { - testBufs("YWJj", "base64"); - testBufs("yKJhYQ==", "base64"); - testBufs("Yci0Ysi1Y8i2", "base64"); - testBufs("YWJj", 4, "base64"); - testBufs("YWJj", SIZE, "base64"); - testBufs("yKJhYQ==", 2, "base64"); - testBufs("yKJhYQ==", 8, "base64"); - testBufs("Yci0Ysi1Y8i2", 4, "base64"); - testBufs("Yci0Ysi1Y8i2", 12, "base64"); - testBufs("YWJj", 4, 1, "base64"); - testBufs("YWJj", 5, 1, "base64"); - testBufs("yKJhYQ==", 8, 1, "base64"); - testBufs("Yci0Ysi1Y8i2", 4, 1, "base64"); - testBufs("Yci0Ysi1Y8i2", 12, 1, "base64"); -}); - -test("BASE64URL encoding", () => { - testBufs("YWJj", "base64url"); - testBufs("yKJhYQ", "base64url"); - testBufs("Yci0Ysi1Y8i2", "base64url"); - testBufs("YWJj", 4, "base64url"); - testBufs("YWJj", SIZE, "base64url"); - testBufs("yKJhYQ", 2, "base64url"); - testBufs("yKJhYQ", 8, "base64url"); - testBufs("Yci0Ysi1Y8i2", 4, "base64url"); - testBufs("Yci0Ysi1Y8i2", 12, "base64url"); - testBufs("YWJj", 4, 1, "base64url"); - testBufs("YWJj", 5, 1, "base64url"); - testBufs("yKJhYQ", 8, 1, "base64url"); - testBufs("Yci0Ysi1Y8i2", 4, 1, "base64url"); - testBufs("Yci0Ysi1Y8i2", 12, 1, "base64url"); -}); - -test("Buffer fill", () => { - function deepStrictEqualValues(buf, arr) { - for (const [index, value] of buf.entries()) { - expect(value).toBe(arr[index]); - } - } - - const buf2Fill = Buffer.allocUnsafe(1).fill(2); - deepStrictEqualValues(genBuffer(4, [buf2Fill]), [2, 2, 2, 2]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1]), [0, 2, 2, 2]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 3]), [0, 2, 2, 0]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 1]), [0, 0, 0, 0]); - const hexBufFill = Buffer.allocUnsafe(2).fill(0).fill("0102", "hex"); - deepStrictEqualValues(genBuffer(4, [hexBufFill]), [1, 2, 1, 2]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1]), [0, 1, 2, 1]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 3]), [0, 1, 2, 0]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 1]), [0, 0, 0, 0]); -}); - -test("Check exceptions", () => { - [ - [0, -1], - [0, 0, buf1.length + 1], - ["", -1], - ["", 0, buf1.length + 1], - ["", 1, -1], - ].forEach(args => { - expect(() => buf1.fill(...args)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - }); - - expect(() => buf1.fill("a", 0, buf1.length, "node rocks!")).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: "Unknown encoding: node rocks!", - }), - ); - - [ - ["a", 0, 0, NaN], - ["a", 0, 0, false], - ].forEach(args => { - expect(() => buf1.fill(...args)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "encoding" argument must be of type string'), - }), - ); - }); - - expect(() => buf1.fill("a", 0, 0, "foo")).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: "Unknown encoding: foo", - }), - ); -}); - -test("Out of range errors", () => { - expect(() => Buffer.allocUnsafe(8).fill("a", -1)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - expect(() => Buffer.allocUnsafe(8).fill("a", 0, 9)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); -}); - -test("Empty fill", () => { - Buffer.allocUnsafe(8).fill(""); - Buffer.alloc(8, ""); -}); - -test("Buffer allocation and fill", () => { - const buf = Buffer.alloc(64, 10); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(10); - - buf.fill(11, 0, buf.length >> 1); - for (let i = 0; i < buf.length >> 1; i++) expect(buf[i]).toBe(11); - for (let i = (buf.length >> 1) + 1; i < buf.length; i++) expect(buf[i]).toBe(10); - - buf.fill("h"); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe("h".charCodeAt(0)); - - buf.fill(0); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(0); - - buf.fill(null); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(0); - - buf.fill(1, 16, 32); - for (let i = 0; i < 16; i++) expect(buf[i]).toBe(0); - for (let i = 16; i < 32; i++) expect(buf[i]).toBe(1); - for (let i = 32; i < buf.length; i++) expect(buf[i]).toBe(0); -}); - -test("Buffer fill with string", () => { - const buf = Buffer.alloc(10, "abc"); - expect(buf.toString()).toBe("abcabcabca"); - buf.fill("է"); - expect(buf.toString()).toBe("էէէէէ"); -}); - -test("Buffer fill with invalid end", () => { - expect(() => { - const end = { - [Symbol.toPrimitive]() { - return 1; - }, - }; - Buffer.alloc(1).fill(Buffer.alloc(1), 0, end); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "end" argument must be of type number. Received'), - }), - ); -}); - -test.todo("Buffer fill with invalid length", () => { - expect(() => { - const buf = Buffer.from("w00t"); - Object.defineProperty(buf, "length", { - value: 1337, - enumerable: true, - }); - buf.fill(""); - }).toThrow( - expect.objectContaining({ - code: "ERR_BUFFER_OUT_OF_BOUNDS", - name: "RangeError", - message: "Attempt to access memory outside buffer bounds", - }), - ); -}); - -test("Buffer fill with utf16le encoding", () => { - expect(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le")).toEqual( - Buffer.from("61006200610062006100620061006200", "hex"), - ); - - expect(Buffer.allocUnsafeSlow(15).fill("ab", "utf16le")).toEqual( - Buffer.from("610062006100620061006200610062", "hex"), - ); - - expect(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le")).toEqual( - Buffer.from("61006200610062006100620061006200", "hex"), - ); - expect(Buffer.allocUnsafeSlow(16).fill("a", "utf16le")).toEqual( - Buffer.from("61006100610061006100610061006100", "hex"), - ); - - expect(Buffer.allocUnsafeSlow(16).fill("a", "utf16le").toString("utf16le")).toBe("a".repeat(8)); - expect(Buffer.allocUnsafeSlow(16).fill("a", "latin1").toString("latin1")).toBe("a".repeat(16)); - expect(Buffer.allocUnsafeSlow(16).fill("a", "utf8").toString("utf8")).toBe("a".repeat(16)); - - expect(Buffer.allocUnsafeSlow(16).fill("Љ", "utf16le").toString("utf16le")).toBe("Љ".repeat(8)); - expect(Buffer.allocUnsafeSlow(16).fill("Љ", "latin1").toString("latin1")).toBe("\t".repeat(16)); - expect(Buffer.allocUnsafeSlow(16).fill("Љ", "utf8").toString("utf8")).toBe("Љ".repeat(8)); -}); - -test("Buffer fill with invalid hex encoding", () => { - expect(() => { - const buf = Buffer.from("a".repeat(1000)); - buf.fill("This is not correctly encoded", "hex"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }), - ); -}); - -test("Buffer fill with empty values", () => { - const bufEmptyString = Buffer.alloc(5, ""); - expect(bufEmptyString.toString()).toBe("\x00\x00\x00\x00\x00"); - - const bufEmptyArray = Buffer.alloc(5, []); - expect(bufEmptyArray.toString()).toBe("\x00\x00\x00\x00\x00"); - - const bufEmptyBuffer = Buffer.alloc(5, Buffer.alloc(5)); - expect(bufEmptyBuffer.toString()).toBe("\x00\x00\x00\x00\x00"); - - const bufZero = Buffer.alloc(5, 0); - expect(bufZero.toString()).toBe("\x00\x00\x00\x00\x00"); -}); - -//<#END_FILE: test-buffer-fill.js diff --git a/test/js/node/test/parallel/buffer-from.test.js b/test/js/node/test/parallel/buffer-from.test.js deleted file mode 100644 index 0d089d4e8c..0000000000 --- a/test/js/node/test/parallel/buffer-from.test.js +++ /dev/null @@ -1,168 +0,0 @@ -//#FILE: test-buffer-from.js -//#SHA1: fdbb08fe98b94d1566ade587f17bb970130e1edd -//----------------- -"use strict"; - -const { runInNewContext } = require("vm"); - -const checkString = "test"; - -const check = Buffer.from(checkString); - -class MyString extends String { - constructor() { - super(checkString); - } -} - -class MyPrimitive { - [Symbol.toPrimitive]() { - return checkString; - } -} - -class MyBadPrimitive { - [Symbol.toPrimitive]() { - return 1; - } -} - -test("Buffer.from with various string-like inputs", () => { - expect(Buffer.from(new String(checkString))).toStrictEqual(check); - expect(Buffer.from(new MyString())).toStrictEqual(check); - expect(Buffer.from(new MyPrimitive())).toStrictEqual(check); - // expect(Buffer.from(runInNewContext("new String(checkString)", { checkString }))).toStrictEqual(check); //TODO: -}); - -describe("Buffer.from with invalid inputs", () => { - const invalidInputs = [ - {}, - new Boolean(true), - { - valueOf() { - return null; - }, - }, - { - valueOf() { - return undefined; - }, - }, - { valueOf: null }, - { __proto__: null }, - new Number(true), - new MyBadPrimitive(), - Symbol(), - 5n, - (one, two, three) => {}, - undefined, - null, - ]; - - for (const input of invalidInputs) { - test(`${Bun.inspect(input)}`, () => { - expect(() => Buffer.from(input)).toThrow( - expect.objectContaining({ - // code: "ERR_INVALID_ARG_TYPE", //TODO: - name: "TypeError", - message: expect.any(String), - }), - ); - expect(() => Buffer.from(input, "hex")).toThrow( - expect.objectContaining({ - // code: "ERR_INVALID_ARG_TYPE", //TODO: - name: "TypeError", - message: expect.any(String), - }), - ); - }); - } -}); - -test("Buffer.allocUnsafe and Buffer.from with valid inputs", () => { - expect(() => Buffer.allocUnsafe(10)).not.toThrow(); - expect(() => Buffer.from("deadbeaf", "hex")).not.toThrow(); -}); - -test("Buffer.copyBytesFrom with Uint16Array", () => { - const u16 = new Uint16Array([0xffff]); - const b16 = Buffer.copyBytesFrom(u16); - u16[0] = 0; - expect(b16.length).toBe(2); - expect(b16[0]).toBe(255); - expect(b16[1]).toBe(255); -}); - -test("Buffer.copyBytesFrom with Uint16Array and offset", () => { - const u16 = new Uint16Array([0, 0xffff]); - const b16 = Buffer.copyBytesFrom(u16, 1, 5); - u16[0] = 0xffff; - u16[1] = 0; - expect(b16.length).toBe(2); - expect(b16[0]).toBe(255); - expect(b16[1]).toBe(255); -}); - -test("Buffer.copyBytesFrom with Uint32Array", () => { - const u32 = new Uint32Array([0xffffffff]); - const b32 = Buffer.copyBytesFrom(u32); - u32[0] = 0; - expect(b32.length).toBe(4); - expect(b32[0]).toBe(255); - expect(b32[1]).toBe(255); - expect(b32[2]).toBe(255); - expect(b32[3]).toBe(255); -}); - -test("Buffer.copyBytesFrom with invalid inputs", () => { - expect(() => Buffer.copyBytesFrom()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - const invalidInputs = ["", Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined]; - invalidInputs.forEach(notTypedArray => { - expect(() => Buffer.copyBytesFrom(notTypedArray)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - const invalidSecondArgs = ["", Symbol(), true, false, {}, [], () => {}, 1n]; - invalidSecondArgs.forEach(notANumber => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), notANumber)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - const outOfRangeInputs = [-1, NaN, 1.1, -Infinity]; - outOfRangeInputs.forEach(outOfRange => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), outOfRange)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - }); - - invalidSecondArgs.forEach(notANumber => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - outOfRangeInputs.forEach(outOfRange => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - }); -}); - -//<#END_FILE: test-buffer-from.js diff --git a/test/js/node/test/parallel/buffer-inheritance.test.js b/test/js/node/test/parallel/buffer-inheritance.test.js deleted file mode 100644 index 5939621c9f..0000000000 --- a/test/js/node/test/parallel/buffer-inheritance.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-buffer-inheritance.js -//#SHA1: 01cba7d2cb76cb1d00fa91b3666dc58333b66e1b -//----------------- -"use strict"; - -test("Buffer inheritance", () => { - function T(n) { - const ui8 = new Uint8Array(n); - Object.setPrototypeOf(ui8, T.prototype); - return ui8; - } - Object.setPrototypeOf(T.prototype, Buffer.prototype); - Object.setPrototypeOf(T, Buffer); - - T.prototype.sum = function sum() { - let cntr = 0; - for (let i = 0; i < this.length; i++) cntr += this[i]; - return cntr; - }; - - const vals = [new T(4), T(4)]; - - vals.forEach(function (t) { - expect(t.constructor).toBe(T); - expect(Object.getPrototypeOf(t)).toBe(T.prototype); - expect(Object.getPrototypeOf(Object.getPrototypeOf(t))).toBe(Buffer.prototype); - - t.fill(5); - let cntr = 0; - for (let i = 0; i < t.length; i++) cntr += t[i]; - expect(cntr).toBe(t.length * 5); - - // Check this does not throw - expect(() => t.toString()).not.toThrow(); - }); -}); - -//<#END_FILE: test-buffer-inheritance.js diff --git a/test/js/node/test/parallel/buffer-inspect.test.js b/test/js/node/test/parallel/buffer-inspect.test.js deleted file mode 100644 index d1ba515755..0000000000 --- a/test/js/node/test/parallel/buffer-inspect.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-buffer-inspect.js -//#SHA1: 8578a4ec2de348a758e5c4dcbaa13a2ee7005451 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const util = require("util"); -const buffer = require("buffer"); - -describe("Buffer inspect", () => { - beforeEach(() => { - buffer.INSPECT_MAX_BYTES = 2; - }); - - afterEach(() => { - buffer.INSPECT_MAX_BYTES = Infinity; - }); - - test("Buffer and SlowBuffer inspection with INSPECT_MAX_BYTES = 2", () => { - const b = Buffer.allocUnsafe(4); - b.fill("1234"); - - const s = buffer.SlowBuffer(4); - s.fill("1234"); - - const expected = "Buffer(4) [Uint8Array] [ 49, 50, ... 2 more items ]"; - - expect(util.inspect(b)).toBe(expected); - expect(util.inspect(s)).toBe(expected); - }); - - test("Buffer and SlowBuffer inspection with 2 bytes", () => { - const b = Buffer.allocUnsafe(2); - b.fill("12"); - - const s = buffer.SlowBuffer(2); - s.fill("12"); - - const expected = "Buffer(2) [Uint8Array] [ 49, 50 ]"; - - expect(util.inspect(b)).toBe(expected); - expect(util.inspect(s)).toBe(expected); - }); - - test("Buffer and SlowBuffer inspection with INSPECT_MAX_BYTES = Infinity", () => { - const b = Buffer.allocUnsafe(2); - b.fill("12"); - - const s = buffer.SlowBuffer(2); - s.fill("12"); - - const expected = "Buffer(2) [Uint8Array] [ 49, 50 ]"; - - buffer.INSPECT_MAX_BYTES = Infinity; - - expect(util.inspect(b)).toBe(expected); - expect(util.inspect(s)).toBe(expected); - }); - - test("Buffer inspection with custom properties", () => { - const b = Buffer.allocUnsafe(2); - b.fill("12"); - b.inspect = undefined; - b.prop = new Uint8Array(0); - - expect(util.inspect(b)).toBe( - "Buffer(2) [Uint8Array] [\n 49,\n 50,\n inspect: undefined,\n prop: Uint8Array(0) []\n]", - ); - }); - - test("Empty Buffer inspection with custom property", () => { - const b = Buffer.alloc(0); - b.prop = 123; - - expect(util.inspect(b)).toBe("Buffer(0) [Uint8Array] [ prop: 123 ]"); - }); -}); - -//<#END_FILE: test-buffer-inspect.js diff --git a/test/js/node/test/parallel/buffer-isascii.test.js b/test/js/node/test/parallel/buffer-isascii.test.js deleted file mode 100644 index a8fde2110a..0000000000 --- a/test/js/node/test/parallel/buffer-isascii.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-buffer-isascii.js -//#SHA1: e49cbd0752feaa8042a90129dfb38610eb002ee6 -//----------------- -"use strict"; - -const { isAscii, Buffer } = require("buffer"); -const { TextEncoder } = require("util"); - -const encoder = new TextEncoder(); - -test("isAscii function", () => { - expect(isAscii(encoder.encode("hello"))).toBe(true); - expect(isAscii(encoder.encode("ğ"))).toBe(false); - expect(isAscii(Buffer.from([]))).toBe(true); -}); - -test("isAscii with invalid inputs", () => { - const invalidInputs = [undefined, "", "hello", false, true, 0, 1, 0n, 1n, Symbol(), () => {}, {}, [], null]; - - invalidInputs.forEach(input => { - expect(() => isAscii(input)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); -}); - -test("isAscii with detached array buffer", () => { - const arrayBuffer = new ArrayBuffer(1024); - structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); - - expect(() => isAscii(arrayBuffer)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_STATE", - }), - ); -}); - -//<#END_FILE: test-buffer-isascii.js diff --git a/test/js/node/test/parallel/buffer-isencoding.test.js b/test/js/node/test/parallel/buffer-isencoding.test.js deleted file mode 100644 index 010d80ca3a..0000000000 --- a/test/js/node/test/parallel/buffer-isencoding.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-buffer-isencoding.js -//#SHA1: 438625bd1ca2a23aa8716bea5334f3ac07eb040f -//----------------- -"use strict"; - -describe("Buffer.isEncoding", () => { - describe("should return true for valid encodings", () => { - const validEncodings = [ - "hex", - "utf8", - "utf-8", - "ascii", - "latin1", - "binary", - "base64", - "base64url", - "ucs2", - "ucs-2", - "utf16le", - "utf-16le", - ]; - - for (const enc of validEncodings) { - test(`${enc}`, () => { - expect(Buffer.isEncoding(enc)).toBe(true); - }); - } - }); - - describe("should return false for invalid encodings", () => { - const invalidEncodings = ["utf9", "utf-7", "Unicode-FTW", "new gnu gun", false, NaN, {}, Infinity, [], 1, 0, -1]; - - for (const enc of invalidEncodings) { - test(`${enc}`, () => { - expect(Buffer.isEncoding(enc)).toBe(false); - }); - } - }); -}); - -//<#END_FILE: test-buffer-isencoding.js diff --git a/test/js/node/test/parallel/buffer-isutf8.test.js b/test/js/node/test/parallel/buffer-isutf8.test.js deleted file mode 100644 index fa55fb4c5b..0000000000 --- a/test/js/node/test/parallel/buffer-isutf8.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-buffer-isutf8.js -//#SHA1: 47438bb1ade853e71f1266144d24188ebe75e714 -//----------------- -"use strict"; - -const { isUtf8, Buffer } = require("buffer"); -const { TextEncoder } = require("util"); - -const encoder = new TextEncoder(); - -test("isUtf8 validation", () => { - expect(isUtf8(encoder.encode("hello"))).toBe(true); - expect(isUtf8(encoder.encode("ğ"))).toBe(true); - expect(isUtf8(Buffer.from([]))).toBe(true); -}); - -// Taken from test/fixtures/wpt/encoding/textdecoder-fatal.any.js -test("invalid UTF-8 sequences", () => { - const invalidSequences = [ - [0xff], // 'invalid code' - [0xc0], // 'ends early' - [0xe0], // 'ends early 2' - [0xc0, 0x00], // 'invalid trail' - [0xc0, 0xc0], // 'invalid trail 2' - [0xe0, 0x00], // 'invalid trail 3' - [0xe0, 0xc0], // 'invalid trail 4' - [0xe0, 0x80, 0x00], // 'invalid trail 5' - [0xe0, 0x80, 0xc0], // 'invalid trail 6' - [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], // '> 0x10FFFF' - [0xfe, 0x80, 0x80, 0x80, 0x80, 0x80], // 'obsolete lead byte' - - // Overlong encodings - [0xc0, 0x80], // 'overlong U+0000 - 2 bytes' - [0xe0, 0x80, 0x80], // 'overlong U+0000 - 3 bytes' - [0xf0, 0x80, 0x80, 0x80], // 'overlong U+0000 - 4 bytes' - [0xf8, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 5 bytes' - [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 6 bytes' - - [0xc1, 0xbf], // 'overlong U+007F - 2 bytes' - [0xe0, 0x81, 0xbf], // 'overlong U+007F - 3 bytes' - [0xf0, 0x80, 0x81, 0xbf], // 'overlong U+007F - 4 bytes' - [0xf8, 0x80, 0x80, 0x81, 0xbf], // 'overlong U+007F - 5 bytes' - [0xfc, 0x80, 0x80, 0x80, 0x81, 0xbf], // 'overlong U+007F - 6 bytes' - - [0xe0, 0x9f, 0xbf], // 'overlong U+07FF - 3 bytes' - [0xf0, 0x80, 0x9f, 0xbf], // 'overlong U+07FF - 4 bytes' - [0xf8, 0x80, 0x80, 0x9f, 0xbf], // 'overlong U+07FF - 5 bytes' - [0xfc, 0x80, 0x80, 0x80, 0x9f, 0xbf], // 'overlong U+07FF - 6 bytes' - - [0xf0, 0x8f, 0xbf, 0xbf], // 'overlong U+FFFF - 4 bytes' - [0xf8, 0x80, 0x8f, 0xbf, 0xbf], // 'overlong U+FFFF - 5 bytes' - [0xfc, 0x80, 0x80, 0x8f, 0xbf, 0xbf], // 'overlong U+FFFF - 6 bytes' - - [0xf8, 0x84, 0x8f, 0xbf, 0xbf], // 'overlong U+10FFFF - 5 bytes' - [0xfc, 0x80, 0x84, 0x8f, 0xbf, 0xbf], // 'overlong U+10FFFF - 6 bytes' - - // UTF-16 surrogates encoded as code points in UTF-8 - [0xed, 0xa0, 0x80], // 'lead surrogate' - [0xed, 0xb0, 0x80], // 'trail surrogate' - [0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80], // 'surrogate pair' - ]; - - invalidSequences.forEach(input => { - expect(isUtf8(Buffer.from(input))).toBe(false); - }); -}); - -test("invalid input types", () => { - const invalidInputs = [null, undefined, "hello", true, false]; - - invalidInputs.forEach(input => { - expect(() => isUtf8(input)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); -}); - -test("detached array buffer", () => { - const arrayBuffer = new ArrayBuffer(1024); - structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); - expect(() => isUtf8(arrayBuffer)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_STATE", - }), - ); -}); - -//<#END_FILE: test-buffer-isutf8.js diff --git a/test/js/node/test/parallel/buffer-iterator.test.js b/test/js/node/test/parallel/buffer-iterator.test.js deleted file mode 100644 index ca9cacb93d..0000000000 --- a/test/js/node/test/parallel/buffer-iterator.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-buffer-iterator.js -//#SHA1: cd9bcdf671dc11d86bd194aa3e7500041f03eb4c -//----------------- -"use strict"; - -// Buffers should be iterable -test("Buffers are iterable", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer) { - arr.push(b); - } - - expect(arr).toEqual([1, 2, 3, 4, 5]); -}); - -// Buffer iterators should be iterable -test("Buffer iterators are iterable", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer[Symbol.iterator]()) { - arr.push(b); - } - - expect(arr).toEqual([1, 2, 3, 4, 5]); -}); - -// buffer#values() should return iterator for values -test("buffer.values() returns iterator for values", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer.values()) { - arr.push(b); - } - - expect(arr).toEqual([1, 2, 3, 4, 5]); -}); - -// buffer#keys() should return iterator for keys -test("buffer.keys() returns iterator for keys", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer.keys()) { - arr.push(b); - } - - expect(arr).toEqual([0, 1, 2, 3, 4]); -}); - -// buffer#entries() should return iterator for entries -test("buffer.entries() returns iterator for entries", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer.entries()) { - arr.push(b); - } - - expect(arr).toEqual([ - [0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - ]); -}); - -//<#END_FILE: test-buffer-iterator.js diff --git a/test/js/node/test/parallel/buffer-new.test.js b/test/js/node/test/parallel/buffer-new.test.js deleted file mode 100644 index 7f85579624..0000000000 --- a/test/js/node/test/parallel/buffer-new.test.js +++ /dev/null @@ -1,14 +0,0 @@ -//#FILE: test-buffer-new.js -//#SHA1: 56270fc6342f4ac15433cce1e1b1252ac4dcbb98 -//----------------- -"use strict"; - -test("Buffer constructor with invalid arguments", () => { - expect(() => new Buffer(42, "utf8")).toThrow({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: `The "string" argument must be of type string. Received 42`, - }); -}); - -//<#END_FILE: test-buffer-new.js diff --git a/test/js/node/test/parallel/buffer-no-negative-allocation.test.js b/test/js/node/test/parallel/buffer-no-negative-allocation.test.js deleted file mode 100644 index 2158402336..0000000000 --- a/test/js/node/test/parallel/buffer-no-negative-allocation.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-buffer-no-negative-allocation.js -//#SHA1: c7f13ec857490bc5d1ffbf8da3fff19049c421f8 -//----------------- -"use strict"; - -const { SlowBuffer } = require("buffer"); - -// Test that negative Buffer length inputs throw errors. - -const msg = expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), -}); - -test("Buffer constructor throws on negative or NaN length", () => { - expect(() => Buffer(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer(-100)).toThrow(msg); - expect(() => Buffer(-1)).toThrow(msg); - expect(() => Buffer(NaN)).toThrow(msg); -}); - -test("Buffer.alloc throws on negative or NaN length", () => { - expect(() => Buffer.alloc(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer.alloc(-100)).toThrow(msg); - expect(() => Buffer.alloc(-1)).toThrow(msg); - expect(() => Buffer.alloc(NaN)).toThrow(msg); -}); - -test("Buffer.allocUnsafe throws on negative or NaN length", () => { - expect(() => Buffer.allocUnsafe(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer.allocUnsafe(-100)).toThrow(msg); - expect(() => Buffer.allocUnsafe(-1)).toThrow(msg); - expect(() => Buffer.allocUnsafe(NaN)).toThrow(msg); -}); - -test("Buffer.allocUnsafeSlow throws on negative or NaN length", () => { - expect(() => Buffer.allocUnsafeSlow(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer.allocUnsafeSlow(-100)).toThrow(msg); - expect(() => Buffer.allocUnsafeSlow(-1)).toThrow(msg); - expect(() => Buffer.allocUnsafeSlow(NaN)).toThrow(msg); -}); - -test("SlowBuffer throws on negative or NaN length", () => { - expect(() => SlowBuffer(-Buffer.poolSize)).toThrow(msg); - expect(() => SlowBuffer(-100)).toThrow(msg); - expect(() => SlowBuffer(-1)).toThrow(msg); - expect(() => SlowBuffer(NaN)).toThrow(msg); -}); - -//<#END_FILE: test-buffer-no-negative-allocation.js diff --git a/test/js/node/test/parallel/buffer-nopendingdep-map.test.js b/test/js/node/test/parallel/buffer-nopendingdep-map.test.js deleted file mode 100644 index 5fc9d4efad..0000000000 --- a/test/js/node/test/parallel/buffer-nopendingdep-map.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-buffer-nopendingdep-map.js -//#SHA1: 908b5747ec3c5873c180b66b6e50221fd29169e3 -//----------------- -// Flags: --no-warnings --pending-deprecation -"use strict"; - -test("Buffer methods should not emit deprecation warnings with --pending-deprecation", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - // With the --pending-deprecation flag, the deprecation warning for - // new Buffer() should not be emitted when Uint8Array methods are called. - - Buffer.from("abc").map(i => i); - Buffer.from("abc").filter(i => i); - Buffer.from("abc").slice(1, 2); - - expect(warningListener).not.toHaveBeenCalled(); - - process.removeListener("warning", warningListener); -}); - -//<#END_FILE: test-buffer-nopendingdep-map.js diff --git a/test/js/node/test/parallel/buffer-of-no-deprecation.test.js b/test/js/node/test/parallel/buffer-of-no-deprecation.test.js deleted file mode 100644 index 53c31f103c..0000000000 --- a/test/js/node/test/parallel/buffer-of-no-deprecation.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-buffer-of-no-deprecation.js -//#SHA1: 7c233f8a82411a5d1c293daecef6494d02d7dabf -//----------------- -"use strict"; - -test("Buffer.of() should not emit deprecation warning", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - Buffer.of(0, 1); - - expect(warningListener).not.toHaveBeenCalled(); - - // Clean up the listener - process.removeListener("warning", warningListener); -}); - -//<#END_FILE: test-buffer-of-no-deprecation.js diff --git a/test/js/node/test/parallel/buffer-over-max-length.test.js b/test/js/node/test/parallel/buffer-over-max-length.test.js deleted file mode 100644 index 5ba6d6af4e..0000000000 --- a/test/js/node/test/parallel/buffer-over-max-length.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-buffer-over-max-length.js -//#SHA1: 797cb237a889a5f09d34b2554a46eb4c545f885e -//----------------- -"use strict"; - -const buffer = require("buffer"); -const SlowBuffer = buffer.SlowBuffer; - -const kMaxLength = buffer.kMaxLength; -const bufferMaxSizeMsg = expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.stringContaining(`The value of "size" is out of range.`), -}); - -test("Buffer creation with over max length", () => { - expect(() => Buffer(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => Buffer.alloc(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => Buffer.allocUnsafe(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => Buffer.allocUnsafeSlow(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); -}); - -//<#END_FILE: test-buffer-over-max-length.js diff --git a/test/js/node/test/parallel/buffer-parent-property.test.js b/test/js/node/test/parallel/buffer-parent-property.test.js deleted file mode 100644 index ebf02d3652..0000000000 --- a/test/js/node/test/parallel/buffer-parent-property.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-buffer-parent-property.js -//#SHA1: 1496dde41464d188eecd053b64a320c71f62bd7d -//----------------- -"use strict"; - -// Fix for https://github.com/nodejs/node/issues/8266 -// -// Zero length Buffer objects should expose the `buffer` property of the -// TypedArrays, via the `parent` property. - -test("Buffer parent property", () => { - // If the length of the buffer object is zero - expect(Buffer.alloc(0).parent).toBeInstanceOf(ArrayBuffer); - - // If the length of the buffer object is equal to the underlying ArrayBuffer - expect(Buffer.alloc(Buffer.poolSize).parent).toBeInstanceOf(ArrayBuffer); - - // Same as the previous test, but with user created buffer - const arrayBuffer = new ArrayBuffer(0); - expect(Buffer.from(arrayBuffer).parent).toBe(arrayBuffer); - expect(Buffer.from(arrayBuffer).buffer).toBe(arrayBuffer); - expect(Buffer.from(arrayBuffer).parent).toBe(arrayBuffer); - expect(Buffer.from(arrayBuffer).buffer).toBe(arrayBuffer); -}); - -//<#END_FILE: test-buffer-parent-property.js diff --git a/test/js/node/test/parallel/buffer-prototype-inspect.test.js b/test/js/node/test/parallel/buffer-prototype-inspect.test.js deleted file mode 100644 index f6bb9a8915..0000000000 --- a/test/js/node/test/parallel/buffer-prototype-inspect.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-buffer-prototype-inspect.js -//#SHA1: 3809d957d94134495a61469120087c12580fa3f3 -//----------------- -"use strict"; - -// lib/buffer.js defines Buffer.prototype.inspect() to override how buffers are -// presented by util.inspect(). - -const util = require("util"); -const buffer = require("buffer"); -buffer.INSPECT_MAX_BYTES = 50; - -test("Buffer.prototype.inspect() for non-empty buffer", () => { - const buf = Buffer.from("fhqwhgads"); - expect(util.inspect(buf)).toBe("Buffer(9) [Uint8Array] [\n 102, 104, 113, 119,\n 104, 103, 97, 100,\n 115\n]"); -}); - -test("Buffer.prototype.inspect() for empty buffer", () => { - const buf = Buffer.from(""); - expect(util.inspect(buf)).toBe("Buffer(0) [Uint8Array] []"); -}); - -test("Buffer.prototype.inspect() for large buffer", () => { - const buf = Buffer.from("x".repeat(51)); - expect(util.inspect(buf)).toBe( - `Buffer(51) [Uint8Array] [\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120,\n` + - ` ... 1 more item\n` + - `]`, - ); -}); - -//<#END_FILE: test-buffer-prototype-inspect.js diff --git a/test/js/node/test/parallel/buffer-safe-unsafe.test.js b/test/js/node/test/parallel/buffer-safe-unsafe.test.js deleted file mode 100644 index 84e1db9096..0000000000 --- a/test/js/node/test/parallel/buffer-safe-unsafe.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-buffer-safe-unsafe.js -//#SHA1: 87831e463ab52a79fca3ac2e28eec57666ea9e5e -//----------------- -"use strict"; - -test("Buffer safe and unsafe allocations", () => { - const safe = Buffer.alloc(10); - - function isZeroFilled(buf) { - for (let n = 0; n < buf.length; n++) if (buf[n] !== 0) return false; - return true; - } - - expect(isZeroFilled(safe)).toBe(true); - - // Test that unsafe allocations doesn't affect subsequent safe allocations - Buffer.allocUnsafe(10); - expect(isZeroFilled(new Float64Array(10))).toBe(true); - - new Buffer(10); - expect(isZeroFilled(new Float64Array(10))).toBe(true); - - Buffer.allocUnsafe(10); - expect(isZeroFilled(Buffer.alloc(10))).toBe(true); -}); - -//<#END_FILE: test-buffer-safe-unsafe.js diff --git a/test/js/node/test/parallel/buffer-set-inspect-max-bytes.test.js b/test/js/node/test/parallel/buffer-set-inspect-max-bytes.test.js deleted file mode 100644 index 306fa0f81b..0000000000 --- a/test/js/node/test/parallel/buffer-set-inspect-max-bytes.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-buffer-set-inspect-max-bytes.js -//#SHA1: de73b2a241585e1cf17a057d21cdbabbadf963bb -//----------------- -"use strict"; - -const buffer = require("buffer"); - -describe("buffer.INSPECT_MAX_BYTES", () => { - const rangeErrorObjs = [NaN, -1]; - const typeErrorObj = "and even this"; - - test.each(rangeErrorObjs)("throws RangeError for invalid value: %p", obj => { - expect(() => { - buffer.INSPECT_MAX_BYTES = obj; - }).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - }); - - test("throws TypeError for invalid type", () => { - expect(() => { - buffer.INSPECT_MAX_BYTES = typeErrorObj; - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-buffer-set-inspect-max-bytes.js diff --git a/test/js/node/test/parallel/buffer-slice.test.js b/test/js/node/test/parallel/buffer-slice.test.js deleted file mode 100644 index 5fde7bdf62..0000000000 --- a/test/js/node/test/parallel/buffer-slice.test.js +++ /dev/null @@ -1,114 +0,0 @@ -//#FILE: test-buffer-slice.js -//#SHA1: 1f7289de3a6dd5167f3659cd5119e6489b059d43 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("Buffer slice operations", () => { - expect(Buffer.from("hello", "utf8").slice(0, 0).length).toBe(0); - expect(Buffer("hello", "utf8").slice(0, 0).length).toBe(0); - - const buf = Buffer.from("0123456789", "utf8"); - const expectedSameBufs = [ - [buf.slice(-10, 10), Buffer.from("0123456789", "utf8")], - [buf.slice(-20, 10), Buffer.from("0123456789", "utf8")], - [buf.slice(-20, -10), Buffer.from("", "utf8")], - [buf.slice(), Buffer.from("0123456789", "utf8")], - [buf.slice(0), Buffer.from("0123456789", "utf8")], - [buf.slice(0, 0), Buffer.from("", "utf8")], - [buf.slice(undefined), Buffer.from("0123456789", "utf8")], - [buf.slice("foobar"), Buffer.from("0123456789", "utf8")], - [buf.slice(undefined, undefined), Buffer.from("0123456789", "utf8")], - [buf.slice(2), Buffer.from("23456789", "utf8")], - [buf.slice(5), Buffer.from("56789", "utf8")], - [buf.slice(10), Buffer.from("", "utf8")], - [buf.slice(5, 8), Buffer.from("567", "utf8")], - [buf.slice(8, -1), Buffer.from("8", "utf8")], - [buf.slice(-10), Buffer.from("0123456789", "utf8")], - [buf.slice(0, -9), Buffer.from("0", "utf8")], - [buf.slice(0, -10), Buffer.from("", "utf8")], - [buf.slice(0, -1), Buffer.from("012345678", "utf8")], - [buf.slice(2, -2), Buffer.from("234567", "utf8")], - [buf.slice(0, 65536), Buffer.from("0123456789", "utf8")], - [buf.slice(65536, 0), Buffer.from("", "utf8")], - [buf.slice(-5, -8), Buffer.from("", "utf8")], - [buf.slice(-5, -3), Buffer.from("56", "utf8")], - [buf.slice(-10, 10), Buffer.from("0123456789", "utf8")], - [buf.slice("0", "1"), Buffer.from("0", "utf8")], - [buf.slice("-5", "10"), Buffer.from("56789", "utf8")], - [buf.slice("-10", "10"), Buffer.from("0123456789", "utf8")], - [buf.slice("-10", "-5"), Buffer.from("01234", "utf8")], - [buf.slice("-10", "-0"), Buffer.from("", "utf8")], - [buf.slice("111"), Buffer.from("", "utf8")], - [buf.slice("0", "-111"), Buffer.from("", "utf8")], - ]; - - for (let i = 0, s = buf.toString(); i < buf.length; ++i) { - expectedSameBufs.push( - [buf.slice(i), Buffer.from(s.slice(i))], - [buf.slice(0, i), Buffer.from(s.slice(0, i))], - [buf.slice(-i), Buffer.from(s.slice(-i))], - [buf.slice(0, -i), Buffer.from(s.slice(0, -i))], - ); - } - - for (const [buf1, buf2] of expectedSameBufs) { - expect(Buffer.compare(buf1, buf2)).toBe(0); - } - - const utf16Buf = Buffer.from("0123456789", "utf16le"); - expect(utf16Buf.slice(0, 6)).toStrictEqual(Buffer.from("012", "utf16le")); - - // Try to slice a zero length Buffer. - // See https://github.com/joyent/node/issues/5881 - expect(Buffer.alloc(0).slice(0, 1).length).toBe(0); - - // Single argument slice - expect(Buffer.from("abcde", "utf8").slice(1).toString("utf8")).toBe("bcde"); - - // slice(0,0).length === 0 - expect(Buffer.from("hello", "utf8").slice(0, 0).length).toBe(0); - - // Regression tests for https://github.com/nodejs/node/issues/9096 - const regressionBuf = Buffer.from("abcd", "utf8"); - expect(regressionBuf.slice(regressionBuf.length / 3).toString("utf8")).toBe("bcd"); - expect(regressionBuf.slice(regressionBuf.length / 3, regressionBuf.length).toString()).toBe("bcd"); - - const largeSliceBuf = Buffer.from("abcdefg", "utf8"); - expect(largeSliceBuf.slice(-(-1 >>> 0) - 1).toString("utf8")).toBe(largeSliceBuf.toString("utf8")); - - const floatSliceBuf = Buffer.from("abc", "utf8"); - expect(floatSliceBuf.slice(-0.5).toString("utf8")).toBe(floatSliceBuf.toString("utf8")); - - const complexBuf = Buffer.from([ - 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, - ]); - const chunk1 = Buffer.from([1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0]); - const chunk2 = Buffer.from([0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0]); - const middle = complexBuf.length / 2; - - expect(complexBuf.slice(0, middle)).toStrictEqual(chunk1); - expect(complexBuf.slice(middle)).toStrictEqual(chunk2); -}); - -//<#END_FILE: test-buffer-slice.js diff --git a/test/js/node/test/parallel/buffer-slow.test.js b/test/js/node/test/parallel/buffer-slow.test.js deleted file mode 100644 index 85f35f68e6..0000000000 --- a/test/js/node/test/parallel/buffer-slow.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-buffer-slow.js -//#SHA1: fadf639fe26752f00488a41a29f1977f95fc1c79 -//----------------- -"use strict"; - -const buffer = require("buffer"); -const SlowBuffer = buffer.SlowBuffer; - -const ones = [1, 1, 1, 1]; - -test("SlowBuffer should create a Buffer", () => { - let sb = SlowBuffer(4); - expect(sb).toBeInstanceOf(Buffer); - expect(sb.length).toBe(4); - sb.fill(1); - for (const [key, value] of sb.entries()) { - expect(value).toBe(ones[key]); - } - - // underlying ArrayBuffer should have the same length - expect(sb.buffer.byteLength).toBe(4); -}); - -test("SlowBuffer should work without new", () => { - let sb = SlowBuffer(4); - expect(sb).toBeInstanceOf(Buffer); - expect(sb.length).toBe(4); - sb.fill(1); - for (const [key, value] of sb.entries()) { - expect(value).toBe(ones[key]); - } -}); - -test("SlowBuffer should work with edge cases", () => { - expect(SlowBuffer(0).length).toBe(0); -}); - -test("SlowBuffer should throw with invalid length type", () => { - const bufferInvalidTypeMsg = expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }); - - expect(() => SlowBuffer()).toThrow(bufferInvalidTypeMsg); - expect(() => SlowBuffer({})).toThrow(bufferInvalidTypeMsg); - expect(() => SlowBuffer("6")).toThrow(bufferInvalidTypeMsg); - expect(() => SlowBuffer(true)).toThrow(bufferInvalidTypeMsg); -}); - -test("SlowBuffer should throw with invalid length value", () => { - const bufferMaxSizeMsg = expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }); - - expect(() => SlowBuffer(NaN)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(Infinity)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(-1)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(buffer.kMaxLength + 1)).toThrow(bufferMaxSizeMsg); -}); - -//<#END_FILE: test-buffer-slow.js diff --git a/test/js/node/test/parallel/buffer-swap.test.js b/test/js/node/test/parallel/buffer-swap.test.js deleted file mode 100644 index 89eef58dce..0000000000 --- a/test/js/node/test/parallel/buffer-swap.test.js +++ /dev/null @@ -1,153 +0,0 @@ -//#FILE: test-buffer-swap.js -//#SHA1: 589e4ee82ab5f00e1cffdd4d326e21cc2f06b065 -//----------------- -"use strict"; - -describe("Buffer swap operations", () => { - test("Test buffers small enough to use the JS implementation", () => { - const buf = Buffer.from([ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - ]); - - expect(buf.swap16()).toBe(buf); - expect(buf).toEqual( - Buffer.from([0x02, 0x01, 0x04, 0x03, 0x06, 0x05, 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, 0x0e, 0x0d, 0x10, 0x0f]), - ); - buf.swap16(); // restore - - expect(buf.swap32()).toBe(buf); - expect(buf).toEqual( - Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, 0x10, 0x0f, 0x0e, 0x0d]), - ); - buf.swap32(); // restore - - expect(buf.swap64()).toBe(buf); - expect(buf).toEqual( - Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09]), - ); - }); - - test("Operates in-place", () => { - const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); - buf.slice(1, 5).swap32(); - expect(buf).toEqual(Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); - buf.slice(1, 5).swap16(); - expect(buf).toEqual(Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); - - // Length assertions - const re16 = /Buffer size must be a multiple of 16-bits/; - const re32 = /Buffer size must be a multiple of 32-bits/; - const re64 = /Buffer size must be a multiple of 64-bits/; - - expect(() => Buffer.from(buf).swap16()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.alloc(1025).swap16()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.from(buf).swap32()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => buf.slice(1, 3).swap32()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.alloc(1025).swap32()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => buf.slice(1, 3).swap64()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.alloc(1025).swap64()).toThrow(expect.objectContaining({ message: expect.any(String) })); - }); - - test("Swap64 on a slice", () => { - const buf = Buffer.from([ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - ]); - - buf.slice(2, 18).swap64(); - - expect(buf).toEqual( - Buffer.from([ - 0x01, 0x02, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - ]), - ); - }); - - test("Force use of native code (Buffer size above threshold limit for js impl)", () => { - const bufData = new Uint32Array(256).fill(0x04030201); - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - const otherBufData = new Uint32Array(256).fill(0x03040102); - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - buf.swap16(); - expect(buf).toEqual(otherBuf); - }); - - test("Force use of native code for swap32", () => { - const bufData = new Uint32Array(256).fill(0x04030201); - const buf = Buffer.from(bufData.buffer); - const otherBufData = new Uint32Array(256).fill(0x01020304); - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - buf.swap32(); - expect(buf).toEqual(otherBuf); - }); - - test("Force use of native code for swap64", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 8; - otherBufData[otherBufData.length - i - 1] = i % 8; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - buf.swap64(); - expect(buf).toEqual(otherBuf); - }); - - test("Test native code with buffers that are not memory-aligned (swap16)", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8 - 2); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 2; - } - for (let i = 1; i < otherBufData.length; i++) { - otherBufData[otherBufData.length - i] = (i + 1) % 2; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - // 0|1 0|1 0|1... - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - // 0|0 1|0 1|0... - - buf.slice(1, buf.length - 1).swap16(); - expect(buf.slice(0, otherBuf.length)).toEqual(otherBuf); - }); - - test("Test native code with buffers that are not memory-aligned (swap32)", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8 - 4); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 4; - } - for (let i = 1; i < otherBufData.length; i++) { - otherBufData[otherBufData.length - i] = (i + 1) % 4; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - // 0|1 2 3 0|1 2 3... - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - // 0|0 3 2 1|0 3 2... - - buf.slice(1, buf.length - 3).swap32(); - expect(buf.slice(0, otherBuf.length)).toEqual(otherBuf); - }); - - test("Test native code with buffers that are not memory-aligned (swap64)", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8 - 8); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 8; - } - for (let i = 1; i < otherBufData.length; i++) { - otherBufData[otherBufData.length - i] = (i + 1) % 8; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - // 0|1 2 3 4 5 6 7 0|1 2 3 4... - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - // 0|0 7 6 5 4 3 2 1|0 7 6 5... - - buf.slice(1, buf.length - 7).swap64(); - expect(buf.slice(0, otherBuf.length)).toEqual(otherBuf); - }); -}); - -//<#END_FILE: test-buffer-swap.js diff --git a/test/js/node/test/parallel/buffer-tostring-range.test.js b/test/js/node/test/parallel/buffer-tostring-range.test.js deleted file mode 100644 index a1e72ba714..0000000000 --- a/test/js/node/test/parallel/buffer-tostring-range.test.js +++ /dev/null @@ -1,115 +0,0 @@ -//#FILE: test-buffer-tostring-range.js -//#SHA1: 2bc09c70e84191e47ae345cc3178f28458b10ec2 -//----------------- -"use strict"; - -const rangeBuffer = Buffer.from("abc"); - -test("Buffer.toString range behavior", () => { - // If start >= buffer's length, empty string will be returned - expect(rangeBuffer.toString("ascii", 3)).toBe(""); - expect(rangeBuffer.toString("ascii", +Infinity)).toBe(""); - expect(rangeBuffer.toString("ascii", 3.14, 3)).toBe(""); - expect(rangeBuffer.toString("ascii", "Infinity", 3)).toBe(""); - - // If end <= 0, empty string will be returned - expect(rangeBuffer.toString("ascii", 1, 0)).toBe(""); - expect(rangeBuffer.toString("ascii", 1, -1.2)).toBe(""); - expect(rangeBuffer.toString("ascii", 1, -100)).toBe(""); - expect(rangeBuffer.toString("ascii", 1, -Infinity)).toBe(""); - - // If start < 0, start will be taken as zero - expect(rangeBuffer.toString("ascii", -1, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", -1.99, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", -Infinity, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "-1", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "-1.99", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "-Infinity", 3)).toBe("abc"); - - // If start is an invalid integer, start will be taken as zero - expect(rangeBuffer.toString("ascii", "node.js", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", {}, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", [], 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", NaN, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", null, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", undefined, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", false, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "", 3)).toBe("abc"); - - // But, if start is an integer when coerced, then it will be coerced and used. - expect(rangeBuffer.toString("ascii", "-1", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "1", 3)).toBe("bc"); - expect(rangeBuffer.toString("ascii", "-Infinity", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "3", 3)).toBe(""); - expect(rangeBuffer.toString("ascii", Number(3), 3)).toBe(""); - expect(rangeBuffer.toString("ascii", "3.14", 3)).toBe(""); - expect(rangeBuffer.toString("ascii", "1.99", 3)).toBe("bc"); - expect(rangeBuffer.toString("ascii", "-1.99", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 1.99, 3)).toBe("bc"); - expect(rangeBuffer.toString("ascii", true, 3)).toBe("bc"); - - // If end > buffer's length, end will be taken as buffer's length - expect(rangeBuffer.toString("ascii", 0, 5)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, 6.99)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, Infinity)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "5")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "6.99")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "Infinity")).toBe("abc"); - - // If end is an invalid integer, end will be taken as buffer's length - expect(rangeBuffer.toString("ascii", 0, "node.js")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, {})).toBe(""); - expect(rangeBuffer.toString("ascii", 0, NaN)).toBe(""); - expect(rangeBuffer.toString("ascii", 0, undefined)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, null)).toBe(""); - expect(rangeBuffer.toString("ascii", 0, [])).toBe(""); - expect(rangeBuffer.toString("ascii", 0, false)).toBe(""); - expect(rangeBuffer.toString("ascii", 0, "")).toBe(""); - - // But, if end is an integer when coerced, then it will be coerced and used. - expect(rangeBuffer.toString("ascii", 0, "-1")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, "1")).toBe("a"); - expect(rangeBuffer.toString("ascii", 0, "-Infinity")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, "3")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, Number(3))).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "3.14")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "1.99")).toBe("a"); - expect(rangeBuffer.toString("ascii", 0, "-1.99")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, 1.99)).toBe("a"); - expect(rangeBuffer.toString("ascii", 0, true)).toBe("a"); -}); - -test("toString() with an object as an encoding", () => { - expect( - rangeBuffer.toString({ - toString: function () { - return "ascii"; - }, - }), - ).toBe("abc"); -}); - -test("toString() with 0 and null as the encoding", () => { - expect(() => { - rangeBuffer.toString(0, 1, 2); - }).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => { - rangeBuffer.toString(null, 1, 2); - }).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-buffer-tostring-range.js diff --git a/test/js/node/test/parallel/buffer-tostring-rangeerror.test.js b/test/js/node/test/parallel/buffer-tostring-rangeerror.test.js deleted file mode 100644 index 0e88759c45..0000000000 --- a/test/js/node/test/parallel/buffer-tostring-rangeerror.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-buffer-tostring-rangeerror.js -//#SHA1: c5bd04a7b4f3b7ecfb3898262dd73da29a9ad162 -//----------------- -"use strict"; - -// This test ensures that Node.js throws an Error when trying to convert a -// large buffer into a string. -// Regression test for https://github.com/nodejs/node/issues/649. - -const { - SlowBuffer, - constants: { MAX_STRING_LENGTH }, -} = require("buffer"); - -const len = MAX_STRING_LENGTH + 1; -const errorMatcher = expect.objectContaining({ - code: "ERR_STRING_TOO_LONG", - name: "Error", - message: `Cannot create a string longer than 2147483647 characters`, -}); - -test("Buffer toString with large buffer throws RangeError", () => { - expect(() => Buffer(len).toString("utf8")).toThrow(errorMatcher); - expect(() => SlowBuffer(len).toString("utf8")).toThrow(errorMatcher); - expect(() => Buffer.alloc(len).toString("utf8")).toThrow(errorMatcher); - expect(() => Buffer.allocUnsafe(len).toString("utf8")).toThrow(errorMatcher); - expect(() => Buffer.allocUnsafeSlow(len).toString("utf8")).toThrow(errorMatcher); -}); - -//<#END_FILE: test-buffer-tostring-rangeerror.js diff --git a/test/js/node/test/parallel/buffer-tostring.test.js b/test/js/node/test/parallel/buffer-tostring.test.js deleted file mode 100644 index eb48074506..0000000000 --- a/test/js/node/test/parallel/buffer-tostring.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-buffer-tostring.js -//#SHA1: 0a6490b6dd4c343c01828d1c4ff81b745b6b1552 -//----------------- -"use strict"; - -// utf8, ucs2, ascii, latin1, utf16le -const encodings = ["utf8", "utf-8", "ucs2", "ucs-2", "ascii", "latin1", "binary", "utf16le", "utf-16le"]; - -test("Buffer.from().toString() with various encodings", () => { - encodings - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - expect(Buffer.from("foo", encoding).toString(encoding)).toBe("foo"); - }); -}); - -test("Buffer.from().toString() with base64 encoding", () => { - ["base64", "BASE64"].forEach(encoding => { - expect(Buffer.from("Zm9v", encoding).toString(encoding)).toBe("Zm9v"); - }); -}); - -test("Buffer.from().toString() with hex encoding", () => { - ["hex", "HEX"].forEach(encoding => { - expect(Buffer.from("666f6f", encoding).toString(encoding)).toBe("666f6f"); - }); -}); - -test("Buffer.from().toString() with invalid encodings", () => { - for (let i = 1; i < 10; i++) { - const encoding = String(i).repeat(i); - expect(Buffer.isEncoding(encoding)).toBe(false); - expect(() => Buffer.from("foo").toString(encoding)).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); - } -}); - -//<#END_FILE: test-buffer-tostring.js diff --git a/test/js/node/test/parallel/buffer-write.test.js b/test/js/node/test/parallel/buffer-write.test.js deleted file mode 100644 index ceb7123d5f..0000000000 --- a/test/js/node/test/parallel/buffer-write.test.js +++ /dev/null @@ -1,119 +0,0 @@ -//#FILE: test-buffer-write.js -//#SHA1: 9577e31a533888b164b0abf4ebececbe04e381cb -//----------------- -"use strict"; - -[-1, 10].forEach(offset => { - test(`Buffer.alloc(9).write('foo', ${offset}) throws RangeError`, () => { - expect(() => Buffer.alloc(9).write("foo", offset)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - }); -}); - -const resultMap = new Map([ - ["utf8", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["ucs2", Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], - ["ascii", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["latin1", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["binary", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["utf16le", Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], - ["base64", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["base64url", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["hex", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], -]); - -// utf8, ucs2, ascii, latin1, utf16le -const encodings = ["utf8", "utf-8", "ucs2", "ucs-2", "ascii", "latin1", "binary", "utf16le", "utf-16le"]; - -encodings - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - test(`Buffer.write with encoding ${encoding}`, () => { - const buf = Buffer.alloc(9); - const len = Buffer.byteLength("foo", encoding); - expect(buf.write("foo", 0, len, encoding)).toBe(len); - - if (encoding.includes("-")) encoding = encoding.replace("-", ""); - - expect(buf).toEqual(resultMap.get(encoding.toLowerCase())); - }); - }); - -// base64 -["base64", "BASE64", "base64url", "BASE64URL"].forEach(encoding => { - test(`Buffer.write with encoding ${encoding}`, () => { - const buf = Buffer.alloc(9); - const len = Buffer.byteLength("Zm9v", encoding); - - expect(buf.write("Zm9v", 0, len, encoding)).toBe(len); - expect(buf).toEqual(resultMap.get(encoding.toLowerCase())); - }); -}); - -// hex -["hex", "HEX"].forEach(encoding => { - test(`Buffer.write with encoding ${encoding}`, () => { - const buf = Buffer.alloc(9); - const len = Buffer.byteLength("666f6f", encoding); - - expect(buf.write("666f6f", 0, len, encoding)).toBe(len); - expect(buf).toEqual(resultMap.get(encoding.toLowerCase())); - }); -}); - -// Invalid encodings -for (let i = 1; i < 10; i++) { - const encoding = String(i).repeat(i); - - test(`Invalid encoding ${encoding}`, () => { - expect(Buffer.isEncoding(encoding)).toBe(false); - expect(() => Buffer.alloc(9).write("foo", encoding)).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -} - -// UCS-2 overflow CVE-2018-12115 -for (let i = 1; i < 4; i++) { - test(`UCS-2 overflow test ${i}`, () => { - // Allocate two Buffers sequentially off the pool. Run more than once in case - // we hit the end of the pool and don't get sequential allocations - const x = Buffer.allocUnsafe(4).fill(0); - const y = Buffer.allocUnsafe(4).fill(1); - // Should not write anything, pos 3 doesn't have enough room for a 16-bit char - expect(x.write("ыыыыыы", 3, "ucs2")).toBe(0); - // CVE-2018-12115 experienced via buffer overrun to next block in the pool - expect(Buffer.compare(y, Buffer.alloc(4, 1))).toBe(0); - }); -} - -test("Should not write any data when there is no space for 16-bit chars", () => { - const z = Buffer.alloc(4, 0); - expect(z.write("\u0001", 3, "ucs2")).toBe(0); - expect(Buffer.compare(z, Buffer.alloc(4, 0))).toBe(0); - // Make sure longer strings are written up to the buffer end. - expect(z.write("abcd", 2)).toBe(2); - expect([...z]).toEqual([0, 0, 0x61, 0x62]); -}); - -test("Large overrun should not corrupt the process", () => { - expect(Buffer.alloc(4).write("ыыыыыы".repeat(100), 3, "utf16le")).toBe(0); -}); - -test(".write() does not affect the byte after the written-to slice of the Buffer", () => { - // Refs: https://github.com/nodejs/node/issues/26422 - const buf = Buffer.alloc(8); - expect(buf.write("ыы", 1, "utf16le")).toBe(4); - expect([...buf]).toEqual([0, 0x4b, 0x04, 0x4b, 0x04, 0, 0, 0]); -}); - -//<#END_FILE: test-buffer-write.js diff --git a/test/js/node/test/parallel/buffer-zero-fill-reset.test.js b/test/js/node/test/parallel/buffer-zero-fill-reset.test.js deleted file mode 100644 index d4055fb35a..0000000000 --- a/test/js/node/test/parallel/buffer-zero-fill-reset.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-buffer-zero-fill-reset.js -//#SHA1: 2278dd06a2e113e875c1f0f46580c0fcc4fc5254 -//----------------- -"use strict"; - -test("Uint8Array is zero-filled after Buffer.alloc(0)", () => { - function testUint8Array(ui) { - const length = ui.length; - for (let i = 0; i < length; i++) if (ui[i] !== 0) return false; - return true; - } - - for (let i = 0; i < 100; i++) { - Buffer.alloc(0); - const ui = new Uint8Array(65); - expect(testUint8Array(ui)).toBe(true); - } -}); - -//<#END_FILE: test-buffer-zero-fill-reset.js diff --git a/test/js/node/test/parallel/buffer-zero-fill.test.js b/test/js/node/test/parallel/buffer-zero-fill.test.js deleted file mode 100644 index 003b4ffe08..0000000000 --- a/test/js/node/test/parallel/buffer-zero-fill.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-buffer-zero-fill.js -//#SHA1: b710e0c9405c90f7526cf0efabd4c61ede37b1f7 -//----------------- -"use strict"; - -// Tests deprecated Buffer API on purpose -test("Buffer zero-fill", () => { - const buf1 = Buffer(100); - const buf2 = new Buffer(100); - - for (let n = 0; n < buf1.length; n++) { - expect(buf1[n]).toBe(0); - } - - for (let n = 0; n < buf2.length; n++) { - expect(buf2[n]).toBe(0); - } -}); - -//<#END_FILE: test-buffer-zero-fill.js diff --git a/test/js/node/test/parallel/child-process-exec-encoding.test.js b/test/js/node/test/parallel/child-process-exec-encoding.test.js deleted file mode 100644 index 46ae2d5276..0000000000 --- a/test/js/node/test/parallel/child-process-exec-encoding.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-child-process-exec-encoding.js -//#SHA1: 3ad6878126678aa6ad2c38a43264e5684dae6a72 -//----------------- -"use strict"; - -const stdoutData = "foo"; -const stderrData = "bar"; - -if (process.argv[2] === "child") { - // The following console calls are part of the test. - console.log(stdoutData); - console.error(stderrData); -} else { - const cp = require("child_process"); - const expectedStdout = `${stdoutData}\n`; - const expectedStderr = `${stderrData}\n`; - - function run(options) { - const cmd = `"${process.execPath}" "${__filename}" child`; - - return new Promise((resolve, reject) => { - cp.exec(cmd, options, (error, stdout, stderr) => { - if (error) { - reject(error); - } else { - resolve({ stdout, stderr }); - } - }); - }); - } - - test("Test default encoding, which should be utf8", async () => { - const { stdout, stderr } = await run({}); - expect(typeof stdout).toBe("string"); - expect(typeof stderr).toBe("string"); - expect(stdout).toBe(expectedStdout); - expect(stderr).toBe(expectedStderr); - }); - - test("Test explicit utf8 encoding", async () => { - const { stdout, stderr } = await run({ encoding: "utf8" }); - expect(typeof stdout).toBe("string"); - expect(typeof stderr).toBe("string"); - expect(stdout).toBe(expectedStdout); - expect(stderr).toBe(expectedStderr); - }); - - test("Test cases that result in buffer encodings", async () => { - const encodings = [undefined, null, "buffer", "invalid"]; - - for (const encoding of encodings) { - const { stdout, stderr } = await run({ encoding }); - expect(stdout).toBeInstanceOf(Buffer); - expect(stderr).toBeInstanceOf(Buffer); - expect(stdout.toString()).toBe(expectedStdout); - expect(stderr.toString()).toBe(expectedStderr); - } - }); -} - -//<#END_FILE: test-child-process-exec-encoding.js diff --git a/test/js/node/test/parallel/child-process-exec-kill-throws.test.js b/test/js/node/test/parallel/child-process-exec-kill-throws.test.js deleted file mode 100644 index b0b8482f24..0000000000 --- a/test/js/node/test/parallel/child-process-exec-kill-throws.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-child-process-exec-kill-throws.js -//#SHA1: 968879ddf3244351dea40c681343ea8defc02a0b -//----------------- -"use strict"; - -const cp = require("child_process"); - -if (process.argv[2] === "child") { - // Since maxBuffer is 0, this should trigger an error. - console.log("foo"); -} else { - const originalKill = cp.ChildProcess.prototype.kill; - - beforeEach(() => { - // Monkey patch ChildProcess#kill() to kill the process and then throw. - cp.ChildProcess.prototype.kill = function () { - originalKill.apply(this, arguments); - throw new Error("mock error"); - }; - }); - - afterEach(() => { - // Restore original kill method - cp.ChildProcess.prototype.kill = originalKill; - }); - - test("ChildProcess#kill() throws error", done => { - const cmd = `"${process.execPath}" "${__filename}" child`; - const options = { maxBuffer: 0, killSignal: "SIGKILL" }; - - const child = cp.exec(cmd, options, (err, stdout, stderr) => { - // Verify that if ChildProcess#kill() throws, the error is reported. - expect(err).toEqual( - expect.objectContaining({ - message: "mock error", - }), - ); - expect(stdout).toBe(""); - expect(stderr).toBe(""); - expect(child.killed).toBe(true); - done(); - }); - }); -} - -//<#END_FILE: test-child-process-exec-kill-throws.js diff --git a/test/js/node/test/parallel/child-process-exec-std-encoding.test.js b/test/js/node/test/parallel/child-process-exec-std-encoding.test.js deleted file mode 100644 index 010bc7097e..0000000000 --- a/test/js/node/test/parallel/child-process-exec-std-encoding.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-child-process-exec-std-encoding.js -//#SHA1: fa74583780f5256e46fd7a5ad02aed4b20bb0b76 -//----------------- -"use strict"; - -const cp = require("child_process"); - -const stdoutData = "foo"; -const stderrData = "bar"; -const expectedStdout = `${stdoutData}\n`; -const expectedStderr = `${stderrData}\n`; - -if (process.argv[2] === "child") { - // The following console calls are part of the test. - console.log(stdoutData); - console.error(stderrData); -} else { - test("child process exec with stdout and stderr encoding", done => { - const cmd = `"${process.execPath}" "${__filename}" child`; - const child = cp.exec(cmd, (error, stdout, stderr) => { - expect(error).toBeNull(); - expect(stdout).toBe(expectedStdout); - expect(stderr).toBe(expectedStderr); - done(); - }); - child.stdout.setEncoding("utf-8"); - child.stderr.setEncoding("utf-8"); - }); -} - -//<#END_FILE: test-child-process-exec-std-encoding.js diff --git a/test/js/node/test/parallel/child-process-exec-stdout-stderr-data-string.test.js b/test/js/node/test/parallel/child-process-exec-stdout-stderr-data-string.test.js deleted file mode 100644 index 42a1a555ab..0000000000 --- a/test/js/node/test/parallel/child-process-exec-stdout-stderr-data-string.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-child-process-exec-stdout-stderr-data-string.js -//#SHA1: 342c40f3dbb506150172c2471ae228fd8632b900 -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/7342 -const { exec } = require("child_process"); - -const command = process.platform === "win32" ? "dir" : "ls"; - -test("exec stdout data is called at least once", done => { - const child = exec(command); - const onData = jest.fn(); - child.stdout.on("data", onData); - - child.on("close", () => { - expect(onData).toHaveBeenCalled(); - done(); - }); -}); - -test("exec stderr data is called at least once and receives string", done => { - const child = exec("fhqwhgads"); - const onData = jest.fn(data => { - expect(typeof data).toBe("string"); - }); - child.stderr.on("data", onData); - - child.on("close", () => { - expect(onData).toHaveBeenCalled(); - done(); - }); -}); - -//<#END_FILE: test-child-process-exec-stdout-stderr-data-string.js diff --git a/test/js/node/test/parallel/child-process-exec-timeout-kill.test.js b/test/js/node/test/parallel/child-process-exec-timeout-kill.test.js deleted file mode 100644 index e3bc2b8e9b..0000000000 --- a/test/js/node/test/parallel/child-process-exec-timeout-kill.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-child-process-exec-timeout-kill.js -//#SHA1: 01bc25d258b4d8905a2387e9a08b9ceb8c38c141 -//----------------- -"use strict"; - -// Test exec() with both a timeout and a killSignal. - -const cp = require("child_process"); -const path = require("path"); - -const { kExpiringChildRunTime, kExpiringParentTimer } = require("../common/child_process"); - -const logAfterTime = time => { - setTimeout(() => { - console.log(`Logged after ${time}ms`); - }, time); -}; - -if (process.argv[2] === "child") { - logAfterTime(kExpiringChildRunTime); - process.exit(0); -} - -const cmd = `"${process.execPath}" "${__filename}" child`; - -test("exec with timeout and killSignal", done => { - // Test with a different kill signal. - cp.exec( - cmd, - { - timeout: kExpiringParentTimer, - killSignal: "SIGKILL", - }, - (err, stdout, stderr) => { - console.log("[stdout]", stdout.trim()); - console.log("[stderr]", stderr.trim()); - expect(stdout.trim()).toBe(""); - expect(stderr.trim()).toBe(""); - - expect(err?.killed).toBe(true); - expect(err?.code).toBeNull(); - expect(err?.signal).toBe("SIGKILL"); - expect(err?.cmd).toBe(cmd); - done(); - }, - ); -}); - -//<#END_FILE: test-child-process-exec-timeout-kill.js diff --git a/test/js/node/test/parallel/child-process-execfile-promisified-abortcontroller.test.js b/test/js/node/test/parallel/child-process-execfile-promisified-abortcontroller.test.js deleted file mode 100644 index acebc661e9..0000000000 --- a/test/js/node/test/parallel/child-process-execfile-promisified-abortcontroller.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-child-process-execFile-promisified-abortController.js -//#SHA1: 133445acf9aaafea4be11eb7965f222c5827f2f3 -//----------------- -"use strict"; - -const { promisify } = require("util"); -const execFile = require("child_process").execFile; -const fixtures = require("../common/fixtures"); - -const echoFixture = fixtures.path("echo.js"); -const promisified = promisify(execFile); -const invalidArgTypeError = { - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", -}; - -test("Verify that the signal option works properly", async () => { - const ac = new AbortController(); - const signal = ac.signal; - const promise = promisified(process.execPath, [echoFixture, 0], { signal }); - - ac.abort(); - - await expect(promise).rejects.toThrow( - expect.objectContaining({ - name: "AbortError", - message: expect.any(String), - }), - ); -}); - -test("Verify that the signal option works properly when already aborted", async () => { - const signal = AbortSignal.abort(); - - await expect(promisified(process.execPath, [echoFixture, 0], { signal })).rejects.toThrow( - expect.objectContaining({ - name: "AbortError", - message: expect.any(String), - }), - ); -}); - -test("Verify that if something different than Abortcontroller.signal is passed, ERR_INVALID_ARG_TYPE is thrown", () => { - const signal = {}; - expect(() => { - promisified(process.execPath, [echoFixture, 0], { signal }); - }).toThrow(expect.objectContaining(invalidArgTypeError)); -}); - -test("Verify that if a string is passed as signal, ERR_INVALID_ARG_TYPE is thrown", () => { - const signal = "world!"; - expect(() => { - promisified(process.execPath, [echoFixture, 0], { signal }); - }).toThrow(expect.objectContaining(invalidArgTypeError)); -}); - -//<#END_FILE: test-child-process-execFile-promisified-abortController.js diff --git a/test/js/node/test/parallel/child-process-flush-stdio.test.js b/test/js/node/test/parallel/child-process-flush-stdio.test.js deleted file mode 100644 index 53e4419b66..0000000000 --- a/test/js/node/test/parallel/child-process-flush-stdio.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-child-process-flush-stdio.js -//#SHA1: 42d6ab8508587f13b81d2ec70d28fd88efe8fe05 -//----------------- -"use strict"; - -const cp = require("child_process"); - -// Windows' `echo` command is a built-in shell command and not an external -// executable like on *nix -const opts = { shell: process.platform === "win32" }; - -test("spawn echo without arguments", done => { - const p = cp.spawn("echo", [], opts); - - p.on("close", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - done(); - }); - - p.stdout.read(); -}); - -test("spawn echo with argument", done => { - const buffer = []; - const p = cp.spawn("echo", ["123"], opts); - - p.on("close", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - expect(Buffer.concat(buffer).toString().trim()).toBe("123"); - done(); - }); - - p.stdout.on("readable", () => { - let buf; - while ((buf = p.stdout.read()) !== null) buffer.push(buf); - }); -}); - -//<#END_FILE: test-child-process-flush-stdio.js diff --git a/test/js/node/test/parallel/child-process-fork-abort-signal.test.js b/test/js/node/test/parallel/child-process-fork-abort-signal.test.js deleted file mode 100644 index f3c1dcfe65..0000000000 --- a/test/js/node/test/parallel/child-process-fork-abort-signal.test.js +++ /dev/null @@ -1,115 +0,0 @@ -//#FILE: test-child-process-fork-abort-signal.js -//#SHA1: 4805d5dd4e3cb22ffd5a21fd9d92b6ccd6bc73cf -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const { fork } = require("child_process"); - -test("aborting a forked child_process after calling fork", done => { - const ac = new AbortController(); - const { signal } = ac; - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - done(); - }); - process.nextTick(() => ac.abort()); -}); - -test("aborting with custom error", done => { - const ac = new AbortController(); - const { signal } = ac; - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - expect(err.cause.name).toBe("Error"); - expect(err.cause.message).toBe("boom"); - done(); - }); - process.nextTick(() => ac.abort(new Error("boom"))); -}); - -test("passing an already aborted signal to a forked child_process", done => { - const signal = AbortSignal.abort(); - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - done(); - }); -}); - -test("passing an aborted signal with custom error to a forked child_process", done => { - const signal = AbortSignal.abort(new Error("boom")); - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - expect(err.cause.name).toBe("Error"); - expect(err.cause.message).toBe("boom"); - done(); - }); -}); - -test("passing a different kill signal", done => { - const signal = AbortSignal.abort(); - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - killSignal: "SIGKILL", - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGKILL"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - done(); - }); -}); - -test("aborting a cp before close but after exit", done => { - const ac = new AbortController(); - const { signal } = ac; - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", () => { - ac.abort(); - done(); - }); - cp.on("error", () => { - done(new Error("Should not have errored")); - }); - - setTimeout(() => cp.kill(), 1); -}); - -//<#END_FILE: test-child-process-fork-abort-signal.js diff --git a/test/js/node/test/parallel/child-process-fork-args.test.js b/test/js/node/test/parallel/child-process-fork-args.test.js deleted file mode 100644 index f2efa98aa5..0000000000 --- a/test/js/node/test/parallel/child-process-fork-args.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-child-process-fork-args.js -//#SHA1: 172297ab2ed7887ced1b830b8c36d2d6a508deed -//----------------- -"use strict"; -const fixtures = require("../common/fixtures"); -const { fork } = require("child_process"); - -// This test check the arguments of `fork` method -// Refs: https://github.com/nodejs/node/issues/20749 -const expectedEnv = { foo: "bar" }; - -// Ensure that first argument `modulePath` must be provided -// and be of type string -test("fork modulePath argument must be a string", () => { - const invalidModulePath = [0, true, undefined, null, [], {}, () => {}, Symbol("t")]; - invalidModulePath.forEach(modulePath => { - expect(() => fork(modulePath)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringMatching(/^The "modulePath" argument must be of type string/), - }), - ); - }); -}); - -test("fork with valid modulePath", done => { - const cp = fork(fixtures.path("child-process-echo-options.js")); - cp.on("exit", code => { - expect(code).toBe(0); - done(); - }); -}); - -// Ensure that the second argument of `fork` -// and `fork` should parse options -// correctly if args is undefined or null -test("fork second argument validation", () => { - const invalidSecondArgs = [0, true, () => {}, Symbol("t")]; - invalidSecondArgs.forEach(arg => { - expect(() => { - fork(fixtures.path("child-process-echo-options.js"), arg); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -test("fork with valid second argument", async () => { - const argsLists = [undefined, null, []]; - - for (const args of argsLists) { - const cp = fork(fixtures.path("child-process-echo-options.js"), args, { - env: { ...process.env, ...expectedEnv }, - }); - - await new Promise(resolve => { - cp.on("message", ({ env }) => { - expect(env.foo).toBe(expectedEnv.foo); - }); - - cp.on("exit", code => { - expect(code).toBe(0); - resolve(); - }); - }); - } -}); - -// Ensure that the third argument should be type of object if provided -test("fork third argument must be an object if provided", () => { - const invalidThirdArgs = [0, true, () => {}, Symbol("t")]; - invalidThirdArgs.forEach(arg => { - expect(() => { - fork(fixtures.path("child-process-echo-options.js"), [], arg); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -//<#END_FILE: test-child-process-fork-args.js diff --git a/test/js/node/test/parallel/child-process-fork-close.test.js b/test/js/node/test/parallel/child-process-fork-close.test.js deleted file mode 100644 index d7dd94759a..0000000000 --- a/test/js/node/test/parallel/child-process-fork-close.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-child-process-fork-close.js -//#SHA1: d196e4b4f06991be7c2a48169cb89b815c0f172b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { fork } = require("child_process"); -const path = require("path"); - -test("child process fork close events", async () => { - const cp = fork(path.resolve(__dirname, "../fixtures/child-process-message-and-exit.js")); - - let gotMessage = false; - let gotExit = false; - let gotClose = false; - - const messagePromise = new Promise(resolve => { - cp.on("message", message => { - expect(gotMessage).toBe(false); - expect(gotClose).toBe(false); - expect(message).toBe("hello"); - gotMessage = true; - resolve(); - }); - }); - - const exitPromise = new Promise(resolve => { - cp.on("exit", () => { - expect(gotExit).toBe(false); - expect(gotClose).toBe(false); - gotExit = true; - resolve(); - }); - }); - - const closePromise = new Promise(resolve => { - cp.on("close", () => { - expect(gotMessage).toBe(true); - expect(gotExit).toBe(true); - expect(gotClose).toBe(false); - gotClose = true; - resolve(); - }); - }); - - await Promise.all([messagePromise, exitPromise, closePromise]); -}); - -//<#END_FILE: test-child-process-fork-close.js diff --git a/test/js/node/test/parallel/child-process-fork-detached.test.js b/test/js/node/test/parallel/child-process-fork-detached.test.js deleted file mode 100644 index 18c3f6a0cb..0000000000 --- a/test/js/node/test/parallel/child-process-fork-detached.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-child-process-fork-detached.js -//#SHA1: 289d7a5f7c5d58ae50a06e7427730c57d78fe39c -//----------------- -"use strict"; - -const { fork } = require("child_process"); -const path = require("path"); - -const fixturesPath = path.resolve(__dirname, "..", "fixtures"); - -test("fork detached child process", done => { - const nonPersistentNode = fork(path.join(fixturesPath, "parent-process-nonpersistent-fork.js"), [], { silent: true }); - - let childId = -1; - - nonPersistentNode.stdout.on("data", data => { - childId = parseInt(data, 10); - nonPersistentNode.kill(); - }); - - nonPersistentNode.on("exit", () => { - expect(childId).not.toBe(-1); - - // Killing the child process should not throw an error - expect(() => process.kill(childId)).not.toThrow(); - - done(); - }); -}); - -//<#END_FILE: test-child-process-fork-detached.js diff --git a/test/js/node/test/parallel/child-process-fork-ref2.test.js b/test/js/node/test/parallel/child-process-fork-ref2.test.js deleted file mode 100644 index ecea4bfd3a..0000000000 --- a/test/js/node/test/parallel/child-process-fork-ref2.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-child-process-fork-ref2.js -//#SHA1: 6d8a2bec4198f9d9f71ce9f2cc880cfcd1c9d883 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { fork } = require("child_process"); -const { debuglog } = require("util"); -const debug = debuglog("test"); - -const platformTimeout = ms => ms; - -if (process.argv[2] === "child") { - test("child process", () => { - debug("child -> call disconnect"); - process.disconnect(); - - return new Promise(resolve => { - setTimeout(() => { - debug("child -> will this keep it alive?"); - const mockFn = jest.fn(); - process.on("message", mockFn); - expect(mockFn).not.toHaveBeenCalled(); - resolve(); - }, platformTimeout(400)); - }); - }); -} else { - test("parent process", () => { - return new Promise(resolve => { - const child = fork(__filename, ["child"]); - - const disconnectSpy = jest.fn(() => { - debug("parent -> disconnect"); - }); - child.on("disconnect", disconnectSpy); - - const exitSpy = jest.fn(() => { - debug("parent -> exit"); - expect(disconnectSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - child.once("exit", exitSpy); - - expect(exitSpy).toHaveBeenCalledTimes(0); - }); - }); -} - -//<#END_FILE: test-child-process-fork-ref2.js diff --git a/test/js/node/test/parallel/child-process-send-type-error.test.js b/test/js/node/test/parallel/child-process-send-type-error.test.js deleted file mode 100644 index cc4908b7da..0000000000 --- a/test/js/node/test/parallel/child-process-send-type-error.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-child-process-send-type-error.js -//#SHA1: 85b82f9c15ca3d5368e22ccd1e7f44672ce2fb0c -//----------------- -"use strict"; - -const cp = require("child_process"); - -function fail(proc, args) { - expect(() => { - proc.send.apply(proc, args); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); -} - -let target = process; - -if (process.argv[2] !== "child") { - test("child process send type error", () => { - target = cp.fork(__filename, ["child"]); - target.on("exit", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - }); - - fail(target, ["msg", null, null]); - fail(target, ["msg", null, ""]); - fail(target, ["msg", null, "foo"]); - fail(target, ["msg", null, 0]); - fail(target, ["msg", null, NaN]); - fail(target, ["msg", null, 1]); - fail(target, ["msg", null, null, jest.fn()]); - }); -} else { - test("process send type error", () => { - fail(target, ["msg", null, null]); - fail(target, ["msg", null, ""]); - fail(target, ["msg", null, "foo"]); - fail(target, ["msg", null, 0]); - fail(target, ["msg", null, NaN]); - fail(target, ["msg", null, 1]); - fail(target, ["msg", null, null, jest.fn()]); - }); -} - -//<#END_FILE: test-child-process-send-type-error.js diff --git a/test/js/node/test/parallel/child-process-stdio-big-write-end.test.js b/test/js/node/test/parallel/child-process-stdio-big-write-end.test.js deleted file mode 100644 index ff1ea0cc16..0000000000 --- a/test/js/node/test/parallel/child-process-stdio-big-write-end.test.js +++ /dev/null @@ -1,92 +0,0 @@ -//#FILE: test-child-process-stdio-big-write-end.js -//#SHA1: 728a12ebb5484fcc628e82386c3b521ab95e0456 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { spawn } = require("child_process"); -const assert = require("assert"); -const debug = require("util").debuglog("test"); - -let bufsize = 0; - -function runParent() { - return new Promise(resolve => { - const child = spawn(process.execPath, [__filename, "child"]); - let sent = 0; - - let n = ""; - child.stdout.setEncoding("ascii"); - child.stdout.on("data", c => { - n += c; - }); - child.stdout.on("end", () => { - expect(+n).toBe(sent); - debug("ok"); - resolve(); - }); - - // Write until the buffer fills up. - let buf; - do { - bufsize += 1024; - buf = Buffer.alloc(bufsize, "."); - sent += bufsize; - } while (child.stdin.write(buf)); - - // Then write a bunch more times. - for (let i = 0; i < 100; i++) { - const buf = Buffer.alloc(bufsize, "."); - sent += bufsize; - child.stdin.write(buf); - } - - // Now end, before it's all flushed. - child.stdin.end(); - - // now we wait... - }); -} - -function runChild() { - return new Promise(resolve => { - let received = 0; - process.stdin.on("data", c => { - received += c.length; - }); - process.stdin.on("end", () => { - // This console.log is part of the test. - console.log(received); - resolve(); - }); - }); -} - -if (process.argv[2] === "child") { - runChild(); -} else { - test("child process stdio big write end", async () => { - await runParent(); - }); -} - -//<#END_FILE: test-child-process-stdio-big-write-end.js diff --git a/test/js/node/test/parallel/cli-eval-event.test.js b/test/js/node/test/parallel/cli-eval-event.test.js deleted file mode 100644 index 152c3da4dc..0000000000 --- a/test/js/node/test/parallel/cli-eval-event.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-cli-eval-event.js -//#SHA1: d64fa25056a9ecf2e87e46560d477b42d3e32909 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); - -test("CLI eval event", () => { - return new Promise(resolve => { - const child = spawn(process.execPath, [ - "-e", - ` - const server = require('net').createServer().listen(0); - server.once('listening', server.close); - `, - ]); - - child.once("exit", (exitCode, signalCode) => { - expect(exitCode).toBe(0); - expect(signalCode).toBeNull(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-cli-eval-event.js diff --git a/test/js/node/test/parallel/client-request-destroy.test.js b/test/js/node/test/parallel/client-request-destroy.test.js deleted file mode 100644 index 165fbbdd33..0000000000 --- a/test/js/node/test/parallel/client-request-destroy.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-client-request-destroy.js -//#SHA1: 343919bc022f2956e9aab5c9a215cbadca2364f1 -//----------------- -"use strict"; - -// Test that http.ClientRequest.prototype.destroy() returns `this`. - -const http = require("http"); - -test("http.ClientRequest.prototype.destroy() returns `this`", () => { - const clientRequest = new http.ClientRequest({ createConnection: () => {} }); - - expect(clientRequest.destroyed).toBe(false); - expect(clientRequest.destroy()).toBe(clientRequest); - expect(clientRequest.destroyed).toBe(true); - expect(clientRequest.destroy()).toBe(clientRequest); -}); - -//<#END_FILE: test-client-request-destroy.js diff --git a/test/js/node/test/parallel/cluster-bind-privileged-port.test.js b/test/js/node/test/parallel/cluster-bind-privileged-port.test.js deleted file mode 100644 index c83b370b56..0000000000 --- a/test/js/node/test/parallel/cluster-bind-privileged-port.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-cluster-bind-privileged-port.js -//#SHA1: f3a12e75717db9c1a1edd4f51fb2985787a50706 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); -const net = require("net"); -const { readFileSync } = require("fs"); - -const isLinux = process.platform === "linux"; -const isOSX = process.platform === "darwin"; -const isIBMi = process.platform === "os400"; -const isWindows = process.platform === "win32"; - -const skipTest = () => { - test.skip("Skipping test", () => {}); - process.exit(0); -}; - -if (isLinux) { - try { - const unprivilegedPortStart = parseInt(readFileSync("/proc/sys/net/ipv4/ip_unprivileged_port_start")); - if (unprivilegedPortStart <= 42) { - skipTest(); - } - } catch { - // Do nothing, feature doesn't exist, minimum is 1024 so 42 is usable. - // Continue... - } -} - -// Skip on OS X Mojave. https://github.com/nodejs/node/issues/21679 -if (isOSX) skipTest(); - -if (isIBMi) skipTest(); - -if (isWindows) skipTest(); - -if (process.getuid() === 0) skipTest(); - -if (cluster.isPrimary) { - test("primary cluster process", () => { - const worker = cluster.fork(); - worker.on("exit", exitCode => { - expect(exitCode).toBe(0); - }); - }); -} else { - test("worker cluster process", () => { - const s = net.createServer(jest.fn()); - s.listen(42, jest.fn()); - s.on("error", err => { - expect(err.code).toBe("EACCES"); - process.disconnect(); - }); - }); -} - -//<#END_FILE: test-cluster-bind-privileged-port.js diff --git a/test/js/node/test/parallel/cluster-call-and-destroy.test.js b/test/js/node/test/parallel/cluster-call-and-destroy.test.js deleted file mode 100644 index 1e2914833f..0000000000 --- a/test/js/node/test/parallel/cluster-call-and-destroy.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-cluster-call-and-destroy.js -//#SHA1: 840777cd738f6257dd874035b1c0291ebe16e326 -//----------------- -"use strict"; -const cluster = require("cluster"); - -if (cluster.isPrimary) { - test("worker disconnection and destruction", () => { - const worker = cluster.fork(); - - return new Promise(resolve => { - worker.on("disconnect", () => { - expect(worker.isConnected()).toBe(false); - worker.destroy(); - resolve(); - }); - }); - }); -} else { - test("worker connection in child process", () => { - expect(cluster.worker.isConnected()).toBe(true); - cluster.worker.disconnect(); - }); -} - -//<#END_FILE: test-cluster-call-and-destroy.js diff --git a/test/js/node/test/parallel/cluster-dgram-reuse.test.js b/test/js/node/test/parallel/cluster-dgram-reuse.test.js deleted file mode 100644 index 143db050b6..0000000000 --- a/test/js/node/test/parallel/cluster-dgram-reuse.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-cluster-dgram-reuse.js -//#SHA1: b7bfc0764ebc95fa5ef85ce1d860aebd3f7df539 -//----------------- -"use strict"; - -const cluster = require("cluster"); -const dgram = require("dgram"); - -if (process.platform === "win32") { - test.skip("dgram clustering is currently not supported on windows."); -} else { - if (cluster.isPrimary) { - test("Primary process", () => { - const worker = cluster.fork(); - worker.on("exit", code => { - expect(code).toBe(0); - }); - }); - } else { - test("Worker process", async () => { - let waiting = 2; - function close() { - if (--waiting === 0) cluster.worker.disconnect(); - } - - const options = { type: "udp4", reuseAddr: true }; - const socket1 = dgram.createSocket(options); - const socket2 = dgram.createSocket(options); - - await new Promise(resolve => { - socket1.bind(0, () => { - socket2.bind(socket1.address().port, () => { - // Work around health check issue - process.nextTick(() => { - socket1.close(close); - socket2.close(close); - resolve(); - }); - }); - }); - }); - }); - } -} - -//<#END_FILE: test-cluster-dgram-reuse.js diff --git a/test/js/node/test/parallel/cluster-eaddrinuse.test.js b/test/js/node/test/parallel/cluster-eaddrinuse.test.js deleted file mode 100644 index c72c6d6af5..0000000000 --- a/test/js/node/test/parallel/cluster-eaddrinuse.test.js +++ /dev/null @@ -1,94 +0,0 @@ -//#FILE: test-cluster-eaddrinuse.js -//#SHA1: a17cbbd83e23565c1cb547999357c14f03be6efa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Check that having a worker bind to a port that's already taken doesn't -// leave the primary process in a confused state. Releasing the port and -// trying again should Just Work[TM]. - -const { fork } = require("child_process"); -const net = require("net"); - -const id = String(process.argv[2]); -const port = String(process.argv[3]); - -if (id === "undefined") { - test("primary process", async () => { - const server = net.createServer(() => { - throw new Error("Server should not receive connections"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const worker = fork(__filename, ["worker", server.address().port]); - worker.on("message", msg => { - if (msg !== "stop-listening") return; - server.close(() => { - worker.send("stopped-listening"); - }); - }); - resolve(); - }); - }); - }); -} else if (id === "worker") { - test("worker process", async () => { - let server = net.createServer(() => { - throw new Error("Server should not receive connections"); - }); - - await expect( - new Promise((resolve, reject) => { - server.listen(port, () => reject(new Error("Server should not listen"))); - server.on("error", resolve); - }), - ).resolves.toMatchObject({ - code: "EADDRINUSE", - message: expect.any(String), - }); - - process.send("stop-listening"); - - await new Promise(resolve => { - process.once("message", msg => { - if (msg !== "stopped-listening") return; - server = net.createServer(() => { - throw new Error("Server should not receive connections"); - }); - server.listen(port, () => { - server.close(); - resolve(); - }); - }); - }); - }); -} else { - test("invalid argument", () => { - expect(() => { - throw new Error("Bad argument"); - }).toThrow("Bad argument"); - }); -} - -//<#END_FILE: test-cluster-eaddrinuse.js diff --git a/test/js/node/test/parallel/cluster-listen-pipe-readable-writable.test.js b/test/js/node/test/parallel/cluster-listen-pipe-readable-writable.test.js deleted file mode 100644 index ea008eda16..0000000000 --- a/test/js/node/test/parallel/cluster-listen-pipe-readable-writable.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-cluster-listen-pipe-readable-writable.js -//#SHA1: ec8b0021cb41214529af900b088dce4d31db708d -//----------------- -"use strict"; - -const cluster = require("cluster"); -const net = require("net"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const PIPE = path.join(os.tmpdir(), "test.sock"); - -if (process.platform === "win32") { - test.skip("skip on Windows", () => {}); -} else { - if (cluster.isPrimary) { - test("cluster worker can listen on pipe with readable and writable permissions", () => { - const worker = cluster.fork(); - worker.on("exit", code => { - expect(code).toBe(0); - }); - }); - } else { - test("server listens on pipe with correct permissions", done => { - const server = net.createServer().listen( - { - path: PIPE, - readableAll: true, - writableAll: true, - }, - () => { - const stat = fs.statSync(PIPE); - expect(stat.mode & 0o777).toBe(0o777); - server.close(); - process.disconnect(); - done(); - }, - ); - }); - } -} - -//<#END_FILE: test-cluster-listen-pipe-readable-writable.js diff --git a/test/js/node/test/parallel/cluster-send-handle-twice.test.js b/test/js/node/test/parallel/cluster-send-handle-twice.test.js deleted file mode 100644 index 06cb47d583..0000000000 --- a/test/js/node/test/parallel/cluster-send-handle-twice.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-cluster-send-handle-twice.js -//#SHA1: d667e299035da70aa7831cb6964ba4e974c6bdc8 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Testing to send an handle twice to the primary process. - -const cluster = require("cluster"); -const net = require("net"); - -const workers = { - toStart: 1, -}; - -if (cluster.isPrimary) { - test("primary process", () => { - for (let i = 0; i < workers.toStart; ++i) { - const worker = cluster.fork(); - worker.on("exit", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - }); - } - }); -} else { - test("worker process", async () => { - const server = net.createServer(socket => { - process.send("send-handle-1", socket); - process.send("send-handle-2", socket); - }); - - await new Promise((resolve, reject) => { - server.listen(0, () => { - const client = net.connect({ - host: "localhost", - port: server.address().port, - }); - client.on("close", () => { - cluster.worker.disconnect(); - resolve(); - }); - client.on("connect", () => { - client.end(); - }); - }); - - server.on("error", e => { - console.error(e); - reject(new Error("server.listen failed")); - }); - }); - }); -} - -//<#END_FILE: test-cluster-send-handle-twice.js diff --git a/test/js/node/test/parallel/cluster-setup-primary-cumulative.test.js b/test/js/node/test/parallel/cluster-setup-primary-cumulative.test.js deleted file mode 100644 index 178c7f77ab..0000000000 --- a/test/js/node/test/parallel/cluster-setup-primary-cumulative.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-cluster-setup-primary-cumulative.js -//#SHA1: 8a64228ac6d42c930b2426bbc53009f5d8b96a17 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const assert = require("assert"); -const cluster = require("cluster"); - -test("cluster setup primary cumulative", () => { - expect(cluster.isPrimary).toBe(true); - - // cluster.settings should not be initialized until needed - expect(cluster.settings).toEqual({}); - - cluster.setupPrimary(); - expect(cluster.settings).toEqual({ - args: process.argv.slice(2), - exec: process.argv[1], - execArgv: process.execArgv, - silent: false, - }); - - cluster.setupPrimary({ exec: "overridden" }); - expect(cluster.settings.exec).toBe("overridden"); - - cluster.setupPrimary({ args: ["foo", "bar"] }); - expect(cluster.settings.exec).toBe("overridden"); - expect(cluster.settings.args).toEqual(["foo", "bar"]); - - cluster.setupPrimary({ execArgv: ["baz", "bang"] }); - expect(cluster.settings.exec).toBe("overridden"); - expect(cluster.settings.args).toEqual(["foo", "bar"]); - expect(cluster.settings.execArgv).toEqual(["baz", "bang"]); - - cluster.setupPrimary(); - expect(cluster.settings).toEqual({ - args: ["foo", "bar"], - exec: "overridden", - execArgv: ["baz", "bang"], - silent: false, - }); -}); - -//<#END_FILE: test-cluster-setup-primary-cumulative.js diff --git a/test/js/node/test/parallel/cluster-setup-primary-emit.test.js b/test/js/node/test/parallel/cluster-setup-primary-emit.test.js deleted file mode 100644 index dc036034bd..0000000000 --- a/test/js/node/test/parallel/cluster-setup-primary-emit.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-cluster-setup-primary-emit.js -//#SHA1: 965f86ef2ea557510e956759d3f540edfa7b03de -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); - -expect(cluster.isPrimary).toBe(true); - -function emitAndCatch(next) { - return new Promise(resolve => { - cluster.once("setup", settings => { - expect(settings.exec).toBe("new-exec"); - setImmediate(() => { - next(); - resolve(); - }); - }); - cluster.setupPrimary({ exec: "new-exec" }); - }); -} - -function emitAndCatch2(next) { - return new Promise(resolve => { - cluster.once("setup", settings => { - expect(settings).toHaveProperty("exec"); - setImmediate(() => { - next(); - resolve(); - }); - }); - cluster.setupPrimary(); - }); -} - -test("cluster setup primary emit", async () => { - const nextSpy1 = jest.fn(); - const nextSpy2 = jest.fn(); - - await emitAndCatch(nextSpy1); - expect(nextSpy1).toHaveBeenCalledTimes(1); - - await emitAndCatch2(nextSpy2); - expect(nextSpy2).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-cluster-setup-primary-emit.js diff --git a/test/js/node/test/parallel/cluster-setup-primary-multiple.test.js b/test/js/node/test/parallel/cluster-setup-primary-multiple.test.js deleted file mode 100644 index 05a3ca9cf5..0000000000 --- a/test/js/node/test/parallel/cluster-setup-primary-multiple.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-cluster-setup-primary-multiple.js -//#SHA1: a0b16cb2b01b0265f98508f2a6a9974396b6b03a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); -const debug = require("util").debuglog("test"); - -test("cluster setup primary multiple times", async () => { - expect(cluster.isPrimary).toBe(true); - - // The cluster.settings object is cloned even though the current implementation - // makes that unnecessary. This is to make the test less fragile if the - // implementation ever changes such that cluster.settings is mutated instead of - // replaced. - const cheapClone = obj => JSON.parse(JSON.stringify(obj)); - - const configs = []; - - // Capture changes - cluster.on("setup", () => { - debug(`"setup" emitted ${JSON.stringify(cluster.settings)}`); - configs.push(cheapClone(cluster.settings)); - }); - - const execs = ["node-next", "node-next-2", "node-next-3"]; - - // Make changes to cluster settings - for (let i = 0; i < execs.length; i++) { - await new Promise(resolve => { - setTimeout(() => { - cluster.setupPrimary({ exec: execs[i] }); - resolve(); - }, i * 100); - }); - } - - // Cluster emits 'setup' asynchronously, so we must stay alive long - // enough for that to happen - await new Promise(resolve => { - setTimeout( - () => { - debug("cluster setup complete"); - resolve(); - }, - (execs.length + 1) * 100, - ); - }); - - // Tests that "setup" is emitted for every call to setupPrimary - expect(configs.length).toBe(execs.length); - - expect(configs[0].exec).toBe(execs[0]); - expect(configs[1].exec).toBe(execs[1]); - expect(configs[2].exec).toBe(execs[2]); -}); - -//<#END_FILE: test-cluster-setup-primary-multiple.js diff --git a/test/js/node/test/parallel/cluster-worker-constructor.test.js b/test/js/node/test/parallel/cluster-worker-constructor.test.js deleted file mode 100644 index 470481560e..0000000000 --- a/test/js/node/test/parallel/cluster-worker-constructor.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-cluster-worker-constructor.js -//#SHA1: ef3237d09cf6339e487f14c40d4c047c6871ead2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// test-cluster-worker-constructor.js -// validates correct behavior of the cluster.Worker constructor - -const cluster = require("cluster"); - -describe("cluster.Worker constructor", () => { - test("creates a worker with default values", () => { - const worker = new cluster.Worker(); - expect(worker.exitedAfterDisconnect).toBeUndefined(); - expect(worker.state).toBe("none"); - expect(worker.id).toBe(0); - expect(worker.process).toBeUndefined(); - }); - - test("creates a worker with custom values", () => { - const worker = new cluster.Worker({ - id: 3, - state: "online", - process: process, - }); - expect(worker.exitedAfterDisconnect).toBeUndefined(); - expect(worker.state).toBe("online"); - expect(worker.id).toBe(3); - expect(worker.process).toBe(process); - }); - - test("creates a worker using call method", () => { - const worker = cluster.Worker.call({}, { id: 5 }); - expect(worker).toBeInstanceOf(cluster.Worker); - expect(worker.id).toBe(5); - }); -}); - -//<#END_FILE: test-cluster-worker-constructor.js diff --git a/test/js/node/test/parallel/cluster-worker-destroy.test.js b/test/js/node/test/parallel/cluster-worker-destroy.test.js deleted file mode 100644 index 607cbbb866..0000000000 --- a/test/js/node/test/parallel/cluster-worker-destroy.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-cluster-worker-destroy.js -//#SHA1: 277a85b7c8fdda347d1f753601ac4b843a9a1c8d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// The goal of this test is to cover the Workers' implementation of -// Worker.prototype.destroy. Worker.prototype.destroy is called within -// the worker's context: once when the worker is still connected to the -// primary, and another time when it's not connected to it, so that we cover -// both code paths. - -const assert = require("assert"); -const cluster = require("cluster"); - -let worker1, worker2; - -if (cluster.isPrimary) { - test("primary process", () => { - worker1 = cluster.fork(); - worker2 = cluster.fork(); - - [worker1, worker2].forEach(worker => { - const disconnectSpy = jest.fn(); - const exitSpy = jest.fn(); - - worker.on("disconnect", disconnectSpy); - worker.on("exit", exitSpy); - - worker.on("exit", () => { - expect(disconnectSpy).toHaveBeenCalledTimes(1); - expect(exitSpy).toHaveBeenCalledTimes(1); - }); - }); - }); -} else if (cluster.worker.id === 1) { - test("worker 1: call destroy when worker is disconnected", () => { - // Call destroy when worker is disconnected - cluster.worker.process.on("disconnect", () => { - cluster.worker.destroy(); - }); - - const w = cluster.worker.disconnect(); - expect(w).toBe(cluster.worker); - }); -} else { - test("worker 2: call destroy when worker is not disconnected yet", () => { - // Call destroy when worker is not disconnected yet - cluster.worker.destroy(); - }); -} - -//<#END_FILE: test-cluster-worker-destroy.js diff --git a/test/js/node/test/parallel/cluster-worker-handle-close.test.js b/test/js/node/test/parallel/cluster-worker-handle-close.test.js deleted file mode 100644 index f6f608e0ac..0000000000 --- a/test/js/node/test/parallel/cluster-worker-handle-close.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-cluster-worker-handle-close.js -//#SHA1: 8aa4bcd8641fe9274b97853b80734ea6f18eafbb -//----------------- -"use strict"; -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - test("Primary process", () => { - cluster.schedulingPolicy = cluster.SCHED_RR; - expect(cluster.fork).not.toThrow(); - }); -} else { - let server; - - beforeAll(() => { - server = net.createServer(jest.fn()); - }); - - test("Worker process", done => { - const serverListenSpy = jest.spyOn(server, "listen"); - const netConnectSpy = jest.spyOn(net, "connect"); - - server.listen(0, () => { - expect(serverListenSpy).toHaveBeenCalledTimes(1); - expect(netConnectSpy).toHaveBeenCalledWith(server.address().port); - done(); - }); - }); - - test("Internal message handling", done => { - const handleCloseSpy = jest.fn(callback => callback()); - const handle = { close: handleCloseSpy }; - - const messageHandler = (message, messageHandle) => { - if (message.act !== "newconn") { - return; - } - - server.close(); - messageHandle.close = jest.fn(() => { - handleCloseSpy.call(messageHandle, () => { - expect(handleCloseSpy).toHaveBeenCalledTimes(1); - process.exit(); - }); - }); - - expect(messageHandle.close).toHaveBeenCalledTimes(1); - done(); - }; - - process.prependListener("internalMessage", messageHandler); - - // Simulate an internal message - process.emit("internalMessage", { act: "newconn" }, handle); - }); -} - -//<#END_FILE: test-cluster-worker-handle-close.js diff --git a/test/js/node/test/parallel/cluster-worker-isconnected.test.js b/test/js/node/test/parallel/cluster-worker-isconnected.test.js deleted file mode 100644 index 4ca42ebe61..0000000000 --- a/test/js/node/test/parallel/cluster-worker-isconnected.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-cluster-worker-isconnected.js -//#SHA1: cf1e0243c030fe4cf872716099e517daec3efffc -//----------------- -"use strict"; -const cluster = require("cluster"); - -if (cluster.isPrimary) { - test("worker isConnected() in primary", () => { - const worker = cluster.fork(); - - expect(worker.isConnected()).toBe(true); - - worker.on("disconnect", () => { - expect(worker.isConnected()).toBe(false); - }); - - worker.on("message", function (msg) { - if (msg === "readyToDisconnect") { - worker.disconnect(); - } - }); - }); -} else { - test("worker isConnected() in worker", () => { - function assertNotConnected() { - expect(cluster.worker.isConnected()).toBe(false); - } - - expect(cluster.worker.isConnected()).toBe(true); - - cluster.worker.on("disconnect", assertNotConnected); - cluster.worker.process.on("disconnect", assertNotConnected); - - process.send("readyToDisconnect"); - }); -} - -//<#END_FILE: test-cluster-worker-isconnected.js diff --git a/test/js/node/test/parallel/common-countdown.test.js b/test/js/node/test/parallel/common-countdown.test.js deleted file mode 100644 index 24c268588e..0000000000 --- a/test/js/node/test/parallel/common-countdown.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-common-countdown.js -//#SHA1: ba753878e7b8cbeaede6057bc05a7d3b542949a5 -//----------------- -"use strict"; - -const assert = require("assert"); -const Countdown = require("../common/countdown"); -const fixtures = require("../common/fixtures"); -const { execFile } = require("child_process"); - -test("Countdown functionality", () => { - let done = ""; - const countdown = new Countdown(2, () => (done = true)); - expect(countdown.remaining).toBe(2); - countdown.dec(); - expect(countdown.remaining).toBe(1); - countdown.dec(); - expect(countdown.remaining).toBe(0); - expect(done).toBe(true); -}); - -const failFixtures = [ - [fixtures.path("failcounter.js"), "Mismatched function calls. Expected exactly 1, actual 0."], -]; - -test.each(failFixtures)("Fail fixture: %s", async (file, expected) => { - await new Promise(resolve => { - execFile(process.argv[0], [file], (ex, stdout, stderr) => { - expect(ex).toBeTruthy(); - expect(stderr).toBe(""); - const firstLine = stdout.split("\n").shift(); - expect(firstLine).toBe(expected); - resolve(); - }); - }); -}); - -//<#END_FILE: test-common-countdown.js diff --git a/test/js/node/test/parallel/console-assign-undefined.test.js b/test/js/node/test/parallel/console-assign-undefined.test.js deleted file mode 100644 index cc46f41b42..0000000000 --- a/test/js/node/test/parallel/console-assign-undefined.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-console-assign-undefined.js -//#SHA1: ccd5cd3087520e692e5123679c1753d168d310f0 -//----------------- -"use strict"; - -// Patch global.console before importing modules that may modify the console -// object. - -let originalConsole; - -beforeAll(() => { - originalConsole = global.console; - global.console = 42; -}); - -afterAll(() => { - // Reset the console - global.console = originalConsole; -}); - -test("console can be assigned a non-object value", () => { - // Originally the console had a getter. Test twice to verify it had no side - // effect. - expect(global.console).toBe(42); - expect(global.console).toBe(42); - - expect(() => console.log("foo")).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - - global.console = 1; - expect(global.console).toBe(1); - expect(console).toBe(1); -}); - -test("console can be reset and used", () => { - global.console = originalConsole; - const consoleSpy = jest.spyOn(console, "log"); - console.log("foo"); - expect(consoleSpy).toHaveBeenCalledWith("foo"); - consoleSpy.mockRestore(); -}); - -//<#END_FILE: test-console-assign-undefined.js diff --git a/test/js/node/test/parallel/console-issue-43095.test.js b/test/js/node/test/parallel/console-issue-43095.test.js deleted file mode 100644 index 815c2d86ad..0000000000 --- a/test/js/node/test/parallel/console-issue-43095.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-console-issue-43095.js -//#SHA1: 1c0c5cce62bcee4d50c6b716dd1430db2784c3f4 -//----------------- -"use strict"; - -const { inspect } = require("node:util"); - -test("console output for revoked proxy", () => { - const consoleSpy = { - dir: jest.spyOn(console, "dir").mockImplementation(), - log: jest.spyOn(console, "log").mockImplementation(), - }; - - const r = Proxy.revocable({}, {}); - r.revoke(); - - console.dir(r); - console.dir(r.proxy); - console.log(r.proxy); - console.log(inspect(r.proxy, { showProxy: true })); - - expect(consoleSpy.dir).toHaveBeenCalledTimes(2); - expect(consoleSpy.log).toHaveBeenCalledTimes(2); - - // Check that console.dir was called with the revoked proxy object - expect(consoleSpy.dir.mock.calls[0][0]).toBe(r); - expect(consoleSpy.dir.mock.calls[1][0]).toBe(r.proxy); - - // Check that console.log was called with the revoked proxy - expect(consoleSpy.log.mock.calls[0][0]).toBe(r.proxy); - - // Check that console.log was called with the inspected revoked proxy - expect(consoleSpy.log.mock.calls[1][0]).toBe(inspect(r.proxy, { showProxy: true })); - - // Clean up - consoleSpy.dir.mockRestore(); - consoleSpy.log.mockRestore(); -}); - -//<#END_FILE: test-console-issue-43095.js diff --git a/test/js/node/test/parallel/console-log-stdio-broken-dest.test.js b/test/js/node/test/parallel/console-log-stdio-broken-dest.test.js deleted file mode 100644 index b93dfe3824..0000000000 --- a/test/js/node/test/parallel/console-log-stdio-broken-dest.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-console-log-stdio-broken-dest.js -//#SHA1: c2c2e85eeb28db4ace2c4bb0a86f46f7e7bf2682 -//----------------- -"use strict"; - -const { Writable } = require("stream"); -const { Console } = require("console"); -const { EventEmitter } = require("events"); - -test("Console log with broken destination", done => { - const stream = new Writable({ - write(chunk, enc, cb) { - cb(); - }, - writev(chunks, cb) { - setTimeout(cb, 10, new Error("kaboom")); - }, - }); - const myConsole = new Console(stream, stream); - - const warningListener = jest.fn(); - process.on("warning", warningListener); - - stream.cork(); - for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { - myConsole.log("a message"); - } - stream.uncork(); - - // We need to wait for the next tick to ensure the error has time to propagate - process.nextTick(() => { - expect(warningListener).not.toHaveBeenCalled(); - process.removeListener("warning", warningListener); - done(); - }); -}); - -//<#END_FILE: test-console-log-stdio-broken-dest.js diff --git a/test/js/node/test/parallel/console-log-throw-primitive.test.js b/test/js/node/test/parallel/console-log-throw-primitive.test.js deleted file mode 100644 index e318eaaf4d..0000000000 --- a/test/js/node/test/parallel/console-log-throw-primitive.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-console-log-throw-primitive.js -//#SHA1: a1889badf1058f6fadc8984a5075f3d048e2948c -//----------------- -"use strict"; - -const { Writable } = require("stream"); -const { Console } = require("console"); - -test("Console.log should not throw when stream throws null", () => { - const stream = new Writable({ - write() { - throw null; // eslint-disable-line no-throw-literal - }, - }); - - const console = new Console({ stdout: stream }); - - // Should not throw - expect(() => console.log("test")).not.toThrow(); -}); - -//<#END_FILE: test-console-log-throw-primitive.js diff --git a/test/js/node/test/parallel/console-self-assign.test.js b/test/js/node/test/parallel/console-self-assign.test.js deleted file mode 100644 index e746e9a47a..0000000000 --- a/test/js/node/test/parallel/console-self-assign.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-console-self-assign.js -//#SHA1: 7ed2fd07a18f0485f2592ada8e97e9c33753e691 -//----------------- -"use strict"; - -// Assigning to itself should not throw. -test("console self-assignment", () => { - expect(() => { - global.console = global.console; // eslint-disable-line no-self-assign - }).not.toThrow(); -}); - -//<#END_FILE: test-console-self-assign.js diff --git a/test/js/node/test/parallel/crypto-dh-shared.test.js b/test/js/node/test/parallel/crypto-dh-shared.test.js deleted file mode 100644 index f811ca4df7..0000000000 --- a/test/js/node/test/parallel/crypto-dh-shared.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-crypto-dh-shared.js -//#SHA1: 8d5e31de4aa93f435c4c6d05d7b394156a38fb8e -//----------------- -"use strict"; - -const crypto = require("crypto"); - -test("Diffie-Hellman shared secret computation", () => { - const alice = crypto.createDiffieHellmanGroup("modp5"); - const bob = crypto.createDiffieHellmanGroup("modp5"); - - alice.generateKeys(); - bob.generateKeys(); - - const aSecret = alice.computeSecret(bob.getPublicKey()).toString("hex"); - const bSecret = bob.computeSecret(alice.getPublicKey()).toString("hex"); - - expect(aSecret).toBe(bSecret); -}); - -//<#END_FILE: test-crypto-dh-shared.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-elliptic-curve-jwk-rsa.test.js b/test/js/node/test/parallel/crypto-keygen-async-elliptic-curve-jwk-rsa.test.js deleted file mode 100644 index f19582afbd..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-elliptic-curve-jwk-rsa.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-crypto-keygen-async-elliptic-curve-jwk-rsa.js -//#SHA1: 4337f1e36680f21ed3439d7ab546ea855a0a842e -//----------------- -"use strict"; - -const { generateKeyPair } = require("crypto"); - -// Test async elliptic curve key generation with 'jwk' encoding and RSA. -test("async elliptic curve key generation with jwk encoding and RSA", async () => { - const { publicKey, privateKey } = await new Promise((resolve, reject) => { - generateKeyPair( - "rsa", - { - modulusLength: 1024, - publicKeyEncoding: { - format: "jwk", - }, - privateKeyEncoding: { - format: "jwk", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }); - - expect(typeof publicKey).toBe("object"); - expect(typeof privateKey).toBe("object"); - expect(publicKey.kty).toBe("RSA"); - expect(publicKey.kty).toBe(privateKey.kty); - expect(typeof publicKey.n).toBe("string"); - expect(publicKey.n).toBe(privateKey.n); - expect(typeof publicKey.e).toBe("string"); - expect(publicKey.e).toBe(privateKey.e); - expect(typeof privateKey.d).toBe("string"); - expect(typeof privateKey.p).toBe("string"); - expect(typeof privateKey.q).toBe("string"); - expect(typeof privateKey.dp).toBe("string"); - expect(typeof privateKey.dq).toBe("string"); - expect(typeof privateKey.qi).toBe("string"); -}); - -//<#END_FILE: test-crypto-keygen-async-elliptic-curve-jwk-rsa.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-encrypted-private-key-der.test.js b/test/js/node/test/parallel/crypto-keygen-async-encrypted-private-key-der.test.js deleted file mode 100644 index 01e5b30494..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-encrypted-private-key-der.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-crypto-keygen-async-encrypted-private-key-der.js -//#SHA1: 30f86c68619f3f24294d5f062eed48b13c116b0c -//----------------- -"use strict"; - -const crypto = require("crypto"); -const { assertApproximateSize, testEncryptDecrypt, testSignVerify } = require("../common/crypto"); - -// Test async RSA key generation with an encrypted private key, but encoded as DER. -test("RSA key generation with encrypted private key (DER)", async () => { - const { publicKey: publicKeyDER, privateKey: privateKeyDER } = await new Promise((resolve, reject) => { - crypto.generateKeyPair( - "rsa", - { - publicExponent: 0x10001, - modulusLength: 512, - publicKeyEncoding: { - type: "pkcs1", - format: "der", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "der", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }); - - expect(Buffer.isBuffer(publicKeyDER)).toBe(true); - assertApproximateSize(publicKeyDER, 74); - - expect(Buffer.isBuffer(privateKeyDER)).toBe(true); - - const publicKey = { - key: publicKeyDER, - type: "pkcs1", - format: "der", - }; - const privateKey = { - key: privateKeyDER, - format: "der", - type: "pkcs8", - passphrase: "secret", - }; - - await testEncryptDecrypt(publicKey, privateKey); - await testSignVerify(publicKey, privateKey); -}); - -//<#END_FILE: test-crypto-keygen-async-encrypted-private-key-der.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-explicit-elliptic-curve.test.js b/test/js/node/test/parallel/crypto-keygen-async-explicit-elliptic-curve.test.js deleted file mode 100644 index 4d6f637147..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-explicit-elliptic-curve.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-crypto-keygen-async-explicit-elliptic-curve.js -//#SHA1: be1eabf816f52e5f53cb2535d47050b2552d21cd -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto support is not available -if (!crypto.generateKeyPair) { - test.skip("missing crypto support", () => {}); -} else { - const { generateKeyPair } = crypto; - - const { testSignVerify, spkiExp, sec1Exp } = require("../common/crypto"); - - // Test async explicit elliptic curve key generation, e.g. for ECDSA, - // with a SEC1 private key with paramEncoding explicit. - test("async explicit elliptic curve key generation", async () => { - await new Promise((resolve, reject) => { - generateKeyPair( - "ec", - { - namedCurve: "prime256v1", - paramEncoding: "explicit", - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "sec1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }).then(({ publicKey, privateKey }) => { - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(spkiExp); - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(sec1Exp); - - return testSignVerify(publicKey, privateKey); - }); - }); -} - -//<#END_FILE: test-crypto-keygen-async-explicit-elliptic-curve.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-named-elliptic-curve.test.js b/test/js/node/test/parallel/crypto-keygen-async-named-elliptic-curve.test.js deleted file mode 100644 index 8721a4551f..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-named-elliptic-curve.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-crypto-keygen-async-named-elliptic-curve.js -//#SHA1: 77822175b9b2c2206ec4ab8a3e1182e3576b23bd -//----------------- -"use strict"; - -const crypto = require("crypto"); -const { testSignVerify } = require("../common/crypto"); - -if (!crypto.generateKeyPair) { - test.skip("missing crypto.generateKeyPair"); -} - -const spkiExp = - /^-----BEGIN PUBLIC KEY-----\n(?:[A-Za-z0-9+/=]{64}\n)*[A-Za-z0-9+/=]{1,64}\n-----END PUBLIC KEY-----\n$/; -const sec1Exp = - /^-----BEGIN EC PRIVATE KEY-----\n(?:[A-Za-z0-9+/=]{64}\n)*[A-Za-z0-9+/=]{1,64}\n-----END EC PRIVATE KEY-----\n$/; - -// Test async named elliptic curve key generation, e.g. for ECDSA, -// with a SEC1 private key. -test("async named elliptic curve key generation with SEC1 private key", async () => { - const { publicKey, privateKey } = await new Promise((resolve, reject) => { - crypto.generateKeyPair( - "ec", - { - namedCurve: "prime256v1", - paramEncoding: "named", - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "sec1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }); - - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(spkiExp); - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(sec1Exp); - - await testSignVerify(publicKey, privateKey); -}); - -//<#END_FILE: test-crypto-keygen-async-named-elliptic-curve.js diff --git a/test/js/node/test/parallel/crypto-keygen-empty-passphrase-no-error.test.js b/test/js/node/test/parallel/crypto-keygen-empty-passphrase-no-error.test.js deleted file mode 100644 index ed029ce08d..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-empty-passphrase-no-error.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-crypto-keygen-empty-passphrase-no-error.js -//#SHA1: a949b38385a5dd05507975cbc44b3beba764bd95 -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto is not available -if (typeof crypto.generateKeyPair !== "function") { - test.skip("missing crypto", () => {}); -} else { - test("generateKeyPair with empty passphrase should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE", async () => { - // Passing an empty passphrase string should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE even on OpenSSL 3. - // Regression test for https://github.com/nodejs/node/issues/41428. - await expect( - new Promise((resolve, reject) => { - crypto.generateKeyPair( - "rsa", - { - modulusLength: 1024, - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "pem", - cipher: "aes-256-cbc", - passphrase: "", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }), - ).resolves.toEqual( - expect.objectContaining({ - publicKey: expect.any(String), - privateKey: expect.any(String), - }), - ); - }); -} - -//<#END_FILE: test-crypto-keygen-empty-passphrase-no-error.js diff --git a/test/js/node/test/parallel/crypto-keygen-key-object-without-encoding.test.js b/test/js/node/test/parallel/crypto-keygen-key-object-without-encoding.test.js deleted file mode 100644 index 53d01c929f..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-key-object-without-encoding.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-crypto-keygen-key-object-without-encoding.js -//#SHA1: da408ed128f913dc7343ef88743b362c16420ba0 -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto is not available -if (!crypto.generateKeyPair) { - test.skip("missing crypto"); -} - -const { generateKeyPair } = crypto; - -// Helper functions for testing encryption/decryption and signing/verifying -const testEncryptDecrypt = (publicKey, privateKey) => { - const plaintext = "Hello, World!"; - const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(plaintext)); - const decrypted = crypto.privateDecrypt(privateKey, encrypted); - expect(decrypted.toString()).toBe(plaintext); -}; - -const testSignVerify = (publicKey, privateKey) => { - const data = "Hello, World!"; - const sign = crypto.createSign("SHA256"); - sign.update(data); - const signature = sign.sign(privateKey); - const verify = crypto.createVerify("SHA256"); - verify.update(data); - expect(verify.verify(publicKey, signature)).toBe(true); -}; - -// Tests key objects are returned when key encodings are not specified. -describe("generateKeyPair without encoding", () => { - // If no publicKeyEncoding is specified, a key object should be returned. - test("returns key object for public key when no encoding specified", done => { - generateKeyPair( - "rsa", - { - modulusLength: 1024, - privateKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - expect(err).toBe(null); - expect(typeof publicKey).toBe("object"); - expect(publicKey.type).toBe("public"); - expect(publicKey.asymmetricKeyType).toBe("rsa"); - - // The private key should still be a string. - expect(typeof privateKey).toBe("string"); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - done(); - }, - ); - }); - - // If no privateKeyEncoding is specified, a key object should be returned. - test("returns key object for private key when no encoding specified", done => { - generateKeyPair( - "rsa", - { - modulusLength: 1024, - publicKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - expect(err).toBe(null); - // The public key should still be a string. - expect(typeof publicKey).toBe("string"); - - expect(typeof privateKey).toBe("object"); - expect(privateKey.type).toBe("private"); - expect(privateKey.asymmetricKeyType).toBe("rsa"); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - done(); - }, - ); - }); -}); - -//<#END_FILE: test-crypto-keygen-key-object-without-encoding.js diff --git a/test/js/node/test/parallel/crypto-keygen-key-objects.test.js b/test/js/node/test/parallel/crypto-keygen-key-objects.test.js deleted file mode 100644 index 29d4bbe5f8..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-key-objects.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-crypto-keygen-key-objects.js -//#SHA1: 7a2dce611ba70e533ebb73d9f281896bdf75051f -//----------------- -"use strict"; - -if (!process.versions.bun) { - const common = require("../common"); - if (!common.hasCrypto) common.skip("missing crypto"); -} - -const { generateKeyPairSync } = require("crypto"); - -// Test sync key generation with key objects. -test("generateKeyPairSync with RSA", () => { - const { publicKey, privateKey } = generateKeyPairSync("rsa", { - modulusLength: 512, - }); - - expect(typeof publicKey).toBe("object"); - expect(publicKey.type).toBe("public"); - expect(publicKey.asymmetricKeyType).toBe("rsa"); - expect(publicKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 65537n, - }); - - expect(typeof privateKey).toBe("object"); - expect(privateKey.type).toBe("private"); - expect(privateKey.asymmetricKeyType).toBe("rsa"); - expect(privateKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 65537n, - }); -}); - -//<#END_FILE: test-crypto-keygen-key-objects.js diff --git a/test/js/node/test/parallel/crypto-keygen-missing-oid.test.js b/test/js/node/test/parallel/crypto-keygen-missing-oid.test.js deleted file mode 100644 index b91248c3a6..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-missing-oid.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-crypto-keygen-missing-oid.js -//#SHA1: ffcbc53b115cce8795ca1a0e73a533b04789a930 -//----------------- -"use strict"; - -const { generateKeyPair, generateKeyPairSync, getCurves } = require("crypto"); - -// This test creates EC key pairs on curves without associated OIDs. -// Specifying a key encoding should not crash. -test("EC key pairs on curves without associated OIDs", () => { - if (process.versions.openssl >= "1.1.1i") { - const curves = ["Oakley-EC2N-3", "Oakley-EC2N-4"]; - const availableCurves = getCurves(); - - for (const namedCurve of curves) { - if (!availableCurves.includes(namedCurve)) continue; - - const expectedErrorCode = process.versions.openssl.startsWith("3.") - ? "ERR_OSSL_MISSING_OID" - : "ERR_OSSL_EC_MISSING_OID"; - - const params = { - namedCurve, - publicKeyEncoding: { - format: "der", - type: "spki", - }, - }; - - expect(() => { - generateKeyPairSync("ec", params); - }).toThrow( - expect.objectContaining({ - code: expectedErrorCode, - message: expect.any(String), - }), - ); - - return new Promise(resolve => { - generateKeyPair("ec", params, err => { - expect(err).toMatchObject({ - code: expectedErrorCode, - message: expect.any(String), - }); - resolve(); - }); - }); - } - } else { - // Skip test if OpenSSL version is less than 1.1.1i - test.skip("OpenSSL version is less than 1.1.1i"); - } -}); - -//<#END_FILE: test-crypto-keygen-missing-oid.js diff --git a/test/js/node/test/parallel/crypto-keygen-non-standard-public-exponent.test.js b/test/js/node/test/parallel/crypto-keygen-non-standard-public-exponent.test.js deleted file mode 100644 index 46fc35ffdb..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-non-standard-public-exponent.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-crypto-keygen-non-standard-public-exponent.js -//#SHA1: 955e956a08102b75fcf0571213a1dd939d1f51ac -//----------------- -"use strict"; - -const crypto = require("crypto"); - -if (!crypto.generateKeyPairSync) { - test.skip("missing crypto.generateKeyPairSync"); -} - -// Test sync key generation with key objects with a non-standard -// publicExponent -test("generateKeyPairSync with non-standard publicExponent", () => { - const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", { - publicExponent: 3, - modulusLength: 512, - }); - - expect(typeof publicKey).toBe("object"); - expect(publicKey.type).toBe("public"); - expect(publicKey.asymmetricKeyType).toBe("rsa"); - expect(publicKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 3n, - }); - - expect(typeof privateKey).toBe("object"); - expect(privateKey.type).toBe("private"); - expect(privateKey.asymmetricKeyType).toBe("rsa"); - expect(privateKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 3n, - }); -}); - -//<#END_FILE: test-crypto-keygen-non-standard-public-exponent.js diff --git a/test/js/node/test/parallel/crypto-keygen-promisify.test.js b/test/js/node/test/parallel/crypto-keygen-promisify.test.js deleted file mode 100644 index 845d68a970..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-promisify.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-crypto-keygen-promisify.js -//#SHA1: 70eb7089a04950a2ce28a3af9949105eefd420fa -//----------------- -"use strict"; - -const crypto = require("crypto"); -const util = require("util"); - -const { - assertApproximateSize, - testEncryptDecrypt, - testSignVerify, - pkcs1PubExp, - pkcs1PrivExp, -} = require("../common/crypto"); - -// Skip the test if crypto support is not available -if (!crypto.generateKeyPair) { - test.skip("missing crypto support", () => {}); -} else { - // Test the util.promisified API with async RSA key generation. - test("util.promisified generateKeyPair with RSA", async () => { - const generateKeyPairPromise = util.promisify(crypto.generateKeyPair); - const keys = await generateKeyPairPromise("rsa", { - publicExponent: 0x10001, - modulusLength: 512, - publicKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - }); - - const { publicKey, privateKey } = keys; - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(pkcs1PubExp); - assertApproximateSize(publicKey, 180); - - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(pkcs1PrivExp); - assertApproximateSize(privateKey, 512); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - }); -} - -//<#END_FILE: test-crypto-keygen-promisify.js diff --git a/test/js/node/test/parallel/crypto-keygen-sync.test.js b/test/js/node/test/parallel/crypto-keygen-sync.test.js deleted file mode 100644 index 26d3a218fb..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-sync.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-crypto-keygen-sync.js -//#SHA1: 57749dc903b0d5f9b64a3d61f313be6e5549323f -//----------------- -"use strict"; - -const crypto = require("crypto"); -const { - assertApproximateSize, - testEncryptDecrypt, - testSignVerify, - pkcs1PubExp, - pkcs8Exp, -} = require("../common/crypto"); - -// Skip the test if crypto support is not available -if (typeof crypto.generateKeyPairSync !== "function") { - test.skip("missing crypto support", () => {}); -} else { - // To make the test faster, we will only test sync key generation once and - // with a relatively small key. - test("generateKeyPairSync", () => { - const ret = crypto.generateKeyPairSync("rsa", { - publicExponent: 3, - modulusLength: 512, - publicKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "pem", - }, - }); - - expect(Object.keys(ret)).toHaveLength(2); - const { publicKey, privateKey } = ret; - - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(pkcs1PubExp); - assertApproximateSize(publicKey, 162); - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(pkcs8Exp); - assertApproximateSize(privateKey, 512); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - }); -} - -//<#END_FILE: test-crypto-keygen-sync.js diff --git a/test/js/node/test/parallel/crypto-lazy-transform-writable.test.js b/test/js/node/test/parallel/crypto-lazy-transform-writable.test.js deleted file mode 100644 index 6df20ee83f..0000000000 --- a/test/js/node/test/parallel/crypto-lazy-transform-writable.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-crypto-lazy-transform-writable.js -//#SHA1: 29f694c4ea89a94302b3aa84677b5e41c73077d7 -//----------------- -"use strict"; - -const crypto = require("crypto"); -const Stream = require("stream"); - -if (!crypto) it.skip("missing crypto", () => {}); - -test("crypto lazy transform writable", done => { - const hasher1 = crypto.createHash("sha256"); - const hasher2 = crypto.createHash("sha256"); - - // Calculate the expected result. - hasher1.write(Buffer.from("hello world")); - hasher1.end(); - - const expected = hasher1.read().toString("hex"); - - class OldStream extends Stream { - constructor() { - super(); - this.readable = true; - } - } - - const stream = new OldStream(); - - stream.pipe(hasher2).on("finish", () => { - const hash = hasher2.read().toString("hex"); - expect(hash).toBe(expected); - done(); - }); - - stream.emit("data", Buffer.from("hello")); - stream.emit("data", Buffer.from(" world")); - stream.emit("end"); -}); - -//<#END_FILE: test-crypto-lazy-transform-writable.js diff --git a/test/js/node/test/parallel/crypto-padding-aes256.test.js b/test/js/node/test/parallel/crypto-padding-aes256.test.js deleted file mode 100644 index 079e7a15b6..0000000000 --- a/test/js/node/test/parallel/crypto-padding-aes256.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-crypto-padding-aes256.js -//#SHA1: 96fb5beb94bedbc768788ba2726dcd0e61733c5a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const crypto = require("crypto"); - -const iv = Buffer.from("00000000000000000000000000000000", "hex"); -const key = Buffer.from("0123456789abcdef0123456789abcdef" + "0123456789abcdef0123456789abcdef", "hex"); - -function encrypt(val, pad) { - const c = crypto.createCipheriv("aes256", key, iv); - c.setAutoPadding(pad); - return c.update(val, "utf8", "latin1") + c.final("latin1"); -} - -function decrypt(val, pad) { - const c = crypto.createDecipheriv("aes256", key, iv); - c.setAutoPadding(pad); - return c.update(val, "latin1", "utf8") + c.final("utf8"); -} - -test("AES256 encryption and decryption with no padding (multiple of block size)", () => { - // echo 0123456789abcdef0123456789abcdef \ - // | openssl enc -e -aes256 -nopad -K -iv \ - // | openssl enc -d -aes256 -nopad -K -iv - const plaintext = "0123456789abcdef0123456789abcdef"; // Multiple of block size - const encrypted = encrypt(plaintext, false); - const decrypted = decrypt(encrypted, false); - expect(decrypted).toBe(plaintext); -}); - -test("AES256 encryption and decryption with padding (not a multiple of block size)", () => { - // echo 0123456789abcdef0123456789abcde \ - // | openssl enc -e -aes256 -K -iv \ - // | openssl enc -d -aes256 -K -iv - const plaintext = "0123456789abcdef0123456789abcde"; // not a multiple - const encrypted = encrypt(plaintext, true); - const decrypted = decrypt(encrypted, true); - expect(decrypted).toBe(plaintext); -}); - -//<#END_FILE: test-crypto-padding-aes256.js diff --git a/test/js/node/test/parallel/crypto-randomfillsync-regression.test.js b/test/js/node/test/parallel/crypto-randomfillsync-regression.test.js deleted file mode 100644 index aa57030f2a..0000000000 --- a/test/js/node/test/parallel/crypto-randomfillsync-regression.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-crypto-randomfillsync-regression.js -//#SHA1: f37bc7cc1eab82ab93665b246c0d44e9fce8d112 -//----------------- -"use strict"; - -// Skip the test if crypto is not available -let randomFillSync; -try { - ({ randomFillSync } = require("crypto")); -} catch { - test.skip("missing crypto", () => {}); -} - -if (randomFillSync) { - test("randomFillSync regression test", () => { - const ab = new ArrayBuffer(20); - const buf = Buffer.from(ab, 10); - - const before = buf.toString("hex"); - - randomFillSync(buf); - - const after = buf.toString("hex"); - - expect(before).not.toBe(after); - }); -} - -//<#END_FILE: test-crypto-randomfillsync-regression.js diff --git a/test/js/node/test/parallel/crypto-subtle-zero-length.test.js b/test/js/node/test/parallel/crypto-subtle-zero-length.test.js deleted file mode 100644 index 9113969480..0000000000 --- a/test/js/node/test/parallel/crypto-subtle-zero-length.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-crypto-subtle-zero-length.js -//#SHA1: aa21bbc5fd9db7bc09dad3ec61cd743d655f5e3b -//----------------- -"use strict"; - -// Skip test if crypto is not available -if (typeof crypto === "undefined" || !crypto.subtle) { - test.skip("missing crypto"); -} - -test("SubtleCrypto with zero-length input", async () => { - const { subtle } = globalThis.crypto; - - const k = await subtle.importKey("raw", new Uint8Array(32), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]); - expect(k).toBeInstanceOf(CryptoKey); - - const e = await subtle.encrypt( - { - name: "AES-GCM", - iv: new Uint8Array(12), - }, - k, - new Uint8Array(0), - ); - expect(e).toBeInstanceOf(ArrayBuffer); - expect(Buffer.from(e)).toEqual( - Buffer.from([0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b]), - ); - - const v = await subtle.decrypt( - { - name: "AES-GCM", - iv: new Uint8Array(12), - }, - k, - e, - ); - expect(v).toBeInstanceOf(ArrayBuffer); - expect(v.byteLength).toBe(0); -}); - -//<#END_FILE: test-crypto-subtle-zero-length.js diff --git a/test/js/node/test/parallel/crypto-update-encoding.test.js b/test/js/node/test/parallel/crypto-update-encoding.test.js deleted file mode 100644 index 1c0d269234..0000000000 --- a/test/js/node/test/parallel/crypto-update-encoding.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-crypto-update-encoding.js -//#SHA1: dfe3c7e71e22a772cf6b2e6a6540be161fda3418 -//----------------- -"use strict"; - -const crypto = require("crypto"); - -const zeros = Buffer.alloc; -const key = zeros(16); -const iv = zeros(16); - -const cipher = () => crypto.createCipheriv("aes-128-cbc", key, iv); -const decipher = () => crypto.createDecipheriv("aes-128-cbc", key, iv); -const hash = () => crypto.createSign("sha256"); -const hmac = () => crypto.createHmac("sha256", key); -const sign = () => crypto.createSign("sha256"); -const verify = () => crypto.createVerify("sha256"); - -test("crypto update ignores inputEncoding for Buffer input", () => { - const functions = [cipher, decipher, hash, hmac, sign, verify]; - const sizes = [15, 16]; - - functions.forEach(f => { - sizes.forEach(n => { - const instance = f(); - expect(() => { - instance.update(zeros(n), "hex"); - }).not.toThrow(); - }); - }); -}); - -//<#END_FILE: test-crypto-update-encoding.js diff --git a/test/js/node/test/parallel/crypto-webcrypto-aes-decrypt-tag-too-small.test.js b/test/js/node/test/parallel/crypto-webcrypto-aes-decrypt-tag-too-small.test.js deleted file mode 100644 index de241c13af..0000000000 --- a/test/js/node/test/parallel/crypto-webcrypto-aes-decrypt-tag-too-small.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-crypto-webcrypto-aes-decrypt-tag-too-small.js -//#SHA1: e58d2e4e7dcfc3a29a6e9acbe177f32a1d6bf280 -//----------------- -"use strict"; - -if (!globalThis.crypto?.subtle) { - test.skip("missing crypto"); -} - -test("AES-GCM decrypt with tag too small", async () => { - const { subtle } = globalThis.crypto; - - const key = await subtle.importKey( - "raw", - new Uint8Array(32), - { - name: "AES-GCM", - }, - false, - ["encrypt", "decrypt"], - ); - - await expect( - subtle.decrypt( - { - name: "AES-GCM", - iv: new Uint8Array(12), - }, - key, - new Uint8Array(0), - ), - ).rejects.toThrow( - expect.objectContaining({ - name: "OperationError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-crypto-webcrypto-aes-decrypt-tag-too-small.js diff --git a/test/js/node/test/parallel/dgram-abort-closed.test.js b/test/js/node/test/parallel/dgram-abort-closed.test.js deleted file mode 100644 index eb09ac67eb..0000000000 --- a/test/js/node/test/parallel/dgram-abort-closed.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-dgram-abort-closed.js -//#SHA1: 8d3ab4d13dda99cdccb6994f165f2ddacf58360c -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("AbortController with closed dgram socket", () => { - const controller = new AbortController(); - const socket = dgram.createSocket({ type: "udp4", signal: controller.signal }); - - socket.close(); - - // This should not throw or cause any issues - expect(() => { - controller.abort(); - }).not.toThrow(); -}); - -//<#END_FILE: test-dgram-abort-closed.js diff --git a/test/js/node/test/parallel/dgram-bind-default-address.test.js b/test/js/node/test/parallel/dgram-bind-default-address.test.js deleted file mode 100644 index 542f335c6e..0000000000 --- a/test/js/node/test/parallel/dgram-bind-default-address.test.js +++ /dev/null @@ -1,82 +0,0 @@ -//#FILE: test-dgram-bind-default-address.js -//#SHA1: f29269b15b1205e37cc43e02b76cc5d8eb3b70be -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -// Skip test in FreeBSD jails since 0.0.0.0 will resolve to default interface -const inFreeBSDJail = process.platform === "freebsd" && process.env.CI === "true"; -if (inFreeBSDJail) { - test.skip("In a FreeBSD jail"); -} - -test("UDP4 socket bind to default address", async () => { - const socket = dgram.createSocket("udp4"); - - await new Promise(resolve => { - socket.bind(0, () => { - const address = socket.address(); - expect(typeof address.port).toBe("number"); - expect(isFinite(address.port)).toBe(true); - expect(address.port).toBeGreaterThan(0); - expect(address.address).toBe("0.0.0.0"); - socket.close(); - resolve(); - }); - }); -}); - -const hasIPv6 = (() => { - try { - const socket = dgram.createSocket("udp6"); - socket.close(); - return true; - } catch { - return false; - } -})(); - -if (!hasIPv6) { - test.skip("udp6 part of test, because no IPv6 support"); -} else { - test("UDP6 socket bind to default address", async () => { - const socket = dgram.createSocket("udp6"); - - await new Promise(resolve => { - socket.bind(0, () => { - const address = socket.address(); - expect(typeof address.port).toBe("number"); - expect(isFinite(address.port)).toBe(true); - expect(address.port).toBeGreaterThan(0); - let addressValue = address.address; - if (addressValue === "::ffff:0.0.0.0") addressValue = "::"; - expect(addressValue).toBe("::"); - socket.close(); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-dgram-bind-default-address.js diff --git a/test/js/node/test/parallel/dgram-close-in-listening.test.js b/test/js/node/test/parallel/dgram-close-in-listening.test.js deleted file mode 100644 index e669bea76c..0000000000 --- a/test/js/node/test/parallel/dgram-close-in-listening.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-dgram-close-in-listening.js -//#SHA1: b37e742b092d70824b67c4ad4d3e1bb17a8c5cd5 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram socket closed before sendQueue is drained does not crash", done => { - const buf = Buffer.alloc(1024, 42); - - const socket = dgram.createSocket("udp4"); - - socket.on("listening", function () { - socket.close(); - }); - - // Get a random port for send - const portGetter = dgram.createSocket("udp4").bind(0, "localhost", () => { - // Adds a listener to 'listening' to send the data when - // the socket is available - socket.send(buf, 0, buf.length, portGetter.address().port, portGetter.address().address); - - portGetter.close(); - done(); // Signal test completion - }); -}); - -//<#END_FILE: test-dgram-close-in-listening.js diff --git a/test/js/node/test/parallel/dgram-close.test.js b/test/js/node/test/parallel/dgram-close.test.js deleted file mode 100644 index fe89cc0f66..0000000000 --- a/test/js/node/test/parallel/dgram-close.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-dgram-close.js -//#SHA1: c396ba7a9c9ef45206989b36e4b5db0b95503e38 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Flags: --expose-internals -"use strict"; -// Ensure that if a dgram socket is closed before the DNS lookup completes, it -// won't crash. - -const dgram = require("dgram"); - -const buf = Buffer.alloc(1024, 42); - -test("dgram socket close before DNS lookup completes", done => { - let socket = dgram.createSocket("udp4"); - - // Get a random port for send - const portGetter = dgram.createSocket("udp4"); - - portGetter.bind(0, "localhost", () => { - socket.send(buf, 0, buf.length, portGetter.address().port, portGetter.address().address); - - expect(socket.close()).toBe(socket); - - socket.on("close", () => { - socket = null; - - // Verify that accessing handle after closure doesn't throw - setImmediate(() => { - setImmediate(() => { - // We can't access internal symbols, so we'll just check if this doesn't throw - expect(() => { - console.log("Handle fd is: ", "placeholder"); - }).not.toThrow(); - - portGetter.close(); - done(); - }); - }); - }); - }); -}); - -//<#END_FILE: test-dgram-close.js diff --git a/test/js/node/test/parallel/dgram-cluster-close-in-listening.test.js b/test/js/node/test/parallel/dgram-cluster-close-in-listening.test.js deleted file mode 100644 index 3a882a49d2..0000000000 --- a/test/js/node/test/parallel/dgram-cluster-close-in-listening.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-dgram-cluster-close-in-listening.js -//#SHA1: f288642fce76ef0138f8e44cd8eb09ded9dc4640 -//----------------- -"use strict"; -// Ensure that closing dgram sockets in 'listening' callbacks of cluster workers -// won't throw errors. - -const dgram = require("dgram"); -const cluster = require("cluster"); - -if (process.platform === "win32") { - it.skip("dgram clustering is currently not supported on windows.", () => {}); -} else { - if (cluster.isPrimary) { - test("Primary cluster forks workers", () => { - for (let i = 0; i < 3; i += 1) { - expect(() => cluster.fork()).not.toThrow(); - } - }); - } else { - test("Worker handles dgram socket lifecycle", done => { - const socket = dgram.createSocket("udp4"); - - socket.on("error", () => { - done(new Error("Error event should not be called")); - }); - - socket.on("listening", () => { - socket.close(); - }); - - socket.on("close", () => { - cluster.worker.disconnect(); - done(); - }); - - socket.bind(0); - }); - } -} - -//<#END_FILE: test-dgram-cluster-close-in-listening.js diff --git a/test/js/node/test/parallel/dgram-connect-send-callback-multi-buffer.test.js b/test/js/node/test/parallel/dgram-connect-send-callback-multi-buffer.test.js deleted file mode 100644 index 2d014e97c6..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-callback-multi-buffer.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-dgram-connect-send-callback-multi-buffer.js -//#SHA1: f30fbed996bbcd2adb268e9e3412a5f83119f8ae -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram connect send callback multi buffer", done => { - const client = dgram.createSocket("udp4"); - - const messageSent = jest.fn((err, bytes) => { - expect(bytes).toBe(buf1.length + buf2.length); - }); - - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", () => { - const port = client.address().port; - client.connect(port, () => { - client.send([buf1, buf2], messageSent); - }); - }); - - client.on("message", (buf, info) => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - client.close(); - expect(messageSent).toHaveBeenCalledTimes(1); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-connect-send-callback-multi-buffer.js diff --git a/test/js/node/test/parallel/dgram-connect-send-default-host.test.js b/test/js/node/test/parallel/dgram-connect-send-default-host.test.js deleted file mode 100644 index 6e597bae7c..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-default-host.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-dgram-connect-send-default-host.js -//#SHA1: 78d734d664f2bf2f6376846bba7c909d8253c4dc -//----------------- -"use strict"; - -const dgram = require("dgram"); - -const toSend = [Buffer.alloc(256, "x"), Buffer.alloc(256, "y"), Buffer.alloc(256, "z"), "hello"]; - -const received = []; - -test("dgram connect and send with default host", async () => { - const client = dgram.createSocket("udp4"); - const server = dgram.createSocket("udp4"); - - const serverListening = new Promise(resolve => { - server.on("listening", resolve); - }); - - server.on("message", (buf, info) => { - received.push(buf.toString()); - - if (received.length === toSend.length * 2) { - // The replies may arrive out of order -> sort them before checking. - received.sort(); - - const expected = toSend.concat(toSend).map(String).sort(); - expect(received).toEqual(expected); - client.close(); - server.close(); - } - }); - - server.bind(0); - - await serverListening; - - const port = server.address().port; - await new Promise((resolve, reject) => { - client.connect(port, err => { - if (err) reject(err); - else resolve(); - }); - }); - - client.send(toSend[0], 0, toSend[0].length); - client.send(toSend[1]); - client.send([toSend[2]]); - client.send(toSend[3], 0, toSend[3].length); - - client.send(new Uint8Array(toSend[0]), 0, toSend[0].length); - client.send(new Uint8Array(toSend[1])); - client.send([new Uint8Array(toSend[2])]); - client.send(new Uint8Array(Buffer.from(toSend[3])), 0, toSend[3].length); - - // Wait for all messages to be received - await new Promise(resolve => { - const checkInterval = setInterval(() => { - if (received.length === toSend.length * 2) { - clearInterval(checkInterval); - resolve(); - } - }, 100); - }); - - expect(received.length).toBe(toSend.length * 2); -}); - -//<#END_FILE: test-dgram-connect-send-default-host.js diff --git a/test/js/node/test/parallel/dgram-connect-send-empty-array.test.js b/test/js/node/test/parallel/dgram-connect-send-empty-array.test.js deleted file mode 100644 index 32b665f932..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-empty-array.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-dgram-connect-send-empty-array.js -//#SHA1: 81de5b211c0e3be3158d2c06178577f39e62f0d1 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram.connect() and send empty array", () => { - const client = dgram.createSocket("udp4"); - - expect.assertions(1); - - return new Promise(resolve => { - client.on("message", (buf, info) => { - const expected = Buffer.alloc(0); - expect(buf).toEqual(expected); - client.close(); - resolve(); - }); - - client.on("listening", () => { - client.connect(client.address().port, "127.0.0.1", () => client.send([])); - }); - - client.bind(0); - }); -}); - -//<#END_FILE: test-dgram-connect-send-empty-array.js diff --git a/test/js/node/test/parallel/dgram-connect-send-empty-buffer.test.js b/test/js/node/test/parallel/dgram-connect-send-empty-buffer.test.js deleted file mode 100644 index 7c4209dbf1..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-empty-buffer.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-dgram-connect-send-empty-buffer.js -//#SHA1: 08e8b667af8e6f97e6df2c95360a3a3aec05d435 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram connect and send empty buffer", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - const port = client.address().port; - client.connect(port, () => { - const buf = Buffer.alloc(0); - client.send(buf, 0, 0, err => { - expect(err).toBeNull(); - }); - }); - - client.on("message", buffer => { - expect(buffer.length).toBe(0); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-dgram-connect-send-empty-buffer.js diff --git a/test/js/node/test/parallel/dgram-connect-send-empty-packet.test.js b/test/js/node/test/parallel/dgram-connect-send-empty-packet.test.js deleted file mode 100644 index d5f56ddb5d..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-empty-packet.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-dgram-connect-send-empty-packet.js -//#SHA1: 107d20a1e7a2628097091471ffdad75fc714b1fb -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram connect and send empty packet", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - expect.hasAssertions(); - client.connect(client.address().port, () => { - client.on("message", callback); - const buf = Buffer.alloc(1); - - const interval = setInterval(() => { - client.send(buf, 0, 0, callback); - }, 10); - - function callback(firstArg) { - // If client.send() callback, firstArg should be null. - // If client.on('message') listener, firstArg should be a 0-length buffer. - if (firstArg instanceof Buffer) { - expect(firstArg.length).toBe(0); - clearInterval(interval); - client.close(); - done(); - } - } - }); - }); -}); - -//<#END_FILE: test-dgram-connect-send-empty-packet.js diff --git a/test/js/node/test/parallel/dgram-connect-send-multi-string-array.test.js b/test/js/node/test/parallel/dgram-connect-send-multi-string-array.test.js deleted file mode 100644 index 7ae5672c0f..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-multi-string-array.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-dgram-connect-send-multi-string-array.js -//#SHA1: 611c15bc8089ffcae85adaa91bff5031c776a8ab -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram.createSocket can send multi-string array", done => { - const socket = dgram.createSocket("udp4"); - const data = ["foo", "bar", "baz"]; - - socket.on("message", (msg, rinfo) => { - socket.close(); - expect(msg.toString()).toBe(data.join("")); - done(); - }); - - socket.bind(0, () => { - socket.connect(socket.address().port, () => { - socket.send(data); - }); - }); -}); - -//<#END_FILE: test-dgram-connect-send-multi-string-array.js diff --git a/test/js/node/test/parallel/dgram-implicit-bind.test.js b/test/js/node/test/parallel/dgram-implicit-bind.test.js deleted file mode 100644 index d733e2109f..0000000000 --- a/test/js/node/test/parallel/dgram-implicit-bind.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-dgram-implicit-bind.js -//#SHA1: 3b390facaac3ee5e617c9fdc11acdb9f019fabfa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram implicit bind", async () => { - const source = dgram.createSocket("udp4"); - const target = dgram.createSocket("udp4"); - let messages = 0; - - const messageHandler = jest.fn(buf => { - if (buf.toString() === "abc") ++messages; - if (buf.toString() === "def") ++messages; - if (messages === 2) { - source.close(); - target.close(); - } - }); - - target.on("message", messageHandler); - - await new Promise(resolve => { - target.on("listening", resolve); - target.bind(0); - }); - - // Second .send() call should not throw a bind error. - const port = target.address().port; - source.send(Buffer.from("abc"), 0, 3, port, "127.0.0.1"); - source.send(Buffer.from("def"), 0, 3, port, "127.0.0.1"); - - // Wait for the messages to be processed - await new Promise(resolve => setTimeout(resolve, 100)); - - expect(messageHandler).toHaveBeenCalledTimes(2); - expect(messages).toBe(2); -}); - -//<#END_FILE: test-dgram-implicit-bind.js diff --git a/test/js/node/test/parallel/dgram-send-callback-buffer-empty-address.test.js b/test/js/node/test/parallel/dgram-send-callback-buffer-empty-address.test.js deleted file mode 100644 index de86cfc036..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-buffer-empty-address.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-dgram-send-callback-buffer-empty-address.js -//#SHA1: 5c76ad150693dcec8921099fa994f61aa783713c -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram send callback with buffer and empty address", done => { - const client = dgram.createSocket("udp4"); - - const buf = Buffer.alloc(256, "x"); - - const onMessage = jest.fn(bytes => { - expect(bytes).toBe(buf.length); - client.close(); - done(); - }); - - client.bind(0, () => { - client.send(buf, client.address().port, error => { - expect(error).toBeFalsy(); - onMessage(buf.length); - }); - }); -}); - -//<#END_FILE: test-dgram-send-callback-buffer-empty-address.js diff --git a/test/js/node/test/parallel/dgram-send-callback-multi-buffer-empty-address.test.js b/test/js/node/test/parallel/dgram-send-callback-multi-buffer-empty-address.test.js deleted file mode 100644 index 429e4cd9eb..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-multi-buffer-empty-address.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-dgram-send-callback-multi-buffer-empty-address.js -//#SHA1: 61d00ee31b25f144989e0d3ced884a70f4e7d07a -//----------------- -"use strict"; - -const dgram = require("dgram"); - -let client; - -beforeEach(() => { - client = dgram.createSocket("udp4"); -}); - -afterEach(() => { - client.close(); -}); - -test("send callback multi buffer empty address", done => { - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", function () { - const port = this.address().port; - client.send([buf1, buf2], port, (err, bytes) => { - expect(err).toBeNull(); - expect(bytes).toBe(buf1.length + buf2.length); - }); - }); - - client.on("message", buf => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-callback-multi-buffer-empty-address.js diff --git a/test/js/node/test/parallel/dgram-send-callback-multi-buffer.test.js b/test/js/node/test/parallel/dgram-send-callback-multi-buffer.test.js deleted file mode 100644 index 36cf0d1eaa..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-multi-buffer.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-dgram-send-callback-multi-buffer.js -//#SHA1: 622d513f7897c216601b50a2960a8a36259b2595 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram send callback with multiple buffers", done => { - const client = dgram.createSocket("udp4"); - - const messageSent = jest.fn((err, bytes) => { - expect(err).toBeNull(); - expect(bytes).toBe(buf1.length + buf2.length); - }); - - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", () => { - const port = client.address().port; - client.send([buf1, buf2], port, "localhost", messageSent); - }); - - client.on("message", (buf, info) => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - expect(messageSent).toHaveBeenCalledTimes(1); - client.close(); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-callback-multi-buffer.js diff --git a/test/js/node/test/parallel/dgram-send-callback-recursive.test.js b/test/js/node/test/parallel/dgram-send-callback-recursive.test.js deleted file mode 100644 index e42d990c9f..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-recursive.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-dgram-send-callback-recursive.js -//#SHA1: fac7c8b29bd2122d4de273c54128b5a6100ad437 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -let received = 0; -let sent = 0; -const limit = 10; -let async = false; -let port; -const chunk = "abc"; - -test("dgram send callback recursive", done => { - const client = dgram.createSocket("udp4"); - - function onsend() { - if (sent++ < limit) { - client.send(chunk, 0, chunk.length, port, "127.0.0.1", onsend); - } else { - expect(async).toBe(true); - } - } - - client.on("listening", function () { - port = this.address().port; - - process.nextTick(() => { - async = true; - }); - - onsend(); - }); - - client.on("message", (buf, info) => { - received++; - if (received === limit) { - client.close(); - } - }); - - client.on("close", () => { - expect(received).toBe(limit); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-callback-recursive.js diff --git a/test/js/node/test/parallel/dgram-send-cb-quelches-error.test.js b/test/js/node/test/parallel/dgram-send-cb-quelches-error.test.js deleted file mode 100644 index 96364d73fd..0000000000 --- a/test/js/node/test/parallel/dgram-send-cb-quelches-error.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-dgram-send-cb-quelches-error.js -//#SHA1: 7525b0a8af0df192c36a848b23332424245d2937 -//----------------- -"use strict"; - -const assert = require("assert"); -const dgram = require("dgram"); -const dns = require("dns"); - -test("dgram send callback quelches error", () => { - const socket = dgram.createSocket("udp4"); - const buffer = Buffer.from("gary busey"); - - dns.setServers([]); - - const onEvent = jest.fn(() => { - throw new Error("Error should not be emitted if there is callback"); - }); - - socket.once("error", onEvent); - - // assert that: - // * callbacks act as "error" listeners if given. - // * error is never emitter for missing dns entries - // if a callback that handles error is present - // * error is emitted if a callback with no argument is passed - socket.send(buffer, 0, buffer.length, 100, "dne.example.com", callbackOnly); - - function callbackOnly(err) { - expect(err).toBeTruthy(); - socket.removeListener("error", onEvent); - socket.on("error", onError); - socket.send(buffer, 0, buffer.length, 100, "dne.invalid"); - } - - function onError(err) { - expect(err).toBeTruthy(); - socket.close(); - } - - expect(onEvent).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-dgram-send-cb-quelches-error.js diff --git a/test/js/node/test/parallel/dgram-send-empty-buffer.test.js b/test/js/node/test/parallel/dgram-send-empty-buffer.test.js deleted file mode 100644 index 3dcd6ffe3b..0000000000 --- a/test/js/node/test/parallel/dgram-send-empty-buffer.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-dgram-send-empty-buffer.js -//#SHA1: ac60fc545252e681b648a7038d1bebe46ffbbac0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram send empty buffer", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - const port = client.address().port; - - client.on("message", buffer => { - expect(buffer.length).toBe(0); - clearInterval(interval); - client.close(); - done(); - }); - - const buf = Buffer.alloc(0); - const interval = setInterval(() => { - client.send(buf, 0, 0, port, "127.0.0.1", () => { - // This callback is expected to be called - }); - }, 10); - }); -}); - -//<#END_FILE: test-dgram-send-empty-buffer.js diff --git a/test/js/node/test/parallel/dgram-send-empty-packet.test.js b/test/js/node/test/parallel/dgram-send-empty-packet.test.js deleted file mode 100644 index 07802512c7..0000000000 --- a/test/js/node/test/parallel/dgram-send-empty-packet.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-dgram-send-empty-packet.js -//#SHA1: f39fb8a7245893f0f6f55aeb110d458e2f265013 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("send empty packet", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - client.on("message", jest.fn(callback)); - - const port = client.address().port; - const buf = Buffer.alloc(1); - - const interval = setInterval(() => { - client.send(buf, 0, 0, port, "127.0.0.1", jest.fn(callback)); - }, 10); - - function callback(firstArg) { - // If client.send() callback, firstArg should be null. - // If client.on('message') listener, firstArg should be a 0-length buffer. - if (firstArg instanceof Buffer) { - expect(firstArg.length).toBe(0); - clearInterval(interval); - client.close(); - done(); - } - } - }); -}); - -//<#END_FILE: test-dgram-send-empty-packet.js diff --git a/test/js/node/test/parallel/dgram-send-multi-buffer-copy.test.js b/test/js/node/test/parallel/dgram-send-multi-buffer-copy.test.js deleted file mode 100644 index b9baba6443..0000000000 --- a/test/js/node/test/parallel/dgram-send-multi-buffer-copy.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-dgram-send-multi-buffer-copy.js -//#SHA1: 6adf8291a5dd40cb6a71ad3779f0d26d2150249a -//----------------- -"use strict"; - -const dgram = require("dgram"); - -let client; - -beforeEach(() => { - client = dgram.createSocket("udp4"); -}); - -afterEach(() => { - client.close(); -}); - -test("dgram send multi buffer copy", done => { - const onMessage = jest.fn((err, bytes) => { - expect(bytes).toBe(buf1.length + buf2.length); - }); - - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", function () { - const toSend = [buf1, buf2]; - client.send(toSend, this.address().port, "127.0.0.1", onMessage); - toSend.splice(0, 2); - }); - - client.on("message", (buf, info) => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - expect(onMessage).toHaveBeenCalledTimes(1); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-multi-buffer-copy.js diff --git a/test/js/node/test/parallel/dgram-send-multi-string-array.test.js b/test/js/node/test/parallel/dgram-send-multi-string-array.test.js deleted file mode 100644 index 804ea7c285..0000000000 --- a/test/js/node/test/parallel/dgram-send-multi-string-array.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-dgram-send-multi-string-array.js -//#SHA1: 8ea2007ac52bfde3742aabe352aab19bf91a4ac2 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram send multiple strings as array", done => { - const socket = dgram.createSocket("udp4"); - const data = ["foo", "bar", "baz"]; - - socket.on("message", (msg, rinfo) => { - socket.close(); - expect(msg.toString()).toBe(data.join("")); - done(); - }); - - socket.bind(() => { - socket.send(data, socket.address().port, "localhost"); - }); -}); - -//<#END_FILE: test-dgram-send-multi-string-array.js diff --git a/test/js/node/test/parallel/dgram-sendto.test.js b/test/js/node/test/parallel/dgram-sendto.test.js deleted file mode 100644 index e3b3d88c2a..0000000000 --- a/test/js/node/test/parallel/dgram-sendto.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-dgram-sendto.js -//#SHA1: 8047210c86bed6536f5ff3132e228a2ee9d0bb11 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -describe("dgram.sendto", () => { - let socket; - - beforeEach(() => { - socket = dgram.createSocket("udp4"); - }); - - afterEach(() => { - socket.close(); - }); - - test("throws when called with no arguments", () => { - expect(() => socket.sendto()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "length" argument is invalid', () => { - expect(() => socket.sendto("buffer", 1, "offset", "port", "address", "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "offset" argument is invalid', () => { - expect(() => socket.sendto("buffer", "offset", 1, "port", "address", "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "address" argument is invalid', () => { - expect(() => socket.sendto("buffer", 1, 1, 10, false, "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "port" argument is invalid', () => { - expect(() => socket.sendto("buffer", 1, 1, false, "address", "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-dgram-sendto.js diff --git a/test/js/node/test/parallel/dgram-udp4.test.js b/test/js/node/test/parallel/dgram-udp4.test.js deleted file mode 100644 index 27be2e63fc..0000000000 --- a/test/js/node/test/parallel/dgram-udp4.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-dgram-udp4.js -//#SHA1: 588735591046212fc512f69d9001ccb820c57a71 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -const message_to_send = "A message to send"; -const localhostIPv4 = "127.0.0.1"; - -test("UDP4 server and client communication", async () => { - const server = dgram.createSocket("udp4"); - - const serverMessagePromise = new Promise(resolve => { - server.on("message", (msg, rinfo) => { - expect(rinfo.address).toBe(localhostIPv4); - expect(msg.toString()).toBe(message_to_send); - server.send(msg, 0, msg.length, rinfo.port, rinfo.address); - resolve(); - }); - }); - - const listeningPromise = new Promise(resolve => { - server.on("listening", resolve); - }); - - server.bind(0); - - await listeningPromise; - - const client = dgram.createSocket("udp4"); - const port = server.address().port; - - const clientMessagePromise = new Promise(resolve => { - client.on("message", (msg, rinfo) => { - expect(rinfo.address).toBe(localhostIPv4); - expect(rinfo.port).toBe(port); - expect(msg.toString()).toBe(message_to_send); - resolve(); - }); - }); - - client.send(message_to_send, 0, message_to_send.length, port, "localhost"); - - await Promise.all([serverMessagePromise, clientMessagePromise]); - - const clientClosePromise = new Promise(resolve => { - client.on("close", resolve); - }); - - const serverClosePromise = new Promise(resolve => { - server.on("close", resolve); - }); - - client.close(); - server.close(); - - await Promise.all([clientClosePromise, serverClosePromise]); -}); - -//<#END_FILE: test-dgram-udp4.js diff --git a/test/js/node/test/parallel/dgram-unref-in-cluster.test.js b/test/js/node/test/parallel/dgram-unref-in-cluster.test.js deleted file mode 100644 index dcca2428e3..0000000000 --- a/test/js/node/test/parallel/dgram-unref-in-cluster.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-dgram-unref-in-cluster.js -//#SHA1: eca71c33b1bf5be34a28e6cc82df49c73e775153 -//----------------- -"use strict"; - -const dgram = require("dgram"); -const cluster = require("cluster"); - -if (process.platform === "win32") { - test.skip("dgram clustering is currently not supported on Windows."); -} else { - if (cluster.isPrimary) { - test("dgram unref in cluster", () => { - cluster.fork(); - }); - } else { - test("dgram unref in cluster worker", () => { - const socket = dgram.createSocket("udp4"); - socket.unref(); - socket.bind(); - - return new Promise(resolve => { - socket.on("listening", () => { - const sockets = process.getActiveResourcesInfo().filter(item => { - return item === "UDPWrap"; - }); - expect(sockets.length).toBe(0); - process.disconnect(); - resolve(); - }); - }); - }); - } -} - -//<#END_FILE: test-dgram-unref-in-cluster.js diff --git a/test/js/node/test/parallel/diagnostics-channel-has-subscribers.test.js b/test/js/node/test/parallel/diagnostics-channel-has-subscribers.test.js deleted file mode 100644 index 2e2f119c73..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-has-subscribers.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-diagnostics-channel-has-subscribers.js -//#SHA1: 8040abda8d37916d6f6d5f3966e8c531d1770e1a -//----------------- -"use strict"; - -const { channel, hasSubscribers } = require("diagnostics_channel"); - -describe("diagnostics_channel", () => { - test("hasSubscribers returns correct state", () => { - const dc = channel("test"); - expect(hasSubscribers("test")).toBe(false); - - dc.subscribe(() => {}); - expect(hasSubscribers("test")).toBe(true); - }); -}); - -//<#END_FILE: test-diagnostics-channel-has-subscribers.js diff --git a/test/js/node/test/parallel/diagnostics-channel-http-server-start.test.js b/test/js/node/test/parallel/diagnostics-channel-http-server-start.test.js deleted file mode 100644 index a5446eec21..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-http-server-start.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-diagnostics-channel-http-server-start.js -//#SHA1: 5540b17246152983b832ddafa0be55502ee83a23 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); -const http = require("http"); - -const als = new AsyncLocalStorage(); -let context; - -describe("diagnostics_channel http server start", () => { - let server; - let request; - let response; - - beforeAll(() => { - // Bind requests to an AsyncLocalStorage context - dc.subscribe("http.server.request.start", message => { - als.enterWith(message); - context = message; - }); - - // When the request ends, verify the context has been maintained - // and that the messages contain the expected data - dc.subscribe("http.server.response.finish", message => { - const data = { - request, - response, - server, - socket: request.socket, - }; - - // Context is maintained - compare(als.getStore(), context); - - compare(context, data); - compare(message, data); - }); - - server = http.createServer((req, res) => { - request = req; - response = res; - - setTimeout(() => { - res.end("done"); - }, 1); - }); - }); - - afterAll(() => { - server.close(); - }); - - it("should maintain context and contain expected data", done => { - server.listen(() => { - const { port } = server.address(); - http.get(`http://localhost:${port}`, res => { - res.resume(); - res.on("end", () => { - done(); - }); - }); - }); - }); -}); - -function compare(a, b) { - expect(a.request).toBe(b.request); - expect(a.response).toBe(b.response); - expect(a.socket).toBe(b.socket); - expect(a.server).toBe(b.server); -} - -//<#END_FILE: test-diagnostics-channel-http-server-start.js diff --git a/test/js/node/test/parallel/diagnostics-channel-object-channel-pub-sub.test.js b/test/js/node/test/parallel/diagnostics-channel-object-channel-pub-sub.test.js deleted file mode 100644 index ae95ea487b..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-object-channel-pub-sub.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-diagnostics-channel-object-channel-pub-sub.js -//#SHA1: 185a8134da179dfda5f6b2d1a1a2622752431a90 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); -const { Channel } = dc; - -const input = { - foo: "bar", -}; - -test("Channel creation and subscription", () => { - // Should not have named channel - expect(dc.hasSubscribers("test")).toBe(false); - - // Individual channel objects can be created to avoid future lookups - const channel = dc.channel("test"); - expect(channel).toBeInstanceOf(Channel); - - // No subscribers yet, should not publish - expect(channel.hasSubscribers).toBe(false); - - const subscriber = jest.fn((message, name) => { - expect(name).toBe(channel.name); - expect(message).toEqual(input); - }); - - // Now there's a subscriber, should publish - channel.subscribe(subscriber); - expect(channel.hasSubscribers).toBe(true); - - // The ActiveChannel prototype swap should not fail instanceof - expect(channel).toBeInstanceOf(Channel); - - // Should trigger the subscriber once - channel.publish(input); - expect(subscriber).toHaveBeenCalledTimes(1); - - // Should not publish after subscriber is unsubscribed - expect(channel.unsubscribe(subscriber)).toBe(true); - expect(channel.hasSubscribers).toBe(false); - - // unsubscribe() should return false when subscriber is not found - expect(channel.unsubscribe(subscriber)).toBe(false); -}); - -test("Invalid subscriber", () => { - const channel = dc.channel("test"); - expect(() => { - channel.subscribe(null); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-diagnostics-channel-object-channel-pub-sub.js diff --git a/test/js/node/test/parallel/diagnostics-channel-pub-sub.test.js b/test/js/node/test/parallel/diagnostics-channel-pub-sub.test.js deleted file mode 100644 index f20d19d136..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-pub-sub.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-diagnostics-channel-pub-sub.js -//#SHA1: cc5eee7f44117c2fd8446e8050ac19f5341f85a5 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); -const { Channel } = dc; - -const name = "test"; -const input = { - foo: "bar", -}; - -test("diagnostics_channel pub/sub functionality", () => { - // Individual channel objects can be created to avoid future lookups - const channel = dc.channel(name); - expect(channel).toBeInstanceOf(Channel); - - // No subscribers yet, should not publish - expect(channel.hasSubscribers).toBeFalsy(); - - const subscriber = jest.fn((message, channelName) => { - expect(channelName).toBe(channel.name); - expect(message).toEqual(input); - }); - - // Now there's a subscriber, should publish - dc.subscribe(name, subscriber); - expect(channel.hasSubscribers).toBeTruthy(); - - // The ActiveChannel prototype swap should not fail instanceof - expect(channel).toBeInstanceOf(Channel); - - // Should trigger the subscriber once - channel.publish(input); - expect(subscriber).toHaveBeenCalledTimes(1); - - // Should not publish after subscriber is unsubscribed - expect(dc.unsubscribe(name, subscriber)).toBeTruthy(); - expect(channel.hasSubscribers).toBeFalsy(); - - // unsubscribe() should return false when subscriber is not found - expect(dc.unsubscribe(name, subscriber)).toBeFalsy(); - - expect(() => { - dc.subscribe(name, null); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - // Reaching zero subscribers should not delete from the channels map as there - // will be no more weakref to incRef if another subscribe happens while the - // channel object itself exists. - channel.subscribe(subscriber); - channel.unsubscribe(subscriber); - channel.subscribe(subscriber); -}); - -//<#END_FILE: test-diagnostics-channel-pub-sub.js diff --git a/test/js/node/test/parallel/diagnostics-channel-symbol-named.test.js b/test/js/node/test/parallel/diagnostics-channel-symbol-named.test.js deleted file mode 100644 index d2d6065687..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-symbol-named.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-diagnostics-channel-symbol-named.js -//#SHA1: e0ae87b803333891439e11fa2306eefd23b507e4 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const input = { - foo: "bar", -}; - -const symbol = Symbol("test"); - -// Individual channel objects can be created to avoid future lookups -const channel = dc.channel(symbol); - -test("diagnostics channel with symbol name", () => { - // Expect two successful publishes later - const subscriber = jest.fn((message, name) => { - expect(name).toBe(symbol); - expect(message).toEqual(input); - }); - - channel.subscribe(subscriber); - - channel.publish(input); - - expect(subscriber).toHaveBeenCalledTimes(1); -}); - -test("channel creation with invalid argument", () => { - expect(() => { - dc.channel(null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-diagnostics-channel-symbol-named.js diff --git a/test/js/node/test/parallel/diagnostics-channel-sync-unsubscribe.test.js b/test/js/node/test/parallel/diagnostics-channel-sync-unsubscribe.test.js deleted file mode 100644 index eb5ceea25b..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-sync-unsubscribe.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-diagnostics-channel-sync-unsubscribe.js -//#SHA1: 85d73bd9ce82a4293daca05617b2aece359745ff -//----------------- -"use strict"; - -const dc = require("node:diagnostics_channel"); - -const channel_name = "test:channel"; -const published_data = "some message"; - -test("diagnostics channel sync unsubscribe", () => { - const onMessageHandler = jest.fn(() => dc.unsubscribe(channel_name, onMessageHandler)); - - dc.subscribe(channel_name, onMessageHandler); - - // This must not throw. - expect(() => { - dc.channel(channel_name).publish(published_data); - }).not.toThrow(); - - expect(onMessageHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-sync-unsubscribe.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-callback-run-stores.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-callback-run-stores.test.js deleted file mode 100644 index 15fcdedaf9..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-callback-run-stores.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-callback-run-stores.js -//#SHA1: 8ed3a87eb9d6c1a3a624245ce5f430b7e3730d2d -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); -const store = new AsyncLocalStorage(); - -const firstContext = { foo: "bar" }; -const secondContext = { baz: "buz" }; - -test("tracing channel callback run stores", async () => { - const startBindStoreMock = jest.fn(() => firstContext); - const asyncStartBindStoreMock = jest.fn(() => secondContext); - - channel.start.bindStore(store, startBindStoreMock); - channel.asyncStart.bindStore(store, asyncStartBindStoreMock); - - expect(store.getStore()).toBeUndefined(); - - await new Promise(resolve => { - channel.traceCallback( - cb => { - expect(store.getStore()).toEqual(firstContext); - setImmediate(cb); - }, - 0, - {}, - null, - () => { - expect(store.getStore()).toEqual(secondContext); - resolve(); - }, - ); - }); - - expect(store.getStore()).toBeUndefined(); - expect(startBindStoreMock).toHaveBeenCalledTimes(1); - expect(asyncStartBindStoreMock).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-callback-run-stores.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise-run-stores.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise-run-stores.test.js deleted file mode 100644 index 6c75c54789..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise-run-stores.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-promise-run-stores.js -//#SHA1: 4e5abb65d92cb3e649073803971180493c1a30ca -//----------------- -"use strict"; - -const { setTimeout } = require("node:timers/promises"); -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); -const store = new AsyncLocalStorage(); - -const firstContext = { foo: "bar" }; -const secondContext = { baz: "buz" }; - -test("tracingChannel promise run stores", async () => { - const startBindStoreMock = jest.fn(() => firstContext); - const asyncStartBindStoreMock = jest.fn(() => secondContext); - - channel.start.bindStore(store, startBindStoreMock); - channel.asyncStart.bindStore(store, asyncStartBindStoreMock); - - expect(store.getStore()).toBeUndefined(); - - await channel.tracePromise(async () => { - expect(store.getStore()).toEqual(firstContext); - await setTimeout(1); - // Should _not_ switch to second context as promises don't have an "after" - // point at which to do a runStores. - expect(store.getStore()).toEqual(firstContext); - }); - - expect(store.getStore()).toBeUndefined(); - - expect(startBindStoreMock).toHaveBeenCalledTimes(1); - expect(asyncStartBindStoreMock).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-promise-run-stores.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise.test.js deleted file mode 100644 index 032a552cb6..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-promise.js -//#SHA1: 6a692d8400685da6c930b662562adb3e36b2da9a -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); - -const expectedResult = { foo: "bar" }; -const input = { foo: "bar" }; -const thisArg = { baz: "buz" }; - -function check(found) { - expect(found).toEqual(input); -} - -function checkAsync(found) { - check(found); - expect(found.error).toBeUndefined(); - expect(found.result).toEqual(expectedResult); -} - -const handlers = { - start: jest.fn(check), - end: jest.fn(check), - asyncStart: jest.fn(checkAsync), - asyncEnd: jest.fn(checkAsync), - error: jest.fn(), -}; - -test("diagnostics_channel tracing channel promise", async () => { - channel.subscribe(handlers); - - await channel.tracePromise( - function (value) { - expect(this).toEqual(thisArg); - return Promise.resolve(value); - }, - input, - thisArg, - expectedResult, - ); - - expect(handlers.start).toHaveBeenCalledTimes(1); - expect(handlers.end).toHaveBeenCalledTimes(1); - expect(handlers.asyncStart).toHaveBeenCalledTimes(1); - expect(handlers.asyncEnd).toHaveBeenCalledTimes(1); - expect(handlers.error).not.toHaveBeenCalled(); - - const value = await channel.tracePromise( - function (value) { - expect(this).toEqual(thisArg); - return Promise.resolve(value); - }, - input, - thisArg, - expectedResult, - ); - - expect(value).toEqual(expectedResult); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-promise.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-error.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-error.test.js deleted file mode 100644 index 2906e447cf..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-error.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-sync-error.js -//#SHA1: faf279d48c76f3c97ddf7e258df9ed49154a4cac -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); - -const expectedError = new Error("test"); -const input = { foo: "bar" }; -const thisArg = { baz: "buz" }; - -function check(found) { - expect(found).toEqual(input); -} - -test("traceSync with error", () => { - const handlers = { - start: jest.fn(check), - end: jest.fn(check), - asyncStart: jest.fn(), - asyncEnd: jest.fn(), - error: jest.fn(found => { - check(found); - expect(found.error).toBe(expectedError); - }), - }; - - channel.subscribe(handlers); - - expect(() => { - channel.traceSync( - function (err) { - expect(this).toEqual(thisArg); - expect(err).toBe(expectedError); - throw err; - }, - input, - thisArg, - expectedError, - ); - }).toThrow(expectedError); - - expect(handlers.start).toHaveBeenCalledTimes(1); - expect(handlers.end).toHaveBeenCalledTimes(1); - expect(handlers.asyncStart).not.toHaveBeenCalled(); - expect(handlers.asyncEnd).not.toHaveBeenCalled(); - expect(handlers.error).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-sync-error.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-run-stores.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-run-stores.test.js deleted file mode 100644 index ed7212e92d..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-run-stores.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-sync-run-stores.js -//#SHA1: 51ffe2c7cb7160b565bfe6bbca0d29005a6bf876 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); -const store = new AsyncLocalStorage(); - -const context = { foo: "bar" }; - -test("diagnostics channel tracing channel sync run stores", () => { - const startCallback = jest.fn(() => context); - channel.start.bindStore(store, startCallback); - - expect(store.getStore()).toBeUndefined(); - - const traceCallback = jest.fn(() => { - expect(store.getStore()).toEqual(context); - }); - - channel.traceSync(traceCallback); - - expect(store.getStore()).toBeUndefined(); - - expect(startCallback).toHaveBeenCalledTimes(1); - expect(traceCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-sync-run-stores.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync.test.js deleted file mode 100644 index f4a4c3b923..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync.test.js +++ /dev/null @@ -1,80 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-sync.js -//#SHA1: fad9bfb35032ed67643b026f8e79611d8197a131 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); - -const expectedResult = { foo: "bar" }; -const input = { foo: "bar" }; -const thisArg = { baz: "buz" }; -const arg = { baz: "buz" }; - -function check(found) { - expect(found).toBe(input); -} - -describe("diagnostics_channel tracing channel sync", () => { - const handlers = { - start: jest.fn(check), - end: jest.fn(found => { - check(found); - expect(found.result).toBe(expectedResult); - }), - asyncStart: jest.fn(), - asyncEnd: jest.fn(), - error: jest.fn(), - }; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - test("channel subscription and traceSync", () => { - expect(channel.start.hasSubscribers).toBe(false); - channel.subscribe(handlers); - expect(channel.start.hasSubscribers).toBe(true); - - const result1 = channel.traceSync( - function (arg1) { - expect(arg1).toBe(arg); - expect(this).toBe(thisArg); - return expectedResult; - }, - input, - thisArg, - arg, - ); - - expect(result1).toBe(expectedResult); - expect(handlers.start).toHaveBeenCalledTimes(1); - expect(handlers.end).toHaveBeenCalledTimes(1); - expect(handlers.asyncStart).not.toHaveBeenCalled(); - expect(handlers.asyncEnd).not.toHaveBeenCalled(); - expect(handlers.error).not.toHaveBeenCalled(); - }); - - test("channel unsubscription", () => { - channel.unsubscribe(handlers); - expect(channel.start.hasSubscribers).toBe(false); - - const result2 = channel.traceSync( - function (arg1) { - expect(arg1).toBe(arg); - expect(this).toBe(thisArg); - return expectedResult; - }, - input, - thisArg, - arg, - ); - - expect(result2).toBe(expectedResult); - expect(handlers.start).not.toHaveBeenCalled(); - expect(handlers.end).not.toHaveBeenCalled(); - }); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-sync.js diff --git a/test/js/node/test/parallel/diagnostics-channel-udp.test.js b/test/js/node/test/parallel/diagnostics-channel-udp.test.js deleted file mode 100644 index 449df21666..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-udp.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-diagnostics-channel-udp.js -//#SHA1: 13d46f3f2404ee7cd53a551b90fc1ced191d4a81 -//----------------- -"use strict"; - -const dgram = require("dgram"); -const dc = require("diagnostics_channel"); - -const udpSocketChannel = dc.channel("udp.socket"); - -const isUDPSocket = socket => socket instanceof dgram.Socket; - -test("udp.socket channel emits UDP socket", () => { - const channelCallback = jest.fn(({ socket }) => { - expect(isUDPSocket(socket)).toBe(true); - }); - - udpSocketChannel.subscribe(channelCallback); - - const socket = dgram.createSocket("udp4"); - socket.close(); - - expect(channelCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-udp.js diff --git a/test/js/node/test/parallel/dns-resolve-promises.test.js b/test/js/node/test/parallel/dns-resolve-promises.test.js deleted file mode 100644 index 737ae7e467..0000000000 --- a/test/js/node/test/parallel/dns-resolve-promises.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-dns-resolve-promises.js -//#SHA1: ca56d4fe55a765a3e9db340661c96d8b1fd7a7a9 -//----------------- -"use strict"; - -const dns = require("dns"); - -test("DNS resolve promises error handling", async () => { - // Mock the dns.promises.resolve function to simulate an error - dns.promises.resolve = jest.fn().mockRejectedValue({ - code: "EPERM", - syscall: "queryA", - hostname: "example.org", - }); - - await expect(dns.promises.resolve("example.org")).rejects.toMatchObject({ - code: "EPERM", - syscall: "queryA", - hostname: "example.org", - }); - - expect(dns.promises.resolve).toHaveBeenCalledWith("example.org"); -}); - -//<#END_FILE: test-dns-resolve-promises.js diff --git a/test/js/node/test/parallel/domain-crypto.test.js b/test/js/node/test/parallel/domain-crypto.test.js deleted file mode 100644 index 406da90236..0000000000 --- a/test/js/node/test/parallel/domain-crypto.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-domain-crypto.js -//#SHA1: d7d6352f0f2684220baef0cfa1029278c3f05f8f -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const crypto = require("crypto"); - -// Pollution of global is intentional as part of test. -// See https://github.com/nodejs/node/commit/d1eff9ab -global.domain = require("domain"); - -describe("domain-crypto", () => { - beforeAll(() => { - if (!crypto) { - test.skip("node compiled without OpenSSL."); - } - }); - - test("crypto.randomBytes should not throw", () => { - expect(() => crypto.randomBytes(8)).not.toThrow(); - }); - - test("crypto.randomBytes with callback should succeed", () => { - return new Promise(resolve => { - crypto.randomBytes(8, (err, buffer) => { - expect(err).toBeNull(); - expect(buffer).toBeInstanceOf(Buffer); - expect(buffer.length).toBe(8); - resolve(); - }); - }); - }); - - test("crypto.randomFillSync should not throw", () => { - const buf = Buffer.alloc(8); - expect(() => crypto.randomFillSync(buf)).not.toThrow(); - }); - - test("crypto.pseudoRandomBytes should not throw", () => { - expect(() => crypto.pseudoRandomBytes(8)).not.toThrow(); - }); - - test("crypto.pseudoRandomBytes with callback should succeed", () => { - return new Promise(resolve => { - crypto.pseudoRandomBytes(8, (err, buffer) => { - expect(err).toBeNull(); - expect(buffer).toBeInstanceOf(Buffer); - expect(buffer.length).toBe(8); - resolve(); - }); - }); - }); - - test("crypto.pbkdf2 should succeed", () => { - return new Promise(resolve => { - crypto.pbkdf2("password", "salt", 8, 8, "sha1", (err, derivedKey) => { - expect(err).toBeNull(); - expect(derivedKey).toBeInstanceOf(Buffer); - expect(derivedKey.length).toBe(8); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-domain-crypto.js diff --git a/test/js/node/test/parallel/domain-dep0097.test.js b/test/js/node/test/parallel/domain-dep0097.test.js deleted file mode 100644 index 03a74830e1..0000000000 --- a/test/js/node/test/parallel/domain-dep0097.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-domain-dep0097.js -//#SHA1: d0eeaeed86d045c8deba99d4ca589d35b368f17d -//----------------- -"use strict"; - -// Skip this test if inspector is disabled -if (typeof inspector === "undefined") { - test.skip("Inspector is disabled", () => {}); -} else { - const domain = require("domain"); - const inspector = require("inspector"); - - test("DEP0097 warning is emitted", () => { - const warningHandler = jest.fn(warning => { - expect(warning.code).toBe("DEP0097"); - expect(warning.message).toMatch(/Triggered by calling emit on process/); - }); - - process.on("warning", warningHandler); - - domain.create().run(() => { - inspector.open(0); - }); - - expect(warningHandler).toHaveBeenCalledTimes(1); - - // Clean up - process.removeListener("warning", warningHandler); - }); -} - -//<#END_FILE: test-domain-dep0097.js diff --git a/test/js/node/test/parallel/domain-thrown-error-handler-stack.test.js b/test/js/node/test/parallel/domain-thrown-error-handler-stack.test.js deleted file mode 100644 index f3c8d0b8b7..0000000000 --- a/test/js/node/test/parallel/domain-thrown-error-handler-stack.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-domain-thrown-error-handler-stack.js -//#SHA1: fd9aef2a4c852c0d092708bb6e712ad345cd4d2d -//----------------- -"use strict"; - -const domain = require("domain"); - -// Make sure that when an error is thrown from a nested domain, its error -// handler runs outside of that domain, but within the context of any parent -// domain. - -test("nested domain error handling", () => { - const d = domain.create(); - const d2 = domain.create(); - - d2.on("error", err => { - expect(domain._stack.length).toBe(1); - expect(process.domain).toBe(d); - - process.nextTick(() => { - expect(domain._stack.length).toBe(1); - expect(process.domain).toBe(d); - }); - }); - - expect(() => { - d.run(() => { - d2.run(() => { - throw new Error("oops"); - }); - }); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-domain-thrown-error-handler-stack.js diff --git a/test/js/node/test/parallel/domain-vm-promise-isolation.test.js b/test/js/node/test/parallel/domain-vm-promise-isolation.test.js deleted file mode 100644 index fe9ef2198e..0000000000 --- a/test/js/node/test/parallel/domain-vm-promise-isolation.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-domain-vm-promise-isolation.js -//#SHA1: 866cac427030e1696a6583c234815beed16bf5c8 -//----------------- -"use strict"; - -const domain = require("domain"); -const vm = require("vm"); - -// A promise created in a VM should not include a domain field but -// domains should still be able to propagate through them. -// -// See; https://github.com/nodejs/node/issues/40999 - -const context = vm.createContext({}); - -function run(code) { - const d = domain.createDomain(); - d.run(() => { - const p = vm.runInContext(code, context)(); - expect(p.domain).toBeUndefined(); - return p.then(() => { - expect(process.domain).toBe(d); - }); - }); -} - -test("VM promise isolation and domain propagation", async () => { - const runPromises = []; - for (let i = 0; i < 1000; i++) { - runPromises.push(run("async () => null")); - } - await Promise.all(runPromises); -}, 30000); // Increased timeout for multiple iterations - -//<#END_FILE: test-domain-vm-promise-isolation.js diff --git a/test/js/node/test/parallel/double-tls-client.test.js b/test/js/node/test/parallel/double-tls-client.test.js deleted file mode 100644 index 0913a2a5b7..0000000000 --- a/test/js/node/test/parallel/double-tls-client.test.js +++ /dev/null @@ -1,85 +0,0 @@ -//#FILE: test-double-tls-client.js -//#SHA1: f0d01e282b2d7efc1e1770700f742345216a8bb3 -//----------------- -"use strict"; - -const assert = require("assert"); -const fixtures = require("../common/fixtures"); -const tls = require("tls"); - -// In reality, this can be a HTTP CONNECT message, signaling the incoming -// data is TLS encrypted -const HEAD = "XXXX"; - -let subserver; -let server; - -beforeAll(() => { - subserver = tls.createServer({ - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }); - - subserver.on("secureConnection", () => { - process.exit(0); - }); - - server = tls.createServer({ - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }); - - server.on("secureConnection", serverTlsSock => { - serverTlsSock.on("data", chunk => { - expect(chunk.toString()).toBe(HEAD); - subserver.emit("connection", serverTlsSock); - }); - }); -}); - -afterAll(() => { - server.close(); - subserver.close(); -}); - -test("double TLS client", done => { - const onSecureConnect = jest.fn(); - - server.listen(() => { - const down = tls.connect({ - host: "127.0.0.1", - port: server.address().port, - rejectUnauthorized: false, - }); - - down.on("secureConnect", () => { - onSecureConnect(); - down.write(HEAD, err => { - expect(err).toBeFalsy(); - - // Sending tls data on a client TLSSocket with an active write led to a crash: - // - // node[16862]: ../src/crypto/crypto_tls.cc:963:virtual int node::crypto::TLSWrap::DoWrite(node::WriteWrap*, - // uv_buf_t*, size_t, uv_stream_t*): Assertion `!current_write_' failed. - // 1: 0xb090e0 node::Abort() [node] - // 2: 0xb0915e [node] - // 3: 0xca8413 node::crypto::TLSWrap::DoWrite(node::WriteWrap*, uv_buf_t*, unsigned long, uv_stream_s*) [node] - // 4: 0xcaa549 node::StreamBase::Write(uv_buf_t*, unsigned long, uv_stream_s*, v8::Local) [node] - // 5: 0xca88d7 node::crypto::TLSWrap::EncOut() [node] - // 6: 0xd3df3e [node] - // 7: 0xd3f35f v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node] - // 8: 0x15d9ef9 [node] - // Aborted - tls.connect({ - socket: down, - rejectUnauthorized: false, - }); - - expect(onSecureConnect).toHaveBeenCalledTimes(1); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-double-tls-client.js diff --git a/test/js/node/test/parallel/error-format-list.test.js b/test/js/node/test/parallel/error-format-list.test.js deleted file mode 100644 index ea5fb2dfc9..0000000000 --- a/test/js/node/test/parallel/error-format-list.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-error-format-list.js -//#SHA1: ed33f55f6c42ff9671add8288b1d2984393bdfc1 -//----------------- -"use strict"; - -if (!Intl) { - test.skip("missing Intl", () => {}); -} else { - test("formatList function", () => { - const and = new Intl.ListFormat("en", { style: "long", type: "conjunction" }); - const or = new Intl.ListFormat("en", { style: "long", type: "disjunction" }); - - const input = ["apple", "banana", "orange", "pear"]; - for (let i = 0; i < input.length; i++) { - const slicedInput = input.slice(0, i); - expect(formatList(slicedInput)).toBe(and.format(slicedInput)); - expect(formatList(slicedInput, "or")).toBe(or.format(slicedInput)); - } - }); -} - -// Helper function to replicate the behavior of internal/errors formatList -function formatList(list, type = "and") { - const formatter = new Intl.ListFormat("en", { - style: "long", - type: type === "and" ? "conjunction" : "disjunction", - }); - return formatter.format(list); -} - -//<#END_FILE: test-error-format-list.js diff --git a/test/js/node/test/parallel/errors-systemerror-stacktracelimit-deleted-and-error-sealed.test.js b/test/js/node/test/parallel/errors-systemerror-stacktracelimit-deleted-and-error-sealed.test.js deleted file mode 100644 index f2b27f0e1f..0000000000 --- a/test/js/node/test/parallel/errors-systemerror-stacktracelimit-deleted-and-error-sealed.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-errors-systemerror-stackTraceLimit-deleted-and-Error-sealed.js -//#SHA1: 5d9d37ff8651fd7b7ffd5e9ae1b54e4ebc900355 -//----------------- -"use strict"; - -test("SystemError with deleted stackTraceLimit and sealed Error", () => { - delete Error.stackTraceLimit; - Object.seal(Error); - - const ctx = { - code: "ETEST", - message: "code message", - syscall: "syscall_test", - path: "/str", - dest: "/str2", - }; - - const errorThrowingFunction = () => { - const error = new Error("custom message"); - error.code = "ERR_TEST"; - error.name = "SystemError"; - error.info = ctx; - throw error; - }; - - expect(errorThrowingFunction).toThrow( - expect.objectContaining({ - code: "ERR_TEST", - name: "SystemError", - info: ctx, - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-errors-systemerror-stackTraceLimit-deleted-and-Error-sealed.js diff --git a/test/js/node/test/parallel/eval.test.js b/test/js/node/test/parallel/eval.test.js deleted file mode 100644 index 97bc6069a4..0000000000 --- a/test/js/node/test/parallel/eval.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-eval.js -//#SHA1: 3ea606b33a3ee4b40ef918317b32008e0f5d5e49 -//----------------- -"use strict"; - -// Verify that eval is allowed by default. -test("eval is allowed by default", () => { - expect(eval('"eval"')).toBe("eval"); -}); - -//<#END_FILE: test-eval.js diff --git a/test/js/node/test/parallel/event-emitter-check-listener-leaks.test.js b/test/js/node/test/parallel/event-emitter-check-listener-leaks.test.js deleted file mode 100644 index 49c67758b6..0000000000 --- a/test/js/node/test/parallel/event-emitter-check-listener-leaks.test.js +++ /dev/null @@ -1,103 +0,0 @@ -//#FILE: test-event-emitter-check-listener-leaks.js -//#SHA1: c9d313a0879cc331d8ad1afafe5b9f482597bc7c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const events = require("events"); - -test("default", () => { - const e = new events.EventEmitter(); - - for (let i = 0; i < 10; i++) { - e.on("default", jest.fn()); - } - expect(Object.hasOwn(e._events.default, "warned")).toBe(false); - e.on("default", jest.fn()); - expect(e._events.default.warned).toBe(true); - - // symbol - const symbol = Symbol("symbol"); - e.setMaxListeners(1); - e.on(symbol, jest.fn()); - expect(Object.hasOwn(e._events[symbol], "warned")).toBe(false); - e.on(symbol, jest.fn()); - expect(Object.hasOwn(e._events[symbol], "warned")).toBe(true); - - // specific - e.setMaxListeners(5); - for (let i = 0; i < 5; i++) { - e.on("specific", jest.fn()); - } - expect(Object.hasOwn(e._events.specific, "warned")).toBe(false); - e.on("specific", jest.fn()); - expect(e._events.specific.warned).toBe(true); - - // only one - e.setMaxListeners(1); - e.on("only one", jest.fn()); - expect(Object.hasOwn(e._events["only one"], "warned")).toBe(false); - e.on("only one", jest.fn()); - expect(Object.hasOwn(e._events["only one"], "warned")).toBe(true); - - // unlimited - e.setMaxListeners(0); - for (let i = 0; i < 1000; i++) { - e.on("unlimited", jest.fn()); - } - expect(Object.hasOwn(e._events.unlimited, "warned")).toBe(false); -}); - -test("process-wide", () => { - events.EventEmitter.defaultMaxListeners = 42; - const e = new events.EventEmitter(); - - for (let i = 0; i < 42; ++i) { - e.on("fortytwo", jest.fn()); - } - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(false); - e.on("fortytwo", jest.fn()); - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(true); - delete e._events.fortytwo.warned; - - events.EventEmitter.defaultMaxListeners = 44; - e.on("fortytwo", jest.fn()); - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(false); - e.on("fortytwo", jest.fn()); - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(true); -}); - -test("_maxListeners precedence over defaultMaxListeners", () => { - events.EventEmitter.defaultMaxListeners = 42; - const e = new events.EventEmitter(); - e.setMaxListeners(1); - e.on("uno", jest.fn()); - expect(Object.hasOwn(e._events.uno, "warned")).toBe(false); - e.on("uno", jest.fn()); - expect(Object.hasOwn(e._events.uno, "warned")).toBe(true); - - // chainable - expect(e.setMaxListeners(1)).toBe(e); -}); - -//<#END_FILE: test-event-emitter-check-listener-leaks.js diff --git a/test/js/node/test/parallel/event-emitter-emit-context.test.js b/test/js/node/test/parallel/event-emitter-emit-context.test.js deleted file mode 100644 index b39c26258e..0000000000 --- a/test/js/node/test/parallel/event-emitter-emit-context.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-event-emitter-emit-context.js -//#SHA1: 66f963c1a1351deff53d036d86f821c5e932c832 -//----------------- -"use strict"; -const EventEmitter = require("events"); - -// Test emit called by other context -const EE = new EventEmitter(); - -// Works as expected if the context has no `constructor.name` -test("emit called with context having no constructor.name", () => { - const ctx = { __proto__: null }; - expect(() => EE.emit.call(ctx, "error", new Error("foo"))).toThrow( - expect.objectContaining({ - name: "Error", - message: expect.any(String), - }), - ); -}); - -test("emit called with empty object context", () => { - expect(EE.emit.call({}, "foo")).toBe(false); -}); - -//<#END_FILE: test-event-emitter-emit-context.js diff --git a/test/js/node/test/parallel/event-emitter-get-max-listeners.test.js b/test/js/node/test/parallel/event-emitter-get-max-listeners.test.js deleted file mode 100644 index 612a3f1323..0000000000 --- a/test/js/node/test/parallel/event-emitter-get-max-listeners.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-event-emitter-get-max-listeners.js -//#SHA1: ff5c2f7b9525ae4137ea8eddd742572bd399c5ce -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -test("EventEmitter getMaxListeners", () => { - const emitter = new EventEmitter(); - - expect(emitter.getMaxListeners()).toBe(EventEmitter.defaultMaxListeners); - - emitter.setMaxListeners(0); - expect(emitter.getMaxListeners()).toBe(0); - - emitter.setMaxListeners(3); - expect(emitter.getMaxListeners()).toBe(3); -}); - -// https://github.com/nodejs/node/issues/523 - second call should not throw. -test("EventEmitter.prototype.on should not throw on second call", () => { - const recv = {}; - expect(() => { - EventEmitter.prototype.on.call(recv, "event", () => {}); - EventEmitter.prototype.on.call(recv, "event", () => {}); - }).not.toThrow(); -}); - -//<#END_FILE: test-event-emitter-get-max-listeners.js diff --git a/test/js/node/test/parallel/event-emitter-listener-count.test.js b/test/js/node/test/parallel/event-emitter-listener-count.test.js deleted file mode 100644 index b9772949ad..0000000000 --- a/test/js/node/test/parallel/event-emitter-listener-count.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-event-emitter-listener-count.js -//#SHA1: 0f4b7f14fe432472b51524b3853db6d8615614f1 -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -describe("EventEmitter.listenerCount and emitter.listenerCount", () => { - let emitter; - - beforeEach(() => { - emitter = new EventEmitter(); - emitter.on("foo", () => {}); - emitter.on("foo", () => {}); - emitter.on("baz", () => {}); - // Allow any type - emitter.on(123, () => {}); - }); - - test("EventEmitter.listenerCount returns correct count", () => { - expect(EventEmitter.listenerCount(emitter, "foo")).toBe(2); - }); - - test("emitter.listenerCount returns correct counts for various events", () => { - expect(emitter.listenerCount("foo")).toBe(2); - expect(emitter.listenerCount("bar")).toBe(0); - expect(emitter.listenerCount("baz")).toBe(1); - expect(emitter.listenerCount(123)).toBe(1); - }); -}); - -//<#END_FILE: test-event-emitter-listener-count.js diff --git a/test/js/node/test/parallel/event-emitter-modify-in-emit.test.js b/test/js/node/test/parallel/event-emitter-modify-in-emit.test.js deleted file mode 100644 index 422054923c..0000000000 --- a/test/js/node/test/parallel/event-emitter-modify-in-emit.test.js +++ /dev/null @@ -1,89 +0,0 @@ -//#FILE: test-event-emitter-modify-in-emit.js -//#SHA1: 6d378f9c7700ce7946f7d59bbc32b7cb82efc836 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const events = require("events"); - -describe("EventEmitter modify in emit", () => { - let callbacks_called; - let e; - - function callback1() { - callbacks_called.push("callback1"); - e.on("foo", callback2); - e.on("foo", callback3); - e.removeListener("foo", callback1); - } - - function callback2() { - callbacks_called.push("callback2"); - e.removeListener("foo", callback2); - } - - function callback3() { - callbacks_called.push("callback3"); - e.removeListener("foo", callback3); - } - - beforeEach(() => { - callbacks_called = []; - e = new events.EventEmitter(); - }); - - test("listeners are modified during emit", () => { - e.on("foo", callback1); - expect(e.listeners("foo").length).toBe(1); - - e.emit("foo"); - expect(e.listeners("foo").length).toBe(2); - expect(callbacks_called).toEqual(["callback1"]); - - e.emit("foo"); - expect(e.listeners("foo").length).toBe(0); - expect(callbacks_called).toEqual(["callback1", "callback2", "callback3"]); - - e.emit("foo"); - expect(e.listeners("foo").length).toBe(0); - expect(callbacks_called).toEqual(["callback1", "callback2", "callback3"]); - }); - - test("removeAllListeners removes all listeners", () => { - e.on("foo", callback1); - e.on("foo", callback2); - expect(e.listeners("foo").length).toBe(2); - e.removeAllListeners("foo"); - expect(e.listeners("foo").length).toBe(0); - }); - - test("removing callbacks during emit allows emits to propagate to all listeners", () => { - e.on("foo", callback2); - e.on("foo", callback3); - expect(e.listeners("foo").length).toBe(2); - e.emit("foo"); - expect(callbacks_called).toEqual(["callback2", "callback3"]); - expect(e.listeners("foo").length).toBe(0); - }); -}); - -//<#END_FILE: test-event-emitter-modify-in-emit.js diff --git a/test/js/node/test/parallel/event-emitter-num-args.test.js b/test/js/node/test/parallel/event-emitter-num-args.test.js deleted file mode 100644 index e295eb022c..0000000000 --- a/test/js/node/test/parallel/event-emitter-num-args.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-event-emitter-num-args.js -//#SHA1: b17b5bfd071180f4c53c8c408dc14ec860fd4225 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const events = require("events"); - -describe("EventEmitter number of arguments", () => { - let e; - let num_args_emitted; - - beforeEach(() => { - e = new events.EventEmitter(); - num_args_emitted = []; - - e.on("numArgs", function () { - const numArgs = arguments.length; - num_args_emitted.push(numArgs); - }); - - e.on("foo", function () { - num_args_emitted.push(arguments.length); - }); - - e.on("foo", function () { - num_args_emitted.push(arguments.length); - }); - }); - - it("should emit correct number of arguments", () => { - e.emit("numArgs"); - e.emit("numArgs", null); - e.emit("numArgs", null, null); - e.emit("numArgs", null, null, null); - e.emit("numArgs", null, null, null, null); - e.emit("numArgs", null, null, null, null, null); - - e.emit("foo", null, null, null, null); - - expect(num_args_emitted).toEqual([0, 1, 2, 3, 4, 5, 4, 4]); - }); -}); - -//<#END_FILE: test-event-emitter-num-args.js diff --git a/test/js/node/test/parallel/event-emitter-prepend.test.js b/test/js/node/test/parallel/event-emitter-prepend.test.js deleted file mode 100644 index f8f7d47b8c..0000000000 --- a/test/js/node/test/parallel/event-emitter-prepend.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-event-emitter-prepend.js -//#SHA1: 9a753f44fc304ff6584a31c20e95037e622579d7 -//----------------- -"use strict"; - -const EventEmitter = require("events"); -const stream = require("stream"); - -describe("EventEmitter prepend", () => { - test("prepend listeners in correct order", () => { - const myEE = new EventEmitter(); - let m = 0; - - // This one comes last. - myEE.on("foo", () => { - expect(m).toBe(2); - }); - - // This one comes second. - myEE.prependListener("foo", () => { - expect(m).toBe(1); - m++; - }); - - // This one comes first. - myEE.prependOnceListener("foo", () => { - expect(m).toBe(0); - m++; - }); - - myEE.emit("foo"); - expect.assertions(3); - }); - - test("fallback if prependListener is undefined", () => { - // Test fallback if prependListener is undefined. - delete EventEmitter.prototype.prependListener; - - function Writable() { - this.writable = true; - stream.Stream.call(this); - } - Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); - Object.setPrototypeOf(Writable, stream.Stream); - - function Readable() { - this.readable = true; - stream.Stream.call(this); - } - Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); - Object.setPrototypeOf(Readable, stream.Stream); - - const w = new Writable(); - const r = new Readable(); - r.pipe(w); - - // If we reach this point without throwing, the test passes - expect(true).toBe(true); - }); -}); - -//<#END_FILE: test-event-emitter-prepend.js diff --git a/test/js/node/test/parallel/event-emitter-special-event-names.test.js b/test/js/node/test/parallel/event-emitter-special-event-names.test.js deleted file mode 100644 index 60d8071a33..0000000000 --- a/test/js/node/test/parallel/event-emitter-special-event-names.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-event-emitter-special-event-names.js -//#SHA1: 3ae93e3a9f5cd01560264a5c297a2fae25c5c18f -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -describe("EventEmitter with special event names", () => { - let ee; - - beforeEach(() => { - ee = new EventEmitter(); - }); - - test("initial eventNames() is empty", () => { - expect(ee.eventNames()).toEqual([]); - }); - - test("_events does not have hasOwnProperty or toString", () => { - expect(ee._events.hasOwnProperty).toBeUndefined(); - expect(ee._events.toString).toBeUndefined(); - }); - - test("can add and list special event names", () => { - const handler = jest.fn(); - - ee.on("__proto__", handler); - ee.on("__defineGetter__", handler); - ee.on("toString", handler); - - expect(ee.eventNames()).toEqual(["__proto__", "__defineGetter__", "toString"]); - - expect(ee.listeners("__proto__")).toEqual([handler]); - expect(ee.listeners("__defineGetter__")).toEqual([handler]); - expect(ee.listeners("toString")).toEqual([handler]); - }); - - test("can emit __proto__ event", () => { - const handler = jest.fn(); - ee.on("__proto__", handler); - ee.emit("__proto__", 1); - expect(handler).toHaveBeenCalledWith(1); - }); - - test("process can emit __proto__ event", () => { - const handler = jest.fn(); - process.on("__proto__", handler); - process.emit("__proto__", 1); - expect(handler).toHaveBeenCalledWith(1); - }); -}); - -//<#END_FILE: test-event-emitter-special-event-names.js diff --git a/test/js/node/test/parallel/event-emitter-symbols.test.js b/test/js/node/test/parallel/event-emitter-symbols.test.js deleted file mode 100644 index bfb0e2b774..0000000000 --- a/test/js/node/test/parallel/event-emitter-symbols.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-event-emitter-symbols.js -//#SHA1: c3fa4a8db31f2a88317d3c60fb52aa2921eaa20d -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -test("EventEmitter with Symbol events", () => { - const ee = new EventEmitter(); - const foo = Symbol("foo"); - const listener = jest.fn(); - - ee.on(foo, listener); - expect(ee.listeners(foo)).toEqual([listener]); - - ee.emit(foo); - expect(listener).toHaveBeenCalledTimes(1); - - ee.removeAllListeners(); - expect(ee.listeners(foo)).toEqual([]); - - ee.on(foo, listener); - expect(ee.listeners(foo)).toEqual([listener]); - - ee.removeListener(foo, listener); - expect(ee.listeners(foo)).toEqual([]); -}); - -//<#END_FILE: test-event-emitter-symbols.js diff --git a/test/js/node/test/parallel/event-target.test.js b/test/js/node/test/parallel/event-target.test.js deleted file mode 100644 index 0b5fc40712..0000000000 --- a/test/js/node/test/parallel/event-target.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-event-target.js -//#SHA1: 3e70912640aa5270e13723e1895636bfd238420a -//----------------- -"use strict"; - -const eventPhases = { - NONE: 0, - CAPTURING_PHASE: 1, - AT_TARGET: 2, - BUBBLING_PHASE: 3, -}; - -describe("Event phases", () => { - test.each(Object.entries(eventPhases))("Event.%s should be %d", (prop, value) => { - // Check if the value of the property matches the expected value - expect(Event[prop]).toBe(value); - - const desc = Object.getOwnPropertyDescriptor(Event, prop); - expect(desc.writable).toBe(false); - expect(desc.configurable).toBe(false); - expect(desc.enumerable).toBe(true); - }); -}); - -//<#END_FILE: test-event-target.js diff --git a/test/js/node/test/parallel/events-list.test.js b/test/js/node/test/parallel/events-list.test.js deleted file mode 100644 index bde2b1a0e6..0000000000 --- a/test/js/node/test/parallel/events-list.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-events-list.js -//#SHA1: 946973d6c9e19c6410b4486cbef3e2ed032715fc -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -test("EventEmitter.eventNames()", () => { - const EE = new EventEmitter(); - const m = () => {}; - - EE.on("foo", () => {}); - expect(EE.eventNames()).toEqual(["foo"]); - - EE.on("bar", m); - expect(EE.eventNames()).toEqual(["foo", "bar"]); - - EE.removeListener("bar", m); - expect(EE.eventNames()).toEqual(["foo"]); - - const s = Symbol("s"); - EE.on(s, m); - expect(EE.eventNames()).toEqual(["foo", s]); - - EE.removeListener(s, m); - expect(EE.eventNames()).toEqual(["foo"]); -}); - -//<#END_FILE: test-events-list.js diff --git a/test/js/node/test/parallel/events-on-async-iterator.test.js b/test/js/node/test/parallel/events-on-async-iterator.test.js deleted file mode 100644 index eb67f58c5b..0000000000 --- a/test/js/node/test/parallel/events-on-async-iterator.test.js +++ /dev/null @@ -1,417 +0,0 @@ -import { test, expect } from "bun:test"; -const common = require("../common"); -const assert = require("assert"); -const { on, EventEmitter, listenerCount } = require("events"); - -test("basic", async () => { - const ee = new EventEmitter(); - process.nextTick(() => { - ee.emit("foo", "bar"); - // 'bar' is a spurious event, we are testing - // that it does not show up in the iterable - ee.emit("bar", 24); - ee.emit("foo", 42); - }); - - const iterable = on(ee, "foo"); - - const expected = [["bar"], [42]]; - - for await (const event of iterable) { - const current = expected.shift(); - - expect(current).toStrictEqual(event); - - if (expected.length === 0) { - break; - } - } - expect(ee.listenerCount("foo")).toBe(0); - expect(ee.listenerCount("error")).toBe(0); -}); - -test("invalidArgType", async () => { - assert.throws( - () => on({}, "foo"), - common.expectsError({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - const ee = new EventEmitter(); - - [1, "hi", null, false, () => {}, Symbol(), 1n].map(options => { - return assert.throws( - () => on(ee, "foo", options), - common.expectsError({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -test("error", async () => { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - process.nextTick(() => { - ee.emit("error", _err); - }); - - const iterable = on(ee, "foo"); - let looped = false; - let thrown = false; - - try { - // eslint-disable-next-line no-unused-vars - for await (const event of iterable) { - looped = true; - } - } catch (err) { - thrown = true; - expect(err).toStrictEqual(_err); - } - expect(thrown).toBe(true); - expect(looped).toBe(false); -}); - -test("errorDelayed", async () => { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - process.nextTick(() => { - ee.emit("foo", 42); - ee.emit("error", _err); - }); - - const iterable = on(ee, "foo"); - const expected = [[42]]; - let thrown = false; - - try { - for await (const event of iterable) { - const current = expected.shift(); - assert.deepStrictEqual(current, event); - } - } catch (err) { - thrown = true; - expect(err).toStrictEqual(_err); - } - expect(thrown).toBe(true); - expect(ee.listenerCount("foo")).toBe(0); - expect(ee.listenerCount("error")).toBe(0); -}); - -test("throwInLoop", async () => { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - - process.nextTick(() => { - ee.emit("foo", 42); - }); - - try { - for await (const event of on(ee, "foo")) { - assert.deepStrictEqual(event, [42]); - throw _err; - } - } catch (err) { - expect(err).toStrictEqual(_err); - } - - expect(ee.listenerCount("foo")).toBe(0); - expect(ee.listenerCount("error")).toBe(0); -}); - -test("next", async () => { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - - process.nextTick(function () { - ee.emit("foo", "bar"); - ee.emit("foo", 42); - iterable.return(); - }); - - const results = await Promise.all([iterable.next(), iterable.next(), iterable.next()]); - - expect(results).toStrictEqual([ - { - value: ["bar"], - done: false, - }, - { - value: [42], - done: false, - }, - { - value: undefined, - done: true, - }, - ]); - - expect(await iterable.next()).toStrictEqual({ - value: undefined, - done: true, - }); -}); - -test("nextError", async () => { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - const _err = new Error("kaboom"); - process.nextTick(function () { - ee.emit("error", _err); - }); - const results = await Promise.allSettled([iterable.next(), iterable.next(), iterable.next()]); - assert.deepStrictEqual(results, [ - { - status: "rejected", - reason: _err, - }, - { - status: "fulfilled", - value: { - value: undefined, - done: true, - }, - }, - { - status: "fulfilled", - value: { - value: undefined, - done: true, - }, - }, - ]); - expect(ee.listeners("error").length).toBe(0); -}); - -test("iterableThrow", async () => { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - - process.nextTick(() => { - ee.emit("foo", "bar"); - ee.emit("foo", 42); // lost in the queue - iterable.throw(_err); - }); - - const _err = new Error("kaboom"); - let thrown = false; - - assert.throws( - () => { - // No argument - iterable.throw(); - }, - { - message: 'The "EventEmitter.AsyncIterator" argument must be' + " of type Error. Received: undefined", - name: "TypeError", - }, - ); - - const expected = [["bar"], [42]]; - - try { - for await (const event of iterable) { - assert.deepStrictEqual(event, expected.shift()); - } - } catch (err) { - thrown = true; - assert.strictEqual(err, _err); - } - assert.strictEqual(thrown, true); - assert.strictEqual(expected.length, 0); - assert.strictEqual(ee.listenerCount("foo"), 0); - assert.strictEqual(ee.listenerCount("error"), 0); -}); - -test("eventTarget", async () => { - const et = new EventTarget(); - const tick = () => et.dispatchEvent(new Event("tick")); - const interval = setInterval(tick, 0); - let count = 0; - for await (const [event] of on(et, "tick")) { - count++; - assert.strictEqual(event.type, "tick"); - if (count >= 5) { - break; - } - } - assert.strictEqual(count, 5); - clearInterval(interval); -}); - -test("errorListenerCount", async () => { - const et = new EventEmitter(); - on(et, "foo"); - assert.strictEqual(et.listenerCount("error"), 1); -}); - -test.skip("nodeEventTarget", async () => { - const et = new NodeEventTarget(); - const tick = () => et.dispatchEvent(new Event("tick")); - const interval = setInterval(tick, 0); - let count = 0; - for await (const [event] of on(et, "tick")) { - count++; - assert.strictEqual(event.type, "tick"); - if (count >= 5) { - break; - } - } - assert.strictEqual(count, 5); - clearInterval(interval); -}); - -test("abortableOnBefore", async () => { - const ee = new EventEmitter(); - const abortedSignal = AbortSignal.abort(); - [1, {}, null, false, "hi"].forEach(signal => { - assert.throws(() => on(ee, "foo", { signal }), { - code: "ERR_INVALID_ARG_TYPE", - }); - }); - assert.throws(() => on(ee, "foo", { signal: abortedSignal }), { - name: "AbortError", - }); -}); - -test("eventTargetAbortableOnBefore", async () => { - const et = new EventTarget(); - const abortedSignal = AbortSignal.abort(); - [1, {}, null, false, "hi"].forEach(signal => { - assert.throws(() => on(et, "foo", { signal }), { - code: "ERR_INVALID_ARG_TYPE", - }); - }); - assert.throws(() => on(et, "foo", { signal: abortedSignal }), { - name: "AbortError", - }); -}); - -test("abortableOnAfter", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - const i = setInterval(() => ee.emit("foo", "foo"), 10); - - async function foo() { - for await (const f of on(ee, "foo", { signal: ac.signal })) { - assert.strictEqual(f, "foo"); - } - } - - foo() - .catch( - common.mustCall(error => { - assert.strictEqual(error.name, "AbortError"); - }), - ) - .finally(() => { - clearInterval(i); - }); - - process.nextTick(() => ac.abort()); -}); - -test("eventTargetAbortableOnAfter", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - - const i = setInterval(() => et.dispatchEvent(new Event("foo")), 10); - - async function foo() { - for await (const f of on(et, "foo", { signal: ac.signal })) { - assert(f); - } - } - - foo() - .catch( - common.mustCall(error => { - assert.strictEqual(error.name, "AbortError"); - }), - ) - .finally(() => { - clearInterval(i); - }); - - process.nextTick(() => ac.abort()); -}); - -test("eventTargetAbortableOnAfter2", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - - const i = setInterval(() => et.dispatchEvent(new Event("foo")), 10); - - async function foo() { - for await (const f of on(et, "foo", { signal: ac.signal })) { - assert(f); - // Cancel after a single event has been triggered. - ac.abort(); - } - } - - foo() - .catch( - common.mustCall(error => { - assert.strictEqual(error.name, "AbortError"); - }), - ) - .finally(() => { - clearInterval(i); - }); -}); - -test("abortableOnAfterDone", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - const i = setInterval(() => ee.emit("foo", "foo"), 1); - let count = 0; - - async function foo() { - for await (const f of on(ee, "foo", { signal: ac.signal })) { - assert.strictEqual(f[0], "foo"); - if (++count === 5) break; - } - ac.abort(); // No error will occur - } - - foo().finally(() => { - clearInterval(i); - }); -}); - -test("abortListenerRemovedAfterComplete", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - const i = setInterval(() => ee.emit("foo", "foo"), 1); - try { - // Below: either the kEvents map is empty or the 'abort' listener list is empty - - // Return case - const endedIterator = on(ee, "foo", { signal: ac.signal }); - expect(listenerCount(ac.signal, "abort")).toBeGreaterThan(0); - endedIterator.return(); - expect(listenerCount(ac.signal, "abort")).toBe(0); - - // Throw case - const throwIterator = on(ee, "foo", { signal: ac.signal }); - expect(listenerCount(ac.signal, "abort")).toBeGreaterThan(0); - throwIterator.throw(new Error()); - expect(listenerCount(ac.signal, "abort")).toBe(0); - - // Abort case - on(ee, "foo", { signal: ac.signal }); - expect(listenerCount(ac.signal, "abort")).toBeGreaterThan(0); - ac.abort(new Error()); - expect(listenerCount(ac.signal, "abort")).toBe(0); - } finally { - clearInterval(i); - } -}); diff --git a/test/js/node/test/parallel/events-once.test.js b/test/js/node/test/parallel/events-once.test.js deleted file mode 100644 index 3e0c4c9ecb..0000000000 --- a/test/js/node/test/parallel/events-once.test.js +++ /dev/null @@ -1,254 +0,0 @@ -import { test, expect } from "bun:test"; -const { once, EventEmitter, listenerCount } = require("events"); -const { deepStrictEqual, fail, rejects, strictEqual } = require("assert"); -// const { kEvents } = require("internal/event_target"); - -test("onceAnEvent", async () => { - const ee = new EventEmitter(); - - process.nextTick(() => { - ee.emit("myevent", 42); - }); - - const [value] = await once(ee, "myevent"); - strictEqual(value, 42); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("onceAnEventWithInvalidOptions", async () => { - const ee = new EventEmitter(); - - await Promise.all( - [1, "hi", null, false, () => {}, Symbol(), 1n].map(options => { - expect.toThrowWithCode(() => once(ee, "myevent", options), "ERR_INVALID_ARG_TYPE"); - }), - ); -}); - -test("onceAnEventWithTwoArgs", async () => { - const ee = new EventEmitter(); - - process.nextTick(() => { - ee.emit("myevent", 42, 24); - }); - - const value = await once(ee, "myevent"); - deepStrictEqual(value, [42, 24]); -}); - -test("catchesErrors", async () => { - const ee = new EventEmitter(); - - const expected = new Error("kaboom"); - let err; - process.nextTick(() => { - ee.emit("error", expected); - }); - - try { - await once(ee, "myevent"); - } catch (_e) { - err = _e; - } - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("catchesErrorsWithAbortSignal", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - const signal = ac.signal; - - const expected = new Error("boom"); - let err; - process.nextTick(() => { - ee.emit("error", expected); - }); - - try { - const promise = once(ee, "myevent", { signal }); - strictEqual(ee.listenerCount("error"), 1); - strictEqual(listenerCount(signal, "abort"), 1); - - await promise; - } catch (e) { - err = e; - } - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); - strictEqual(listenerCount(signal, "abort"), 0); -}); - -test("stopListeningAfterCatchingError", async () => { - const ee = new EventEmitter(); - - const expected = new Error("kaboom"); - let err; - process.nextTick(() => { - ee.emit("error", expected); - ee.emit("myevent", 42, 24); - }); - - try { - await once(ee, "myevent"); - } catch (_e) { - err = _e; - } - process.removeAllListeners("multipleResolves"); - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("onceError", async () => { - const ee = new EventEmitter(); - - const expected = new Error("kaboom"); - process.nextTick(() => { - ee.emit("error", expected); - }); - - const promise = once(ee, "error"); - strictEqual(ee.listenerCount("error"), 1); - const [err] = await promise; - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("onceWithEventTarget", async () => { - const et = new EventTarget(); - const event = new Event("myevent"); - process.nextTick(() => { - et.dispatchEvent(event); - }); - const [value] = await once(et, "myevent"); - strictEqual(value, event); -}); - -test("onceWithEventTargetError", async () => { - const et = new EventTarget(); - const error = new Event("error"); - process.nextTick(() => { - et.dispatchEvent(error); - }); - - const [err] = await once(et, "error"); - strictEqual(err, error); -}); - -test("onceWithInvalidEventEmmiter", async () => { - const ac = new AbortController(); - expect.toThrowWithCode(() => once(ac, "myevent"), "ERR_INVALID_ARG_TYPE"); -}); - -test("prioritizesEventEmitter", async () => { - const ee = new EventEmitter(); - ee.addEventListener = fail; - ee.removeAllListeners = fail; - process.nextTick(() => ee.emit("foo")); - await once(ee, "foo"); -}); - -test("abortSignalBefore", async () => { - const ee = new EventEmitter(); - ee.on("error", () => expect(false).toEqual(true)); - const abortedSignal = AbortSignal.abort(); - - await Promise.all( - [1, {}, "hi", null, false].map(signal => { - expect.toThrowWithCode(() => once(ee, "foo", { signal }), "ERR_INVALID_ARG_TYPE"); - }), - ); - - expect(() => once(ee, "foo", { signal: abortedSignal })).toThrow(); -}); - -test("abortSignalAfter", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - ee.on("error", () => expect(false).toEqual(true)); - const r = rejects(once(ee, "foo", { signal: ac.signal }), { - name: "AbortError", - }); - process.nextTick(() => ac.abort()); - return r; -}); - -test("abortSignalAfterEvent", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - process.nextTick(() => { - ee.emit("foo"); - ac.abort(); - }); - const promise = once(ee, "foo", { signal: ac.signal }); - strictEqual(listenerCount(ac.signal, "abort"), 1); - await promise; - strictEqual(listenerCount(ac.signal, "abort"), 0); -}); - -test("abortSignalRemoveListener", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - try { - process.nextTick(() => ac.abort()); - await once(ee, "test", { signal: ac.signal }); - } catch { - strictEqual(ee.listeners("test").length, 0); - strictEqual(ee.listeners("error").length, 0); - } -}); - -test.skip("eventTargetAbortSignalBefore", async () => { - const et = new EventTarget(); - const abortedSignal = AbortSignal.abort(); - - await Promise.all( - [1, {}, "hi", null, false].map(signal => { - return rejects(once(et, "foo", { signal }), { - code: "ERR_INVALID_ARG_TYPE", - }); - }), - ); - - return rejects(once(et, "foo", { signal: abortedSignal }), { - name: "AbortError", - }); -}); - -test.skip("eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - const { signal } = ac; - signal.addEventListener("abort", e => e.stopImmediatePropagation(), { once: true }); - - process.nextTick(() => ac.abort()); - return rejects(once(et, "foo", { signal }), { - name: "AbortError", - }); -}); - -test("eventTargetAbortSignalAfter", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - const r = rejects(once(et, "foo", { signal: ac.signal }), { - name: "AbortError", - }); - process.nextTick(() => ac.abort()); - return r; -}); - -test("eventTargetAbortSignalAfterEvent", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - process.nextTick(() => { - et.dispatchEvent(new Event("foo")); - ac.abort(); - }); - await once(et, "foo", { signal: ac.signal }); -}); diff --git a/test/js/node/test/parallel/eventsource-disabled.test.js b/test/js/node/test/parallel/eventsource-disabled.test.js deleted file mode 100644 index 0b7c76c1e2..0000000000 --- a/test/js/node/test/parallel/eventsource-disabled.test.js +++ /dev/null @@ -1,10 +0,0 @@ -//#FILE: test-eventsource-disabled.js -//#SHA1: ebb7581b56a8dd86c5385eba1befa9ef984a8065 -//----------------- -"use strict"; - -test("EventSource is undefined", () => { - expect(typeof EventSource).toBe("undefined"); -}); - -//<#END_FILE: test-eventsource-disabled.js diff --git a/test/js/node/test/parallel/eventtarget-once-twice.test.js b/test/js/node/test/parallel/eventtarget-once-twice.test.js deleted file mode 100644 index 74f19e01b0..0000000000 --- a/test/js/node/test/parallel/eventtarget-once-twice.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-eventtarget-once-twice.js -//#SHA1: dd2fea0f3c839d77c64423ed3c2cbdd319365141 -//----------------- -"use strict"; - -const { once } = require("events"); - -test("once can be called twice on EventTarget", async () => { - const et = new EventTarget(); - - const promise = (async () => { - await once(et, "foo"); - await once(et, "foo"); - })(); - - et.dispatchEvent(new Event("foo")); - setImmediate(() => { - et.dispatchEvent(new Event("foo")); - }); - - await expect(promise).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-eventtarget-once-twice.js diff --git a/test/js/node/test/parallel/fetch-mock.test.js b/test/js/node/test/parallel/fetch-mock.test.js deleted file mode 100644 index 84bce7c9a7..0000000000 --- a/test/js/node/test/parallel/fetch-mock.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-fetch-mock.js -//#SHA1: 92268be36b00e63e18a837088b9718344c3bff4f -//----------------- -"use strict"; - -test("should correctly stub globalThis.fetch", async () => { - const customFetch = async url => { - return { - text: async () => "foo", - }; - }; - - const originalFetch = globalThis.fetch; - globalThis.fetch = jest.fn(customFetch); - - const response = await globalThis.fetch("some-url"); - const text = await response.text(); - - expect(text).toBe("foo"); - expect(globalThis.fetch).toHaveBeenCalledWith("some-url"); - - // Restore the original fetch - globalThis.fetch = originalFetch; -}); - -//<#END_FILE: test-fetch-mock.js diff --git a/test/js/node/test/parallel/file-read-noexist.test.js b/test/js/node/test/parallel/file-read-noexist.test.js deleted file mode 100644 index 199afac796..0000000000 --- a/test/js/node/test/parallel/file-read-noexist.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-file-read-noexist.js -//#SHA1: c4cbb5dc6abca53c1dd513a4ece2741c1fc2235a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); - -const filename = path.join(__dirname, "fixtures", "does_not_exist.txt"); - -test("fs.readFile with non-existent file", async () => { - await expect( - new Promise((resolve, reject) => { - fs.readFile(filename, "latin1", (err, content) => { - if (err) reject(err); - else resolve(content); - }); - }), - ).rejects.toMatchObject({ - code: "ENOENT", - message: expect.any(String), - }); -}); - -//<#END_FILE: test-file-read-noexist.js diff --git a/test/js/node/test/parallel/finalization-registry-shutdown.test.js b/test/js/node/test/parallel/finalization-registry-shutdown.test.js deleted file mode 100644 index 9dfd3c99c2..0000000000 --- a/test/js/node/test/parallel/finalization-registry-shutdown.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-finalization-registry-shutdown.js -//#SHA1: c5da440f8ea977c501bb5920f55ce69b1da6e28d -//----------------- -// Flags: --expose-gc -"use strict"; - -// This test verifies that when a V8 FinalizationRegistryCleanupTask is queue -// at the last moment when JavaScript can be executed, the callback of a -// FinalizationRegistry will not be invoked and the process should exit -// normally. - -test("FinalizationRegistry callback should not be called during shutdown", () => { - const mockCallback = jest.fn(); - const reg = new FinalizationRegistry(mockCallback); - - function register() { - // Create a temporary object in a new function scope to allow it to be GC-ed. - reg.register({}); - } - - const exitHandler = () => { - // This is the final chance to execute JavaScript. - register(); - // Queue a FinalizationRegistryCleanupTask by a testing gc request. - global.gc(); - }; - - process.on("exit", exitHandler); - - // Simulate the exit process - exitHandler(); - - // Verify that the callback was not called - expect(mockCallback).not.toHaveBeenCalled(); - - // Clean up - process.removeListener("exit", exitHandler); -}); - -//<#END_FILE: test-finalization-registry-shutdown.js diff --git a/test/js/node/test/parallel/fs-chown-type-check.test.js b/test/js/node/test/parallel/fs-chown-type-check.test.js deleted file mode 100644 index 4a899dd46e..0000000000 --- a/test/js/node/test/parallel/fs-chown-type-check.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-fs-chown-type-check.js -//#SHA1: 6ecbd3ae2da32793768e64e9252d749ddf36c85a -//----------------- -"use strict"; - -const fs = require("fs"); - -describe("fs.chown and fs.chownSync type checks", () => { - test("invalid path argument", () => { - [false, 1, {}, [], null, undefined].forEach(invalidPath => { - expect(() => fs.chown(invalidPath, 1, 1, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chownSync(invalidPath, 1, 1)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); - }); - - test("invalid uid or gid arguments", () => { - [false, "test", {}, [], null, undefined].forEach(invalidId => { - expect(() => fs.chown("not_a_file_that_exists", invalidId, 1, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chown("not_a_file_that_exists", 1, invalidId, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chownSync("not_a_file_that_exists", invalidId, 1)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chownSync("not_a_file_that_exists", 1, invalidId)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); - }); -}); - -//<#END_FILE: test-fs-chown-type-check.js diff --git a/test/js/node/test/parallel/fs-constants.test.js b/test/js/node/test/parallel/fs-constants.test.js deleted file mode 100644 index 0d1332c1bb..0000000000 --- a/test/js/node/test/parallel/fs-constants.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-fs-constants.js -//#SHA1: 6113ac0dd5e6b3d59252a25e6ddae61b589ca362 -//----------------- -"use strict"; - -const fs = require("fs"); - -test("fs constants for Windows chmod() are defined", () => { - expect(fs.constants.S_IRUSR).toBeDefined(); - expect(fs.constants.S_IWUSR).toBeDefined(); -}); - -//<#END_FILE: test-fs-constants.js diff --git a/test/js/node/test/parallel/fs-copyfile-respect-permissions.test.js b/test/js/node/test/parallel/fs-copyfile-respect-permissions.test.js deleted file mode 100644 index 39d72c9cd6..0000000000 --- a/test/js/node/test/parallel/fs-copyfile-respect-permissions.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-fs-copyfile-respect-permissions.js -//#SHA1: 5a5d15dd2a31fab3f8cffa86fcaedc7dab528c9f -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const isWindows = process.platform === 'win32'; -const isIBMi = process.platform === 'os400'; - -if (!isWindows && process.getuid() === 0) { - it.skip('should not run as root', () => {}); -} else if (isIBMi) { - it.skip('IBMi has a different access permission mechanism', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-copyfile-respect-permissions'); - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - let n = 0; - - function beforeEach() { - n++; - const source = path.join(tmpdir, `source${n}`); - const dest = path.join(tmpdir, `dest${n}`); - fs.writeFileSync(source, 'source'); - fs.writeFileSync(dest, 'dest'); - fs.chmodSync(dest, '444'); - - const check = (err) => { - const expected = ['EACCES', 'EPERM']; - expect(expected).toContain(err.code); - expect(fs.readFileSync(dest, 'utf8')).toBe('dest'); - return true; - }; - - return { source, dest, check }; - } - - test('synchronous API', () => { - const { source, dest, check } = beforeEach(); - expect(() => { fs.copyFileSync(source, dest); }).toThrow(expect.objectContaining({ - message: expect.any(String), - code: expect.stringMatching(/^(EACCES|EPERM)$/) - })); - }); - - test('promises API', async () => { - const { source, dest, check } = beforeEach(); - await expect(fs.promises.copyFile(source, dest)).rejects.toThrow(expect.objectContaining({ - message: expect.any(String), - code: expect.stringMatching(/^(EACCES|EPERM)$/) - })); - }); - - test('callback API', (done) => { - const { source, dest, check } = beforeEach(); - fs.copyFile(source, dest, (err) => { - expect(check(err)).toBe(true); - done(); - }); - }); -} - -//<#END_FILE: test-fs-copyfile-respect-permissions.js diff --git a/test/js/node/test/parallel/fs-exists.test.js b/test/js/node/test/parallel/fs-exists.test.js deleted file mode 100644 index 3cea91fc1c..0000000000 --- a/test/js/node/test/parallel/fs-exists.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-fs-exists.js -//#SHA1: b7dcbc226b81e0ae4a111cbab39b87672a02172c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const f = __filename; - -test("fs.exists throws with invalid arguments", () => { - expect(() => fs.exists(f)).toThrow(expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE" })); - expect(() => fs.exists()).toThrow(expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE" })); - expect(() => fs.exists(f, {})).toThrow(expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE" })); -}); - -test("fs.exists with valid file", done => { - fs.exists(f, y => { - expect(y).toBe(true); - done(); - }); -}); - -test("fs.exists with non-existent file", done => { - fs.exists(`${f}-NO`, y => { - expect(y).toBe(false); - done(); - }); -}); - -test("fs.exists with invalid path", done => { - fs.exists(new URL("https://foo"), y => { - expect(y).toBe(false); - done(); - }); -}); - -test("fs.exists with object as path", done => { - fs.exists({}, y => { - expect(y).toBe(false); - done(); - }); -}); - -test("fs.existsSync", () => { - expect(fs.existsSync(f)).toBe(true); - expect(fs.existsSync(`${f}-NO`)).toBe(false); -}); - -test("fs.existsSync never throws", () => { - expect(fs.existsSync()).toBe(false); - expect(fs.existsSync({})).toBe(false); - expect(fs.existsSync(new URL("https://foo"))).toBe(false); -}); - -//<#END_FILE: test-fs-exists.js diff --git a/test/js/node/test/parallel/fs-fsync.test.js b/test/js/node/test/parallel/fs-fsync.test.js deleted file mode 100644 index b4d17d9ac9..0000000000 --- a/test/js/node/test/parallel/fs-fsync.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-fs-fsync.js -//#SHA1: 4225be75eaedfd17c32e0472e6739ba232b7f28e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const fileFixture = path.join(__dirname, '..', 'fixtures', 'a.js'); -const tmpdir = path.join(os.tmpdir(), 'test-fs-fsync'); -const fileTemp = path.join(tmpdir, 'a.js'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.copyFileSync(fileFixture, fileTemp); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('fsync and fdatasync operations', (done) => { - fs.open(fileTemp, 'a', 0o777, (err, fd) => { - expect(err).toBeNull(); - - fs.fdatasyncSync(fd); - fs.fsyncSync(fd); - - fs.fdatasync(fd, (err) => { - expect(err).toBeNull(); - fs.fsync(fd, (err) => { - expect(err).toBeNull(); - fs.closeSync(fd); - done(); - }); - }); - }); -}); - -test('invalid inputs throw TypeError', () => { - const invalidInputs = ['', false, null, undefined, {}, []]; - const errObj = { - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError' - }; - - invalidInputs.forEach((input) => { - expect(() => fs.fdatasync(input)).toThrow(expect.objectContaining(errObj)); - expect(() => fs.fdatasyncSync(input)).toThrow(expect.objectContaining(errObj)); - expect(() => fs.fsync(input)).toThrow(expect.objectContaining(errObj)); - expect(() => fs.fsyncSync(input)).toThrow(expect.objectContaining(errObj)); - }); -}); - -//<#END_FILE: test-fs-fsync.js diff --git a/test/js/node/test/parallel/fs-link.test.js b/test/js/node/test/parallel/fs-link.test.js deleted file mode 100644 index 4b6be724f2..0000000000 --- a/test/js/node/test/parallel/fs-link.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-fs-link.js -//#SHA1: 255940f3f953a4bd693b3e475bc466d5f759875f -//----------------- -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = { - refresh: () => { - // Implement a simple tmpdir.refresh() function - const testDir = path.join(os.tmpdir(), "test-fs-link"); - fs.rmSync(testDir, { recursive: true, force: true }); - fs.mkdirSync(testDir, { recursive: true }); - return testDir; - }, - resolve: filename => path.join(tmpdir.refresh(), filename), -}; - -test("Test creating and reading hard link", done => { - const srcPath = tmpdir.resolve("hardlink-target.txt"); - const dstPath = tmpdir.resolve("link1.js"); - fs.writeFileSync(srcPath, "hello world"); - - fs.link(srcPath, dstPath, err => { - expect(err).toBeFalsy(); - const dstContent = fs.readFileSync(dstPath, "utf8"); - expect(dstContent).toBe("hello world"); - done(); - }); -}); - -test("test error outputs", () => { - [false, 1, [], {}, null, undefined].forEach(i => { - expect(() => fs.link(i, "", () => {})).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.link("", i, () => {})).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.linkSync(i, "")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.linkSync("", i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -//<#END_FILE: test-fs-link.js diff --git a/test/js/node/test/parallel/fs-make-callback.test.js b/test/js/node/test/parallel/fs-make-callback.test.js deleted file mode 100644 index db3860cbd6..0000000000 --- a/test/js/node/test/parallel/fs-make-callback.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-fs-make-callback.js -//#SHA1: 40bbb673a0865f464a2b9e40110e903b6cc9e0d6 -//----------------- -"use strict"; - -const fs = require("fs"); -const { sep } = require("path"); -const tmpdir = require("../common/tmpdir"); - -const callbackThrowValues = [null, true, false, 0, 1, "foo", /foo/, [], {}]; - -beforeEach(() => { - tmpdir.refresh(); -}); - -function testMakeCallback(cb) { - return function () { - // fs.mkdtemp() calls makeCallback() on its third argument - return fs.mkdtemp(`${tmpdir.path}${sep}`, {}, cb); - }; -} - -describe("fs.makeCallback", () => { - test("invalid callbacks throw TypeError", () => { - callbackThrowValues.forEach(value => { - expect(testMakeCallback(value)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - }); -}); - -//<#END_FILE: test-fs-make-callback.js diff --git a/test/js/node/test/parallel/fs-makestatscallback.test.js b/test/js/node/test/parallel/fs-makestatscallback.test.js deleted file mode 100644 index 839e3b4f45..0000000000 --- a/test/js/node/test/parallel/fs-makestatscallback.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-makeStatsCallback.js -//#SHA1: e8c59eddd5ca920ba0a1aaa4dd87c3af879db3b1 -//----------------- -'use strict'; -const fs = require('fs'); - -function testMakeStatsCallback(cb) { - return function() { - // fs.stat() calls makeStatsCallback() on its second argument - fs.stat(__filename, cb); - }; -} - -test('Verify the case where a callback function is provided', (done) => { - testMakeStatsCallback(() => { - done(); - })(); -}); - -test('Invalid callback throws TypeError', () => { - const callbackThrowValues = [null, true, false, 0, 1, 'foo', /foo/, [], {}]; - - callbackThrowValues.forEach((value) => { - expect(testMakeStatsCallback(value)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); -}); - -//<#END_FILE: test-fs-makeStatsCallback.js diff --git a/test/js/node/test/parallel/fs-mkdir-recursive-eaccess.test.js b/test/js/node/test/parallel/fs-mkdir-recursive-eaccess.test.js deleted file mode 100644 index 570d2617ff..0000000000 --- a/test/js/node/test/parallel/fs-mkdir-recursive-eaccess.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-fs-mkdir-recursive-eaccess.js -//#SHA1: 1e0e4f480b7573549c130b4177759bd60adc1890 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); -const os = require('os'); - -const isWindows = process.platform === 'win32'; -const isIBMi = process.platform === 'os400'; - -if (isIBMi) { - console.log('Skipped: IBMi has a different access permission mechanism'); - process.exit(0); -} - -const tmpdir = path.join(os.tmpdir(), 'test-fs-mkdir-recursive-eaccess'); - -let n = 0; - -function makeDirectoryReadOnly(dir) { - let accessErrorCode = 'EACCES'; - if (isWindows) { - accessErrorCode = 'EPERM'; - execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC,AD,WD)"`); - } else { - fs.chmodSync(dir, '444'); - } - return accessErrorCode; -} - -function makeDirectoryWritable(dir) { - if (isWindows) { - execSync(`icacls ${dir} /remove:d "everyone"`); - } -} - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('Synchronous API should return an EACCES/EPERM error with path populated', () => { - const dir = path.join(tmpdir, `mkdirp_${n++}`); - fs.mkdirSync(dir); - const codeExpected = makeDirectoryReadOnly(dir); - - expect(() => { - fs.mkdirSync(path.join(dir, '/foo'), { recursive: true }); - }).toThrow(expect.objectContaining({ - code: codeExpected, - path: expect.any(String) - })); - - makeDirectoryWritable(dir); -}); - -test('Asynchronous API should return an EACCES/EPERM error with path populated', (done) => { - const dir = path.join(tmpdir, `mkdirp_${n++}`); - fs.mkdirSync(dir); - const codeExpected = makeDirectoryReadOnly(dir); - - fs.mkdir(path.join(dir, '/bar'), { recursive: true }, (err) => { - makeDirectoryWritable(dir); - expect(err).toEqual(expect.objectContaining({ - code: codeExpected, - path: expect.any(String) - })); - done(); - }); -}); - -//<#END_FILE: test-fs-mkdir-recursive-eaccess.js diff --git a/test/js/node/test/parallel/fs-mkdtemp-prefix-check.test.js b/test/js/node/test/parallel/fs-mkdtemp-prefix-check.test.js deleted file mode 100644 index 81690d9c17..0000000000 --- a/test/js/node/test/parallel/fs-mkdtemp-prefix-check.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-fs-mkdtemp-prefix-check.js -//#SHA1: f17d58f63200ae9b8c855b9def7da223fc5db531 -//----------------- -"use strict"; -const fs = require("fs"); - -const prefixValues = [undefined, null, 0, true, false, 1]; - -describe("fs.mkdtempSync prefix check", () => { - test.each(prefixValues)("should throw for invalid prefix: %p", value => { - expect(() => { - fs.mkdtempSync(value, {}); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -describe("fs.mkdtemp prefix check", () => { - test.each(prefixValues)("should throw for invalid prefix: %p", value => { - expect(() => { - fs.mkdtemp(value, jest.fn()); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-mkdtemp-prefix-check.js diff --git a/test/js/node/test/parallel/fs-non-number-arguments-throw.test.js b/test/js/node/test/parallel/fs-non-number-arguments-throw.test.js deleted file mode 100644 index fa7ff3127d..0000000000 --- a/test/js/node/test/parallel/fs-non-number-arguments-throw.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-fs-non-number-arguments-throw.js -//#SHA1: 65db5c653216831bc16d38c5d659fbffa296d3d8 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-non-number-arguments-throw'); -const tempFile = path.join(tmpdir, 'fs-non-number-arguments-throw'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(tempFile, 'abc\ndef'); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('createReadStream with valid number arguments', (done) => { - const sanity = 'def'; - const saneEmitter = fs.createReadStream(tempFile, { start: 4, end: 6 }); - - saneEmitter.on('data', (data) => { - expect(data.toString('utf8')).toBe(sanity); - done(); - }); -}); - -test('createReadStream throws with string start argument', () => { - expect(() => { - fs.createReadStream(tempFile, { start: '4', end: 6 }); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); -}); - -test('createReadStream throws with string end argument', () => { - expect(() => { - fs.createReadStream(tempFile, { start: 4, end: '6' }); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); -}); - -test('createWriteStream throws with string start argument', () => { - expect(() => { - fs.createWriteStream(tempFile, { start: '4' }); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); -}); - -//<#END_FILE: test-fs-non-number-arguments-throw.js diff --git a/test/js/node/test/parallel/fs-open-mode-mask.test.js b/test/js/node/test/parallel/fs-open-mode-mask.test.js deleted file mode 100644 index 798005d296..0000000000 --- a/test/js/node/test/parallel/fs-open-mode-mask.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-fs-open-mode-mask.js -//#SHA1: d290e7c1bced1fc3a98f27e2aeb463051581376c -//----------------- -"use strict"; - -// This tests that the lower bits of mode > 0o777 still works in fs.open(). - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const mode = process.platform === "win32" ? 0o444 : 0o644; - -const maskToIgnore = 0o10000; - -const tmpdir = path.join(os.tmpdir(), "test-fs-open-mode-mask"); - -beforeAll(() => { - try { - fs.mkdirSync(tmpdir, { recursive: true }); - } catch (err) { - // Directory might already exist - } -}); - -afterAll(() => { - try { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } catch (err) { - // Ignore errors during cleanup - } -}); - -function test(mode, asString) { - const suffix = asString ? "str" : "num"; - const input = asString ? (mode | maskToIgnore).toString(8) : mode | maskToIgnore; - - it(`should work with ${suffix} input`, () => { - const file = path.join(tmpdir, `openSync-${suffix}.txt`); - const fd = fs.openSync(file, "w+", input); - expect(fs.fstatSync(fd).mode & 0o777).toBe(mode); - fs.closeSync(fd); - expect(fs.statSync(file).mode & 0o777).toBe(mode); - }); - - it(`should work with ${suffix} input using callback`, done => { - const file = path.join(tmpdir, `open-${suffix}.txt`); - fs.open(file, "w+", input, (err, fd) => { - expect(err).toBeNull(); - expect(fs.fstatSync(fd).mode & 0o777).toBe(mode); - fs.closeSync(fd); - expect(fs.statSync(file).mode & 0o777).toBe(mode); - done(); - }); - }); -} - -test(mode, true); -test(mode, false); - -//<#END_FILE: test-fs-open-mode-mask.js diff --git a/test/js/node/test/parallel/fs-open-no-close.test.js b/test/js/node/test/parallel/fs-open-no-close.test.js deleted file mode 100644 index fbd17dc78c..0000000000 --- a/test/js/node/test/parallel/fs-open-no-close.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-fs-open-no-close.js -//#SHA1: 3f09a04c65d9a376e5d9b82882d375ab1dc99ad9 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const debuglog = (arg) => { - console.log(new Date().toLocaleString(), arg); -}; - -const tmpdir = path.join(os.tmpdir(), 'test-fs-open-no-close'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('fs.open should not keep the event loop open if file is not closed', (done) => { - let openFd; - - fs.open(path.join(tmpdir, 'dummy'), 'wx+', (err, fd) => { - debuglog('fs open() callback'); - expect(err).toBeFalsy(); - openFd = fd; - done(); - }); - - debuglog('waiting for callback'); - - // Simulate process.on('beforeExit') behavior - process.nextTick(() => { - if (openFd) { - fs.closeSync(openFd); - } - }); -}); - -//<#END_FILE: test-fs-open-no-close.js diff --git a/test/js/node/test/parallel/fs-open-numeric-flags.test.js b/test/js/node/test/parallel/fs-open-numeric-flags.test.js deleted file mode 100644 index 4edec0e495..0000000000 --- a/test/js/node/test/parallel/fs-open-numeric-flags.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-fs-open-numeric-flags.js -//#SHA1: 31a49fd78cbd63ab0b41de5f051d029bbe22fded -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Create a temporary directory for our tests -const tmpdir = path.join(os.tmpdir(), "test-fs-open-numeric-flags"); - -beforeEach(() => { - // Ensure the temporary directory exists and is empty - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterEach(() => { - // Clean up the temporary directory after each test - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } -}); - -test("O_WRONLY without O_CREAT shall fail with ENOENT", () => { - const pathNE = path.join(tmpdir, "file-should-not-exist"); - - expect(() => { - fs.openSync(pathNE, fs.constants.O_WRONLY); - }).toThrow( - expect.objectContaining({ - code: "ENOENT", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-fs-open-numeric-flags.js diff --git a/test/js/node/test/parallel/fs-open.test.js b/test/js/node/test/parallel/fs-open.test.js deleted file mode 100644 index c8c102d7a3..0000000000 --- a/test/js/node/test/parallel/fs-open.test.js +++ /dev/null @@ -1,102 +0,0 @@ -//#FILE: test-fs-open.js -//#SHA1: 0466ad8882a3256fdd8da5fc8da3167f6dde4fd6 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); - -test('fs.openSync throws ENOENT for non-existent file', () => { - expect(() => { - fs.openSync('/8hvftyuncxrt/path/to/file/that/does/not/exist', 'r'); - }).toThrow(expect.objectContaining({ - code: 'ENOENT', - message: expect.any(String) - })); -}); - -test('fs.openSync succeeds for existing file', () => { - expect(() => fs.openSync(__filename)).not.toThrow(); -}); - -test('fs.open succeeds with various valid arguments', async () => { - await expect(fs.promises.open(__filename)).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'r')).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'rs')).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'r', 0)).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'r', null)).resolves.toBeDefined(); -}); - -test('fs.open throws for invalid mode argument', () => { - expect(() => fs.open(__filename, 'r', 'boom', () => {})).toThrow(({ - code: 'ERR_INVALID_ARG_VALUE', - name: 'TypeError', - message: `The argument 'mode' must be a 32-bit unsigned integer or an octal string. Received boom` - })); - expect(() => fs.open(__filename, 'r', 5.5, () => {})).toThrow(({ - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: `The value of "mode" is out of range. It must be an integer. Received 5.5` - })); - expect(() => fs.open(__filename, 'r', -7, () => {})).toThrow(({ - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: `The value of "mode" is out of range. It must be >= 0 and <= 4294967295. Received -7` - })); - expect(() => fs.open(__filename, 'r', 4304967295, () => {})).toThrow(({ - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: `The value of "mode" is out of range. It must be >= 0 and <= 4294967295. Received 4304967295` - })); -}); - -test('fs.open throws for invalid argument combinations', () => { - const invalidArgs = [[], ['r'], ['r', 0], ['r', 0, 'bad callback']]; - invalidArgs.forEach(args => { - expect(() => fs.open(__filename, ...args)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); -}); - -test('fs functions throw for invalid path types', () => { - const invalidPaths = [false, 1, [], {}, null, undefined]; - invalidPaths.forEach(path => { - expect(() => fs.open(path, 'r', () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - expect(() => fs.openSync(path, 'r')).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - expect(fs.promises.open(path, 'r')).rejects.toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); -}); - -test('fs functions throw for invalid modes', () => { - const invalidModes = [false, [], {}]; - invalidModes.forEach(mode => { - expect(() => fs.open(__filename, 'r', mode, () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - message: expect.any(String) - })); - expect(() => fs.openSync(__filename, 'r', mode)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - message: expect.any(String) - })); - expect(fs.promises.open(__filename, 'r', mode)).rejects.toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - message: expect.any(String) - })); - }); -}); - -//<#END_FILE: test-fs-open.js diff --git a/test/js/node/test/parallel/fs-operations-with-surrogate-pairs.test.js b/test/js/node/test/parallel/fs-operations-with-surrogate-pairs.test.js deleted file mode 100644 index 54c9021472..0000000000 --- a/test/js/node/test/parallel/fs-operations-with-surrogate-pairs.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-operations-with-surrogate-pairs.js -//#SHA1: c59fe103e9ec4edee50c9186d341f5fdd32e0af4 -//----------------- -"use strict"; - -const fs = require("node:fs"); -const path = require("node:path"); -const tmpdir = require("../common/tmpdir"); - -tmpdir.refresh(); - -describe("File operations with filenames containing surrogate pairs", () => { - it("should write, read, and delete a file with surrogate pairs in the filename", () => { - // Create a temporary directory - const tempdir = fs.mkdtempSync(tmpdir.resolve("emoji-fruit-🍇 🍈 🍉 🍊 🍋")); - expect(fs.existsSync(tempdir)).toBe(true); - - const filename = "🚀🔥🛸.txt"; - const content = "Test content"; - - // Write content to a file - fs.writeFileSync(path.join(tempdir, filename), content); - - // Read content from the file - const readContent = fs.readFileSync(path.join(tempdir, filename), "utf8"); - - // Check if the content matches - expect(readContent).toBe(content); - }); -}); - -//<#END_FILE: test-fs-operations-with-surrogate-pairs.js diff --git a/test/js/node/test/parallel/fs-options-immutable.test.js b/test/js/node/test/parallel/fs-options-immutable.test.js deleted file mode 100644 index 9beef4c128..0000000000 --- a/test/js/node/test/parallel/fs-options-immutable.test.js +++ /dev/null @@ -1,148 +0,0 @@ -//#FILE: test-fs-options-immutable.js -//#SHA1: 3e986f4e0d29505ada9980c8af5146abd307ddb7 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// These tests make sure that the `options` object passed to these functions are -// never altered. -// -// Refer: https://github.com/nodejs/node/issues/7655 - -const originalOptions = {}; -let options; - -beforeEach(() => { - options = JSON.parse(JSON.stringify(originalOptions)); -}); - -const tmpdir = { - path: path.join(os.tmpdir(), "node-test-fs-options-immutable"), - refresh: () => { - try { - fs.rmSync(tmpdir.path, { recursive: true, force: true }); - } catch (error) { - // Ignore errors - } - fs.mkdirSync(tmpdir.path, { recursive: true }); - }, - resolve: filename => path.join(tmpdir.path, filename), -}; - -tmpdir.refresh(); - -test("fs.readFile", async () => { - await fs.promises.readFile(__filename, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readFileSync", () => { - fs.readFileSync(__filename, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readdir", async () => { - await fs.promises.readdir(__dirname, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readdirSync", () => { - fs.readdirSync(__dirname, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readlink and fs.readlinkSync", async () => { - const canCreateSymLink = await new Promise(resolve => { - fs.symlink(__filename, "dummy-symlink", err => { - if (err) resolve(false); - fs.unlink("dummy-symlink", () => resolve(true)); - }); - }); - - if (canCreateSymLink) { - const sourceFile = tmpdir.resolve("test-readlink"); - const linkFile = tmpdir.resolve("test-readlink-link"); - - await fs.promises.writeFile(sourceFile, ""); - await fs.promises.symlink(sourceFile, linkFile); - - await fs.promises.readlink(linkFile, options); - expect(options).toEqual(originalOptions); - - fs.readlinkSync(linkFile, options); - expect(options).toEqual(originalOptions); - } else { - test.skip("Symlink tests skipped - cannot create symlinks", () => {}); - } -}); - -test("fs.writeFile and fs.writeFileSync", async () => { - const fileName = tmpdir.resolve("writeFile"); - fs.writeFileSync(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); - - await fs.promises.writeFile(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); -}); - -test("fs.appendFile and fs.appendFileSync", async () => { - const fileName = tmpdir.resolve("appendFile"); - fs.appendFileSync(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); - - await fs.promises.appendFile(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); -}); - -test("fs.watch", () => { - if (process.platform === "os400") { - return test.skip("IBMi does not support fs.watch()"); - } - - const watch = fs.watch(__filename, options, () => {}); - watch.close(); - expect(options).toEqual(originalOptions); -}); - -test("fs.watchFile and fs.unwatchFile", () => { - fs.watchFile(__filename, options, () => {}); - fs.unwatchFile(__filename); - expect(options).toEqual(originalOptions); -}); - -test("fs.realpath and fs.realpathSync", async () => { - fs.realpathSync(__filename, options); - expect(options).toEqual(originalOptions); - - await fs.promises.realpath(__filename, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.mkdtemp and fs.mkdtempSync", async () => { - const tempFileName = tmpdir.resolve("mkdtemp-"); - fs.mkdtempSync(tempFileName, options); - expect(options).toEqual(originalOptions); - - await fs.promises.mkdtemp(tempFileName, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.WriteStream and fs.ReadStream", done => { - const fileName = tmpdir.resolve("streams"); - const writeStream = fs.createWriteStream(fileName, options); - writeStream.once("open", () => { - expect(options).toEqual(originalOptions); - const readStream = fs.createReadStream(fileName, options); - readStream.once("open", () => { - expect(options).toEqual(originalOptions); - readStream.destroy(); - writeStream.end(); - done(); - }); - }); -}); - -//<#END_FILE: test-fs-options-immutable.js diff --git a/test/js/node/test/parallel/fs-promises-exists.test.js b/test/js/node/test/parallel/fs-promises-exists.test.js deleted file mode 100644 index 303718b0ef..0000000000 --- a/test/js/node/test/parallel/fs-promises-exists.test.js +++ /dev/null @@ -1,14 +0,0 @@ -//#FILE: test-fs-promises-exists.js -//#SHA1: 3766c49e29d13338f3124165428e3a8a37d47fab -//----------------- -"use strict"; - -const fs = require("fs"); -const fsPromises = require("fs/promises"); - -test("fs.promises exists and is correctly linked", () => { - expect(fsPromises).toBe(fs.promises); - expect(fsPromises.constants).toBe(fs.constants); -}); - -//<#END_FILE: test-fs-promises-exists.js diff --git a/test/js/node/test/parallel/fs-promises-file-handle-append-file.test.js b/test/js/node/test/parallel/fs-promises-file-handle-append-file.test.js deleted file mode 100644 index 746b7614a8..0000000000 --- a/test/js/node/test/parallel/fs-promises-file-handle-append-file.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-fs-promises-file-handle-append-file.js -//#SHA1: 2a1932450418ea18ef00a890342f29ab307006e7 -//----------------- -'use strict'; - -const fs = require('fs'); -const { open } = fs.promises; -const path = require('path'); -const os = require('os'); - -const tmpDir = path.join(os.tmpdir(), 'test-fs-promises-file-handle-append-file'); - -beforeAll(() => { - if (fs.existsSync(tmpDir)) { - fs.rmSync(tmpDir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -test('FileHandle.appendFile with buffer', async () => { - const filePath = path.resolve(tmpDir, 'tmp-append-file-buffer.txt'); - const fileHandle = await open(filePath, 'a'); - const buffer = Buffer.from('a&Dp'.repeat(100), 'utf8'); - - await fileHandle.appendFile(buffer); - const appendedFileData = fs.readFileSync(filePath); - expect(appendedFileData).toEqual(buffer); - - await fileHandle.close(); -}); - -test('FileHandle.appendFile with string', async () => { - const filePath = path.resolve(tmpDir, 'tmp-append-file-string.txt'); - const fileHandle = await open(filePath, 'a'); - const string = 'x~yz'.repeat(100); - - await fileHandle.appendFile(string); - const stringAsBuffer = Buffer.from(string, 'utf8'); - const appendedFileData = fs.readFileSync(filePath); - expect(appendedFileData).toEqual(stringAsBuffer); - - await fileHandle.close(); -}); - -//<#END_FILE: test-fs-promises-file-handle-append-file.js diff --git a/test/js/node/test/parallel/fs-promises-file-handle-chmod.test.js b/test/js/node/test/parallel/fs-promises-file-handle-chmod.test.js deleted file mode 100644 index 8bd751f1d1..0000000000 --- a/test/js/node/test/parallel/fs-promises-file-handle-chmod.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-fs-promises-file-handle-chmod.js -//#SHA1: 50a28df8df34deeca4b2f9d7598fb596894d7541 -//----------------- -"use strict"; - -const fs = require("fs"); -const { open } = fs.promises; -const path = require("path"); -const os = require("os"); - -const tmpDir = os.tmpdir(); - -beforeEach(() => { - jest.spyOn(fs, "statSync"); -}); - -afterEach(() => { - jest.restoreAllMocks(); -}); - -test("FileHandle.chmod base functionality", async () => { - const filePath = path.resolve(tmpDir, "tmp-chmod.txt"); - const fileHandle = await open(filePath, "w+", 0o444); - - // File created with r--r--r-- 444 - const statsBeforeMod = fs.statSync(filePath); - expect(statsBeforeMod.mode & 0o444).toBe(0o444); - - let expectedAccess; - const newPermissions = 0o765; - - if (process.platform === "win32") { - // Chmod in Windows will only toggle read only/write access. The - // fs.Stats.mode in Windows is computed using read/write - // bits (not exec). Read-only at best returns 444; r/w 666. - // Refer: /deps/uv/src/win/fs.cfs; - expectedAccess = 0o664; - } else { - expectedAccess = newPermissions; - } - - // Change the permissions to rwxr--r-x - await fileHandle.chmod(newPermissions); - const statsAfterMod = fs.statSync(filePath); - expect(statsAfterMod.mode & expectedAccess).toBe(expectedAccess); - - await fileHandle.close(); -}); - -//<#END_FILE: test-fs-promises-file-handle-chmod.js diff --git a/test/js/node/test/parallel/fs-promises-file-handle-write.test.js b/test/js/node/test/parallel/fs-promises-file-handle-write.test.js deleted file mode 100644 index 1652a75a05..0000000000 --- a/test/js/node/test/parallel/fs-promises-file-handle-write.test.js +++ /dev/null @@ -1,93 +0,0 @@ -//#FILE: test-fs-promises-file-handle-write.js -//#SHA1: 6ca802494e0ce0ee3187b1661322f115cfd7340c -//----------------- -"use strict"; - -const fs = require("fs"); -const { open } = fs.promises; -const path = require("path"); -const os = require("os"); - -const tmpDir = path.join(os.tmpdir(), "test-fs-promises-file-handle-write"); - -beforeAll(() => { - if (fs.existsSync(tmpDir)) { - fs.rmSync(tmpDir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -test("validateWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const buffer = Buffer.from("Hello world".repeat(100), "utf8"); - - await fileHandle.write(buffer, 0, buffer.length); - const readFileData = fs.readFileSync(filePathForHandle); - expect(readFileData).toEqual(buffer); - - await fileHandle.close(); -}); - -test("validateEmptyWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-empty-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const buffer = Buffer.from(""); // empty buffer - - await fileHandle.write(buffer, 0, buffer.length); - const readFileData = fs.readFileSync(filePathForHandle); - expect(readFileData).toEqual(buffer); - - await fileHandle.close(); -}); - -test("validateNonUint8ArrayWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-data-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const buffer = Buffer.from("Hello world", "utf8").toString("base64"); - - await fileHandle.write(buffer, 0, buffer.length); - const readFileData = fs.readFileSync(filePathForHandle); - expect(readFileData).toEqual(Buffer.from(buffer, "utf8")); - - await fileHandle.close(); -}); - -test("validateNonStringValuesWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-non-string-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const nonStringValues = [ - 123, - {}, - new Map(), - null, - undefined, - 0n, - () => {}, - Symbol(), - true, - new String("notPrimitive"), - { - toString() { - return "amObject"; - }, - }, - { [Symbol.toPrimitive]: hint => "amObject" }, - ]; - for (const nonStringValue of nonStringValues) { - await expect(fileHandle.write(nonStringValue)).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/"buffer"/), - code: "ERR_INVALID_ARG_TYPE", - }), - ); - } - - await fileHandle.close(); -}); - -//<#END_FILE: test-fs-promises-file-handle-write.js diff --git a/test/js/node/test/parallel/fs-promises-readfile-empty.test.js b/test/js/node/test/parallel/fs-promises-readfile-empty.test.js deleted file mode 100644 index dc9d291e5f..0000000000 --- a/test/js/node/test/parallel/fs-promises-readfile-empty.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-fs-promises-readfile-empty.js -//#SHA1: 6fcec9b5d3c9617426d46c79fb79244bc236574b -//----------------- -"use strict"; - -const fs = require("fs").promises; -const path = require("path"); - -const fixturesPath = path.resolve(__dirname, "..", "fixtures"); -const fn = path.join(fixturesPath, "empty.txt"); - -test("fs.readFile on empty file", async () => { - const content = await fs.readFile(fn); - expect(content).toBeTruthy(); -}); - -test("fs.readFile on empty file with utf8 encoding", async () => { - const content = await fs.readFile(fn, "utf8"); - expect(content).toBe(""); -}); - -test("fs.readFile on empty file with options object", async () => { - const content = await fs.readFile(fn, { encoding: "utf8" }); - expect(content).toBe(""); -}); - -//<#END_FILE: test-fs-promises-readfile-empty.js diff --git a/test/js/node/test/parallel/fs-promises-readfile-with-fd.test.js b/test/js/node/test/parallel/fs-promises-readfile-with-fd.test.js deleted file mode 100644 index 1f286daab3..0000000000 --- a/test/js/node/test/parallel/fs-promises-readfile-with-fd.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-fs-promises-readfile-with-fd.js -//#SHA1: 041811f02dddcdb9eba7d97e3943e26ec6b881cd -//----------------- -'use strict'; - -const fs = require('fs'); -const fsPromises = require('fs').promises; -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-promises-readfile-with-fd'); -const fn = path.join(tmpdir, 'test.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(fn, 'Hello World'); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('readFile() reads from current position of the file', async () => { - const handle = await fsPromises.open(fn, 'r'); - - // Read only five bytes, so that the position moves to five. - const buf = Buffer.alloc(5); - const { bytesRead } = await handle.read(buf, 0, 5, null); - expect(bytesRead).toBe(5); - expect(buf.toString()).toBe('Hello'); - - // readFile() should read from position five, instead of zero. - expect((await handle.readFile()).toString()).toBe(' World'); - - await handle.close(); -}); - -//<#END_FILE: test-fs-promises-readfile-with-fd.js diff --git a/test/js/node/test/parallel/fs-promises-writefile-typedarray.test.js b/test/js/node/test/parallel/fs-promises-writefile-typedarray.test.js deleted file mode 100644 index bece880bd3..0000000000 --- a/test/js/node/test/parallel/fs-promises-writefile-typedarray.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-promises-writefile-typedarray.js -//#SHA1: 718d3827c56ad0b11c59a801bf9529a1e6e5ab89 -//----------------- -"use strict"; - -const fs = require("fs"); -const fsPromises = fs.promises; -const path = require("path"); -const os = require("os"); - -const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - -beforeAll(() => { - // Ensure the temporary directory is clean - fs.rmSync(tmpDir, { recursive: true, force: true }); - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - // Clean up the temporary directory - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -const dest = path.resolve(tmpDir, "tmp.txt"); -// Use a file size larger than `kReadFileMaxChunkSize`. -const buffer = Buffer.from("012".repeat(2 ** 14)); - -test("fsPromises.writeFile with TypedArrays", async () => { - const constructors = [Uint8Array, Uint16Array, Uint32Array]; - - for (const Constructor of constructors) { - const array = new Constructor(buffer.buffer); - await fsPromises.writeFile(dest, array); - const data = await fsPromises.readFile(dest); - expect(data).toEqual(buffer); - } -}); - -//<#END_FILE: test-fs-promises-writefile-typedarray.js diff --git a/test/js/node/test/parallel/fs-promises-writefile-with-fd.test.js b/test/js/node/test/parallel/fs-promises-writefile-with-fd.test.js deleted file mode 100644 index 17182bedb6..0000000000 --- a/test/js/node/test/parallel/fs-promises-writefile-with-fd.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-fs-promises-writefile-with-fd.js -//#SHA1: 55be58e0edcbdc914795c46280459a85071f28eb -//----------------- -"use strict"; - -// This test makes sure that `writeFile()` always writes from the current -// position of the file, instead of truncating the file. - -const fs = require("fs"); -const fsPromises = require("fs").promises; -const path = require("path"); -const os = require("os"); - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("writeFile() writes from current position", async () => { - const fn = path.join(tmpdir, "test.txt"); - - const handle = await fsPromises.open(fn, "w"); - - /* Write only five bytes, so that the position moves to five. */ - const buf = Buffer.from("Hello"); - const { bytesWritten } = await handle.write(buf, 0, 5, null); - expect(bytesWritten).toBe(5); - - /* Write some more with writeFile(). */ - await handle.writeFile("World"); - - /* New content should be written at position five, instead of zero. */ - expect(fs.readFileSync(fn, "utf8")).toBe("HelloWorld"); - - await handle.close(); -}); - -//<#END_FILE: test-fs-promises-writefile-with-fd.js diff --git a/test/js/node/test/parallel/fs-promisified.test.js b/test/js/node/test/parallel/fs-promisified.test.js deleted file mode 100644 index 3df07cc98e..0000000000 --- a/test/js/node/test/parallel/fs-promisified.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-fs-promisified.js -//#SHA1: 5366497c2a750295d2c5cf65c2938e27f573e8bb -//----------------- -"use strict"; - -const fs = require("fs"); -const { promisify } = require("util"); - -const read = promisify(fs.read); -const write = promisify(fs.write); -const exists = promisify(fs.exists); - -test("promisified fs.read", async () => { - const fd = fs.openSync(__filename, "r"); - const obj = await read(fd, Buffer.alloc(1024), 0, 1024, null); - expect(typeof obj.bytesRead).toBe("number"); - expect(obj.buffer).toBeInstanceOf(Buffer); - fs.closeSync(fd); -}); - -test("promisified fs.write", async () => { - const tmpdir = require("../common/tmpdir"); - tmpdir.refresh(); - const filename = tmpdir.resolve("write-promise.txt"); - const fd = fs.openSync(filename, "w"); - const obj = await write(fd, Buffer.from("foobar")); - expect(typeof obj.bytesWritten).toBe("number"); - expect(obj.buffer.toString()).toBe("foobar"); - fs.closeSync(fd); -}); - -test("promisified fs.exists", async () => { - const result = await exists(__filename); - expect(result).toBe(true); -}); - -//<#END_FILE: test-fs-promisified.js diff --git a/test/js/node/test/parallel/fs-read-empty-buffer.test.js b/test/js/node/test/parallel/fs-read-empty-buffer.test.js deleted file mode 100644 index 04fe94f967..0000000000 --- a/test/js/node/test/parallel/fs-read-empty-buffer.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-fs-read-empty-buffer.js -//#SHA1: a2dc2c25e5a712b62c41298f885df24dd6106646 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); - -const filepath = path.resolve(__dirname, 'x.txt'); -let fd; - -beforeAll(() => { - // Create a test file - fs.writeFileSync(filepath, 'test content'); - fd = fs.openSync(filepath, 'r'); -}); - -afterAll(() => { - fs.closeSync(fd); - fs.unlinkSync(filepath); -}); - -const buffer = new Uint8Array(); - -test('fs.readSync throws ERR_INVALID_ARG_VALUE for empty buffer', () => { - expect(() => fs.readSync(fd, buffer, 0, 10, 0)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE', - message: expect.stringContaining('The argument \'buffer\' is empty and cannot be written') - })); -}); - -test('fs.read throws ERR_INVALID_ARG_VALUE for empty buffer', () => { - expect(() => fs.read(fd, buffer, 0, 1, 0, () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE', - message: expect.stringContaining('The argument \'buffer\' is empty and cannot be written') - })); -}); - -test('fsPromises.filehandle.read rejects with ERR_INVALID_ARG_VALUE for empty buffer', async () => { - const filehandle = await fs.promises.open(filepath, 'r'); - await expect(filehandle.read(buffer, 0, 1, 0)).rejects.toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE', - message: expect.stringContaining('The argument \'buffer\' is empty and cannot be written') - })); - await filehandle.close(); -}); - -//<#END_FILE: test-fs-read-empty-buffer.js diff --git a/test/js/node/test/parallel/fs-read-optional-params.test.js b/test/js/node/test/parallel/fs-read-optional-params.test.js deleted file mode 100644 index 8abd08c20e..0000000000 --- a/test/js/node/test/parallel/fs-read-optional-params.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-fs-read-optional-params.js -//#SHA1: daea619faa084927d87381fc60aedde3068a13ca -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const filepath = path.join(os.tmpdir(), "x.txt"); -const expected = Buffer.from("xyz\n"); -const defaultBufferAsync = Buffer.alloc(16384); -const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); - -beforeAll(() => { - fs.writeFileSync(filepath, expected); -}); - -afterAll(() => { - fs.unlinkSync(filepath); -}); - -function testValid(message, ...options) { - test(`${message} (as params)`, async () => { - const paramsFilehandle = fs.openSync(filepath, "r"); - await new Promise(resolve => { - fs.read(paramsFilehandle, ...options, (err, bytesRead, buffer) => { - expect(err).toBeNull(); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(defaultBufferAsync.byteLength); - fs.closeSync(paramsFilehandle); - resolve(); - }); - }); - }); - - test(`${message} (as options)`, async () => { - const optionsFilehandle = fs.openSync(filepath, "r"); - await new Promise(resolve => { - fs.read(optionsFilehandle, bufferAsOption, ...options, (err, bytesRead, buffer) => { - expect(err).toBeNull(); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(bufferAsOption.byteLength); - fs.closeSync(optionsFilehandle); - resolve(); - }); - }); - }); -} - -testValid("Not passing in any object"); -testValid("Passing in a null", null); -testValid("Passing in an empty object", {}); -testValid("Passing in an object", { - offset: 0, - length: bufferAsOption.byteLength, - position: 0, -}); - -//<#END_FILE: test-fs-read-optional-params.js diff --git a/test/js/node/test/parallel/fs-read-promises-optional-params.test.js b/test/js/node/test/parallel/fs-read-promises-optional-params.test.js deleted file mode 100644 index 60353b9226..0000000000 --- a/test/js/node/test/parallel/fs-read-promises-optional-params.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-fs-read-promises-optional-params.js -//#SHA1: bc986664534329fd86b9aafd4c73a0159f71d388 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { promisify } = require('util'); -const read = promisify(fs.read); - -const filepath = path.resolve(__dirname, 'x.txt'); -let fd; - -const expected = Buffer.from('xyz\n'); -const defaultBufferAsync = Buffer.alloc(16384); -const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); - -beforeAll(() => { - // Create the test file - fs.writeFileSync(filepath, expected); - fd = fs.openSync(filepath, 'r'); -}); - -afterAll(() => { - fs.closeSync(fd); - fs.unlinkSync(filepath); -}); - -test('read with empty options object', async () => { - const { bytesRead, buffer } = await read(fd, {}); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(defaultBufferAsync.byteLength); -}); - -test('read with buffer and position options', async () => { - const { bytesRead, buffer } = await read(fd, bufferAsOption, { position: 0 }); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(bufferAsOption.byteLength); -}); - -//<#END_FILE: test-fs-read-promises-optional-params.js diff --git a/test/js/node/test/parallel/fs-read-stream-autoclose.test.js b/test/js/node/test/parallel/fs-read-stream-autoclose.test.js deleted file mode 100644 index 36ab013cd8..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-autoclose.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-read-stream-autoClose.js -//#SHA1: 0fbd57ecd5ae02143036c03cdca120bc7c3deea1 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const writeFile = path.join(os.tmpdir(), "write-autoClose.txt"); - -beforeEach(() => { - // Clean up the temporary directory - try { - fs.unlinkSync(writeFile); - } catch (err) { - // Ignore errors if file doesn't exist - } -}); - -test("fs.createWriteStream with autoClose option", done => { - const file = fs.createWriteStream(writeFile, { autoClose: true }); - - file.on("finish", () => { - expect(file.destroyed).toBe(false); - done(); - }); - - file.end("asd"); -}); - -//<#END_FILE: test-fs-read-stream-autoClose.js diff --git a/test/js/node/test/parallel/fs-read-stream-double-close.test.js b/test/js/node/test/parallel/fs-read-stream-double-close.test.js deleted file mode 100644 index 73603d1f1c..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-double-close.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-read-stream-double-close.js -//#SHA1: 066b117ee2b44bedfdce77d06389406b2474eb2f -//----------------- -'use strict'; - -const fs = require('fs'); - -test('double close on ReadStream', (done) => { - const s = fs.createReadStream(__filename); - - let closeCount = 0; - const checkClose = () => { - closeCount++; - if (closeCount === 2) { - done(); - } - }; - - s.close(checkClose); - s.close(checkClose); -}); - -test('double destroy on ReadStream', (done) => { - const s = fs.createReadStream(__filename); - - let destroyCount = 0; - const checkDestroy = () => { - destroyCount++; - if (destroyCount === 2) { - done(); - } - }; - - // This is a private API, but it is worth testing. close calls this - s.destroy(null, checkDestroy); - s.destroy(null, checkDestroy); -}); - -//<#END_FILE: test-fs-read-stream-double-close.js diff --git a/test/js/node/test/parallel/fs-read-stream-fd-leak.test.js b/test/js/node/test/parallel/fs-read-stream-fd-leak.test.js deleted file mode 100644 index 5c4e0dd55b..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-fd-leak.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-read-stream-fd-leak.js -//#SHA1: fc07b42f524d6a2f9743a5a7665c92096f58505b -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); - -let openCount = 0; -const _fsopen = fs.open; -const _fsclose = fs.close; - -const loopCount = 50; -const totalCheck = 50; -const emptyTxt = path.join(__dirname, "../fixtures/empty.txt"); - -fs.open = function () { - openCount++; - return _fsopen.apply(null, arguments); -}; - -fs.close = function () { - openCount--; - return _fsclose.apply(null, arguments); -}; - -function testLeak(endFn) { - return new Promise(resolve => { - console.log(`testing for leaks from fs.createReadStream().${endFn}()...`); - - let i = 0; - let check = 0; - - function checkFunction() { - if (openCount !== 0 && check < totalCheck) { - check++; - setTimeout(checkFunction, 100); - return; - } - - expect(openCount).toBe(0); - openCount = 0; - resolve(); - } - - const interval = setInterval(() => { - const s = fs.createReadStream(emptyTxt); - s[endFn](); - - if (++i === loopCount) { - clearInterval(interval); - setTimeout(checkFunction, 100); - } - }, 2); - }); -} - -test("no leaked file descriptors using close()", async () => { - await testLeak("close"); -}, 10000); - -test("no leaked file descriptors using destroy()", async () => { - await testLeak("destroy"); -}, 10000); - -//<#END_FILE: test-fs-read-stream-fd-leak.js diff --git a/test/js/node/test/parallel/fs-read-stream-pos.test.js b/test/js/node/test/parallel/fs-read-stream-pos.test.js deleted file mode 100644 index bdc551a7c1..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-pos.test.js +++ /dev/null @@ -1,103 +0,0 @@ -//#FILE: test-fs-read-stream-pos.js -//#SHA1: e44b357d8045cfa1e8129a160254dcfb9225d990 -//----------------- -"use strict"; - -// Refs: https://github.com/nodejs/node/issues/33940 - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = { - refresh: () => { - // Implement tmpdir.refresh() if needed - }, - resolve: filename => path.join(os.tmpdir(), filename), -}; - -tmpdir.refresh(); - -const file = tmpdir.resolve("read_stream_pos_test.txt"); - -fs.writeFileSync(file, ""); - -let counter = 0; - -const writeInterval = setInterval(() => { - counter = counter + 1; - const line = `hello at ${counter}\n`; - fs.writeFileSync(file, line, { flag: "a" }); -}, 1); - -const hwm = 10; -let bufs = []; -let isLow = false; -let cur = 0; -let stream; - -const readInterval = setInterval(() => { - if (stream) return; - - stream = fs.createReadStream(file, { - highWaterMark: hwm, - start: cur, - }); - stream.on( - "data", - jest.fn(chunk => { - cur += chunk.length; - bufs.push(chunk); - if (isLow) { - const brokenLines = Buffer.concat(bufs) - .toString() - .split("\n") - .filter(line => { - const s = "hello at".slice(0, line.length); - if (line && !line.startsWith(s)) { - return true; - } - return false; - }); - expect(brokenLines.length).toBe(0); - exitTest(); - return; - } - if (chunk.length !== hwm) { - isLow = true; - } - }), - ); - stream.on("end", () => { - stream = null; - isLow = false; - bufs = []; - }); -}, 10); - -// Time longer than 90 seconds to exit safely -const endTimer = setTimeout(() => { - exitTest(); -}, 90000); - -const exitTest = () => { - clearInterval(readInterval); - clearInterval(writeInterval); - clearTimeout(endTimer); - if (stream && !stream.destroyed) { - stream.on("close", () => { - process.exit(); - }); - stream.destroy(); - } else { - process.exit(); - } -}; - -test("fs read stream position", () => { - // This test is mostly about setting up the environment and running the intervals - // The actual assertions are made within the intervals - expect(true).toBe(true); -}); - -//<#END_FILE: test-fs-read-stream-pos.js diff --git a/test/js/node/test/parallel/fs-readdir-buffer.test.js b/test/js/node/test/parallel/fs-readdir-buffer.test.js deleted file mode 100644 index 4003405487..0000000000 --- a/test/js/node/test/parallel/fs-readdir-buffer.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-fs-readdir-buffer.js -//#SHA1: 333645cb13aa3c15d61428ecfa2794e7393ef91c -//----------------- -"use strict"; - -const fs = require("fs"); - -if (process.platform !== "darwin") { - it("skips test on non-MacOS platforms", () => { - test.skip("this test works only on MacOS"); - }); -} else { - test("readdir with buffer and withFileTypes options on MacOS", () => { - return new Promise(resolve => { - fs.readdir(Buffer.from("/dev"), { withFileTypes: true, encoding: "buffer" }, (err, files) => { - expect(err).toBeNull(); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-fs-readdir-buffer.js diff --git a/test/js/node/test/parallel/fs-readdir-recursive.test.js b/test/js/node/test/parallel/fs-readdir-recursive.test.js deleted file mode 100644 index 32c284e827..0000000000 --- a/test/js/node/test/parallel/fs-readdir-recursive.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-readdir-recursive.js -//#SHA1: daa6ac8e46cd3d530e4546354a11f87f1b1c092d -//----------------- -"use strict"; - -const fs = require("fs"); -const net = require("net"); -const path = require("path"); -const os = require("os"); - -const tmpdir = path.join(os.tmpdir(), "node-test-fs-readdir-recursive"); - -beforeAll(() => { - // Refresh tmpdir - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - // Clean up tmpdir - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("fs.readdirSync with recursive option should not crash", done => { - const server = net.createServer().listen(path.join(tmpdir, "test.sock"), () => { - // The process should not crash - // See https://github.com/nodejs/node/issues/52159 - expect(() => { - fs.readdirSync(tmpdir, { recursive: true }); - }).not.toThrow(); - - server.close(); - done(); - }); -}); - -//<#END_FILE: test-fs-readdir-recursive.js diff --git a/test/js/node/test/parallel/fs-readdir.test.js b/test/js/node/test/parallel/fs-readdir.test.js deleted file mode 100644 index 1e98f2f1b6..0000000000 --- a/test/js/node/test/parallel/fs-readdir.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-fs-readdir.js -//#SHA1: ce2c5a12cb271c5023f965afe712e78b1a484ad5 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const readdirDir = path.join(os.tmpdir(), "test-fs-readdir"); -const files = ["empty", "files", "for", "just", "testing"]; - -beforeAll(() => { - // Make sure tmp directory is clean - if (fs.existsSync(readdirDir)) { - fs.rmSync(readdirDir, { recursive: true, force: true }); - } - fs.mkdirSync(readdirDir, { recursive: true }); - - // Create the necessary files - files.forEach(currentFile => { - fs.closeSync(fs.openSync(path.join(readdirDir, currentFile), "w")); - }); -}); - -afterAll(() => { - // Clean up - fs.rmSync(readdirDir, { recursive: true, force: true }); -}); - -test("fs.readdirSync returns correct files", () => { - expect(fs.readdirSync(readdirDir).sort()).toEqual(files); -}); - -test("fs.readdir returns correct files", async () => { - await new Promise(resolve => { - fs.readdir(readdirDir, (err, f) => { - expect(err).toBeNull(); - expect(f.sort()).toEqual(files); - resolve(); - }); - }); -}); - -test("fs.readdirSync throws ENOTDIR on file", () => { - expect(() => { - fs.readdirSync(__filename); - }).toThrow( - expect.objectContaining({ - code: "ENOTDIR", - message: expect.any(String), - }), - ); -}); - -test("fs.readdir throws ENOTDIR on file", async () => { - await new Promise(resolve => { - fs.readdir(__filename, e => { - expect(e).toEqual( - expect.objectContaining({ - code: "ENOTDIR", - message: expect.any(String), - }), - ); - resolve(); - }); - }); -}); - -test("fs.readdir and fs.readdirSync throw on invalid input", () => { - [false, 1, [], {}, null, undefined].forEach(i => { - expect(() => fs.readdir(i, () => {})).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => fs.readdirSync(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-readdir.js diff --git a/test/js/node/test/parallel/fs-readfile-eof.test.js b/test/js/node/test/parallel/fs-readfile-eof.test.js deleted file mode 100644 index f1b68ffccf..0000000000 --- a/test/js/node/test/parallel/fs-readfile-eof.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-fs-readfile-eof.js -//#SHA1: 89b7efe6c30d2316249bfae1d01f16f97e32be04 -//----------------- -"use strict"; - -const fs = require("fs/promises"); -const { exec } = require("child_process"); - -const childType = ["child-encoding", "child-non-encoding"]; - -if (process.argv[2] === childType[0]) { - fs.readFile("/dev/stdin", "utf8").then(data => { - process.stdout.write(data); - }); -} else if (process.argv[2] === childType[1]) { - fs.readFile("/dev/stdin").then(data => { - process.stdout.write(data); - }); -} else { - const data1 = "Hello"; - const data2 = "World"; - const expected = `${data1}\n${data2}\n`; - - const f = JSON.stringify(__filename); - const node = JSON.stringify(process.execPath); - - function testReadFile(child) { - return new Promise((resolve, reject) => { - const cmd = `(echo ${data1}; sleep 0.5; echo ${data2}) | ${node} ${f} ${child}`; - exec(cmd, (error, stdout, stderr) => { - if (error) reject(error); - else resolve({ stdout, stderr }); - }); - }); - } - - if (process.platform === "win32" || process.platform === "aix" || process.platform === "os400") { - test.skip(`No /dev/stdin on ${process.platform}.`, () => {}); - } else { - test("readFile with encoding", async () => { - const { stdout, stderr } = await testReadFile(childType[0]); - expect(stdout).toBe(expected); - expect(stderr).toBe(""); - }); - - test("readFile without encoding", async () => { - const { stdout, stderr } = await testReadFile(childType[1]); - expect(stdout).toBe(expected); - expect(stderr).toBe(""); - }); - } -} - -//<#END_FILE: test-fs-readfile-eof.js diff --git a/test/js/node/test/parallel/fs-readfile-fd.test.js b/test/js/node/test/parallel/fs-readfile-fd.test.js deleted file mode 100644 index b62d15b9e6..0000000000 --- a/test/js/node/test/parallel/fs-readfile-fd.test.js +++ /dev/null @@ -1,111 +0,0 @@ -//#FILE: test-fs-readfile-fd.js -//#SHA1: ec2bc78cb0bab7b8e9b23c1c44a77b227294d8b4 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-readfile-fd'); -const emptyFilePath = path.join(tmpdir, 'empty.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(emptyFilePath, ''); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -function tempFd(callback) { - fs.open(emptyFilePath, 'r', (err, fd) => { - expect(err).toBeFalsy(); - callback(fd, () => { - fs.close(fd, (err) => { - expect(err).toBeFalsy(); - }); - }); - }); -} - -function tempFdSync(callback) { - const fd = fs.openSync(emptyFilePath, 'r'); - callback(fd); - fs.closeSync(fd); -} - -test('fs.readFile with file descriptor', (done) => { - tempFd((fd, close) => { - fs.readFile(fd, (err, data) => { - expect(data).toBeTruthy(); - close(); - done(); - }); - }); -}); - -test('fs.readFile with file descriptor and utf8 encoding', (done) => { - tempFd((fd, close) => { - fs.readFile(fd, 'utf8', (err, data) => { - expect(data).toBe(''); - close(); - done(); - }); - }); -}); - -test('fs.readFileSync with file descriptor', () => { - tempFdSync((fd) => { - expect(fs.readFileSync(fd)).toBeTruthy(); - }); -}); - -test('fs.readFileSync with file descriptor and utf8 encoding', () => { - tempFdSync((fd) => { - expect(fs.readFileSync(fd, 'utf8')).toBe(''); - }); -}); - -test('readFile() reads from current position of file descriptor', (done) => { - const filename = path.join(tmpdir, 'test.txt'); - fs.writeFileSync(filename, 'Hello World'); - - fs.open(filename, 'r', (err, fd) => { - expect(err).toBeFalsy(); - const buf = Buffer.alloc(5); - - fs.read(fd, buf, 0, 5, null, (err, bytes) => { - expect(err).toBeFalsy(); - expect(bytes).toBe(5); - expect(buf.toString()).toBe('Hello'); - - fs.readFile(fd, (err, data) => { - expect(err).toBeFalsy(); - expect(data.toString()).toBe(' World'); - fs.closeSync(fd); - done(); - }); - }); - }); -}); - -test('readFileSync() reads from current position of file descriptor', () => { - const filename = path.join(tmpdir, 'test.txt'); - fs.writeFileSync(filename, 'Hello World'); - - const fd = fs.openSync(filename, 'r'); - - const buf = Buffer.alloc(5); - expect(fs.readSync(fd, buf, 0, 5)).toBe(5); - expect(buf.toString()).toBe('Hello'); - - expect(fs.readFileSync(fd).toString()).toBe(' World'); - - fs.closeSync(fd); -}); - -//<#END_FILE: test-fs-readfile-fd.js diff --git a/test/js/node/test/parallel/fs-readfile-pipe-large.test.js b/test/js/node/test/parallel/fs-readfile-pipe-large.test.js deleted file mode 100644 index 815db4cedd..0000000000 --- a/test/js/node/test/parallel/fs-readfile-pipe-large.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-fs-readfile-pipe-large.js -//#SHA1: 5e2fa068dc742cfe617ccf3f08df6725e92a51f6 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const { exec } = require('child_process'); -const os = require('os'); - -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; -const isIBMi = process.platform === 'os400'; - -const skipPlatforms = ['win32', 'aix', 'os400']; - -// Separate child process logic -if (process.argv[2] === 'child') { - fs.readFile('/dev/stdin', (err, data) => { - if (err) { - console.error(err); - process.exit(1); - } - process.stdout.write(data); - }); -} else { - // Jest test code - describe('fs.readFile pipe large', () => { - const tmpdir = os.tmpdir(); - const filename = path.join(tmpdir, 'readfile_pipe_large_test.txt'); - const dataExpected = 'a'.repeat(999999); - - beforeAll(() => { - if (!skipPlatforms.includes(process.platform)) { - fs.writeFileSync(filename, dataExpected); - } - }); - - afterAll(() => { - if (!skipPlatforms.includes(process.platform)) { - fs.unlinkSync(filename); - } - }); - - test('should read from /dev/stdin and write to stdout', () => { - if (skipPlatforms.includes(process.platform)) { - return test.skip(`No /dev/stdin on ${process.platform}.`); - } - - const f = JSON.stringify(__filename); - const node = JSON.stringify(process.execPath); - const cmd = `cat ${filename} | ${node} ${f} child`; - - return new Promise((resolve, reject) => { - exec(cmd, { maxBuffer: 1000000 }, (error, stdout, stderr) => { - if (error) { - reject(error); - return; - } - - expect(stdout).toBe(dataExpected); - expect(stderr).toBe(''); - resolve(); - }); - }); - }); - }); -} -//<#END_FILE: test-fs-readfile-pipe-large.js diff --git a/test/js/node/test/parallel/fs-readfile-pipe.test.js b/test/js/node/test/parallel/fs-readfile-pipe.test.js deleted file mode 100644 index 9d9cec5cb7..0000000000 --- a/test/js/node/test/parallel/fs-readfile-pipe.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-fs-readfile-pipe.js -//#SHA1: b78e6ea1bbcdaf74b6363f4740bdf2393ed28938 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const { exec } = require('child_process'); - -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; -const isIBMi = process.platform === 'os400'; - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); - -if (isWindows || isAIX || isIBMi) { - test.skip(`No /dev/stdin on ${process.platform}.`, () => {}); -} else { - if (process.argv[2] === 'child') { - fs.readFile('/dev/stdin', (err, data) => { - if (err) { - console.error(err); - process.exit(1); - } - process.stdout.write(data); - }); - } else { - test('readFile pipe test', (done) => { - const filename = path.join(fixturesPath, 'readfile_pipe_test.txt'); - const dataExpected = fs.readFileSync(filename, 'utf8'); - - const f = JSON.stringify(__filename); - const node = JSON.stringify(process.execPath); - const cmd = `cat ${filename} | ${node} ${f} child`; - - exec(cmd, (error, stdout, stderr) => { - if (error) { - done(error); - return; - } - try { - expect(stdout).toBe(dataExpected); - expect(stderr).toBe(''); - done(); - } catch (error) { - done(error); - } - }); - }, 10000); // Increase timeout to 10 seconds - } -} - -//<#END_FILE: test-fs-readfile-pipe.js diff --git a/test/js/node/test/parallel/fs-readfilesync-pipe-large.test.js b/test/js/node/test/parallel/fs-readfilesync-pipe-large.test.js deleted file mode 100644 index 8df6c2e1f0..0000000000 --- a/test/js/node/test/parallel/fs-readfilesync-pipe-large.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-readfilesync-pipe-large.js -//#SHA1: 669e419b344b375a028fa352c7a29eec2d5d52af -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const { exec } = require('child_process'); -const os = require('os'); -const { describe, test, expect, beforeAll, afterAll } = require('@jest/globals'); - -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; -const isIBMi = process.platform === 'os400'; - -const shouldSkip = isWindows || isAIX || isIBMi; - -const tmpdir = os.tmpdir(); - -if (process.argv[2] === 'child') { - process.stdout.write(fs.readFileSync('/dev/stdin', 'utf8')); - process.exit(0); -} - -describe('fs.readFileSync pipe large', () => { - const filename = path.join(tmpdir, 'readfilesync_pipe_large_test.txt'); - const dataExpected = 'a'.repeat(999999); - - beforeAll(() => { - if (!shouldSkip) { - fs.writeFileSync(filename, dataExpected); - } - }); - - afterAll(() => { - if (!shouldSkip) { - fs.unlinkSync(filename); - } - }); - - const testFn = shouldSkip ? test.skip : test; - - testFn('should read large file through pipe', (done) => { - const childScriptPath = path.join(__dirname, 'child-script.js'); - fs.writeFileSync(childScriptPath, ` - const fs = require('fs'); - process.stdout.write(fs.readFileSync('/dev/stdin', 'utf8')); - `); - - const cmd = `cat ${filename} | "${process.execPath}" "${childScriptPath}"`; - - exec(cmd, { maxBuffer: 1000000 }, (error, stdout, stderr) => { - try { - expect(error).toBeNull(); - expect(stdout).toBe(dataExpected); - expect(stderr).toBe(''); - fs.unlinkSync(childScriptPath); - done(); - } catch (err) { - fs.unlinkSync(childScriptPath); - done(err); - } - }); - }, 30000); // Increase timeout to 30 seconds -}); - -//<#END_FILE: test-fs-readfilesync-pipe-large.js diff --git a/test/js/node/test/parallel/fs-readlink-type-check.test.js b/test/js/node/test/parallel/fs-readlink-type-check.test.js deleted file mode 100644 index c0ee6e8b00..0000000000 --- a/test/js/node/test/parallel/fs-readlink-type-check.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-fs-readlink-type-check.js -//#SHA1: dd36bda8e12e6c22c342325345dd8d1de2097d9c -//----------------- -"use strict"; - -const fs = require("fs"); - -[false, 1, {}, [], null, undefined].forEach(i => { - test(`fs.readlink throws for invalid input: ${JSON.stringify(i)}`, () => { - expect(() => fs.readlink(i, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test(`fs.readlinkSync throws for invalid input: ${JSON.stringify(i)}`, () => { - expect(() => fs.readlinkSync(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-readlink-type-check.js diff --git a/test/js/node/test/parallel/fs-readv-promises.test.js b/test/js/node/test/parallel/fs-readv-promises.test.js deleted file mode 100644 index ff66cc9354..0000000000 --- a/test/js/node/test/parallel/fs-readv-promises.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-fs-readv-promises.js -//#SHA1: 43d801fa8a2eabf438e98f5aa713eb9680fe798b -//----------------- -"use strict"; - -const fs = require("fs").promises; -const path = require("path"); -const os = require("os"); - -const expected = "ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف"; -const expectedBuff = Buffer.from(expected); - -let cnt = 0; -function getFileName() { - return path.join(os.tmpdir(), `readv_promises_${++cnt}.txt`); -} - -const allocateEmptyBuffers = combinedLength => { - const bufferArr = []; - // Allocate two buffers, each half the size of expectedBuff - bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); - bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); - - return bufferArr; -}; - -describe("fs.promises.readv", () => { - beforeEach(() => { - cnt = 0; - }); - - test("readv with position", async () => { - const filename = getFileName(); - await fs.writeFile(filename, expectedBuff); - const handle = await fs.open(filename, "r"); - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const expectedLength = expectedBuff.length; - - let { bytesRead, buffers } = await handle.readv([Buffer.from("")], null); - expect(bytesRead).toBe(0); - expect(buffers).toEqual([Buffer.from("")]); - - ({ bytesRead, buffers } = await handle.readv(bufferArr, null)); - expect(bytesRead).toBe(expectedLength); - expect(buffers).toEqual(bufferArr); - expect(Buffer.concat(bufferArr)).toEqual(await fs.readFile(filename)); - await handle.close(); - }); - - test("readv without position", async () => { - const filename = getFileName(); - await fs.writeFile(filename, expectedBuff); - const handle = await fs.open(filename, "r"); - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const expectedLength = expectedBuff.length; - - let { bytesRead, buffers } = await handle.readv([Buffer.from("")]); - expect(bytesRead).toBe(0); - expect(buffers).toEqual([Buffer.from("")]); - - ({ bytesRead, buffers } = await handle.readv(bufferArr)); - expect(bytesRead).toBe(expectedLength); - expect(buffers).toEqual(bufferArr); - expect(Buffer.concat(bufferArr)).toEqual(await fs.readFile(filename)); - await handle.close(); - }); -}); - -//<#END_FILE: test-fs-readv-promises.js diff --git a/test/js/node/test/parallel/fs-readv-sync.test.js b/test/js/node/test/parallel/fs-readv-sync.test.js deleted file mode 100644 index f4ab916f05..0000000000 --- a/test/js/node/test/parallel/fs-readv-sync.test.js +++ /dev/null @@ -1,104 +0,0 @@ -//#FILE: test-fs-readv-sync.js -//#SHA1: e9a4527b118e4a814a04c976eaafb5127f7c7c9d -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const expected = "ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف"; - -const exptectedBuff = Buffer.from(expected); -const expectedLength = exptectedBuff.length; - -let filename; -let tmpdir; - -beforeAll(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-fs-readv-sync-")); - filename = path.join(tmpdir, "readv_sync.txt"); - fs.writeFileSync(filename, exptectedBuff); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -const allocateEmptyBuffers = combinedLength => { - const bufferArr = []; - // Allocate two buffers, each half the size of exptectedBuff - bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); - bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); - - return bufferArr; -}; - -// fs.readvSync with array of buffers with all parameters -test("fs.readvSync with array of buffers with all parameters", () => { - const fd = fs.openSync(filename, "r"); - - const bufferArr = allocateEmptyBuffers(exptectedBuff.length); - - let read = fs.readvSync(fd, [Buffer.from("")], 0); - expect(read).toBe(0); - - read = fs.readvSync(fd, bufferArr, 0); - expect(read).toBe(expectedLength); - - fs.closeSync(fd); - - expect(Buffer.concat(bufferArr)).toEqual(fs.readFileSync(filename)); -}); - -// fs.readvSync with array of buffers without position -test("fs.readvSync with array of buffers without position", () => { - const fd = fs.openSync(filename, "r"); - - const bufferArr = allocateEmptyBuffers(exptectedBuff.length); - - let read = fs.readvSync(fd, [Buffer.from("")]); - expect(read).toBe(0); - - read = fs.readvSync(fd, bufferArr); - expect(read).toBe(expectedLength); - - fs.closeSync(fd); - - expect(Buffer.concat(bufferArr)).toEqual(fs.readFileSync(filename)); -}); - -/** - * Testing with incorrect arguments - */ -const wrongInputs = [false, "test", {}, [{}], ["sdf"], null, undefined]; - -test("fs.readvSync with incorrect arguments", () => { - const fd = fs.openSync(filename, "r"); - - for (const wrongInput of wrongInputs) { - expect(() => fs.readvSync(fd, wrongInput, null)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } - - fs.closeSync(fd); -}); - -test("fs.readvSync with wrong fd argument", () => { - for (const wrongInput of wrongInputs) { - expect(() => fs.readvSync(wrongInput)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } -}); - -//<#END_FILE: test-fs-readv-sync.js diff --git a/test/js/node/test/parallel/fs-readv.test.js b/test/js/node/test/parallel/fs-readv.test.js deleted file mode 100644 index 58f9977c3b..0000000000 --- a/test/js/node/test/parallel/fs-readv.test.js +++ /dev/null @@ -1,110 +0,0 @@ -//#FILE: test-fs-readv.js -//#SHA1: 07d6fe434017163aea491c98db8127bc2c942b96 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const expected = "ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف"; - -let cnt = 0; -const getFileName = () => path.join(os.tmpdir(), `readv_${++cnt}.txt`); -const expectedBuff = Buffer.from(expected); - -const allocateEmptyBuffers = combinedLength => { - const bufferArr = []; - // Allocate two buffers, each half the size of expectedBuff - bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); - bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); - - return bufferArr; -}; - -const getCallback = (fd, bufferArr) => { - return (err, bytesRead, buffers) => { - expect(err).toBeNull(); - expect(bufferArr).toEqual(buffers); - const expectedLength = expectedBuff.length; - expect(bytesRead).toBe(expectedLength); - fs.closeSync(fd); - - expect(Buffer.concat(bufferArr).equals(expectedBuff)).toBe(true); - }; -}; - -beforeEach(() => { - jest.spyOn(fs, "writeSync"); - jest.spyOn(fs, "writeFileSync"); - jest.spyOn(fs, "openSync"); - jest.spyOn(fs, "closeSync"); -}); - -afterEach(() => { - jest.restoreAllMocks(); -}); - -test("fs.readv with array of buffers with all parameters", done => { - const filename = getFileName(); - const fd = fs.openSync(filename, "w+"); - fs.writeSync(fd, expectedBuff); - - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const callback = getCallback(fd, bufferArr); - - fs.readv(fd, bufferArr, 0, (err, bytesRead, buffers) => { - callback(err, bytesRead, buffers); - done(); - }); -}); - -test("fs.readv with array of buffers without position", done => { - const filename = getFileName(); - fs.writeFileSync(filename, expectedBuff); - const fd = fs.openSync(filename, "r"); - - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const callback = getCallback(fd, bufferArr); - - fs.readv(fd, bufferArr, (err, bytesRead, buffers) => { - callback(err, bytesRead, buffers); - done(); - }); -}); - -describe("Testing with incorrect arguments", () => { - const wrongInputs = [false, "test", {}, [{}], ["sdf"], null, undefined]; - - test("fs.readv with wrong buffers argument", () => { - const filename = getFileName(); - fs.writeFileSync(filename, expectedBuff); - const fd = fs.openSync(filename, "r"); - - for (const wrongInput of wrongInputs) { - expect(() => fs.readv(fd, wrongInput, null, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } - - fs.closeSync(fd); - }); - - test("fs.readv with wrong fd argument", () => { - for (const wrongInput of wrongInputs) { - expect(() => fs.readv(wrongInput, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } - }); -}); - -//<#END_FILE: test-fs-readv.js diff --git a/test/js/node/test/parallel/fs-realpath-pipe.test.js b/test/js/node/test/parallel/fs-realpath-pipe.test.js deleted file mode 100644 index 6c461e9d36..0000000000 --- a/test/js/node/test/parallel/fs-realpath-pipe.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-fs-realpath-pipe.js -//#SHA1: 2a876967f5134cd77e2214f2abcbf753d46983cf -//----------------- -"use strict"; - -const { spawnSync } = require("child_process"); - -// Skip test for Windows, AIX, and IBMi -const isSkippedPlatform = ["win32", "aix", "os400"].includes(process.platform); -const testName = `No /dev/stdin on ${process.platform}.`; - -(isSkippedPlatform ? test.skip : test)(testName, () => { - const testCases = [ - `require('fs').realpath('/dev/stdin', (err, resolvedPath) => { - if (err) { - console.error(err); - process.exit(1); - } - if (resolvedPath) { - process.exit(2); - } - });`, - `try { - if (require('fs').realpathSync('/dev/stdin')) { - process.exit(2); - } - } catch (e) { - console.error(e); - process.exit(1); - }`, - ]; - - for (const code of testCases) { - const child = spawnSync(process.execPath, ["-e", code], { - stdio: "pipe", - }); - - if (child.status !== 2) { - console.log(code); - console.log(child.stderr.toString()); - } - - expect(child.status).toBe(2); - } -}); - -//<#END_FILE: test-fs-realpath-pipe.js diff --git a/test/js/node/test/parallel/fs-rmdir-type-check.test.js b/test/js/node/test/parallel/fs-rmdir-type-check.test.js deleted file mode 100644 index 3148f0ba3a..0000000000 --- a/test/js/node/test/parallel/fs-rmdir-type-check.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-fs-rmdir-type-check.js -//#SHA1: 2a00191160af6f0f76a82dcaef31d13c9b223d3b -//----------------- -"use strict"; - -const fs = require("fs"); - -test("fs.rmdir and fs.rmdirSync with invalid arguments", () => { - [false, 1, [], {}, null, undefined].forEach(i => { - expect(() => fs.rmdir(i, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => fs.rmdirSync(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-rmdir-type-check.js diff --git a/test/js/node/test/parallel/fs-sir-writes-alot.test.js b/test/js/node/test/parallel/fs-sir-writes-alot.test.js deleted file mode 100644 index b8eefc6642..0000000000 --- a/test/js/node/test/parallel/fs-sir-writes-alot.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-fs-sir-writes-alot.js -//#SHA1: d6f4574d48b9a85ee1276e4e0499f3fc32096d24 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-sir-writes-alot'); -const filename = path.join(tmpdir, 'out.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('multiple async writes to a file', async () => { - const fd = fs.openSync(filename, 'w'); - - const line = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'; - const N = 10240; - let complete = 0; - let bytesChecked = 0; - - function testBuffer(b) { - for (let i = 0; i < b.length; i++) { - bytesChecked++; - if (b[i] !== 'a'.charCodeAt(0) && b[i] !== '\n'.charCodeAt(0)) { - throw new Error(`invalid char ${i},${b[i]}`); - } - } - } - - await new Promise((resolve) => { - for (let i = 0; i < N; i++) { - // Create a new buffer for each write. Before the write is actually - // executed by the thread pool, the buffer will be collected. - const buffer = Buffer.from(line); - fs.write(fd, buffer, 0, buffer.length, null, function(er, written) { - complete++; - if (complete === N) { - fs.closeSync(fd); - const s = fs.createReadStream(filename); - s.on('data', testBuffer); - s.on('end', resolve); - } - }); - } - }); - - // Probably some of the writes are going to overlap, so we can't assume - // that we get (N * line.length). Let's just make sure we've checked a - // few... - expect(bytesChecked).toBeGreaterThan(1000); -}); - -//<#END_FILE: test-fs-sir-writes-alot.js diff --git a/test/js/node/test/parallel/fs-stat-bigint.test.js b/test/js/node/test/parallel/fs-stat-bigint.test.js deleted file mode 100644 index d7eb29c3f0..0000000000 --- a/test/js/node/test/parallel/fs-stat-bigint.test.js +++ /dev/null @@ -1,209 +0,0 @@ -//#FILE: test-fs-stat-bigint.js -//#SHA1: c8ba0bacb927432a68a677cd3a304e8e058fb070 -//----------------- -"use strict"; - -const fs = require("fs"); -const promiseFs = require("fs").promises; -const tmpdir = require("../common/tmpdir"); -const { isDate } = require("util").types; -const { inspect } = require("util"); - -tmpdir.refresh(); - -let testIndex = 0; - -function getFilename() { - const filename = tmpdir.resolve(`test-file-${++testIndex}`); - fs.writeFileSync(filename, "test"); - return filename; -} - -function verifyStats(bigintStats, numStats, allowableDelta) { - // allowableDelta: It's possible that the file stats are updated between the - // two stat() calls so allow for a small difference. - for (const key of Object.keys(numStats)) { - const val = numStats[key]; - if (isDate(val)) { - const time = val.getTime(); - const time2 = bigintStats[key].getTime(); - expect(time - time2).toBeLessThanOrEqual(allowableDelta); - } else if (key === "mode") { - expect(bigintStats[key]).toBe(BigInt(val)); - expect(bigintStats.isBlockDevice()).toBe(numStats.isBlockDevice()); - expect(bigintStats.isCharacterDevice()).toBe(numStats.isCharacterDevice()); - expect(bigintStats.isDirectory()).toBe(numStats.isDirectory()); - expect(bigintStats.isFIFO()).toBe(numStats.isFIFO()); - expect(bigintStats.isFile()).toBe(numStats.isFile()); - expect(bigintStats.isSocket()).toBe(numStats.isSocket()); - expect(bigintStats.isSymbolicLink()).toBe(numStats.isSymbolicLink()); - } else if (key.endsWith("Ms")) { - const nsKey = key.replace("Ms", "Ns"); - const msFromBigInt = bigintStats[key]; - const nsFromBigInt = bigintStats[nsKey]; - const msFromBigIntNs = Number(nsFromBigInt / 10n ** 6n); - const msFromNum = numStats[key]; - - expect(msFromNum - Number(msFromBigInt)).toBeLessThanOrEqual(allowableDelta); - expect(msFromNum - Number(msFromBigIntNs)).toBeLessThanOrEqual(allowableDelta); - } else if (Number.isSafeInteger(val)) { - expect(bigintStats[key]).toBe(BigInt(val)); - } else { - expect(Number(bigintStats[key]) - val).toBeLessThan(1); - } - } -} - -const runSyncTest = (func, arg) => { - const startTime = process.hrtime.bigint(); - const bigintStats = func(arg, { bigint: true }); - const numStats = func(arg); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); -}; - -test("fs.statSync", () => { - const filename = getFilename(); - runSyncTest(fs.statSync, filename); -}); - -if (!process.platform.startsWith("win")) { - test("fs.lstatSync", () => { - const filename = getFilename(); - const link = `${filename}-link`; - fs.symlinkSync(filename, link); - runSyncTest(fs.lstatSync, link); - }); -} - -test("fs.fstatSync", () => { - const filename = getFilename(); - const fd = fs.openSync(filename, "r"); - runSyncTest(fs.fstatSync, fd); - fs.closeSync(fd); -}); - -test("fs.statSync with non-existent file", () => { - expect(() => fs.statSync("does_not_exist")).toThrow(expect.objectContaining({ code: "ENOENT" })); - expect(fs.statSync("does_not_exist", { throwIfNoEntry: false })).toBeUndefined(); -}); - -test("fs.lstatSync with non-existent file", () => { - expect(() => fs.lstatSync("does_not_exist")).toThrow(expect.objectContaining({ code: "ENOENT" })); - expect(fs.lstatSync("does_not_exist", { throwIfNoEntry: false })).toBeUndefined(); -}); - -test("fs.fstatSync with invalid file descriptor", () => { - expect(() => fs.fstatSync(9999)).toThrow(expect.objectContaining({ code: "EBADF" })); - expect(() => fs.fstatSync(9999, { throwIfNoEntry: false })).toThrow(expect.objectContaining({ code: "EBADF" })); -}); - -const runCallbackTest = (func, arg) => { - return new Promise(resolve => { - const startTime = process.hrtime.bigint(); - func(arg, { bigint: true }, (err, bigintStats) => { - expect(err).toBeFalsy(); - func(arg, (err, numStats) => { - expect(err).toBeFalsy(); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); - resolve(); - }); - }); - }); -}; - -test("fs.stat callback", async () => { - const filename = getFilename(); - await runCallbackTest(fs.stat, filename); -}); - -if (!process.platform.startsWith("win")) { - test("fs.lstat callback", async () => { - const filename = getFilename(); - const link = `${filename}-link`; - fs.symlinkSync(filename, link); - await runCallbackTest(fs.lstat, link); - }); -} - -test("fs.fstat callback", async () => { - const filename = getFilename(); - const fd = fs.openSync(filename, "r"); - await runCallbackTest(fs.fstat, fd); - fs.closeSync(fd); -}); - -const runPromiseTest = async (func, arg) => { - const startTime = process.hrtime.bigint(); - const bigintStats = await func(arg, { bigint: true }); - const numStats = await func(arg); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); -}; - -test("promiseFs.stat", async () => { - const filename = getFilename(); - await runPromiseTest(promiseFs.stat, filename); -}); - -if (!process.platform.startsWith("win")) { - test("promiseFs.lstat", async () => { - const filename = getFilename(); - const link = `${filename}-link`; - fs.symlinkSync(filename, link); - await runPromiseTest(promiseFs.lstat, link); - }); -} - -test("promiseFs handle.stat", async () => { - const filename = getFilename(); - const handle = await promiseFs.open(filename, "r"); - const startTime = process.hrtime.bigint(); - const bigintStats = await handle.stat({ bigint: true }); - const numStats = await handle.stat(); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); - await handle.close(); -}); - -test("BigIntStats Date properties can be set before reading them", done => { - fs.stat(__filename, { bigint: true }, (err, s) => { - expect(err).toBeFalsy(); - s.atime = 2; - s.mtime = 3; - s.ctime = 4; - s.birthtime = 5; - - expect(s.atime).toBe(2); - expect(s.mtime).toBe(3); - expect(s.ctime).toBe(4); - expect(s.birthtime).toBe(5); - done(); - }); -}); - -test("BigIntStats Date properties can be set after reading them", done => { - fs.stat(__filename, { bigint: true }, (err, s) => { - expect(err).toBeFalsy(); - // eslint-disable-next-line no-unused-expressions - s.atime, s.mtime, s.ctime, s.birthtime; - - s.atime = 2; - s.mtime = 3; - s.ctime = 4; - s.birthtime = 5; - - expect(s.atime).toBe(2); - expect(s.mtime).toBe(3); - expect(s.ctime).toBe(4); - expect(s.birthtime).toBe(5); - done(); - }); -}); - -//<#END_FILE: test-fs-stat-bigint.js diff --git a/test/js/node/test/parallel/fs-stream-double-close.test.js b/test/js/node/test/parallel/fs-stream-double-close.test.js deleted file mode 100644 index 0e89c7bf26..0000000000 --- a/test/js/node/test/parallel/fs-stream-double-close.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-fs-stream-double-close.js -//#SHA1: 25fa219f7ee462e67611751f996393afc1869490 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = path.join(os.tmpdir(), "node-test-fs-stream-double-close"); -beforeAll(() => { - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("test1 with ReadStream", () => { - test1(fs.createReadStream(__filename)); -}); - -test("test2 with ReadStream", () => { - test2(fs.createReadStream(__filename)); -}); - -test("test3 with ReadStream", () => { - test3(fs.createReadStream(__filename)); -}); - -test("test1 with WriteStream", () => { - test1(fs.createWriteStream(path.join(tmpdir, "dummy1"))); -}); - -test("test2 with WriteStream", () => { - test2(fs.createWriteStream(path.join(tmpdir, "dummy2"))); -}); - -test("test3 with WriteStream", () => { - test3(fs.createWriteStream(path.join(tmpdir, "dummy3"))); -}); - -function test1(stream) { - stream.destroy(); - stream.destroy(); -} - -function test2(stream) { - stream.destroy(); - stream.on("open", jest.fn()); -} - -function test3(stream) { - const openHandler = jest.fn(); - stream.on("open", openHandler); - stream.emit("open"); - expect(openHandler).toHaveBeenCalledTimes(1); - stream.destroy(); - stream.destroy(); -} - -//<#END_FILE: test-fs-stream-double-close.js diff --git a/test/js/node/test/parallel/fs-symlink-buffer-path.test.js b/test/js/node/test/parallel/fs-symlink-buffer-path.test.js deleted file mode 100644 index 17bde4dccb..0000000000 --- a/test/js/node/test/parallel/fs-symlink-buffer-path.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-fs-symlink-buffer-path.js -//#SHA1: 73fa7d9b492bd23730f1a8763caac92a9f4a1896 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const canCreateSymLink = () => { - try { - fs.symlinkSync('test-file', 'test-symlink'); - fs.unlinkSync('test-symlink'); - return true; - } catch (err) { - return false; - } -}; - -if (!canCreateSymLink()) { - test.skip('insufficient privileges', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-symlink-buffer-path'); - const fixturesPath = path.join(__dirname, '..', 'fixtures'); - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - test('creating and reading symbolic link', async () => { - const linkData = path.join(fixturesPath, 'cycles', 'root.js'); - const linkPath = path.join(tmpdir, 'symlink1.js'); - - fs.symlinkSync(Buffer.from(linkData), linkPath); - - const linkStats = await fs.promises.lstat(linkPath); - const linkTime = linkStats.mtime.getTime(); - - const fileStats = await fs.promises.stat(linkPath); - const fileTime = fileStats.mtime.getTime(); - - const destination = await fs.promises.readlink(linkPath); - expect(destination).toBe(linkData); - - expect(linkTime).not.toBe(fileTime); - }); -} - -//<#END_FILE: test-fs-symlink-buffer-path.js diff --git a/test/js/node/test/parallel/fs-symlink.test.js b/test/js/node/test/parallel/fs-symlink.test.js deleted file mode 100644 index c4e68336ba..0000000000 --- a/test/js/node/test/parallel/fs-symlink.test.js +++ /dev/null @@ -1,148 +0,0 @@ -//#FILE: test-fs-symlink.js -//#SHA1: 4861a453e314d789a1b933d7179da96b7a35378c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const canCreateSymLink = () => { - try { - fs.symlinkSync("", ""); - fs.unlinkSync(""); - return true; - } catch (e) { - return false; - } -}; - -if (!canCreateSymLink()) { - it.skip("insufficient privileges", () => {}); -} else { - let linkTime; - let fileTime; - const tmpdir = os.tmpdir(); - - beforeEach(() => { - jest.spyOn(fs, "symlink"); - jest.spyOn(fs, "lstat"); - jest.spyOn(fs, "stat"); - jest.spyOn(fs, "readlink"); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - test("Test creating and reading symbolic link", async () => { - const linkData = path.resolve(__dirname, "../fixtures/cycles/root.js"); - const linkPath = path.resolve(tmpdir, "symlink1.js"); - - await new Promise(resolve => { - fs.symlink(linkData, linkPath, resolve); - }); - - expect(fs.symlink).toHaveBeenCalled(); - - await new Promise(resolve => { - fs.lstat(linkPath, (err, stats) => { - expect(err).toBeNull(); - linkTime = stats.mtime.getTime(); - resolve(); - }); - }); - - await new Promise(resolve => { - fs.stat(linkPath, (err, stats) => { - expect(err).toBeNull(); - fileTime = stats.mtime.getTime(); - resolve(); - }); - }); - - await new Promise(resolve => { - fs.readlink(linkPath, (err, destination) => { - expect(err).toBeNull(); - expect(destination).toBe(linkData); - resolve(); - }); - }); - }); - - test("Test invalid symlink", async () => { - const linkData = path.resolve(__dirname, "../fixtures/not/exists/file"); - const linkPath = path.resolve(tmpdir, "symlink2.js"); - - await new Promise(resolve => { - fs.symlink(linkData, linkPath, resolve); - }); - - expect(fs.existsSync(linkPath)).toBe(false); - }); - - test("Test invalid inputs", () => { - const invalidInputs = [false, 1, {}, [], null, undefined]; - const errObj = expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringMatching(/target|path/), - }); - - invalidInputs.forEach(input => { - expect(() => fs.symlink(input, "", () => {})).toThrow(errObj); - expect(() => fs.symlinkSync(input, "")).toThrow(errObj); - - expect(() => fs.symlink("", input, () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", input)).toThrow(errObj); - }); - }); - - test("Test invalid type inputs", () => { - const errObj = expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }); - - expect(() => fs.symlink("", "", "🍏", () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", "🍏")).toThrow(errObj); - - expect(() => fs.symlink("", "", "nonExistentType", () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", "nonExistentType")).toThrow(errObj); - expect(fs.promises.symlink("", "", "nonExistentType")).rejects.toMatchObject(errObj); - - expect(() => fs.symlink("", "", false, () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", false)).toThrow(errObj); - expect(fs.promises.symlink("", "", false)).rejects.toMatchObject(errObj); - - expect(() => fs.symlink("", "", {}, () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", {})).toThrow(errObj); - expect(fs.promises.symlink("", "", {})).rejects.toMatchObject(errObj); - }); - - test("Link time should not be equal to file time", () => { - expect(linkTime).not.toBe(fileTime); - }); -} - -//<#END_FILE: test-fs-symlink.js diff --git a/test/js/node/test/parallel/fs-truncate-sync.test.js b/test/js/node/test/parallel/fs-truncate-sync.test.js deleted file mode 100644 index cdf9a5f739..0000000000 --- a/test/js/node/test/parallel/fs-truncate-sync.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-fs-truncate-sync.js -//#SHA1: 6b4ccbf9b9fab199c6b258374cf0a1665b1c21fe -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const tmpdir = require("../common/tmpdir"); -const tmp = tmpdir.path; - -describe("fs.truncateSync", () => { - beforeEach(() => { - tmpdir.refresh(); - }); - - test("truncates file correctly", () => { - const filename = path.resolve(tmp, "truncate-sync-file.txt"); - - fs.writeFileSync(filename, "hello world", "utf8"); - - const fd = fs.openSync(filename, "r+"); - - fs.truncateSync(fd, 5); - expect(fs.readFileSync(fd)).toEqual(Buffer.from("hello")); - - fs.closeSync(fd); - fs.unlinkSync(filename); - }); -}); - -//<#END_FILE: test-fs-truncate-sync.js diff --git a/test/js/node/test/parallel/fs-unlink-type-check.test.js b/test/js/node/test/parallel/fs-unlink-type-check.test.js deleted file mode 100644 index 4f819feecd..0000000000 --- a/test/js/node/test/parallel/fs-unlink-type-check.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-fs-unlink-type-check.js -//#SHA1: 337e42f3b15589a7652c32c0a1c92292abf098d0 -//----------------- -"use strict"; - -const fs = require("fs"); - -test("fs.unlink and fs.unlinkSync with invalid types", () => { - const invalidTypes = [false, 1, {}, [], null, undefined]; - - invalidTypes.forEach(invalidType => { - expect(() => fs.unlink(invalidType, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => fs.unlinkSync(invalidType)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-unlink-type-check.js diff --git a/test/js/node/test/parallel/fs-util-validateoffsetlength.test.js b/test/js/node/test/parallel/fs-util-validateoffsetlength.test.js deleted file mode 100644 index 1cc49c3be9..0000000000 --- a/test/js/node/test/parallel/fs-util-validateoffsetlength.test.js +++ /dev/null @@ -1,85 +0,0 @@ -//#FILE: test-fs-util-validateoffsetlength.js -//#SHA1: d5c952d2e87072352a6a60351ede415d1925cf21 -//----------------- -'use strict'; - -// Implement the functions we want to test -function validateOffsetLengthRead(offset, length, byteLength) { - if (offset < 0) { - throw new RangeError('The value of "offset" is out of range. ' + - `It must be >= 0. Received ${offset}`); - } - if (length < 0) { - throw new RangeError('The value of "length" is out of range. ' + - `It must be >= 0. Received ${length}`); - } - if (offset + length > byteLength) { - throw new RangeError('The value of "length" is out of range. ' + - `It must be <= ${byteLength - offset}. Received ${length}`); - } -} - -function validateOffsetLengthWrite(offset, length, byteLength) { - if (offset > byteLength) { - throw new RangeError('The value of "offset" is out of range. ' + - `It must be <= ${byteLength}. Received ${offset}`); - } - if (length > byteLength - offset) { - throw new RangeError('The value of "length" is out of range. ' + - `It must be <= ${byteLength - offset}. Received ${length}`); - } -} - -describe('validateOffsetLengthRead', () => { - test('throws RangeError when offset is negative', () => { - const offset = -1; - expect(() => validateOffsetLengthRead(offset, 0, 0)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be >= 0. Received ${offset}`) - })); - }); - - test('throws RangeError when length is negative', () => { - const length = -1; - expect(() => validateOffsetLengthRead(0, length, 0)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be >= 0. Received ${length}`) - })); - }); - - test('throws RangeError when length is out of range', () => { - const offset = 1; - const length = 1; - const byteLength = offset + length - 1; - expect(() => validateOffsetLengthRead(offset, length, byteLength)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be <= ${byteLength - offset}. Received ${length}`) - })); - }); -}); - -describe('validateOffsetLengthWrite', () => { - const kIoMaxLength = 2 ** 31 - 1; - - test('throws RangeError when offset > byteLength', () => { - const offset = 100; - const length = 100; - const byteLength = 50; - expect(() => validateOffsetLengthWrite(offset, length, byteLength)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be <= ${byteLength}. Received ${offset}`) - })); - }); - - test('throws RangeError when byteLength < kIoMaxLength and length > byteLength - offset', () => { - const offset = kIoMaxLength - 150; - const length = 200; - const byteLength = kIoMaxLength - 100; - expect(() => validateOffsetLengthWrite(offset, length, byteLength)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be <= ${byteLength - offset}. Received ${length}`) - })); - }); -}); - -//<#END_FILE: test-fs-util-validateoffsetlength.js diff --git a/test/js/node/test/parallel/fs-watch-abort-signal.test.js b/test/js/node/test/parallel/fs-watch-abort-signal.test.js deleted file mode 100644 index 030e33814e..0000000000 --- a/test/js/node/test/parallel/fs-watch-abort-signal.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-fs-watch-abort-signal.js -//#SHA1: 6f0b7fcc2f597faa8e1353559d5d007cd744614a -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const isIBMi = process.platform === 'os400'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-watch-abort-signal'); - const emptyFile = path.join(tmpdir, 'empty.js'); - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(emptyFile, ''); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - test('Signal aborted after creating the watcher', (done) => { - const ac = new AbortController(); - const { signal } = ac; - const watcher = fs.watch(emptyFile, { signal }); - watcher.once('close', () => { - done(); - }); - setImmediate(() => ac.abort()); - }); - - test('Signal aborted before creating the watcher', (done) => { - const signal = AbortSignal.abort(); - const watcher = fs.watch(emptyFile, { signal }); - watcher.once('close', () => { - done(); - }); - }); -} - -//<#END_FILE: test-fs-watch-abort-signal.js diff --git a/test/js/node/test/parallel/fs-watch-close-when-destroyed.test.js b/test/js/node/test/parallel/fs-watch-close-when-destroyed.test.js deleted file mode 100644 index 5749125642..0000000000 --- a/test/js/node/test/parallel/fs-watch-close-when-destroyed.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-fs-watch-close-when-destroyed.js -//#SHA1: f062b7243d0c42722a289a6228d4c2c1a503be1b -//----------------- -"use strict"; - -// This tests that closing a watcher when the underlying handle is -// already destroyed will result in a noop instead of a crash. - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// fs-watch on folders have limited capability in AIX. -// The testcase makes use of folder watching, and causes -// hang. This behavior is documented. Skip this for AIX. - -if (process.platform === "aix") { - it.skip("folder watch capability is limited in AIX."); -} else if (process.platform === "os400") { - it.skip("IBMi does not support `fs.watch()`"); -} else { - let root; - - beforeEach(() => { - root = path.join(os.tmpdir(), "watched-directory-" + Math.random().toString(36).slice(2)); - fs.mkdirSync(root); - }); - - afterEach(() => { - try { - fs.rmdirSync(root); - } catch (error) { - // Ignore errors, directory might already be removed - } - }); - - it("should not crash when closing watcher after handle is destroyed", done => { - const watcher = fs.watch(root, { persistent: false, recursive: false }); - - // The following listeners may or may not be invoked. - - watcher.addListener("error", () => { - setTimeout( - () => { - watcher.close(); - }, // Should not crash if it's invoked - 10, - ); - }); - - watcher.addListener("change", () => { - setTimeout(() => { - watcher.close(); - }, 10); - }); - - fs.rmdirSync(root); - // Wait for the listener to hit - setTimeout(done, 100); - }); -} - -//<#END_FILE: test-fs-watch-close-when-destroyed.js diff --git a/test/js/node/test/parallel/fs-watch-encoding.test.js b/test/js/node/test/parallel/fs-watch-encoding.test.js deleted file mode 100644 index 472a77d57a..0000000000 --- a/test/js/node/test/parallel/fs-watch-encoding.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-fs-watch-encoding.js -//#SHA1: 63f7e4008743417c7ee5995bbf16a28ade764e48 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-watch-encoding'); -const fn = '新建文夹件.txt'; -const a = path.join(tmpdir, fn); - -let interval; - -beforeAll(() => { - if (process.platform === 'aix') { - return test.skip('folder watch capability is limited in AIX.'); - } - if (process.platform === 'os400') { - return test.skip('IBMi does not support `fs.watch()`'); - } - - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - clearInterval(interval); - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -const watcherTests = [ - { - name: 'with hex encoding', - options: { encoding: 'hex' }, - expectedFilenames: ['e696b0e5bbbae69687e5a4b9e4bbb62e747874', null], - }, - { - name: 'without encoding option', - options: {}, - expectedFilenames: [fn, null], - }, - { - name: 'with buffer encoding', - options: { encoding: 'buffer' }, - expectedFilenames: [Buffer.from(fn), null], - }, -]; - -watcherTests.forEach(({ name, options, expectedFilenames }) => { - test(`fs.watch ${name}`, (done) => { - const watcher = fs.watch(tmpdir, options, (event, filename) => { - if (expectedFilenames.some(expected => - expected instanceof Buffer - ? expected.equals(filename) - : expected === filename)) { - watcher.close(); - done(); - } - }); - - // Start the interval after setting up the watcher - if (!interval) { - interval = setInterval(() => { - const fd = fs.openSync(a, 'w+'); - fs.closeSync(fd); - fs.unlinkSync(a); - }, 100); - } - }, 10000); // Increased timeout to allow for file operations -}); - -//<#END_FILE: test-fs-watch-encoding.js diff --git a/test/js/node/test/parallel/fs-watch-file-enoent-after-deletion.test.js b/test/js/node/test/parallel/fs-watch-file-enoent-after-deletion.test.js deleted file mode 100644 index ee439c8272..0000000000 --- a/test/js/node/test/parallel/fs-watch-file-enoent-after-deletion.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-fs-watch-file-enoent-after-deletion.js -//#SHA1: d6c93db608d119bd35fcab0e1e9307bfd6558b68 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Make sure the deletion event gets reported in the following scenario: -// 1. Watch a file. -// 2. The initial stat() goes okay. -// 3. Something deletes the watched file. -// 4. The second stat() fails with ENOENT. - -// The second stat() translates into the first 'change' event but a logic error -// stopped it from getting emitted. -// https://github.com/nodejs/node-v0.x-archive/issues/4027 - -test("fs.watchFile reports deletion", done => { - const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - const filename = path.join(tmpdir, "watched"); - fs.writeFileSync(filename, "quis custodiet ipsos custodes"); - - const watcher = jest.fn(); - fs.watchFile(filename, { interval: 50 }, watcher); - - setTimeout(() => { - fs.unlinkSync(filename); - - setTimeout(() => { - expect(watcher).toHaveBeenCalledTimes(1); - const [curr, prev] = watcher.mock.calls[0]; - expect(curr.nlink).toBe(0); - expect(prev.nlink).toBe(1); - - fs.unwatchFile(filename); - fs.rmdirSync(tmpdir); - done(); - }, 100); - }, 100); -}); - -//<#END_FILE: test-fs-watch-file-enoent-after-deletion.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-file-to-existing-subfolder.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-file-to-existing-subfolder.test.js deleted file mode 100644 index 3e2c067792..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-file-to-existing-subfolder.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-file-to-existing-subfolder.js -//#SHA1: 7d4414be8a9ba35f2ebdb685037951133137b6ef -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const testDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-add-file-to-existing-subfolder'); - -beforeAll(() => { - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }); - } - fs.mkdirSync(testDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); -}); - -test('fs.watch detects file added to existing subfolder', (done) => { - const rootDirectory = fs.mkdtempSync(path.join(testDir, 'test-')); - const testDirectory = path.join(rootDirectory, 'test-4'); - fs.mkdirSync(testDirectory); - - const file = 'folder-5'; - const filePath = path.join(testDirectory, file); - fs.mkdirSync(filePath); - - const subfolderPath = path.join(filePath, 'subfolder-6'); - fs.mkdirSync(subfolderPath); - - const childrenFile = 'file-7.txt'; - const childrenAbsolutePath = path.join(subfolderPath, childrenFile); - const relativePath = path.join(file, path.basename(subfolderPath), childrenFile); - - const watcher = fs.watch(testDirectory, { recursive: true }); - let watcherClosed = false; - - watcher.on('change', (event, filename) => { - expect(event).toBe('rename'); - - if (filename === relativePath) { - watcher.close(); - watcherClosed = true; - expect(watcherClosed).toBe(true); - done(); - } - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout(() => { - fs.writeFileSync(childrenAbsolutePath, 'world'); - }, 200); -}, 10000); // Increased timeout to 10 seconds - -//<#END_FILE: test-fs-watch-recursive-add-file-to-existing-subfolder.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-file-with-url.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-file-with-url.test.js deleted file mode 100644 index 2131b1dbe5..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-file-with-url.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-file-with-url.js -//#SHA1: e6498ea80abdf69cb66d888bbd7d631931970c0a -//----------------- -"use strict"; - -const { setTimeout } = require("timers/promises"); -const path = require("path"); -const fs = require("fs"); -const { pathToFileURL } = require("url"); -const os = require("os"); - -const isIBMi = process.platform === "os400"; -const isAIX = process.platform === "aix"; - -if (isIBMi) { - it.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - it.skip("folder watch capability is limited in AIX.", () => {}); -} else { - it("should watch for file changes using URL as path", async () => { - const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - - // Add a file to already watching folder, and use URL as the path - const rootDirectory = fs.mkdtempSync(path.join(testDir, path.sep)); - const testDirectory = path.join(rootDirectory, "test-5"); - fs.mkdirSync(testDirectory); - - const filePath = path.join(testDirectory, "file-8.txt"); - const url = pathToFileURL(testDirectory); - - const watcher = fs.watch(url, { recursive: true }); - let watcherClosed = false; - - const watchPromise = new Promise(resolve => { - watcher.on("change", function (event, filename) { - expect(event).toBe("rename"); - - if (filename === path.basename(filePath)) { - watcher.close(); - watcherClosed = true; - resolve(); - } - }); - }); - - await setTimeout(100); - fs.writeFileSync(filePath, "world"); - - await watchPromise; - - expect(watcherClosed).toBe(true); - }, 10000); // Increase timeout to 10 seconds -} - -//<#END_FILE: test-fs-watch-recursive-add-file-with-url.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-file.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-file.test.js deleted file mode 100644 index 00c5fcf7e6..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-file.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-file.js -//#SHA1: e87d2c9f4789a6e6a83fbdca56e39683625bd0af -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = os.platform() === "os400"; -const isAIX = os.platform() === "aix"; - -if (isIBMi) { - it.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - it.skip("folder watch capability is limited in AIX.", () => {}); -} else { - const tmpdir = { - path: path.join(os.tmpdir(), "jest-test-fs-watch-recursive-add-file"), - refresh: () => { - if (fs.existsSync(tmpdir.path)) { - fs.rmSync(tmpdir.path, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir.path, { recursive: true }); - }, - }; - - beforeEach(() => { - tmpdir.refresh(); - }); - - it("should detect file added to already watching folder", done => { - const rootDirectory = fs.mkdtempSync(tmpdir.path + path.sep); - const testDirectory = path.join(rootDirectory, "test-1"); - fs.mkdirSync(testDirectory); - - const testFile = path.join(testDirectory, "file-1.txt"); - - const watcher = fs.watch(testDirectory, { recursive: true }); - let watcherClosed = false; - - watcher.on("change", function (event, filename) { - expect(event).toBe("rename"); - - if (filename === path.basename(testFile)) { - watcher.close(); - watcherClosed = true; - expect(watcherClosed).toBe(true); - done(); - } - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout( - () => { - fs.writeFileSync(testFile, "world"); - }, - process.platform === "win32" ? 200 : 100, - ); - }); -} - -//<#END_FILE: test-fs-watch-recursive-add-file.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-folder.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-folder.test.js deleted file mode 100644 index dbe75002f5..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-folder.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-folder.js -//#SHA1: 4c2908ccc8502f5f760963a9b9a6db6ddadd4c1c -//----------------- -"use strict"; - -const { setTimeout } = require("timers/promises"); -const assert = require("assert"); -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = os.platform() === "os400"; -const isAIX = os.platform() === "aix"; - -if (isIBMi) { - it.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - it.skip("folder watch capability is limited in AIX.", () => {}); -} else { - const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - - afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); - }); - - test("Add a folder to already watching folder", async () => { - // Add a folder to already watching folder - - const rootDirectory = fs.mkdtempSync(path.join(testDir, "root-")); - const testDirectory = path.join(rootDirectory, "test-2"); - fs.mkdirSync(testDirectory); - - const testFile = path.join(testDirectory, "folder-2"); - - const watcher = fs.watch(testDirectory, { recursive: true }); - let watcherClosed = false; - - const watchPromise = new Promise(resolve => { - watcher.on("change", function (event, filename) { - expect(event).toBe("rename"); - - if (filename === path.basename(testFile)) { - watcher.close(); - watcherClosed = true; - resolve(); - } - }); - }); - - await setTimeout(100); - fs.mkdirSync(testFile); - - await watchPromise; - - expect(watcherClosed).toBe(true); - }); -} - -//<#END_FILE: test-fs-watch-recursive-add-folder.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-assert-leaks.test.js b/test/js/node/test/parallel/fs-watch-recursive-assert-leaks.test.js deleted file mode 100644 index 69ec1c857c..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-assert-leaks.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-fs-watch-recursive-assert-leaks.js -//#SHA1: 316f1b184840ce5a8f2f92f4ab205038f4acaf9d -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); -const { setTimeout } = require('timers/promises'); - -const testDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-assert-leaks'); - -beforeAll(() => { - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }); - } - fs.mkdirSync(testDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); -}); - -// Skip test for IBMi and AIX -const isIBMi = process.platform === 'os400'; -const isAIX = process.platform === 'aix'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else if (isAIX) { - test.skip('folder watch capability is limited in AIX', () => {}); -} else { - test('recursive watch does not leak handles', async () => { - const rootDirectory = fs.mkdtempSync(path.join(testDir, 'root-')); - const testDirectory = path.join(rootDirectory, 'test-7'); - const filePath = path.join(testDirectory, 'only-file.txt'); - fs.mkdirSync(testDirectory); - - let watcherClosed = false; - const watcher = fs.watch(testDirectory, { recursive: true }); - - const watchPromise = new Promise((resolve) => { - watcher.on('change', async (event, filename) => { - await setTimeout(100); - if (filename === path.basename(filePath)) { - watcher.close(); - watcherClosed = true; - resolve(); - } - await setTimeout(100); - expect(process._getActiveHandles().some((handle) => handle.constructor.name === 'StatWatcher')).toBe(false); - }); - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - await setTimeout(200); - fs.writeFileSync(filePath, 'content'); - - await watchPromise; - expect(watcherClosed).toBe(true); - }, 10000); // Increased timeout to 10 seconds -} - -//<#END_FILE: test-fs-watch-recursive-assert-leaks.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-linux-parallel-remove.test.js b/test/js/node/test/parallel/fs-watch-recursive-linux-parallel-remove.test.js deleted file mode 100644 index b109622f57..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-linux-parallel-remove.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-fs-watch-recursive-linux-parallel-remove.js -//#SHA1: ed10536d8d54febe24a3dcf494a26eab06bc4f66 -//----------------- -"use strict"; - -const path = require("node:path"); -const fs = require("node:fs"); -const { spawn } = require("node:child_process"); -const os = require("node:os"); - -// Skip test if not running on Linux -if (os.platform() !== "linux") { - test.skip("This test can run only on Linux", () => {}); -} else { - // Test that the watcher do not crash if the file "disappears" while - // watch is being set up. - - let testDir; - let watcher; - - beforeEach(() => { - testDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - }); - - afterEach(() => { - if (watcher) { - watcher.close(); - } - fs.rmSync(testDir, { recursive: true, force: true }); - }); - - test("fs.watch does not crash on parallel file removal", done => { - watcher = fs.watch(testDir, { recursive: true }); - watcher.on("change", function (event, filename) { - // This console.log makes the error happen - // do not remove - console.log(filename, event); - }); - - const testFile = path.join(testDir, "a"); - const child = spawn( - process.argv[0], - [ - "-e", - `const fs = require('node:fs'); for (let i = 0; i < 10000; i++) { const fd = fs.openSync('${testFile}', 'w'); fs.writeSync(fd, Buffer.from('hello')); fs.rmSync('${testFile}') }`, - ], - { - stdio: "inherit", - }, - ); - - child.on("exit", function () { - watcher.close(); - done(); - }); - }); -} - -//<#END_FILE: test-fs-watch-recursive-linux-parallel-remove.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-sync-write.test.js b/test/js/node/test/parallel/fs-watch-recursive-sync-write.test.js deleted file mode 100644 index fa80c9a8e3..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-sync-write.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-fs-watch-recursive-sync-write.js -//#SHA1: 436087aa83502744252800b9a93dbe88a4ca3822 -//----------------- -'use strict'; - -const fs = require('node:fs'); -const path = require('node:path'); -const os = require('os'); - -const tmpDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-sync-write'); -const filename = path.join(tmpDir, 'test.file'); - -beforeAll(() => { - if (fs.existsSync(tmpDir)) { - fs.rmSync(tmpDir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -// Skip test for IBMi and AIX -const isIBMi = process.platform === 'os400'; -const isAIX = process.platform === 'aix'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else if (isAIX) { - test.skip('folder watch capability is limited in AIX', () => {}); -} else { - test('fs.watch detects file creation with recursive option', (done) => { - const timeout = setTimeout(() => { - done(new Error('timed out')); - }, 30000); - - function doWatch() { - const watcher = fs.watch(tmpDir, { recursive: true }, (eventType, _filename) => { - clearTimeout(timeout); - watcher.close(); - expect(eventType).toBe('rename'); - expect(path.join(tmpDir, _filename)).toBe(filename); - done(); - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout(() => { - fs.writeFileSync(filename, 'foobar2'); - }, 200); - } - - if (process.platform === 'darwin') { - // On macOS delay watcher start to avoid leaking previous events. - // Refs: https://github.com/libuv/libuv/pull/4503 - setTimeout(doWatch, 100); - } else { - doWatch(); - } - }, 35000); // Increase timeout to account for the 30 second timeout in the test -} - -//<#END_FILE: test-fs-watch-recursive-sync-write.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-update-file.test.js b/test/js/node/test/parallel/fs-watch-recursive-update-file.test.js deleted file mode 100644 index 331df95da8..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-update-file.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-fs-watch-recursive-update-file.js -//#SHA1: d197449fc5f430b9ce49e7f75b57a44dd4f2259a -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const testDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-update-file'); - -beforeAll(() => { - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }); - } - fs.mkdirSync(testDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); -}); - -test('Watch a folder and update an already existing file in it', (done) => { - const rootDirectory = fs.mkdtempSync(path.join(testDir, 'test-')); - const testDirectory = path.join(rootDirectory, 'test-0'); - fs.mkdirSync(testDirectory); - - const testFile = path.join(testDirectory, 'file-1.txt'); - fs.writeFileSync(testFile, 'hello'); - - const watcher = fs.watch(testDirectory, { recursive: true }); - - watcher.on('change', (event, filename) => { - expect(event === 'change' || event === 'rename').toBe(true); - - if (filename === path.basename(testFile)) { - watcher.close(); - done(); - } - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout(() => { - fs.writeFileSync(testFile, 'hello'); - }, 200); -}, 10000); // Increased timeout to allow for file system operations - -//<#END_FILE: test-fs-watch-recursive-update-file.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-validate.test.js b/test/js/node/test/parallel/fs-watch-recursive-validate.test.js deleted file mode 100644 index aeed19b67d..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-validate.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-fs-watch-recursive-validate.js -//#SHA1: eb5d9ff1caac7f9d4acf694c43e4f634f538befb -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = process.platform === "os400"; -const isAIX = process.platform === "aix"; -const isWindows = process.platform === "win32"; -const isOSX = process.platform === "darwin"; - -if (isIBMi) { - test.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - test.skip("folder watch capability is limited in AIX.", () => {}); -} else { - const tmpdir = { - path: path.join(os.tmpdir(), "jest-fs-watch-recursive-validate"), - refresh: () => { - if (fs.existsSync(tmpdir.path)) { - fs.rmSync(tmpdir.path, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir.path, { recursive: true }); - }, - }; - - beforeEach(() => { - tmpdir.refresh(); - }); - - test("Handle non-boolean values for options.recursive", async () => { - if (!isWindows && !isOSX) { - expect(() => { - const testsubdir = fs.mkdtempSync(tmpdir.path + path.sep); - fs.watch(testsubdir, { recursive: "1" }); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - } - }); -} - -//<#END_FILE: test-fs-watch-recursive-validate.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-watch-file.test.js b/test/js/node/test/parallel/fs-watch-recursive-watch-file.test.js deleted file mode 100644 index bf7b6e0212..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-watch-file.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-watch-recursive-watch-file.js -//#SHA1: 1f06958f6f645cb5c80b424a24b046f107ab83ae -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = os.platform() === "os400"; -const isAIX = os.platform() === "aix"; - -if (isIBMi) { - test.skip("IBMi does not support `fs.watch()`"); -} - -// fs-watch on folders have limited capability in AIX. -// The testcase makes use of folder watching, and causes -// hang. This behavior is documented. Skip this for AIX. - -if (isAIX) { - test.skip("folder watch capability is limited in AIX."); -} - -const platformTimeout = ms => ms * (process.platform === "win32" ? 2 : 1); - -test("Watch a file (not a folder) using fs.watch", async () => { - // Create a temporary directory for testing - const testDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "test-")); - const rootDirectory = await fs.promises.mkdtemp(path.join(testDir, path.sep)); - const testDirectory = path.join(rootDirectory, "test-6"); - await fs.promises.mkdir(testDirectory); - - const filePath = path.join(testDirectory, "only-file.txt"); - await fs.promises.writeFile(filePath, "hello"); - - let watcherClosed = false; - let interval; - - const watcher = fs.watch(filePath, { recursive: true }); - - const watchPromise = new Promise(resolve => { - watcher.on("change", function (event, filename) { - expect(event).toBe("change"); - - if (filename === path.basename(filePath)) { - clearInterval(interval); - interval = null; - watcher.close(); - watcherClosed = true; - resolve(); - } - }); - }); - - interval = setInterval(() => { - fs.writeFileSync(filePath, "world"); - }, platformTimeout(10)); - - await watchPromise; - - expect(watcherClosed).toBe(true); - expect(interval).toBeNull(); -}); - -//<#END_FILE: test-fs-watch-recursive-watch-file.js diff --git a/test/js/node/test/parallel/fs-watch-ref-unref.test.js b/test/js/node/test/parallel/fs-watch-ref-unref.test.js deleted file mode 100644 index c8d92a6eaa..0000000000 --- a/test/js/node/test/parallel/fs-watch-ref-unref.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-watch-ref-unref.js -//#SHA1: ffceabfd7f8fef655b05735b8bba7fb059609980 -//----------------- -"use strict"; - -const fs = require("fs"); - -if (process.platform === "os400") { - test.skip("IBMi does not support `fs.watch()`"); -} - -test("fs.watch() can be unref()ed and ref()ed", () => { - const watcher = fs.watch(__filename, () => { - // This callback should not be called - expect(true).toBe(false); - }); - - watcher.unref(); - - return new Promise(resolve => { - setTimeout( - () => { - watcher.ref(); - watcher.unref(); - resolve(); - }, - process.platform === "win32" ? 100 : 50, - ); - }); -}); - -//<#END_FILE: test-fs-watch-ref-unref.js diff --git a/test/js/node/test/parallel/fs-watch-stop-sync.test.js b/test/js/node/test/parallel/fs-watch-stop-sync.test.js deleted file mode 100644 index fe1eae0f1c..0000000000 --- a/test/js/node/test/parallel/fs-watch-stop-sync.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-fs-watch-stop-sync.js -//#SHA1: 8285d2bd43d2f9be7be525417cf51f9336b2f379 -//----------------- -"use strict"; - -// This test checks that the `stop` event is emitted asynchronously. -// -// If it isn't asynchronous, then the listener will be called during the -// execution of `watch.stop()`. That would be a bug. -// -// If it is asynchronous, then the listener will be removed before the event is -// emitted. - -const fs = require("fs"); - -test("stop event is emitted asynchronously", () => { - const listener = jest.fn(); - - const watch = fs.watchFile(__filename, jest.fn()); - watch.once("stop", listener); - watch.stop(); - watch.removeListener("stop", listener); - - expect(listener).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-fs-watch-stop-sync.js diff --git a/test/js/node/test/parallel/fs-watch.test.js b/test/js/node/test/parallel/fs-watch.test.js deleted file mode 100644 index 117b2611dc..0000000000 --- a/test/js/node/test/parallel/fs-watch.test.js +++ /dev/null @@ -1,122 +0,0 @@ -//#FILE: test-fs-watch.js -//#SHA1: 07373db00b057e796555cac6f75973e9e4358284 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const isIBMi = process.platform === 'os400'; -const isLinux = process.platform === 'linux'; -const isMacOS = process.platform === 'darwin'; -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-watch'); - - class WatchTestCase { - constructor(shouldInclude, dirName, fileName, field) { - this.dirName = dirName; - this.fileName = fileName; - this.field = field; - this.shouldSkip = !shouldInclude; - } - get dirPath() { return path.join(tmpdir, this.dirName); } - get filePath() { return path.join(this.dirPath, this.fileName); } - } - - const cases = [ - new WatchTestCase( - isLinux || isMacOS || isWindows || isAIX, - 'watch1', - 'foo', - 'filePath' - ), - new WatchTestCase( - isLinux || isMacOS || isWindows, - 'watch2', - 'bar', - 'dirPath' - ), - ]; - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - function doWatchTest(testCase) { - return new Promise((resolve, reject) => { - let interval; - const pathToWatch = testCase[testCase.field]; - const watcher = fs.watch(pathToWatch); - - watcher.on('error', (err) => { - if (interval) { - clearInterval(interval); - interval = null; - } - reject(err); - }); - - watcher.on('change', (eventType, argFilename) => { - if (interval) { - clearInterval(interval); - interval = null; - } - if (isMacOS) - expect(['rename', 'change'].includes(eventType)).toBe(true); - else - expect(eventType).toBe('change'); - expect(argFilename).toBe(testCase.fileName); - - watcher.close(); - watcher.close(); // Closing a closed watcher should be a noop - resolve(); - }); - - const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4); - interval = setInterval(() => { - fs.writeFileSync(testCase.filePath, ''); - fs.writeFileSync(testCase.filePath, content2); - }, 100); - }); - } - - test.each(cases.filter(testCase => !testCase.shouldSkip))( - 'Watch test for $dirName', - async (testCase) => { - fs.mkdirSync(testCase.dirPath, { recursive: true }); - const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4); - fs.writeFileSync(testCase.filePath, content1); - - if (isMacOS) { - await new Promise(resolve => setTimeout(resolve, 100)); - } - - await doWatchTest(testCase); - }, - 30000 // Increase timeout to 30 seconds - ); - - test('fs.watch throws for invalid inputs', () => { - [false, 1, {}, [], null, undefined].forEach((input) => { - expect(() => fs.watch(input, () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); - }); -} - -//<#END_FILE: test-fs-watch.js diff --git a/test/js/node/test/parallel/fs-watchfile-bigint.test.js b/test/js/node/test/parallel/fs-watchfile-bigint.test.js deleted file mode 100644 index bf4c61d5b9..0000000000 --- a/test/js/node/test/parallel/fs-watchfile-bigint.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-fs-watchfile-bigint.js -//#SHA1: 3b2e1f656e95137ca75dedd42e71ba49e6405441 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-watchfile-bigint'); -const enoentFile = path.join(tmpdir, 'non-existent-file'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('fs.watchFile with bigint option', (done) => { - let fileExists = false; - const options = { interval: 0, bigint: true }; - - const watcher = fs.watchFile(enoentFile, options, (curr, prev) => { - if (!fileExists) { - // If the file does not exist, all the fields should be zero and the date - // fields should be UNIX EPOCH time - expect(curr.ino).toBe(0n); - expect(prev.ino).toBe(0n); - // Create the file now, so that the callback will be called back once the - // event loop notices it. - fs.closeSync(fs.openSync(enoentFile, 'w')); - fileExists = true; - } else { - // If the ino (inode) value is greater than zero, it means that the file - // is present in the filesystem and it has a valid inode number. - expect(curr.ino).toBeGreaterThan(0n); - // As the file just got created, previous ino value should be lesser than - // or equal to zero (non-existent file). - expect(prev.ino).toBeLessThanOrEqual(0n); - // Stop watching the file - fs.unwatchFile(enoentFile); - watcher.stop(); // Stopping a stopped watcher should be a noop - done(); - } - }); - - // 'stop' should only be emitted once - stopping a stopped watcher should - // not trigger a 'stop' event. - watcher.on('stop', jest.fn()); - - // Ensure the test times out if the callback is not called twice - jest.setTimeout(10000); -}); - -//<#END_FILE: test-fs-watchfile-bigint.js diff --git a/test/js/node/test/parallel/fs-write-file-buffer.test.js b/test/js/node/test/parallel/fs-write-file-buffer.test.js deleted file mode 100644 index 774f93cfb8..0000000000 --- a/test/js/node/test/parallel/fs-write-file-buffer.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-fs-write-file-buffer.js -//#SHA1: f721ad0f6969d6cf1ba78f96ccf9600a7f93458d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); - -let data = [ - "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH", - "Bw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/", - "2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e", - "Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAQABADASIAAhEBAxEB/8QA", - "HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF", - "BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK", - "FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1", - "dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG", - "x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB", - "AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC", - "AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom", - "JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE", - "hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU", - "1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhfBUFl/wk", - "OmPqKJJZw3aiZFBw4z93jnkkc9u9dj8XLfSI/EBt7DTo7ea2Ox5YXVo5FC7g", - "Tjq24nJPXNVtO0KATRvNHCIg3zoWJWQHqp+o4pun+EtJ0zxBq8mnLJa2d1L5", - "0NvnKRjJBUE5PAx3NYxxUY0pRtvYHSc5Ka2X9d7H/9k=", -]; - -data = data.join("\n"); - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(process.env.TEST_TMPDIR || "/tmp", "test-fs-write-file-buffer-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("writeFileSync with Buffer", () => { - const buf = Buffer.from(data, "base64"); - const testFile = path.join(tmpdir, "test.jpg"); - - fs.writeFileSync(testFile, buf); - - expect(fs.existsSync(testFile)).toBe(true); - expect(fs.readFileSync(testFile)).toEqual(buf); -}); - -//<#END_FILE: test-fs-write-file-buffer.js diff --git a/test/js/node/test/parallel/fs-write-no-fd.test.js b/test/js/node/test/parallel/fs-write-no-fd.test.js deleted file mode 100644 index 6750c80c46..0000000000 --- a/test/js/node/test/parallel/fs-write-no-fd.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-fs-write-no-fd.js -//#SHA1: eade06241743a0d7e72b5239633e1ddd947f3a28 -//----------------- -"use strict"; -const fs = require("fs"); - -test("fs.write with null fd and Buffer throws TypeError", () => { - expect(() => { - fs.write(null, Buffer.allocUnsafe(1), 0, 1, () => {}); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("fs.write with null fd and string throws TypeError", () => { - expect(() => { - fs.write(null, "1", 0, 1, () => {}); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-fs-write-no-fd.js diff --git a/test/js/node/test/parallel/fs-write-stream-close-without-callback.test.js b/test/js/node/test/parallel/fs-write-stream-close-without-callback.test.js deleted file mode 100644 index 969a581bb5..0000000000 --- a/test/js/node/test/parallel/fs-write-stream-close-without-callback.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-fs-write-stream-close-without-callback.js -//#SHA1: 63e0c345b440c8cfb157aa84340f387cf314e20f -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = path.join(os.tmpdir(), "test-fs-write-stream-close-without-callback"); - -beforeEach(() => { - // Create a fresh temporary directory before each test - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } -}); - -afterEach(() => { - // Clean up the temporary directory after each test - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } -}); - -test("fs.WriteStream can be closed without a callback", () => { - const filePath = path.join(tmpdir, "nocallback"); - const s = fs.createWriteStream(filePath); - - s.end("hello world"); - s.close(); - - // We don't need to assert anything here as the test is checking - // that the above operations don't throw an error - expect(true).toBe(true); -}); - -//<#END_FILE: test-fs-write-stream-close-without-callback.js diff --git a/test/js/node/test/parallel/fs-write-stream-encoding.test.js b/test/js/node/test/parallel/fs-write-stream-encoding.test.js deleted file mode 100644 index ee0135bd36..0000000000 --- a/test/js/node/test/parallel/fs-write-stream-encoding.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-fs-write-stream-encoding.js -//#SHA1: a2f61bd26151411263b933d254ec75a7ca4056fc -//----------------- -'use strict'; -const fs = require('fs'); -const stream = require('stream'); -const path = require('path'); -const os = require('os'); - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); -const tmpdir = path.join(os.tmpdir(), 'test-fs-write-stream-encoding'); - -const firstEncoding = 'base64'; -const secondEncoding = 'latin1'; - -const examplePath = path.join(fixturesPath, 'x.txt'); -const dummyPath = path.join(tmpdir, 'x.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('write stream encoding', (done) => { - const exampleReadStream = fs.createReadStream(examplePath, { - encoding: firstEncoding - }); - - const dummyWriteStream = fs.createWriteStream(dummyPath, { - encoding: firstEncoding - }); - - exampleReadStream.pipe(dummyWriteStream).on('finish', () => { - const assertWriteStream = new stream.Writable({ - write: function(chunk, enc, next) { - const expected = Buffer.from('xyz\n'); - expect(chunk).toEqual(expected); - next(); - } - }); - assertWriteStream.setDefaultEncoding(secondEncoding); - - fs.createReadStream(dummyPath, { - encoding: secondEncoding - }).pipe(assertWriteStream).on('finish', done); - }); -}); - -//<#END_FILE: test-fs-write-stream-encoding.js diff --git a/test/js/node/test/parallel/fs-writestream-open-write.test.js b/test/js/node/test/parallel/fs-writestream-open-write.test.js deleted file mode 100644 index c641c31ce8..0000000000 --- a/test/js/node/test/parallel/fs-writestream-open-write.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-fs-writestream-open-write.js -//#SHA1: a4cb8508ae1f366c94442a43312a817f00b68de6 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Regression test for https://github.com/nodejs/node/issues/51993 - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-fs-writestream-open-write-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("fs.createWriteStream opens and writes correctly", done => { - const file = path.join(tmpdir, "test-fs-writestream-open-write.txt"); - - const w = fs.createWriteStream(file); - - w.on("open", () => { - w.write("hello"); - - process.nextTick(() => { - w.write("world"); - w.end(); - }); - }); - - w.on("close", () => { - expect(fs.readFileSync(file, "utf8")).toBe("helloworld"); - fs.unlinkSync(file); - done(); - }); -}); - -//<#END_FILE: test-fs-writestream-open-write.js diff --git a/test/js/node/test/parallel/global-customevent.test.js b/test/js/node/test/parallel/global-customevent.test.js deleted file mode 100644 index 988e0cd447..0000000000 --- a/test/js/node/test/parallel/global-customevent.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-global-customevent.js -//#SHA1: 754c1b6babd0e73fa3206c9c9179ff3a034eba9b -//----------------- -"use strict"; - -// Global -test("CustomEvent is defined globally", () => { - expect(CustomEvent).toBeDefined(); -}); - -test("CustomEvent is the same as internal CustomEvent", () => { - // We can't use internal modules in Jest, so we'll skip this test - // and add a comment explaining why. - console.log("Skipping test for internal CustomEvent comparison"); - // The original test was: - // strictEqual(CustomEvent, internalCustomEvent); -}); - -//<#END_FILE: test-global-customevent.js diff --git a/test/js/node/test/parallel/global-domexception.test.js b/test/js/node/test/parallel/global-domexception.test.js deleted file mode 100644 index 2ebf63c2fd..0000000000 --- a/test/js/node/test/parallel/global-domexception.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-global-domexception.js -//#SHA1: 9a8d5eacea5ae98814fa6312b5f10089034c1ef4 -//----------------- -"use strict"; - -// This test checks the global availability and behavior of DOMException - -test("DOMException is a global function", () => { - expect(typeof DOMException).toBe("function"); -}); - -test("atob throws a DOMException for invalid input", () => { - expect(() => { - atob("我要抛错!"); - }).toThrow(DOMException); -}); - -//<#END_FILE: test-global-domexception.js diff --git a/test/js/node/test/parallel/global-encoder.test.js b/test/js/node/test/parallel/global-encoder.test.js deleted file mode 100644 index a9a928fb17..0000000000 --- a/test/js/node/test/parallel/global-encoder.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-global-encoder.js -//#SHA1: 7397937d3493488fc47e8ba6ba8fcb4f5bdd97fa -//----------------- -"use strict"; - -test("TextDecoder and TextEncoder are globally available", () => { - const util = require("util"); - - expect(TextDecoder).toBe(util.TextDecoder); - expect(TextEncoder).toBe(util.TextEncoder); -}); - -//<#END_FILE: test-global-encoder.js diff --git a/test/js/node/test/parallel/global-webcrypto.test.js b/test/js/node/test/parallel/global-webcrypto.test.js deleted file mode 100644 index a936c6b593..0000000000 --- a/test/js/node/test/parallel/global-webcrypto.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-global-webcrypto.js -//#SHA1: 3ae34178f201f6dfeb3ca5ec6e0914e6de63d64b -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto is not available -if (!crypto) { - test.skip("missing crypto", () => {}); -} else { - describe("Global WebCrypto", () => { - test("globalThis.crypto is crypto.webcrypto", () => { - expect(globalThis.crypto).toBe(crypto.webcrypto); - }); - - test("Crypto is the constructor of crypto.webcrypto", () => { - expect(Crypto).toBe(crypto.webcrypto.constructor); - }); - - test("SubtleCrypto is the constructor of crypto.webcrypto.subtle", () => { - expect(SubtleCrypto).toBe(crypto.webcrypto.subtle.constructor); - }); - }); -} - -//<#END_FILE: test-global-webcrypto.js diff --git a/test/js/node/test/parallel/heap-prof-exec-argv.test.js b/test/js/node/test/parallel/heap-prof-exec-argv.test.js deleted file mode 100644 index aa64858dbb..0000000000 --- a/test/js/node/test/parallel/heap-prof-exec-argv.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-heap-prof-exec-argv.js -//#SHA1: 77c3a447116b06f03f52fd56efe928699ba6d60d -//----------------- -"use strict"; - -// Tests --heap-prof generates a heap profile from worker -// when execArgv is set. - -const fixtures = require("../common/fixtures"); -const assert = require("assert"); -const { spawnSync } = require("child_process"); -const tmpdir = require("../common/tmpdir"); -const { getHeapProfiles, verifyFrames } = require("../common/prof"); - -// Skip the test if inspector is disabled -const isInspectorEnabled = process.execArgv.some(arg => arg.startsWith("--inspect")); -if (!isInspectorEnabled) { - test.skip("Inspector is disabled", () => {}); -} else { - test("--heap-prof generates a heap profile from worker when execArgv is set", () => { - tmpdir.refresh(); - const output = spawnSync(process.execPath, [fixtures.path("workload", "allocation-worker-argv.js")], { - cwd: tmpdir.path, - env: { - ...process.env, - HEAP_PROF_INTERVAL: "128", - }, - }); - - if (output.status !== 0) { - console.log(output.stderr.toString()); - } - - expect(output.status).toBe(0); - - const profiles = getHeapProfiles(tmpdir.path); - expect(profiles.length).toBe(1); - - verifyFrames(output, profiles[0], "runAllocation"); - }); -} - -//<#END_FILE: test-heap-prof-exec-argv.js diff --git a/test/js/node/test/parallel/http-abort-queued.test.js b/test/js/node/test/parallel/http-abort-queued.test.js deleted file mode 100644 index d5c7fea669..0000000000 --- a/test/js/node/test/parallel/http-abort-queued.test.js +++ /dev/null @@ -1,102 +0,0 @@ -//#FILE: test-http-abort-queued.js -//#SHA1: e0fcd4a5eb0466a1e218147e8eb53714311a6f42 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -let complete; - -test("http abort queued request", async () => { - const server = http.createServer((req, res) => { - // We should not see the queued /thatotherone request within the server - // as it should be aborted before it is sent. - expect(req.url).toBe("/"); - - res.writeHead(200); - res.write("foo"); - - complete = - complete || - function () { - res.end(); - }; - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const agent = new http.Agent({ maxSockets: 1 }); - expect(Object.keys(agent.sockets)).toHaveLength(0); - - const options = { - hostname: "localhost", - port: server.address().port, - method: "GET", - path: "/", - agent: agent, - }; - - const req1 = http.request(options); - req1.on("response", res1 => { - expect(Object.keys(agent.sockets)).toHaveLength(1); - expect(Object.keys(agent.requests)).toHaveLength(0); - - const req2 = http.request({ - method: "GET", - host: "localhost", - port: server.address().port, - path: "/thatotherone", - agent: agent, - }); - expect(Object.keys(agent.sockets)).toHaveLength(1); - expect(Object.keys(agent.requests)).toHaveLength(1); - - // TODO(jasnell): This event does not appear to currently be triggered. - // is this handler actually required? - req2.on("error", err => { - // This is expected in response to our explicit abort call - expect(err.code).toBe("ECONNRESET"); - }); - - req2.end(); - req2.abort(); - - expect(Object.keys(agent.sockets)).toHaveLength(1); - expect(Object.keys(agent.requests)).toHaveLength(1); - - res1.on("data", chunk => complete()); - - res1.on("end", () => { - setTimeout(() => { - expect(Object.keys(agent.sockets)).toHaveLength(0); - expect(Object.keys(agent.requests)).toHaveLength(0); - - server.close(); - }, 100); - }); - }); - - req1.end(); -}); - -//<#END_FILE: test-http-abort-queued.js diff --git a/test/js/node/test/parallel/http-agent-false.test.js b/test/js/node/test/parallel/http-agent-false.test.js deleted file mode 100644 index c99f236785..0000000000 --- a/test/js/node/test/parallel/http-agent-false.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-agent-false.js -//#SHA1: ba987050ea069a591615a69e341cf6f9c7298e5a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http.request with agent: false and port: null", async () => { - // Sending `agent: false` when `port: null` is also passed in (i.e. the result - // of a `url.parse()` call with the default port used, 80 or 443), should not - // result in an assertion error... - const opts = { - host: "127.0.0.1", - port: null, - path: "/", - method: "GET", - agent: false, - }; - - // We just want an "error" (no local HTTP server on port 80) or "response" - // to happen (user happens ot have HTTP server running on port 80). - // As long as the process doesn't crash from a C++ assertion then we're good. - const req = http.request(opts); - - // Will be called by either the response event or error event, not both - const oneResponse = jest.fn(); - req.on("response", oneResponse); - req.on("error", oneResponse); - req.end(); - - // Wait for the request to complete - await new Promise(resolve => { - req.on("response", resolve); - req.on("error", resolve); - }); - - // Check that oneResponse was called exactly once - expect(oneResponse).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-agent-false.js diff --git a/test/js/node/test/parallel/http-agent-uninitialized-with-handle.test.js b/test/js/node/test/parallel/http-agent-uninitialized-with-handle.test.js deleted file mode 100644 index b78aacf5b6..0000000000 --- a/test/js/node/test/parallel/http-agent-uninitialized-with-handle.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-http-agent-uninitialized-with-handle.js -//#SHA1: 828942acbc68f8fd92425ecdf0e754ab13b4baff -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -test("http agent with uninitialized socket handle", done => { - const agent = new http.Agent({ - keepAlive: true, - }); - const socket = new net.Socket(); - // If _handle exists then internals assume a couple methods exist. - socket._handle = { - ref() {}, - readStart() {}, - }; - - const server = http.createServer((req, res) => { - res.end(); - }); - - server.listen(0, () => { - const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); - - // Manually add the socket without a _handle. - agent.freeSockets[agent.getName(req)] = [socket]; - // Now force the agent to use the socket and check that _handle exists before - // calling asyncReset(). - agent.addRequest(req, {}); - req.on("response", () => { - server.close(); - done(); - }); - req.end(); - }); - - expect(server).toHaveProperty("listen"); - expect(server).toHaveProperty("close"); -}); - -//<#END_FILE: test-http-agent-uninitialized-with-handle.js diff --git a/test/js/node/test/parallel/http-agent-uninitialized.test.js b/test/js/node/test/parallel/http-agent-uninitialized.test.js deleted file mode 100644 index 43e447a063..0000000000 --- a/test/js/node/test/parallel/http-agent-uninitialized.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-http-agent-uninitialized.js -//#SHA1: 00034f4963a5620af8a58e68c262c92ea9ec982b -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -test("http agent handles uninitialized socket", done => { - const agent = new http.Agent({ - keepAlive: true, - }); - const socket = new net.Socket(); - - const server = http - .createServer((req, res) => { - res.end(); - }) - .listen(0, () => { - const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); - - // Manually add the socket without a _handle. - agent.freeSockets[agent.getName(req)] = [socket]; - // Now force the agent to use the socket and check that _handle exists before - // calling asyncReset(). - agent.addRequest(req, {}); - req.on("response", () => { - server.close(); - done(); - }); - req.end(); - }); - - expect(server).toBeDefined(); -}); - -//<#END_FILE: test-http-agent-uninitialized.js diff --git a/test/js/node/test/parallel/http-agent.test.js b/test/js/node/test/parallel/http-agent.test.js deleted file mode 100644 index 900ad21d17..0000000000 --- a/test/js/node/test/parallel/http-agent.test.js +++ /dev/null @@ -1,97 +0,0 @@ -//#FILE: test-http-agent.js -//#SHA1: c5bb5b1b47100659ac17ae6c4ba084c6974ddaa7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const N = 4; -const M = 4; - -let server; - -beforeEach(() => { - server = http.Server((req, res) => { - res.writeHead(200); - res.end("hello world\n"); - }); -}); - -afterEach(() => { - server.close(); -}); - -function makeRequests(outCount, inCount, shouldFail) { - return new Promise(resolve => { - const totalRequests = outCount * inCount; - let completedRequests = 0; - - const onRequest = jest.fn(res => { - completedRequests++; - if (completedRequests === totalRequests) { - resolve(); - } - - if (!shouldFail) { - res.resume(); - } - }); - - server.listen(0, () => { - const port = server.address().port; - for (let i = 0; i < outCount; i++) { - setTimeout(() => { - for (let j = 0; j < inCount; j++) { - const req = http.get({ port: port, path: "/" }, onRequest); - if (shouldFail) { - req.on("error", onRequest); - } else { - req.on("error", e => { - throw e; - }); - } - } - }, i); - } - }); - }); -} - -test("makeRequests successful", async () => { - await makeRequests(N, M); - expect(server.listenerCount("request")).toBe(1); -}); - -test("makeRequests with failing requests", async () => { - const originalCreateConnection = http.Agent.prototype.createConnection; - - http.Agent.prototype.createConnection = function createConnection(_, cb) { - process.nextTick(cb, new Error("nothing")); - }; - - await makeRequests(N, M, true); - - http.Agent.prototype.createConnection = originalCreateConnection; -}); - -//<#END_FILE: test-http-agent.js diff --git a/test/js/node/test/parallel/http-chunked-smuggling.test.js b/test/js/node/test/parallel/http-chunked-smuggling.test.js deleted file mode 100644 index e1ab305b10..0000000000 --- a/test/js/node/test/parallel/http-chunked-smuggling.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-chunked-smuggling.js -//#SHA1: c146d9dc37a522ac07d943b4c40b3301923659fa -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -// Verify that invalid chunk extensions cannot be used to perform HTTP request -// smuggling attacks. - -describe("HTTP Chunked Smuggling", () => { - let server; - let serverPort; - - beforeAll(done => { - server = http.createServer((request, response) => { - expect(request.url).not.toBe("/admin"); - response.end("hello world"); - }); - - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); - }); - - afterAll(done => { - server.close(done); - }); - - test("invalid chunk extensions", done => { - const sock = net.connect(serverPort); - - sock.write( - "" + - "GET / HTTP/1.1\r\n" + - "Host: localhost:8080\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "2;\n" + - "xx\r\n" + - "4c\r\n" + - "0\r\n" + - "\r\n" + - "GET /admin HTTP/1.1\r\n" + - "Host: localhost:8080\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "0\r\n" + - "\r\n", - ); - - sock.resume(); - sock.on("end", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http-chunked-smuggling.js diff --git a/test/js/node/test/parallel/http-client-defaults.test.js b/test/js/node/test/parallel/http-client-defaults.test.js deleted file mode 100644 index f527d6ade8..0000000000 --- a/test/js/node/test/parallel/http-client-defaults.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-http-client-defaults.js -//#SHA1: 7209a7752de52cc378c3b29eda88c82d71e6839d -//----------------- -"use strict"; - -const http = require("http"); - -describe("ClientRequest defaults", () => { - test("default path and method", () => { - const req = new http.ClientRequest({ createConnection: () => {} }); - expect(req.path).toBe("/"); - expect(req.method).toBe("GET"); - }); - - test("empty method defaults to GET", () => { - const req = new http.ClientRequest({ method: "", createConnection: () => {} }); - expect(req.path).toBe("/"); - expect(req.method).toBe("GET"); - }); - - test("empty path defaults to /", () => { - const req = new http.ClientRequest({ path: "", createConnection: () => {} }); - expect(req.path).toBe("/"); - expect(req.method).toBe("GET"); - }); -}); - -//<#END_FILE: test-http-client-defaults.js diff --git a/test/js/node/test/parallel/http-client-get-url.test.js b/test/js/node/test/parallel/http-client-get-url.test.js deleted file mode 100644 index e13f87f7d4..0000000000 --- a/test/js/node/test/parallel/http-client-get-url.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-http-client-get-url.js -//#SHA1: 0329da4beb5be5da0ab6652b246dd912935e56af -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); -const testPath = "/foo?bar"; - -let server; -let serverAddress; - -beforeAll(async () => { - server = http.createServer((req, res) => { - expect(req.method).toBe("GET"); - expect(req.url).toBe(testPath); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("hello\n"); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", () => { - serverAddress = `http://127.0.0.1:${server.address().port}${testPath}`; - resolve(); - }); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("http.get with string URL", async () => { - await new Promise(resolve => { - http.get(serverAddress, () => { - resolve(); - }); - }); -}); - -test("http.get with parsed URL", async () => { - await new Promise(resolve => { - http.get(url.parse(serverAddress), () => { - resolve(); - }); - }); -}); - -test("http.get with URL object", async () => { - await new Promise(resolve => { - http.get(new URL(serverAddress), () => { - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-client-get-url.js diff --git a/test/js/node/test/parallel/http-client-input-function.test.js b/test/js/node/test/parallel/http-client-input-function.test.js deleted file mode 100644 index e34ee9a0d5..0000000000 --- a/test/js/node/test/parallel/http-client-input-function.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-http-client-input-function.js -//#SHA1: 2ca0147b992331ea69803031f33076c685bce264 -//----------------- -"use strict"; - -const http = require("http"); - -test("http.ClientRequest with server response", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end("hello world"); - }); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", resolve); - }); - - const serverAddress = server.address(); - - const responsePromise = new Promise(resolve => { - const req = new http.ClientRequest(serverAddress, response => { - let body = ""; - response.setEncoding("utf8"); - response.on("data", chunk => { - body += chunk; - }); - - response.on("end", () => { - resolve(body); - }); - }); - - req.end(); - }); - - const body = await responsePromise; - expect(body).toBe("hello world"); - - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-client-input-function.js diff --git a/test/js/node/test/parallel/http-client-keep-alive-release-before-finish.test.js b/test/js/node/test/parallel/http-client-keep-alive-release-before-finish.test.js deleted file mode 100644 index 5e7ca84113..0000000000 --- a/test/js/node/test/parallel/http-client-keep-alive-release-before-finish.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-http-client-keep-alive-release-before-finish.js -//#SHA1: 198cd4a6c28c8a7dda45f003305e8fa80f05469d -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP client keep-alive release before finish", done => { - const server = http.createServer((req, res) => { - res.end(); - }); - - server.listen(0, () => { - const agent = new http.Agent({ - maxSockets: 1, - keepAlive: true, - }); - - const port = server.address().port; - - const post = http.request( - { - agent, - method: "POST", - port, - }, - res => { - res.resume(); - }, - ); - - // What happens here is that the server `end`s the response before we send - // `something`, and the client thought that this is a green light for sending - // next GET request - post.write(Buffer.alloc(16 * 1024, "X")); - setTimeout(() => { - post.end("something"); - }, 100); - - http - .request( - { - agent, - method: "GET", - port, - }, - res => { - server.close(); - res.connection.end(); - done(); - }, - ) - .end(); - }); -}); - -//<#END_FILE: test-http-client-keep-alive-release-before-finish.js diff --git a/test/js/node/test/parallel/http-client-race-2.test.js b/test/js/node/test/parallel/http-client-race-2.test.js deleted file mode 100644 index bc4c83b4f4..0000000000 --- a/test/js/node/test/parallel/http-client-race-2.test.js +++ /dev/null @@ -1,136 +0,0 @@ -//#FILE: test-http-client-race-2.js -//#SHA1: f1e2a4ecdd401cb9fcf615496d1376ce0a94ad73 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -// -// Slight variation on test-http-client-race to test for another race -// condition involving the parsers FreeList used internally by http.Client. -// - -const body1_s = "1111111111111111"; -const body2_s = "22222"; -const body3_s = "3333333333333333333"; - -let server; -let port; - -beforeAll(() => { - return new Promise(resolve => { - server = http.createServer(function (req, res) { - const pathname = url.parse(req.url).pathname; - - let body; - switch (pathname) { - case "/1": - body = body1_s; - break; - case "/2": - body = body2_s; - break; - default: - body = body3_s; - } - - res.writeHead(200, { - "Content-Type": "text/plain", - "Content-Length": body.length, - }); - res.end(body); - }); - - server.listen(0, () => { - port = server.address().port; - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(resolve); - }); -}); - -test("HTTP client race condition", async () => { - let body1 = ""; - let body2 = ""; - let body3 = ""; - - // Client #1 is assigned Parser #1 - const req1 = http.get({ port, path: "/1" }); - await new Promise(resolve => { - req1.on("response", function (res1) { - res1.setEncoding("utf8"); - - res1.on("data", function (chunk) { - body1 += chunk; - }); - - res1.on("end", function () { - // Delay execution a little to allow the 'close' event to be processed - // (required to trigger this bug!) - setTimeout(resolve, 500); - }); - }); - }); - - // The bug would introduce itself here: Client #2 would be allocated the - // parser that previously belonged to Client #1. But we're not finished - // with Client #1 yet! - // - // At this point, the bug would manifest itself and crash because the - // internal state of the parser was no longer valid for use by Client #1 - const req2 = http.get({ port, path: "/2" }); - await new Promise(resolve => { - req2.on("response", function (res2) { - res2.setEncoding("utf8"); - res2.on("data", function (chunk) { - body2 += chunk; - }); - res2.on("end", resolve); - }); - }); - - // Just to be really sure we've covered all our bases, execute a - // request using client2. - const req3 = http.get({ port, path: "/3" }); - await new Promise(resolve => { - req3.on("response", function (res3) { - res3.setEncoding("utf8"); - res3.on("data", function (chunk) { - body3 += chunk; - }); - res3.on("end", resolve); - }); - }); - - expect(body1).toBe(body1_s); - expect(body2).toBe(body2_s); - expect(body3).toBe(body3_s); -}); - -//<#END_FILE: test-http-client-race-2.js diff --git a/test/js/node/test/parallel/http-client-race.test.js b/test/js/node/test/parallel/http-client-race.test.js deleted file mode 100644 index ab780f28d8..0000000000 --- a/test/js/node/test/parallel/http-client-race.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-http-client-race.js -//#SHA1: 0ad515567d91a194670069b476e166d398543cc0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -const body1_s = "1111111111111111"; -const body2_s = "22222"; - -test("http client race condition", async () => { - const server = http.createServer((req, res) => { - const body = url.parse(req.url).pathname === "/1" ? body1_s : body2_s; - res.writeHead(200, { - "Content-Type": "text/plain", - "Content-Length": body.length, - }); - res.end(body); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - let body1 = ""; - let body2 = ""; - - const makeRequest = path => { - return new Promise((resolve, reject) => { - const req = http.request({ port: server.address().port, path }); - req.end(); - req.on("response", res => { - res.setEncoding("utf8"); - let body = ""; - res.on("data", chunk => { - body += chunk; - }); - res.on("end", () => resolve(body)); - }); - req.on("error", reject); - }); - }; - - body1 = await makeRequest("/1"); - body2 = await makeRequest("/2"); - - await new Promise(resolve => server.close(resolve)); - - expect(body1).toBe(body1_s); - expect(body2).toBe(body2_s); -}); - -//<#END_FILE: test-http-client-race.js diff --git a/test/js/node/test/parallel/http-client-read-in-error.test.js b/test/js/node/test/parallel/http-client-read-in-error.test.js deleted file mode 100644 index 2dd33c52de..0000000000 --- a/test/js/node/test/parallel/http-client-read-in-error.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http-client-read-in-error.js -//#SHA1: a7bd75283f46ff8f1246208c72bf0773a27f0fb0 -//----------------- -"use strict"; - -const net = require("net"); -const http = require("http"); - -class Agent extends http.Agent { - createConnection() { - const socket = new net.Socket(); - - socket.on("error", function () { - socket.push("HTTP/1.1 200\r\n\r\n"); - }); - - let onNewListener; - socket.on( - "newListener", - (onNewListener = name => { - if (name !== "error") return; - socket.removeListener("newListener", onNewListener); - - // Let other listeners to be set up too - process.nextTick(() => { - this.breakSocket(socket); - }); - }), - ); - - return socket; - } - - breakSocket(socket) { - socket.emit("error", new Error("Intentional error")); - } -} - -test("http client read in error", () => { - const agent = new Agent(); - const dataHandler = jest.fn(); - - const request = http.request({ agent }); - - request.once("error", function () { - console.log("ignore"); - this.on("data", dataHandler); - }); - - return new Promise(resolve => { - // Give some time for the 'data' event to potentially be called - setTimeout(() => { - expect(dataHandler).not.toHaveBeenCalled(); - resolve(); - }, 100); - }); -}); - -//<#END_FILE: test-http-client-read-in-error.js diff --git a/test/js/node/test/parallel/http-client-res-destroyed.test.js b/test/js/node/test/parallel/http-client-res-destroyed.test.js deleted file mode 100644 index 7b7662ea52..0000000000 --- a/test/js/node/test/parallel/http-client-res-destroyed.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http-client-res-destroyed.js -//#SHA1: 9a7e890355cecb3eb88b6963b0c37df3f01bc8d7 -//----------------- -"use strict"; - -const http = require("http"); - -describe("HTTP Client Response Destroyed", () => { - test("Response destruction after manually calling destroy()", async () => { - const server = http.createServer((req, res) => { - res.end("asd"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get( - { - port: server.address().port, - }, - res => { - expect(res.destroyed).toBe(false); - res.destroy(); - expect(res.destroyed).toBe(true); - res.on("close", () => { - server.close(resolve); - }); - }, - ); - }); - }); - }); - - test("Response destruction after end of response", async () => { - const server = http.createServer((req, res) => { - res.end("asd"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get( - { - port: server.address().port, - }, - res => { - expect(res.destroyed).toBe(false); - res - .on("close", () => { - expect(res.destroyed).toBe(true); - server.close(resolve); - }) - .resume(); - }, - ); - }); - }); - }); -}); - -//<#END_FILE: test-http-client-res-destroyed.js diff --git a/test/js/node/test/parallel/http-client-timeout-connect-listener.test.js b/test/js/node/test/parallel/http-client-timeout-connect-listener.test.js deleted file mode 100644 index a70f33512b..0000000000 --- a/test/js/node/test/parallel/http-client-timeout-connect-listener.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http-client-timeout-connect-listener.js -//#SHA1: 4311732db4ce9958ec0ed01be68786e522ed6ca8 -//----------------- -"use strict"; - -// This test ensures that `ClientRequest.prototype.setTimeout()` does -// not add a listener for the `'connect'` event to the socket if the -// socket is already connected. - -const http = require("http"); - -// Maximum allowed value for timeouts. -const timeout = 2 ** 31 - 1; - -let server; -let agent; - -beforeAll(() => { - return new Promise(resolve => { - server = http.createServer((req, res) => { - res.end(); - }); - - server.listen(0, () => { - agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - agent.destroy(); - server.close(resolve); - }); -}); - -function doRequest(options) { - return new Promise(resolve => { - const req = http.get(options, res => { - res.on("end", resolve); - res.resume(); - }); - - req.setTimeout(timeout); - return req; - }); -} - -test("ClientRequest.prototype.setTimeout() does not add connect listener to connected socket", async () => { - const options = { port: server.address().port, agent: agent }; - - await doRequest(options); - - const req = http.get(options); - req.setTimeout(timeout); - - await new Promise(resolve => { - req.on("socket", socket => { - expect(socket.listenerCount("connect")).toBe(0); - resolve(); - }); - }); - - await new Promise(resolve => { - req.on("response", res => { - res.on("end", resolve); - res.resume(); - }); - }); -}); - -//<#END_FILE: test-http-client-timeout-connect-listener.js diff --git a/test/js/node/test/parallel/http-client-upload-buf.test.js b/test/js/node/test/parallel/http-client-upload-buf.test.js deleted file mode 100644 index 9891a442dc..0000000000 --- a/test/js/node/test/parallel/http-client-upload-buf.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http-client-upload-buf.js -//#SHA1: bbfd7c52e710f53683f5f9a4578f34e451db4eb0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const assert = require("assert"); -const http = require("http"); - -const N = 1024; - -test("HTTP client upload buffer", async () => { - const server = http.createServer((req, res) => { - expect(req.method).toBe("POST"); - let bytesReceived = 0; - - req.on("data", chunk => { - bytesReceived += chunk.length; - }); - - req.on("end", () => { - expect(bytesReceived).toBe(N); - console.log("request complete from server"); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("hello\n"); - res.end(); - }); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const { port } = server.address(); - - const responsePromise = new Promise(resolve => { - const req = http.request( - { - port, - method: "POST", - path: "/", - }, - res => { - res.setEncoding("utf8"); - res.on("data", chunk => { - console.log(chunk); - }); - res.on("end", resolve); - }, - ); - - req.write(Buffer.allocUnsafe(N)); - req.end(); - }); - - await responsePromise; - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-client-upload-buf.js diff --git a/test/js/node/test/parallel/http-client-upload.test.js b/test/js/node/test/parallel/http-client-upload.test.js deleted file mode 100644 index 6adc480232..0000000000 --- a/test/js/node/test/parallel/http-client-upload.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-http-client-upload.js -//#SHA1: 328a7e9989cc28daa5996c83fe9e3cfcb0893e01 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP client upload", async () => { - const serverHandler = jest.fn((req, res) => { - expect(req.method).toBe("POST"); - req.setEncoding("utf8"); - - let sent_body = ""; - - req.on("data", chunk => { - console.log(`server got: ${JSON.stringify(chunk)}`); - sent_body += chunk; - }); - - req.on("end", () => { - expect(sent_body).toBe("1\n2\n3\n"); - console.log("request complete from server"); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("hello\n"); - res.end(); - }); - }); - - const server = http.createServer(serverHandler); - await new Promise(resolve => server.listen(0, resolve)); - - const { port } = server.address(); - - const clientHandler = jest.fn(res => { - res.setEncoding("utf8"); - res.on("data", chunk => { - console.log(chunk); - }); - res.on("end", () => { - server.close(); - }); - }); - - const req = http.request( - { - port, - method: "POST", - path: "/", - }, - clientHandler, - ); - - req.write("1\n"); - req.write("2\n"); - req.write("3\n"); - req.end(); - - await new Promise(resolve => server.on("close", resolve)); - - expect(serverHandler).toHaveBeenCalledTimes(1); - expect(clientHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-client-upload.js diff --git a/test/js/node/test/parallel/http-decoded-auth.test.js b/test/js/node/test/parallel/http-decoded-auth.test.js deleted file mode 100644 index ed117d1954..0000000000 --- a/test/js/node/test/parallel/http-decoded-auth.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-http-decoded-auth.js -//#SHA1: 70ba85653c7479ce80cf528a07aa85f598f85ef8 -//----------------- -"use strict"; - -const http = require("http"); - -const testCases = [ - { - username: 'test@test"', - password: "123456^", - expected: "dGVzdEB0ZXN0IjoxMjM0NTZe", - }, - { - username: "test%40test", - password: "123456", - expected: "dGVzdEB0ZXN0OjEyMzQ1Ng==", - }, - { - username: "not%3Agood", - password: "god", - expected: "bm90Omdvb2Q6Z29k", - }, - { - username: "not%22good", - password: "g%5Eod", - expected: "bm90Imdvb2Q6Z15vZA==", - }, - { - username: "test1234::::", - password: "mypass", - expected: "dGVzdDEyMzQ6Ojo6Om15cGFzcw==", - }, -]; - -testCases.forEach((testCase, index) => { - test(`HTTP decoded auth - case ${index + 1}`, async () => { - const server = http.createServer((request, response) => { - // The correct authorization header is be passed - expect(request.headers.authorization).toBe(`Basic ${testCase.expected}`); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - // make the request - const url = new URL(`http://${testCase.username}:${testCase.password}@localhost:${server.address().port}`); - http.request(url).end(); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-http-decoded-auth.js diff --git a/test/js/node/test/parallel/http-default-encoding.test.js b/test/js/node/test/parallel/http-default-encoding.test.js deleted file mode 100644 index 30fc083b29..0000000000 --- a/test/js/node/test/parallel/http-default-encoding.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http-default-encoding.js -//#SHA1: f5dfdba00ec21efec894e5edf97583c77334a2c3 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const expected = "This is a unicode text: سلام"; - -test("HTTP server with default encoding", async () => { - let result = ""; - - const server = http.Server((req, res) => { - req.setEncoding("utf8"); - req - .on("data", chunk => { - result += chunk; - }) - .on("end", () => { - res.writeHead(200); - res.end("hello world\n"); - server.close(); - }); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - path: "/", - method: "POST", - }, - res => { - expect(res.statusCode).toBe(200); - res.resume(); - resolve(); - }, - ); - - req.on("error", e => { - console.log(e.message); - process.exit(1); - }); - - req.end(expected); - }); - }); - - expect(result).toBe(expected); -}); - -//<#END_FILE: test-http-default-encoding.js diff --git a/test/js/node/test/parallel/http-extra-response.test.js b/test/js/node/test/parallel/http-extra-response.test.js deleted file mode 100644 index b3e73a341a..0000000000 --- a/test/js/node/test/parallel/http-extra-response.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-http-extra-response.js -//#SHA1: 0d2dfa2459e54fa9f1b90a609c328afd478f2793 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const net = require("net"); - -// If an HTTP server is broken and sends data after the end of the response, -// node should ignore it and drop the connection. -// Demos this bug: https://github.com/joyent/node/issues/680 - -const body = "hello world\r\n"; -const fullResponse = - "HTTP/1.1 500 Internal Server Error\r\n" + - `Content-Length: ${body.length}\r\n` + - "Content-Type: text/plain\r\n" + - "Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n" + - "Host: 10.20.149.2\r\n" + - "Access-Control-Allow-Credentials: true\r\n" + - "Server: badly broken/0.1 (OS NAME)\r\n" + - "\r\n" + - body; - -test("HTTP server sending data after response end", async () => { - const server = net.createServer(socket => { - let postBody = ""; - - socket.setEncoding("utf8"); - - socket.on("data", chunk => { - postBody += chunk; - - if (postBody.includes("\r\n")) { - socket.write(fullResponse); - socket.end(fullResponse); - } - }); - - socket.on("error", err => { - expect(err.code).toBe("ECONNRESET"); - }); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(); - - await new Promise(resolve => { - http.get({ port }, res => { - let buffer = ""; - console.log(`Got res code: ${res.statusCode}`); - - res.setEncoding("utf8"); - res.on("data", chunk => { - buffer += chunk; - }); - - res.on("end", () => { - console.log(`Response ended, read ${buffer.length} bytes`); - expect(buffer).toBe(body); - resolve(); - }); - }); - }); - - expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/Got res code: \d+/)); - expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/Response ended, read \d+ bytes/)); - - consoleLogSpy.mockRestore(); - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-extra-response.js diff --git a/test/js/node/test/parallel/http-full-response.test.js b/test/js/node/test/parallel/http-full-response.test.js deleted file mode 100644 index 8e0a8fcc3b..0000000000 --- a/test/js/node/test/parallel/http-full-response.test.js +++ /dev/null @@ -1,104 +0,0 @@ -//#FILE: test-http-full-response.js -//#SHA1: 3494c79026bf858a01bb497a50a8f2fd3166e62d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// This test requires the program 'ab' -const http = require("http"); -const { exec } = require("child_process"); - -const bodyLength = 12345; - -const body = "c".repeat(bodyLength); - -let server; - -beforeAll(() => { - return new Promise(resolve => { - server = http.createServer((req, res) => { - res.writeHead(200, { - "Content-Length": bodyLength, - "Content-Type": "text/plain", - }); - res.end(body); - }); - - server.listen(0, () => { - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -function runAb(opts) { - return new Promise((resolve, reject) => { - const command = `ab ${opts} http://127.0.0.1:${server.address().port}/`; - exec(command, (err, stdout, stderr) => { - if (err) { - if (/ab|apr/i.test(stderr)) { - console.log(`Skipping: problem spawning \`ab\`.\n${stderr}`); - return resolve(); - } - return reject(err); - } - - let m = /Document Length:\s*(\d+) bytes/i.exec(stdout); - const documentLength = parseInt(m[1]); - - m = /Complete requests:\s*(\d+)/i.exec(stdout); - const completeRequests = parseInt(m[1]); - - m = /HTML transferred:\s*(\d+) bytes/i.exec(stdout); - const htmlTransferred = parseInt(m[1]); - - expect(documentLength).toBe(bodyLength); - expect(htmlTransferred).toBe(completeRequests * documentLength); - - resolve(); - }); - }); -} - -test("-c 1 -n 10", async () => { - await runAb("-c 1 -n 10"); - console.log("-c 1 -n 10 okay"); -}); - -test("-c 1 -n 100", async () => { - await runAb("-c 1 -n 100"); - console.log("-c 1 -n 100 okay"); -}); - -test("-c 1 -n 1000", async () => { - await runAb("-c 1 -n 1000"); - console.log("-c 1 -n 1000 okay"); -}); - -//<#END_FILE: test-http-full-response.js diff --git a/test/js/node/test/parallel/http-head-request.test.js b/test/js/node/test/parallel/http-head-request.test.js deleted file mode 100644 index b2f86c5ac2..0000000000 --- a/test/js/node/test/parallel/http-head-request.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-http-head-request.js -//#SHA1: ab54d1748aa92e4fa61cf4994e83ddf5e00bf874 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const body = "hello world\n"; - -async function runTest(headers) { - return new Promise((resolve, reject) => { - const server = http.createServer((req, res) => { - console.error("req: %s headers: %j", req.method, headers); - res.writeHead(200, headers); - res.end(); - server.close(); - }); - - server.listen(0, () => { - const request = http.request( - { - port: server.address().port, - method: "HEAD", - path: "/", - }, - response => { - console.error("response start"); - response.on("end", () => { - console.error("response end"); - resolve(); - }); - response.resume(); - }, - ); - request.end(); - }); - }); -} - -test("HEAD request with Transfer-Encoding: chunked", async () => { - await expect( - runTest({ - "Transfer-Encoding": "chunked", - }), - ).resolves.toBeUndefined(); -}); - -test("HEAD request with Content-Length", async () => { - await expect( - runTest({ - "Content-Length": body.length, - }), - ).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-http-head-request.js diff --git a/test/js/node/test/parallel/http-head-response-has-no-body-end-implicit-headers.test.js b/test/js/node/test/parallel/http-head-response-has-no-body-end-implicit-headers.test.js deleted file mode 100644 index 46f4f2269d..0000000000 --- a/test/js/node/test/parallel/http-head-response-has-no-body-end-implicit-headers.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-http-head-response-has-no-body-end-implicit-headers.js -//#SHA1: e2f884b0a99ba30e0e8065596d00af1ed99b4791 -//----------------- -"use strict"; -const http = require("http"); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request with data to res.end, -// it does not send any body but the response is sent -// anyway. - -test("HTTP HEAD response has no body, end implicit headers", done => { - const server = http.createServer((req, res) => { - res.end("FAIL"); // broken: sends FAIL from hot path. - }); - - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - method: "HEAD", - path: "/", - }, - res => { - res.on("end", () => { - server.close(); - done(); - }); - res.resume(); - }, - ); - req.end(); - }); -}); - -//<#END_FILE: test-http-head-response-has-no-body-end-implicit-headers.js diff --git a/test/js/node/test/parallel/http-head-response-has-no-body.test.js b/test/js/node/test/parallel/http-head-response-has-no-body.test.js deleted file mode 100644 index f87fece95a..0000000000 --- a/test/js/node/test/parallel/http-head-response-has-no-body.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-head-response-has-no-body.js -//#SHA1: f7df6559885b0465d43994e773c961b525b195a9 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request, it does not send any body. -// In this case it was sending '0\r\n\r\n' - -test("HTTP server responds to HEAD request without body", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200); // broken: defaults to TE chunked - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - await new Promise((resolve, reject) => { - const req = http.request( - { - port, - method: "HEAD", - path: "/", - }, - res => { - res.on("end", () => { - server.close(() => { - resolve(); - }); - }); - res.resume(); - }, - ); - req.on("error", reject); - req.end(); - }); -}); - -//<#END_FILE: test-http-head-response-has-no-body.js diff --git a/test/js/node/test/parallel/http-header-obstext.test.js b/test/js/node/test/parallel/http-header-obstext.test.js deleted file mode 100644 index 55248d91c4..0000000000 --- a/test/js/node/test/parallel/http-header-obstext.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-http-header-obstext.js -//#SHA1: 031a5230bc91c831407772f2b8cbeba3559ed1d2 -//----------------- -"use strict"; - -// This test ensures that the http-parser can handle UTF-8 characters -// in the http header. - -const http = require("http"); - -test("http-parser can handle UTF-8 characters in http header", async () => { - const server = http.createServer((req, res) => { - res.end("ok"); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const { port } = server.address(); - - const response = await new Promise(resolve => { - http.get( - { - port, - headers: { Test: "Düsseldorf" }, - }, - resolve, - ); - }); - - expect(response.statusCode).toBe(200); - - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-header-obstext.js diff --git a/test/js/node/test/parallel/http-header-owstext.test.js b/test/js/node/test/parallel/http-header-owstext.test.js deleted file mode 100644 index cfab935f17..0000000000 --- a/test/js/node/test/parallel/http-header-owstext.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-header-owstext.js -//#SHA1: 339bfcf13a4cc9caa39940de3854eeda01b4500c -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -// This test ensures that the http-parser strips leading and trailing OWS from -// header values. It sends the header values in chunks to force the parser to -// build the string up through multiple calls to on_header_value(). - -function check(hdr, snd, rcv) { - return new Promise(resolve => { - const server = http.createServer((req, res) => { - expect(req.headers[hdr]).toBe(rcv); - req.pipe(res); - }); - - server.listen(0, function () { - const client = net.connect(this.address().port, start); - function start() { - client.write("GET / HTTP/1.1\r\n" + hdr + ":", drain); - } - - function drain() { - if (snd.length === 0) { - return client.write("\r\nConnection: close\r\n\r\n"); - } - client.write(snd.shift(), drain); - } - - const bufs = []; - client.on("data", function (chunk) { - bufs.push(chunk); - }); - client.on("end", function () { - const head = Buffer.concat(bufs).toString("latin1").split("\r\n")[0]; - expect(head).toBe("HTTP/1.1 200 OK"); - server.close(); - resolve(); - }); - }); - }); -} - -test("http header OWS text parsing", async () => { - await check("host", [" \t foo.com\t"], "foo.com"); - await check("host", [" \t foo\tcom\t"], "foo\tcom"); - await check("host", [" \t", " ", " foo.com\t", "\t "], "foo.com"); - await check("host", [" \t", " \t".repeat(100), "\t "], ""); - await check("host", [" \t", " - - - - ", "\t "], "- - - -"); -}); - -//<#END_FILE: test-http-header-owstext.js diff --git a/test/js/node/test/parallel/http-host-headers.test.js b/test/js/node/test/parallel/http-host-headers.test.js deleted file mode 100644 index cb50ca9b04..0000000000 --- a/test/js/node/test/parallel/http-host-headers.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http-host-headers.js -//#SHA1: 256e8b55e2c545a9f9df89607600f18a93c1c67a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -function reqHandler(req, res) { - if (req.url === "/setHostFalse5") { - expect(req.headers.host).toBeUndefined(); - } else { - expect(req.headers.host).toBe(`localhost:${this.address().port}`); - } - res.writeHead(200, {}); - res.end("ok"); -} - -const httpServer = http.createServer(reqHandler); - -test("HTTP host headers", async () => { - await new Promise(resolve => { - httpServer.listen(0, async () => { - const port = httpServer.address().port; - const makeRequest = (method, path) => { - return new Promise(resolve => { - const req = http.request( - { - method, - path, - host: "localhost", - port, - rejectUnauthorized: false, - }, - res => { - res.resume(); - resolve(); - }, - ); - req.on("error", () => { - throw new Error("Request should not fail"); - }); - req.end(); - }); - }; - - await makeRequest("GET", "/0"); - await makeRequest("GET", "/1"); - await makeRequest("POST", "/2"); - await makeRequest("PUT", "/3"); - await makeRequest("DELETE", "/4"); - - httpServer.close(resolve); - }); - }); -}); - -//<#END_FILE: test-http-host-headers.js diff --git a/test/js/node/test/parallel/http-keep-alive-timeout-custom.test.js b/test/js/node/test/parallel/http-keep-alive-timeout-custom.test.js deleted file mode 100644 index 9d0e874e81..0000000000 --- a/test/js/node/test/parallel/http-keep-alive-timeout-custom.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-http-keep-alive-timeout-custom.js -//#SHA1: 4f7c5a20da7b46bea9198b3854aed7c2042a8691 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP Keep-Alive timeout custom", async () => { - const server = http.createServer((req, res) => { - const body = "hello world\n"; - - res.writeHead(200, { - "Content-Length": body.length, - "Keep-Alive": "timeout=50", - }); - res.write(body); - res.end(); - }); - server.keepAliveTimeout = 12010; - - const agent = new http.Agent({ maxSockets: 1, keepAlive: true }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get( - { - path: "/", - port: server.address().port, - agent: agent, - }, - response => { - response.resume(); - expect(response.headers["keep-alive"]).toBe("timeout=50"); - server.close(); - agent.destroy(); - resolve(); - }, - ); - }); - }); -}); - -//<#END_FILE: test-http-keep-alive-timeout-custom.js diff --git a/test/js/node/test/parallel/http-many-ended-pipelines.test.js b/test/js/node/test/parallel/http-many-ended-pipelines.test.js deleted file mode 100644 index c142011e0d..0000000000 --- a/test/js/node/test/parallel/http-many-ended-pipelines.test.js +++ /dev/null @@ -1,82 +0,0 @@ -//#FILE: test-http-many-ended-pipelines.js -//#SHA1: 930bb6dc614c68f965c7b31e9a1223386234e389 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const net = require("net"); - -const numRequests = 20; -let first = false; - -test("HTTP server handles many ended pipelines", async () => { - const server = http.createServer((req, res) => { - if (!first) { - first = true; - req.socket.on("close", () => { - server.close(); - }); - } - - res.end("ok"); - // Oh no! The connection died! - req.socket.destroy(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const client = net.connect({ - port: server.address().port, - allowHalfOpen: true, - }); - - client.on("error", err => { - // The socket might be destroyed by the other peer while data is still - // being written. The `'EPIPE'` and `'ECONNABORTED'` codes might also be - // valid but they have not been seen yet. - expect(err.code).toBe("ECONNRESET"); - }); - - for (let i = 0; i < numRequests; i++) { - client.write("GET / HTTP/1.1\r\n" + "Host: some.host.name\r\n" + "\r\n\r\n"); - } - client.end(); - client.pipe(process.stdout); - - resolve(); - }); - }); -}); - -const mockWarning = jest.spyOn(process, "emit"); -mockWarning.mockImplementation((event, ...args) => { - if (event === "warning") return; - return process.emit.apply(process, [event, ...args]); -}); - -afterAll(() => { - expect(mockWarning).not.toHaveBeenCalledWith("warning", expect.anything()); - mockWarning.mockRestore(); -}); - -//<#END_FILE: test-http-many-ended-pipelines.js diff --git a/test/js/node/test/parallel/http-missing-header-separator-cr.test.js b/test/js/node/test/parallel/http-missing-header-separator-cr.test.js deleted file mode 100644 index 952d726eed..0000000000 --- a/test/js/node/test/parallel/http-missing-header-separator-cr.test.js +++ /dev/null @@ -1,89 +0,0 @@ -//#FILE: test-http-missing-header-separator-cr.js -//#SHA1: 6e213764778e9edddd0fc6a43c9a3183507054c6 -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -function serverHandler(server, msg) { - const client = net.connect(server.address().port, "localhost"); - - let response = ""; - - client.on("data", chunk => { - response += chunk; - }); - - client.setEncoding("utf8"); - client.on("error", () => { - throw new Error("Client error should not occur"); - }); - client.on("end", () => { - expect(response).toBe("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"); - server.close(); - }); - client.write(msg); - client.resume(); -} - -test("GET request with invalid header", async () => { - const msg = [ - "GET / HTTP/1.1", - "Host: localhost", - "Dummy: x\nContent-Length: 23", - "", - "GET / HTTP/1.1", - "Dummy: GET /admin HTTP/1.1", - "Host: localhost", - "", - "", - ].join("\r\n"); - - const server = http.createServer(() => { - throw new Error("Server should not be called"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - serverHandler(server, msg); - resolve(); - }); - }); -}); - -test("POST request with invalid Transfer-Encoding header", async () => { - const msg = ["POST / HTTP/1.1", "Host: localhost", "x:x\nTransfer-Encoding: chunked", "", "1", "A", "0", "", ""].join( - "\r\n", - ); - - const server = http.createServer(() => { - throw new Error("Server should not be called"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - serverHandler(server, msg); - resolve(); - }); - }); -}); - -test("POST request with invalid header and Transfer-Encoding", async () => { - const msg = ["POST / HTTP/1.1", "Host: localhost", "x:\nTransfer-Encoding: chunked", "", "1", "A", "0", "", ""].join( - "\r\n", - ); - - const server = http.createServer(() => { - throw new Error("Server should not be called"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - serverHandler(server, msg); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-missing-header-separator-cr.js diff --git a/test/js/node/test/parallel/http-no-read-no-dump.test.js b/test/js/node/test/parallel/http-no-read-no-dump.test.js deleted file mode 100644 index 2085ea1981..0000000000 --- a/test/js/node/test/parallel/http-no-read-no-dump.test.js +++ /dev/null @@ -1,86 +0,0 @@ -//#FILE: test-http-no-read-no-dump.js -//#SHA1: 8548eb47a6eb8ec151b9c60e74b026d983145d26 -//----------------- -"use strict"; - -const http = require("http"); - -let onPause = null; - -describe("HTTP no read no dump", () => { - let server; - let port; - - beforeAll(done => { - server = http - .createServer((req, res) => { - if (req.method === "GET") return res.end(); - - res.writeHead(200); - res.flushHeaders(); - - req.on("close", () => { - expect(() => { - req.on("end", () => {}); - }).not.toThrow(); - }); - - req.connection.on("pause", () => { - res.end(); - onPause(); - }); - }) - .listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(done => { - server.close(done); - }); - - test("should handle POST and GET requests correctly", done => { - const agent = new http.Agent({ - maxSockets: 1, - keepAlive: true, - }); - - const post = http.request( - { - agent, - method: "POST", - port, - }, - res => { - res.resume(); - - post.write(Buffer.alloc(64 * 1024).fill("X")); - onPause = () => { - post.end("something"); - }; - }, - ); - - // What happens here is that the server `end`s the response before we send - // `something`, and the client thought that this is a green light for sending - // next GET request - post.write("initial"); - - http - .request( - { - agent, - method: "GET", - port, - }, - res => { - res.connection.end(); - done(); - }, - ) - .end(); - }); -}); - -//<#END_FILE: test-http-no-read-no-dump.js diff --git a/test/js/node/test/parallel/http-outgoing-finished.test.js b/test/js/node/test/parallel/http-outgoing-finished.test.js deleted file mode 100644 index 96363b2382..0000000000 --- a/test/js/node/test/parallel/http-outgoing-finished.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-http-outgoing-finished.js -//#SHA1: 9c1ce8205b113dbb5b4ddfd06c0c90017b344e15 -//----------------- -"use strict"; - -const http = require("http"); -const { finished } = require("stream"); - -let server; - -beforeAll(() => { - return new Promise(resolve => { - server = http - .createServer((req, res) => { - let closed = false; - res - .on("close", () => { - closed = true; - finished(res, () => { - server.close(); - }); - }) - .end(); - finished(res, () => { - expect(closed).toBe(true); - }); - }) - .listen(0, () => { - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -test("HTTP outgoing finished", done => { - const closeHandler = jest.fn(); - const finishedHandler = jest.fn(); - - server.on("request", (req, res) => { - res.on("close", closeHandler); - finished(res, finishedHandler); - }); - - http - .request({ - port: server.address().port, - method: "GET", - }) - .on("response", res => { - res.resume(); - }) - .end(); - - setTimeout(() => { - expect(closeHandler).toHaveBeenCalledTimes(1); - expect(finishedHandler).toHaveBeenCalledTimes(1); - done(); - }, 1000); -}); - -//<#END_FILE: test-http-outgoing-finished.js diff --git a/test/js/node/test/parallel/http-outgoing-writablefinished.test.js b/test/js/node/test/parallel/http-outgoing-writablefinished.test.js deleted file mode 100644 index 2788589d87..0000000000 --- a/test/js/node/test/parallel/http-outgoing-writablefinished.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-http-outgoing-writableFinished.js -//#SHA1: f3fbc0d89cd03168f3ee92ed586b62dd5e3b8edb -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP server response writableFinished", async () => { - const server = http.createServer((req, res) => { - expect(res.writableFinished).toBe(false); - res.on("finish", () => { - expect(res.writableFinished).toBe(true); - server.close(); - }); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const port = server.address().port; - - const clientRequest = http.request({ - port, - method: "GET", - path: "/", - }); - - expect(clientRequest.writableFinished).toBe(false); - - await new Promise(resolve => { - clientRequest.on("finish", () => { - expect(clientRequest.writableFinished).toBe(true); - resolve(); - }); - clientRequest.end(); - expect(clientRequest.writableFinished).toBe(false); - }); -}); - -//<#END_FILE: test-http-outgoing-writableFinished.js diff --git a/test/js/node/test/parallel/http-outgoing-write-types.test.js b/test/js/node/test/parallel/http-outgoing-write-types.test.js deleted file mode 100644 index 6870939a90..0000000000 --- a/test/js/node/test/parallel/http-outgoing-write-types.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-http-outgoing-write-types.js -//#SHA1: bdeac2ab8008bea1c7e0b22f8744176dea0410e2 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP outgoing write types", async () => { - const httpServer = http.createServer((req, res) => { - httpServer.close(); - - expect(() => { - res.write(["Throws."]); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - // should not throw - expect(() => res.write("1a2b3c")).not.toThrow(); - - // should not throw - expect(() => res.write(new Uint8Array(1024))).not.toThrow(); - - // should not throw - expect(() => res.write(Buffer.from("1".repeat(1024)))).not.toThrow(); - - res.end(); - }); - - await new Promise(resolve => { - httpServer.listen(0, () => { - http.get({ port: httpServer.address().port }, resolve); - }); - }); -}); - -//<#END_FILE: test-http-outgoing-write-types.js diff --git a/test/js/node/test/parallel/http-pause-no-dump.test.js b/test/js/node/test/parallel/http-pause-no-dump.test.js deleted file mode 100644 index 1f32f2b0a6..0000000000 --- a/test/js/node/test/parallel/http-pause-no-dump.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-http-pause-no-dump.js -//#SHA1: 30c3bd27f5edd0ba060a0d6833061d1ce6379cd5 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP pause should not dump", done => { - const server = http.createServer((req, res) => { - req.once("data", () => { - req.pause(); - res.writeHead(200); - res.end(); - res.on("finish", () => { - expect(req._dumped).toBeFalsy(); - }); - }); - }); - - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - method: "POST", - path: "/", - }, - res => { - expect(res.statusCode).toBe(200); - res.resume(); - res.on("end", () => { - server.close(); - done(); - }); - }, - ); - - req.end(Buffer.allocUnsafe(1024)); - }); -}); - -//<#END_FILE: test-http-pause-no-dump.js diff --git a/test/js/node/test/parallel/http-pause.test.js b/test/js/node/test/parallel/http-pause.test.js deleted file mode 100644 index 74bca8bc34..0000000000 --- a/test/js/node/test/parallel/http-pause.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-http-pause.js -//#SHA1: d7712077ebe0493c27ffd7180e73fdd409041bf7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const expectedServer = "Request Body from Client"; -let resultServer = ""; -const expectedClient = "Response Body from Server"; -let resultClient = ""; - -test("HTTP pause and resume", async () => { - const server = http.createServer((req, res) => { - console.error("pause server request"); - req.pause(); - setTimeout(() => { - console.error("resume server request"); - req.resume(); - req.setEncoding("utf8"); - req.on("data", chunk => { - resultServer += chunk; - }); - req.on("end", () => { - console.error(resultServer); - res.writeHead(200); - res.end(expectedClient); - }); - }, 100); - }); - - await new Promise(resolve => { - server.listen(0, function () { - // Anonymous function rather than arrow function to test `this` value. - expect(this).toBe(server); - const req = http.request( - { - port: this.address().port, - path: "/", - method: "POST", - }, - res => { - console.error("pause client response"); - res.pause(); - setTimeout(() => { - console.error("resume client response"); - res.resume(); - res.on("data", chunk => { - resultClient += chunk; - }); - res.on("end", () => { - console.error(resultClient); - server.close(); - resolve(); - }); - }, 100); - }, - ); - req.end(expectedServer); - }); - }); - - expect(resultServer).toBe(expectedServer); - expect(resultClient).toBe(expectedClient); -}); - -//<#END_FILE: test-http-pause.js diff --git a/test/js/node/test/parallel/http-pipe-fs.test.js b/test/js/node/test/parallel/http-pipe-fs.test.js deleted file mode 100644 index 8db6bebdd3..0000000000 --- a/test/js/node/test/parallel/http-pipe-fs.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-http-pipe-fs.js -//#SHA1: eb13abd37a9e18b0b28077247a7d336b92b79fbc -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const NUMBER_OF_STREAMS = 2; - -const tmpdir = path.join(os.tmpdir(), "node-test-http-pipe-fs"); -fs.mkdirSync(tmpdir, { recursive: true }); - -const file = path.join(tmpdir, "http-pipe-fs-test.txt"); - -describe("HTTP pipe to fs", () => { - let server; - - beforeAll(() => { - server = http.createServer((req, res) => { - const stream = fs.createWriteStream(file); - req.pipe(stream); - stream.on("close", () => { - res.writeHead(200); - res.end(); - }); - }); - }); - - afterAll(() => { - return new Promise(resolve => server.close(resolve)); - }); - - it("should handle multiple concurrent requests", async () => { - await new Promise(resolve => server.listen(0, resolve)); - - const port = server.address().port; - http.globalAgent.maxSockets = 1; - - const makeRequest = () => { - return new Promise(resolve => { - const req = http.request( - { - port: port, - method: "POST", - headers: { - "Content-Length": 5, - }, - }, - res => { - res.on("end", resolve); - res.resume(); - }, - ); - - req.end("12345"); - }); - }; - - const requests = Array(NUMBER_OF_STREAMS).fill().map(makeRequest); - await Promise.all(requests); - - expect.assertions(1); - expect(true).toBe(true); // Dummy assertion to ensure the test ran - }); -}); - -//<#END_FILE: test-http-pipe-fs.js diff --git a/test/js/node/test/parallel/http-proxy.test.js b/test/js/node/test/parallel/http-proxy.test.js deleted file mode 100644 index dbb116434c..0000000000 --- a/test/js/node/test/parallel/http-proxy.test.js +++ /dev/null @@ -1,118 +0,0 @@ -//#FILE: test-http-proxy.js -//#SHA1: 7b000418a5941e059d64a57b2f0b4fdfb43eb71d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -const cookies = [ - "session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT", - "prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", -]; - -const headers = { "content-type": "text/plain", "set-cookie": cookies, hello: "world" }; - -test("HTTP proxy server", async () => { - const backend = http.createServer((req, res) => { - console.error("backend request"); - res.writeHead(200, headers); - res.write("hello world\n"); - res.end(); - }); - - const proxy = http.createServer((req, res) => { - console.error(`proxy req headers: ${JSON.stringify(req.headers)}`); - http.get( - { - port: backend.address().port, - path: url.parse(req.url).pathname, - }, - proxy_res => { - console.error(`proxy res headers: ${JSON.stringify(proxy_res.headers)}`); - - expect(proxy_res.headers.hello).toBe("world"); - expect(proxy_res.headers["content-type"]).toBe("text/plain"); - expect(proxy_res.headers["set-cookie"]).toEqual(cookies); - - res.writeHead(proxy_res.statusCode, proxy_res.headers); - - proxy_res.on("data", chunk => { - res.write(chunk); - }); - - proxy_res.on("end", () => { - res.end(); - console.error("proxy res"); - }); - }, - ); - }); - - let body = ""; - - await new Promise(resolve => { - let nlistening = 0; - function startReq() { - nlistening++; - if (nlistening < 2) return; - - http.get( - { - port: proxy.address().port, - path: "/test", - }, - res => { - console.error("got res"); - expect(res.statusCode).toBe(200); - - expect(res.headers.hello).toBe("world"); - expect(res.headers["content-type"]).toBe("text/plain"); - expect(res.headers["set-cookie"]).toEqual(cookies); - - res.setEncoding("utf8"); - res.on("data", chunk => { - body += chunk; - }); - res.on("end", () => { - proxy.close(); - backend.close(); - console.error("closed both"); - resolve(); - }); - }, - ); - console.error("client req"); - } - - console.error("listen proxy"); - proxy.listen(0, startReq); - - console.error("listen backend"); - backend.listen(0, startReq); - }); - - expect(body).toBe("hello world\n"); -}); - -//<#END_FILE: test-http-proxy.js diff --git a/test/js/node/test/parallel/http-request-arguments.test.js b/test/js/node/test/parallel/http-request-arguments.test.js deleted file mode 100644 index d202ccc5af..0000000000 --- a/test/js/node/test/parallel/http-request-arguments.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-http-request-arguments.js -//#SHA1: c02b492e2dbf5fa6ffcda8a80c3e4ad41bb0c9e5 -//----------------- -"use strict"; - -const http = require("http"); - -// Test providing both a url and options, with the options partially -// replacing address and port portions of the URL provided. -test("http.get with url and options", done => { - const server = http.createServer((req, res) => { - expect(req.url).toBe("/testpath"); - res.end(); - server.close(); - }); - - server.listen(0, () => { - const port = server.address().port; - http.get("http://example.com/testpath", { hostname: "localhost", port }, res => { - res.resume(); - done(); - }); - }); -}); - -//<#END_FILE: test-http-request-arguments.js diff --git a/test/js/node/test/parallel/http-request-large-payload.test.js b/test/js/node/test/parallel/http-request-large-payload.test.js deleted file mode 100644 index ea69bdc95a..0000000000 --- a/test/js/node/test/parallel/http-request-large-payload.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-http-request-large-payload.js -//#SHA1: 236870617a867c47c0767e351433c5deb7c87120 -//----------------- -"use strict"; - -// This test ensures Node.js doesn't throw an error when making requests with -// the payload 16kb or more in size. -// https://github.com/nodejs/node/issues/2821 - -const http = require("http"); - -test("HTTP request with large payload", done => { - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end(); - - server.close(); - done(); - }); - - server.listen(0, function () { - const req = http.request({ - method: "POST", - port: this.address().port, - }); - - const payload = Buffer.alloc(16390, "Й"); - req.write(payload); - req.end(); - }); -}); - -//<#END_FILE: test-http-request-large-payload.js diff --git a/test/js/node/test/parallel/http-response-writehead-returns-this.test.js b/test/js/node/test/parallel/http-response-writehead-returns-this.test.js deleted file mode 100644 index 9418c9e6b0..0000000000 --- a/test/js/node/test/parallel/http-response-writehead-returns-this.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-http-response-writehead-returns-this.js -//#SHA1: 8a079a3635356290e98a1e7c4eb89b97680b3889 -//----------------- -"use strict"; - -const http = require("http"); - -test("http.ServerResponse.writeHead() returns this", done => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "a-header": "a-header-value" }).end("abc"); - }); - - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.headers["a-header"]).toBe("a-header-value"); - - const chunks = []; - - res.on("data", chunk => chunks.push(chunk)); - res.on("end", () => { - expect(Buffer.concat(chunks).toString()).toBe("abc"); - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http-response-writehead-returns-this.js diff --git a/test/js/node/test/parallel/http-server-close-idle-wait-response.test.js b/test/js/node/test/parallel/http-server-close-idle-wait-response.test.js deleted file mode 100644 index 415d32e729..0000000000 --- a/test/js/node/test/parallel/http-server-close-idle-wait-response.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-http-server-close-idle-wait-response.js -//#SHA1: 04c4c10103faabfd084635c9280824668eb0ba18 -//----------------- -"use strict"; - -const { createServer, get } = require("http"); - -test("HTTP server close idle connections after response", async () => { - const server = createServer( - jest.fn((req, res) => { - req.resume(); - - setTimeout(() => { - res.writeHead(204, { Connection: "keep-alive", "Keep-Alive": "timeout=1" }); - res.end(); - }, 1000); - }), - ); - - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - - get(`http://localhost:${port}`, res => { - server.close(); - }).on("finish", () => { - setTimeout(() => { - server.closeIdleConnections(); - resolve(); - }, 500); - }); - }); - }); - - expect(server.listeners("request")[0]).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-server-close-idle-wait-response.js diff --git a/test/js/node/test/parallel/http-server-delete-parser.test.js b/test/js/node/test/parallel/http-server-delete-parser.test.js deleted file mode 100644 index f16d12c848..0000000000 --- a/test/js/node/test/parallel/http-server-delete-parser.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-http-server-delete-parser.js -//#SHA1: 49465ae50d9dac34e834dcb19c02e75b284acdc2 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP server deletes parser after write", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("okay", () => { - delete res.socket.parser; - }); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", resolve); - }); - - const { port } = server.address(); - - const req = http.request({ - port, - host: "127.0.0.1", - method: "GET", - }); - - await new Promise(resolve => { - req.end(resolve); - }); - - server.close(); -}); - -//<#END_FILE: test-http-server-delete-parser.js diff --git a/test/js/node/test/parallel/http-server-multiheaders.test.js b/test/js/node/test/parallel/http-server-multiheaders.test.js deleted file mode 100644 index f0ba4b4d61..0000000000 --- a/test/js/node/test/parallel/http-server-multiheaders.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-http-server-multiheaders.js -//#SHA1: 48e657fa74fb8aeeb2d04661ac760ff0cf5bf12a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Verify that the HTTP server implementation handles multiple instances -// of the same header as per RFC2616: joining the handful of fields by ', ' -// that support it, and dropping duplicates for other fields. - -const http = require("http"); - -test("HTTP server handles multiple instances of the same header correctly", async () => { - const server = http.createServer((req, res) => { - expect(req.headers.accept).toBe("abc, def, ghijklmnopqrst"); - expect(req.headers.host).toBe("foo"); - expect(req.headers["www-authenticate"]).toBe("foo, bar, baz"); - expect(req.headers["proxy-authenticate"]).toBe("foo, bar, baz"); - expect(req.headers["x-foo"]).toBe("bingo"); - expect(req.headers["x-bar"]).toBe("banjo, bango"); - expect(req.headers["sec-websocket-protocol"]).toBe("chat, share"); - expect(req.headers["sec-websocket-extensions"]).toBe("foo; 1, bar; 2, baz"); - expect(req.headers.constructor).toBe("foo, bar, baz"); - - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("EOF"); - - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ - host: "localhost", - port: server.address().port, - path: "/", - headers: [ - ["accept", "abc"], - ["accept", "def"], - ["Accept", "ghijklmnopqrst"], - ["host", "foo"], - ["Host", "bar"], - ["hOst", "baz"], - ["www-authenticate", "foo"], - ["WWW-Authenticate", "bar"], - ["WWW-AUTHENTICATE", "baz"], - ["proxy-authenticate", "foo"], - ["Proxy-Authenticate", "bar"], - ["PROXY-AUTHENTICATE", "baz"], - ["x-foo", "bingo"], - ["x-bar", "banjo"], - ["x-bar", "bango"], - ["sec-websocket-protocol", "chat"], - ["sec-websocket-protocol", "share"], - ["sec-websocket-extensions", "foo; 1"], - ["sec-websocket-extensions", "bar; 2"], - ["sec-websocket-extensions", "baz"], - ["constructor", "foo"], - ["constructor", "bar"], - ["constructor", "baz"], - ], - }); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-server-multiheaders.js diff --git a/test/js/node/test/parallel/http-server-non-utf8-header.test.js b/test/js/node/test/parallel/http-server-non-utf8-header.test.js deleted file mode 100644 index de10907234..0000000000 --- a/test/js/node/test/parallel/http-server-non-utf8-header.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-http-server-non-utf8-header.js -//#SHA1: bc84accb29cf80323d0fb55455a596f36a7933b2 -//----------------- -"use strict"; -const http = require("http"); - -const nonUtf8Header = "bår"; -const nonUtf8ToLatin1 = Buffer.from(nonUtf8Header).toString("latin1"); - -test("HTTP server with non-UTF8 header", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, ["content-disposition", Buffer.from(nonUtf8Header).toString("binary")]); - res.end("hello"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.statusCode).toBe(200); - expect(res.headers["content-disposition"]).toBe(nonUtf8ToLatin1); - res.resume().on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -test("HTTP server with multi-value non-UTF8 header", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, ["content-disposition", [Buffer.from(nonUtf8Header).toString("binary")]]); - res.end("hello"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.statusCode).toBe(200); - expect(res.headers["content-disposition"]).toBe(nonUtf8ToLatin1); - res.resume().on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -test("HTTP server with non-UTF8 header and Content-Length", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, ["Content-Length", "5", "content-disposition", Buffer.from(nonUtf8Header).toString("binary")]); - res.end("hello"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.statusCode).toBe(200); - expect(res.headers["content-disposition"]).toBe(nonUtf8ToLatin1); - res.resume().on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-server-non-utf8-header.js diff --git a/test/js/node/test/parallel/http-server-options-incoming-message.test.js b/test/js/node/test/parallel/http-server-options-incoming-message.test.js deleted file mode 100644 index e8c595a1aa..0000000000 --- a/test/js/node/test/parallel/http-server-options-incoming-message.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http-server-options-incoming-message.js -//#SHA1: 5d553fff4a2a29f67836269914e5f33b7e91b64e -//----------------- -"use strict"; - -/** - * This test covers http.Server({ IncomingMessage }) option: - * With IncomingMessage option the server should use - * the new class for creating req Object instead of the default - * http.IncomingMessage. - */ -const http = require("http"); - -class MyIncomingMessage extends http.IncomingMessage { - getUserAgent() { - return this.headers["user-agent"] || "unknown"; - } -} - -test("http.Server with custom IncomingMessage", done => { - const server = http.createServer( - { - IncomingMessage: MyIncomingMessage, - }, - (req, res) => { - expect(req.getUserAgent()).toBe("node-test"); - res.statusCode = 200; - res.end(); - }, - ); - - server.listen(() => { - const { port } = server.address(); - - http.get( - { - port, - headers: { - "User-Agent": "node-test", - }, - }, - res => { - expect(res.statusCode).toBe(200); - res.on("end", () => { - server.close(); - done(); - }); - res.resume(); - }, - ); - }); -}); - -//<#END_FILE: test-http-server-options-incoming-message.js diff --git a/test/js/node/test/parallel/http-server-options-server-response.test.js b/test/js/node/test/parallel/http-server-options-server-response.test.js deleted file mode 100644 index 67155a18ac..0000000000 --- a/test/js/node/test/parallel/http-server-options-server-response.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-http-server-options-server-response.js -//#SHA1: ae3128a67e671596c2470bb973747640620b807a -//----------------- -"use strict"; - -/** - * This test covers http.Server({ ServerResponse }) option: - * With ServerResponse option the server should use - * the new class for creating res Object instead of the default - * http.ServerResponse. - */ -const http = require("http"); - -class MyServerResponse extends http.ServerResponse { - status(code) { - return this.writeHead(code, { "Content-Type": "text/plain" }); - } -} - -test("http.Server with custom ServerResponse", done => { - const server = http.Server( - { - ServerResponse: MyServerResponse, - }, - jest.fn((req, res) => { - res.status(200); - res.end(); - }), - ); - - server.listen(() => { - const port = server.address().port; - - http.get({ port }, res => { - expect(res.statusCode).toBe(200); - res.on("end", () => { - server.close(); - done(); - }); - res.resume(); - }); - }); - - server.on("close", () => { - expect(server.listeners("request")[0]).toHaveBeenCalledTimes(1); - }); -}); - -//<#END_FILE: test-http-server-options-server-response.js diff --git a/test/js/node/test/parallel/http-server-reject-chunked-with-content-length.test.js b/test/js/node/test/parallel/http-server-reject-chunked-with-content-length.test.js deleted file mode 100644 index 9bd2f8b82b..0000000000 --- a/test/js/node/test/parallel/http-server-reject-chunked-with-content-length.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-http-server-reject-chunked-with-content-length.js -//#SHA1: e94d6c381c99ba72c2cc2bcbc4c6474a7c63819a -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -const reqstr = "POST / HTTP/1.1\r\n" + "Content-Length: 1\r\n" + "Transfer-Encoding: chunked\r\n\r\n"; - -test("HTTP server rejects chunked with content length", done => { - const server = http.createServer(expect.any(Function)); - - server.on("clientError", err => { - expect(err.message).toMatch(/^Parse Error/); - expect(err.code).toBe("HPE_INVALID_TRANSFER_ENCODING"); - server.close(); - }); - - server.listen(0, () => { - const client = net.connect({ port: server.address().port }, () => { - client.write(reqstr); - client.end(); - }); - - client.on("data", () => { - // Should not get to this point because the server should simply - // close the connection without returning any data. - throw new Error("no data should be returned by the server"); - }); - - client.on("end", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http-server-reject-chunked-with-content-length.js diff --git a/test/js/node/test/parallel/http-server-stale-close.test.js b/test/js/node/test/parallel/http-server-stale-close.test.js deleted file mode 100644 index ea0f99dcf0..0000000000 --- a/test/js/node/test/parallel/http-server-stale-close.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-server-stale-close.js -//#SHA1: 5c246ffb442bd9ff61779bc300db12d2f3394be4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const fork = require("child_process").fork; - -if (process.env.NODE_TEST_FORK_PORT) { - const req = http.request( - { - headers: { "Content-Length": "42" }, - method: "POST", - host: "127.0.0.1", - port: +process.env.NODE_TEST_FORK_PORT, - }, - process.exit, - ); - req.write("BAM"); - req.end(); -} else { - test("HTTP server stale close", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "Content-Length": "42" }); - req.pipe(res); - expect(req.destroyed).toBe(false); - req.on("close", () => { - expect(req.destroyed).toBe(true); - server.close(); - res.end(); - }); - }); - - await new Promise(resolve => { - server.listen(0, function () { - fork(__filename, { - env: { ...process.env, NODE_TEST_FORK_PORT: this.address().port }, - }); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-http-server-stale-close.js diff --git a/test/js/node/test/parallel/http-server-write-after-end.test.js b/test/js/node/test/parallel/http-server-write-after-end.test.js deleted file mode 100644 index c842524c4b..0000000000 --- a/test/js/node/test/parallel/http-server-write-after-end.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-http-server-write-after-end.js -//#SHA1: cacf983393f707ddefc829a25ce16a5bf6f41c19 -//----------------- -"use strict"; - -const http = require("http"); - -// Fix for https://github.com/nodejs/node/issues/14368 - -test("HTTP server write after end", done => { - const server = http.createServer(handle); - - function handle(req, res) { - res.on("error", jest.fn()); - - res.write("hello"); - res.end(); - - setImmediate(() => { - res.write("world", err => { - expect(err).toEqual( - expect.objectContaining({ - code: "ERR_STREAM_WRITE_AFTER_END", - name: "Error", - message: expect.any(String), - }), - ); - server.close(); - done(); - }); - }); - } - - server.listen(0, () => { - http.get(`http://localhost:${server.address().port}`); - }); -}); - -//<#END_FILE: test-http-server-write-after-end.js diff --git a/test/js/node/test/parallel/http-server-write-end-after-end.test.js b/test/js/node/test/parallel/http-server-write-end-after-end.test.js deleted file mode 100644 index 836b835e1f..0000000000 --- a/test/js/node/test/parallel/http-server-write-end-after-end.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http-server-write-end-after-end.js -//#SHA1: 5b7550b3241cd6b99e607419c3b81d2df519b641 -//----------------- -"use strict"; - -const http = require("http"); - -let server; - -beforeAll(() => { - server = http.createServer(handle); -}); - -afterAll(() => { - server.close(); -}); - -function handle(req, res) { - res.on("error", jest.fn()); - - res.write("hello"); - res.end(); - - setImmediate(() => { - res.end("world"); - process.nextTick(() => { - server.close(); - }); - res.write("world", err => { - expect(err).toMatchObject({ - code: "ERR_STREAM_WRITE_AFTER_END", - name: "Error", - message: expect.any(String), - }); - server.close(); - }); - }); -} - -test("http server write end after end", done => { - server.listen(0, () => { - http.get(`http://localhost:${server.address().port}`); - done(); - }); -}); - -//<#END_FILE: test-http-server-write-end-after-end.js diff --git a/test/js/node/test/parallel/http-set-header-chain.test.js b/test/js/node/test/parallel/http-set-header-chain.test.js deleted file mode 100644 index 85aac8f9e1..0000000000 --- a/test/js/node/test/parallel/http-set-header-chain.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-http-set-header-chain.js -//#SHA1: e009f5ffdce12a659bd2d3402449cd0095d79aa2 -//----------------- -"use strict"; - -const http = require("http"); - -const expected = { - __proto__: null, - testheader1: "foo", - testheader2: "bar", - testheader3: "xyz", -}; - -test("HTTP setHeader chaining", async () => { - const server = http.createServer((req, res) => { - let retval = res.setHeader("testheader1", "foo"); - - // Test that the setHeader returns the same response object. - expect(retval).toBe(res); - - retval = res.setHeader("testheader2", "bar").setHeader("testheader3", "xyz"); - // Test that chaining works for setHeader. - expect(res.getHeaders()).toEqual(expected); - res.end("ok"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - res.on("data", () => {}); - res.on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-set-header-chain.js diff --git a/test/js/node/test/parallel/http-upgrade-reconsume-stream.test.js b/test/js/node/test/parallel/http-upgrade-reconsume-stream.test.js deleted file mode 100644 index 57d72bf41d..0000000000 --- a/test/js/node/test/parallel/http-upgrade-reconsume-stream.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-http-upgrade-reconsume-stream.js -//#SHA1: 4117d0b2212d192173b5bd6bf2ef7fe82f627079 -//----------------- -"use strict"; - -const tls = require("tls"); -const http = require("http"); - -// Tests that, after the HTTP parser stopped owning a socket that emits an -// 'upgrade' event, another C++ stream can start owning it (e.g. a TLSSocket). - -test("HTTP upgrade and TLSSocket creation", done => { - const server = http.createServer(expect.any(Function)); - - server.on("upgrade", (request, socket, head) => { - // This should not crash. - new tls.TLSSocket(socket); - server.close(); - socket.destroy(); - done(); - }); - - server.listen(0, () => { - http - .get({ - port: server.address().port, - headers: { - "Connection": "Upgrade", - "Upgrade": "websocket", - }, - }) - .on("error", () => {}); - }); -}); - -//<#END_FILE: test-http-upgrade-reconsume-stream.js diff --git a/test/js/node/test/parallel/http-url.parse-auth-with-header-in-request.test.js b/test/js/node/test/parallel/http-url.parse-auth-with-header-in-request.test.js deleted file mode 100644 index a87bfbd3ac..0000000000 --- a/test/js/node/test/parallel/http-url.parse-auth-with-header-in-request.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http-url.parse-auth-with-header-in-request.js -//#SHA1: 396adc5e441a57d24b11a42513c834b6b11ea7ff -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -test("HTTP request with authorization header in request", async () => { - function check(request) { - // The correct authorization header is be passed - expect(request.headers.authorization).toBe("NoAuthForYOU"); - } - - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const testURL = url.parse(`http://asdf:qwer@localhost:${server.address().port}`); - // The test here is if you set a specific authorization header in the - // request we should not override that with basic auth - testURL.headers = { - Authorization: "NoAuthForYOU", - }; - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-auth-with-header-in-request.js diff --git a/test/js/node/test/parallel/http-url.parse-https.request.test.js b/test/js/node/test/parallel/http-url.parse-https.request.test.js deleted file mode 100644 index 6b8a992569..0000000000 --- a/test/js/node/test/parallel/http-url.parse-https.request.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-http-url.parse-https.request.js -//#SHA1: e9b9e39f28d5d2633f9444150977b748bc8995cb -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const https = require("https"); -const url = require("url"); -const { readKey } = require("../common/fixtures"); - -let common; -try { - common = require("../common"); -} catch (e) { - // For Bun compatibility - common = { - hasCrypto: true, - skip: console.log, - }; -} - -if (!common.hasCrypto) { - common.skip("missing crypto"); - process.exit(0); -} - -// https options -const httpsOptions = { - key: readKey("agent1-key.pem"), - cert: readKey("agent1-cert.pem"), -}; - -function check(request) { - // Assert that I'm https - expect(request.socket._secureEstablished).toBeTruthy(); -} - -test("HTTPS request with URL object", done => { - const server = https.createServer(httpsOptions, function (request, response) { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - server.listen(0, function () { - const testURL = url.parse(`https://localhost:${this.address().port}`); - testURL.rejectUnauthorized = false; - - // make the request - const clientRequest = https.request(testURL); - // Since there is a little magic with the agent - // make sure that the request uses the https.Agent - expect(clientRequest.agent).toBeInstanceOf(https.Agent); - clientRequest.end(); - done(); - }); -}); - -//<#END_FILE: test-http-url.parse-https.request.js diff --git a/test/js/node/test/parallel/http-write-empty-string.test.js b/test/js/node/test/parallel/http-write-empty-string.test.js deleted file mode 100644 index 16e1b0def3..0000000000 --- a/test/js/node/test/parallel/http-write-empty-string.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-write-empty-string.js -//#SHA1: 779199784d3142e353324041eeb30924c7e4d5b1 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http write empty string", async () => { - const server = http.createServer(function (request, response) { - console.log(`responding to ${request.url}`); - - response.writeHead(200, { "Content-Type": "text/plain" }); - response.write("1\n"); - response.write(""); - response.write("2\n"); - response.write(""); - response.end("3\n"); - - this.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - let response = ""; - - expect(res.statusCode).toBe(200); - res.setEncoding("ascii"); - res.on("data", chunk => { - response += chunk; - }); - res.on("end", () => { - expect(response).toBe("1\n2\n3\n"); - resolve(); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-write-empty-string.js diff --git a/test/js/node/test/parallel/http-zerolengthbuffer.test.js b/test/js/node/test/parallel/http-zerolengthbuffer.test.js deleted file mode 100644 index 8c82c7a82f..0000000000 --- a/test/js/node/test/parallel/http-zerolengthbuffer.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-http-zerolengthbuffer.js -//#SHA1: 28fff143238744f829f63936c8902047ad2c2fc5 -//----------------- -"use strict"; -// Serving up a zero-length buffer should work. - -const http = require("http"); - -test("Serve zero-length buffer", done => { - const server = http.createServer((req, res) => { - const buffer = Buffer.alloc(0); - res.writeHead(200, { "Content-Type": "text/html", "Content-Length": buffer.length }); - res.end(buffer); - }); - - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - const dataHandler = jest.fn(); - res.on("data", dataHandler); - - res.on("end", () => { - expect(dataHandler).not.toHaveBeenCalled(); - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http-zerolengthbuffer.js diff --git a/test/js/node/test/parallel/http2-binding.test.js b/test/js/node/test/parallel/http2-binding.test.js deleted file mode 100644 index a7ea7fb939..0000000000 --- a/test/js/node/test/parallel/http2-binding.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-http2-binding.js -//#SHA1: 73c6e6b3c2f9b4c9c06183713dbf28454185a1a0 -//----------------- -const http2 = require('http2'); - -describe('HTTP/2 Binding', () => { - beforeAll(() => { - // Skip all tests in this file - jest.spyOn(console, 'log').mockImplementation(() => {}); - console.log('Skipping HTTP/2 binding tests - internal bindings not available'); - }); - - test('SKIP: HTTP/2 binding tests', () => { - expect(true).toBe(true); - }); -}); - -//<#END_FILE: test-http2-binding.test.js diff --git a/test/js/node/test/parallel/http2-client-priority-before-connect.test.js b/test/js/node/test/parallel/http2-client-priority-before-connect.test.js deleted file mode 100644 index 273aa7bf44..0000000000 --- a/test/js/node/test/parallel/http2-client-priority-before-connect.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-client-priority-before-connect.js -//#SHA1: bc94924856dc82c18ccf699d467d63c28fed0d13 -//----------------- -'use strict'; - -const h2 = require('http2'); - -let server; -let port; - -beforeAll(async () => { - // Check if crypto is available - try { - require('crypto'); - } catch (err) { - return test.skip('missing crypto'); - } -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test('HTTP2 client priority before connect', (done) => { - server = h2.createServer(); - - // We use the lower-level API here - server.on('stream', (stream) => { - stream.respond(); - stream.end('ok'); - }); - - server.listen(0, () => { - port = server.address().port; - const client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - req.priority({}); - - req.on('response', () => { - // Response received - }); - - req.resume(); - - req.on('end', () => { - // Request ended - }); - - req.on('close', () => { - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-client-priority-before-connect.js diff --git a/test/js/node/test/parallel/http2-client-request-listeners-warning.test.js b/test/js/node/test/parallel/http2-client-request-listeners-warning.test.js deleted file mode 100644 index a560ec53ad..0000000000 --- a/test/js/node/test/parallel/http2-client-request-listeners-warning.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http2-client-request-listeners-warning.js -//#SHA1: cb4f9a71d1f670a78f989caed948e88fa5dbd681 -//----------------- -"use strict"; -const http2 = require("http2"); -const EventEmitter = require("events"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -(hasCrypto ? describe : describe.skip)("HTTP2 client request listeners warning", () => { - let server; - let port; - - beforeAll(done => { - server = http2.createServer(); - server.on("stream", stream => { - stream.respond(); - stream.end(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should not emit MaxListenersExceededWarning", done => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - const client = http2.connect(`http://localhost:${port}`); - - function request() { - return new Promise((resolve, reject) => { - const stream = client.request(); - stream.on("error", reject); - stream.on("response", resolve); - stream.end(); - }); - } - - const requests = []; - for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { - requests.push(request()); - } - - Promise.all(requests) - .then(() => { - expect(warningListener).not.toHaveBeenCalled(); - }) - .finally(() => { - process.removeListener("warning", warningListener); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-client-request-listeners-warning.js diff --git a/test/js/node/test/parallel/http2-client-shutdown-before-connect.test.js b/test/js/node/test/parallel/http2-client-shutdown-before-connect.test.js deleted file mode 100644 index 18091d3a31..0000000000 --- a/test/js/node/test/parallel/http2-client-shutdown-before-connect.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-http2-client-shutdown-before-connect.js -//#SHA1: 75a343e9d8b577911242f867708310346fe9ddce -//----------------- -'use strict'; - -const h2 = require('http2'); - -// Skip test if crypto is not available -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - test('HTTP/2 client shutdown before connect', (done) => { - const server = h2.createServer(); - - // We use the lower-level API here - server.on('stream', () => { - throw new Error('Stream should not be created'); - }); - - server.listen(0, () => { - const client = h2.connect(`http://localhost:${server.address().port}`); - client.close(() => { - server.close(() => { - done(); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-client-shutdown-before-connect.js diff --git a/test/js/node/test/parallel/http2-client-write-before-connect.test.js b/test/js/node/test/parallel/http2-client-write-before-connect.test.js deleted file mode 100644 index b245680da9..0000000000 --- a/test/js/node/test/parallel/http2-client-write-before-connect.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-client-write-before-connect.js -//#SHA1: f38213aa6b5fb615d5b80f0213022ea06e2705cc -//----------------- -'use strict'; - -const h2 = require('http2'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - return; - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test('HTTP/2 client write before connect', (done) => { - server = h2.createServer(); - - server.on('stream', (stream, headers, flags) => { - let data = ''; - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', () => { - expect(data).toBe('some data more data'); - }); - stream.respond(); - stream.end('ok'); - }); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - - const req = client.request({ ':method': 'POST' }); - req.write('some data '); - req.end('more data'); - - req.on('response', () => {}); - req.resume(); - req.on('end', () => {}); - req.on('close', () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http2-client-write-before-connect.js diff --git a/test/js/node/test/parallel/http2-client-write-empty-string.test.js b/test/js/node/test/parallel/http2-client-write-empty-string.test.js deleted file mode 100644 index daf8182df6..0000000000 --- a/test/js/node/test/parallel/http2-client-write-empty-string.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-http2-client-write-empty-string.js -//#SHA1: d4371ceba660942fe3c398bbb3144ce691054cec -//----------------- -'use strict'; - -const http2 = require('http2'); - -const runTest = async (chunkSequence) => { - return new Promise((resolve, reject) => { - const server = http2.createServer(); - server.on('stream', (stream, headers, flags) => { - stream.respond({ 'content-type': 'text/html' }); - - let data = ''; - stream.on('data', (chunk) => { - data += chunk.toString(); - }); - stream.on('end', () => { - stream.end(`"${data}"`); - }); - }); - - server.listen(0, async () => { - const port = server.address().port; - const client = http2.connect(`http://localhost:${port}`); - - const req = client.request({ - ':method': 'POST', - ':path': '/' - }); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - expect(headers['content-type']).toBe('text/html'); - }); - - let data = ''; - req.setEncoding('utf8'); - req.on('data', (d) => data += d); - req.on('end', () => { - expect(data).toBe('""'); - server.close(); - client.close(); - resolve(); - }); - - for (const chunk of chunkSequence) { - req.write(chunk); - } - req.end(); - }); - }); -}; - -const testCases = [ - [''], - ['', ''] -]; - -describe('http2 client write empty string', () => { - beforeAll(() => { - if (typeof http2 === 'undefined') { - return test.skip('http2 module not available'); - } - }); - - testCases.forEach((chunkSequence, index) => { - it(`should handle chunk sequence ${index + 1}`, async () => { - await runTest(chunkSequence); - }); - }); -}); - -//<#END_FILE: test-http2-client-write-empty-string.js diff --git a/test/js/node/test/parallel/http2-compat-aborted.test.js b/test/js/node/test/parallel/http2-compat-aborted.test.js deleted file mode 100644 index b304d69e16..0000000000 --- a/test/js/node/test/parallel/http2-compat-aborted.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http2-compat-aborted.js -//#SHA1: 2aaf11840d98c2b8f4387473180ec86626ac48d1 -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - server = h2.createServer((req, res) => { - req.on("aborted", () => { - expect(req.aborted).toBe(true); - expect(req.complete).toBe(true); - }); - expect(req.aborted).toBe(false); - expect(req.complete).toBe(false); - res.write("hello"); - server.close(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("HTTP/2 compat aborted", done => { - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const request = client.request(); - request.on("data", chunk => { - client.destroy(); - }); - request.on("end", () => { - done(); - }); - }); - - client.on("error", err => { - // Ignore client errors as we're forcibly destroying the connection - }); -}); - -//<#END_FILE: test-http2-compat-aborted.js diff --git a/test/js/node/test/parallel/http2-compat-client-upload-reject.test.js b/test/js/node/test/parallel/http2-compat-client-upload-reject.test.js deleted file mode 100644 index a9e085022b..0000000000 --- a/test/js/node/test/parallel/http2-compat-client-upload-reject.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http2-compat-client-upload-reject.js -//#SHA1: 4dff98612ac613af951070f79f07f5c1750045da -//----------------- -'use strict'; - -const http2 = require('http2'); -const fs = require('fs'); -const path = require('path'); - -const fixturesPath = path.resolve(__dirname, '..', 'fixtures'); -const loc = path.join(fixturesPath, 'person-large.jpg'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (server) server.close(); - if (client) client.close(); -}); - -test('HTTP/2 client upload reject', (done) => { - expect(fs.existsSync(loc)).toBe(true); - - fs.readFile(loc, (err, data) => { - expect(err).toBeNull(); - - server = http2.createServer((req, res) => { - setImmediate(() => { - res.writeHead(400); - res.end(); - }); - }); - - server.listen(0, () => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - - const req = client.request({ ':method': 'POST' }); - req.on('response', (headers) => { - expect(headers[':status']).toBe(400); - }); - - req.resume(); - req.on('end', () => { - server.close(); - client.close(); - done(); - }); - - const str = fs.createReadStream(loc); - str.pipe(req); - }); - }); -}); - -//<#END_FILE: test-http2-compat-client-upload-reject.js diff --git a/test/js/node/test/parallel/http2-compat-errors.test.js b/test/js/node/test/parallel/http2-compat-errors.test.js deleted file mode 100644 index e326447865..0000000000 --- a/test/js/node/test/parallel/http2-compat-errors.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-http2-compat-errors.js -//#SHA1: 3a958d2216c02d05272fbc89bd09a532419876a4 -//----------------- -'use strict'; - -const h2 = require('http2'); - -// Simulate crypto check -const hasCrypto = true; -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - let expected = null; - - describe('http2 compat errors', () => { - let server; - let url; - - beforeAll((done) => { - server = h2.createServer((req, res) => { - const resStreamErrorHandler = jest.fn(); - const reqErrorHandler = jest.fn(); - const resErrorHandler = jest.fn(); - const reqAbortedHandler = jest.fn(); - const resAbortedHandler = jest.fn(); - - res.stream.on('error', resStreamErrorHandler); - req.on('error', reqErrorHandler); - res.on('error', resErrorHandler); - req.on('aborted', reqAbortedHandler); - res.on('aborted', resAbortedHandler); - - res.write('hello'); - - expected = new Error('kaboom'); - res.stream.destroy(expected); - - // Use setImmediate to allow event handlers to be called - setImmediate(() => { - expect(resStreamErrorHandler).toHaveBeenCalled(); - expect(reqErrorHandler).not.toHaveBeenCalled(); - expect(resErrorHandler).not.toHaveBeenCalled(); - expect(reqAbortedHandler).toHaveBeenCalled(); - expect(resAbortedHandler).not.toHaveBeenCalled(); - server.close(done); - }); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - test('should handle errors correctly', (done) => { - const client = h2.connect(url, () => { - const request = client.request(); - request.on('data', (chunk) => { - client.destroy(); - done(); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-compat-errors.js diff --git a/test/js/node/test/parallel/http2-compat-expect-continue-check.test.js b/test/js/node/test/parallel/http2-compat-expect-continue-check.test.js deleted file mode 100644 index 8ee10f45fd..0000000000 --- a/test/js/node/test/parallel/http2-compat-expect-continue-check.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http2-compat-expect-continue-check.js -//#SHA1: cfaba2929ccb61aa085572010d7730ceef07859e -//----------------- -'use strict'; - -const http2 = require('http2'); - -const testResBody = 'other stuff!\n'; - -describe('HTTP/2 100-continue flow', () => { - let server; - - beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } - }); - - afterEach(() => { - if (server) { - server.close(); - } - }); - - test('Full 100-continue flow', (done) => { - server = http2.createServer(); - const fullRequestHandler = jest.fn(); - server.on('request', fullRequestHandler); - - server.on('checkContinue', (req, res) => { - res.writeContinue(); - res.writeHead(200, {}); - res.end(testResBody); - - expect(res.writeContinue()).toBe(false); - - res.on('finish', () => { - process.nextTick(() => { - expect(res.writeContinue()).toBe(false); - }); - }); - }); - - server.listen(0, () => { - let body = ''; - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ - ':method': 'POST', - 'expect': '100-continue' - }); - - let gotContinue = false; - req.on('continue', () => { - gotContinue = true; - }); - - req.on('response', (headers) => { - expect(gotContinue).toBe(true); - expect(headers[':status']).toBe(200); - req.end(); - }); - - req.setEncoding('utf-8'); - req.on('data', (chunk) => { body += chunk; }); - - req.on('end', () => { - expect(body).toBe(testResBody); - expect(fullRequestHandler).not.toHaveBeenCalled(); - client.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-compat-expect-continue-check.js diff --git a/test/js/node/test/parallel/http2-compat-expect-continue.test.js b/test/js/node/test/parallel/http2-compat-expect-continue.test.js deleted file mode 100644 index b2e98efb5d..0000000000 --- a/test/js/node/test/parallel/http2-compat-expect-continue.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-http2-compat-expect-continue.js -//#SHA1: 3c95de1bb9a0bf620945ec5fc39ba3a515dfe5fd -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - describe('HTTP/2 100-continue flow', () => { - test('full 100-continue flow with response', (done) => { - const testResBody = 'other stuff!\n'; - const server = http2.createServer(); - let sentResponse = false; - - server.on('request', (req, res) => { - res.end(testResBody); - sentResponse = true; - }); - - server.listen(0, () => { - let body = ''; - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ - ':method': 'POST', - 'expect': '100-continue' - }); - - let gotContinue = false; - req.on('continue', () => { - gotContinue = true; - }); - - req.on('response', (headers) => { - expect(gotContinue).toBe(true); - expect(sentResponse).toBe(true); - expect(headers[':status']).toBe(200); - req.end(); - }); - - req.setEncoding('utf8'); - req.on('data', (chunk) => { body += chunk; }); - req.on('end', () => { - expect(body).toBe(testResBody); - client.close(); - server.close(done); - }); - }); - }); - - test('100-continue flow with immediate response', (done) => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - res.end(); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ - ':path': '/', - 'expect': '100-continue' - }); - - let gotContinue = false; - req.on('continue', () => { - gotContinue = true; - }); - - let gotResponse = false; - req.on('response', () => { - gotResponse = true; - }); - - req.setEncoding('utf8'); - req.on('end', () => { - expect(gotContinue).toBe(true); - expect(gotResponse).toBe(true); - client.close(); - server.close(done); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-compat-expect-continue.js diff --git a/test/js/node/test/parallel/http2-compat-expect-handling.test.js b/test/js/node/test/parallel/http2-compat-expect-handling.test.js deleted file mode 100644 index 2a1940ae23..0000000000 --- a/test/js/node/test/parallel/http2-compat-expect-handling.test.js +++ /dev/null @@ -1,96 +0,0 @@ -//#FILE: test-http2-compat-expect-handling.js -//#SHA1: 015a7b40547c969f4d631e7e743f5293d9e8f843 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -const expectValue = "meoww"; - -describe("HTTP/2 Expect Header Handling", () => { - let server; - let port; - - beforeAll(done => { - server = http2.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("server should not call request handler", () => { - const requestHandler = jest.fn(); - server.on("request", requestHandler); - - return new Promise(resolve => { - server.once("checkExpectation", (req, res) => { - expect(req.headers.expect).toBe(expectValue); - res.statusCode = 417; - res.end(); - expect(requestHandler).not.toHaveBeenCalled(); - resolve(); - }); - - const client = http2.connect(`http://localhost:${port}`); - const req = client.request({ - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - "expect": expectValue, - }); - - req.on("response", headers => { - expect(headers[":status"]).toBe(417); - req.resume(); - }); - - req.on("end", () => { - client.close(); - }); - }); - }); - - test("client should receive 417 status", () => { - return new Promise(resolve => { - const client = http2.connect(`http://localhost:${port}`); - const req = client.request({ - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - "expect": expectValue, - }); - - req.on("response", headers => { - expect(headers[":status"]).toBe(417); - req.resume(); - }); - - req.on("end", () => { - client.close(); - resolve(); - }); - }); - }); -}); - -if (!hasCrypto) { - test.skip("skipping HTTP/2 tests due to missing crypto support", () => {}); -} - -//<#END_FILE: test-http2-compat-expect-handling.js diff --git a/test/js/node/test/parallel/http2-compat-serverrequest-pause.test.js b/test/js/node/test/parallel/http2-compat-serverrequest-pause.test.js deleted file mode 100644 index a42d021210..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverrequest-pause.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-http2-compat-serverrequest-pause.js -//#SHA1: 3f3eff95f840e6321b0d25211ef5116304049dc7 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - const testStr = 'Request Body from Client'; - let server; - let client; - - beforeAll(() => { - server = h2.createServer(); - }); - - afterAll(() => { - if (client) client.close(); - if (server) server.close(); - }); - - test('pause & resume work as expected with Http2ServerRequest', (done) => { - const requestHandler = jest.fn((req, res) => { - let data = ''; - req.pause(); - req.setEncoding('utf8'); - req.on('data', jest.fn((chunk) => (data += chunk))); - setTimeout(() => { - expect(data).toBe(''); - req.resume(); - }, 100); - req.on('end', () => { - expect(data).toBe(testStr); - res.end(); - }); - - res.on('finish', () => process.nextTick(() => { - req.pause(); - req.resume(); - })); - }); - - server.on('request', requestHandler); - - server.listen(0, () => { - const port = server.address().port; - - client = h2.connect(`http://localhost:${port}`); - const request = client.request({ - ':path': '/foobar', - ':method': 'POST', - ':scheme': 'http', - ':authority': `localhost:${port}` - }); - request.resume(); - request.end(testStr); - request.on('end', () => { - expect(requestHandler).toHaveBeenCalled(); - done(); - }); - }); - }); -} -//<#END_FILE: test-http2-compat-serverrequest-pause.js diff --git a/test/js/node/test/parallel/http2-compat-serverrequest-pipe.test.js b/test/js/node/test/parallel/http2-compat-serverrequest-pipe.test.js deleted file mode 100644 index 47ed561685..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverrequest-pipe.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-http2-compat-serverrequest-pipe.js -//#SHA1: c4254ac88df3334dccc8adb4b60856193a6e644e -//----------------- -"use strict"; - -const http2 = require("http2"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); -const { isWindows } = require("harness"); - -const fixtures = path.join(__dirname, "..", "fixtures"); -const tmpdir = os.tmpdir(); - -let server; -let client; -let port; - -beforeAll(async () => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - await fs.promises.mkdir(tmpdir, { recursive: true }); -}); - -afterAll(async () => { - if (server) server.close(); - if (client) client.close(); -}); - -test.todoIf(isWindows)("HTTP/2 server request pipe", done => { - const loc = path.join(fixtures, "person-large.jpg"); - const fn = path.join(tmpdir, "http2-url-tests.js"); - - server = http2.createServer(); - - server.on("request", (req, res) => { - const dest = req.pipe(fs.createWriteStream(fn)); - dest.on("finish", () => { - expect(req.complete).toBe(true); - expect(fs.readFileSync(loc).length).toBe(fs.readFileSync(fn).length); - fs.unlinkSync(fn); - res.end(); - }); - }); - - server.listen(0, () => { - port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - - let remaining = 2; - function maybeClose() { - if (--remaining === 0) { - done(); - } - } - - const req = client.request({ ":method": "POST" }); - req.on("response", () => {}); - req.resume(); - req.on("end", maybeClose); - const str = fs.createReadStream(loc); - str.on("end", maybeClose); - str.pipe(req); - }); -}); - -//<#END_FILE: test-http2-compat-serverrequest-pipe.js diff --git a/test/js/node/test/parallel/http2-compat-serverrequest.test.js b/test/js/node/test/parallel/http2-compat-serverrequest.test.js deleted file mode 100644 index 2349965420..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverrequest.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-http2-compat-serverrequest.js -//#SHA1: f661c6c9249c0cdc770439f7498943fc5edbf86b -//----------------- -"use strict"; - -const h2 = require("http2"); -const net = require("net"); - -let server; -let port; - -beforeAll(done => { - server = h2.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(done => { - server.close(done); -}); - -// today we deatch the socket earlier -test.todo("Http2ServerRequest should expose convenience properties", done => { - expect.assertions(7); - - server.once("request", (request, response) => { - const expected = { - version: "2.0", - httpVersionMajor: 2, - httpVersionMinor: 0, - }; - - expect(request.httpVersion).toBe(expected.version); - expect(request.httpVersionMajor).toBe(expected.httpVersionMajor); - expect(request.httpVersionMinor).toBe(expected.httpVersionMinor); - - expect(request.socket).toBeInstanceOf(net.Socket); - expect(request.connection).toBeInstanceOf(net.Socket); - expect(request.socket).toBe(request.connection); - - response.on("finish", () => { - process.nextTick(() => { - expect(request.socket).toBeTruthy(); - done(); - }); - }); - response.end(); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ":path": "/foobar", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }; - const request = client.request(headers); - request.on("end", () => { - client.close(); - }); - request.end(); - request.resume(); - }); -}); - -//<#END_FILE: test-http2-compat-serverrequest.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-close.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-close.test.js deleted file mode 100644 index 6ae966fc55..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-close.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-close.js -//#SHA1: 6b61a9cea948447ae33843472678ffbed0b47c9a -//----------------- -"use strict"; - -const h2 = require("http2"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -(hasCrypto ? describe : describe.skip)("HTTP/2 server response close", () => { - let server; - let url; - - beforeAll(done => { - server = h2.createServer((req, res) => { - res.writeHead(200); - res.write("a"); - - const reqCloseMock = jest.fn(); - const resCloseMock = jest.fn(); - const reqErrorMock = jest.fn(); - - req.on("close", reqCloseMock); - res.on("close", resCloseMock); - req.on("error", reqErrorMock); - - // Use Jest's fake timers to ensure the test doesn't hang - setTimeout(() => { - expect(reqCloseMock).toHaveBeenCalled(); - expect(resCloseMock).toHaveBeenCalled(); - expect(reqErrorMock).not.toHaveBeenCalled(); - done(); - }, 1000); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("Server request and response should receive close event if connection terminated before response.end", done => { - const client = h2.connect(url, () => { - const request = client.request(); - request.on("data", chunk => { - client.destroy(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-close.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-drain.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-drain.test.js deleted file mode 100644 index 4976ad2284..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-drain.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-drain.js -//#SHA1: 4ec55745f622a31b4729fcb9daf9bfd707a3bdb3 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -const testString = 'tests'; - -test('HTTP/2 server response drain event', async () => { - if (!hasCrypto) { - test.skip('missing crypto'); - return; - } - - const server = h2.createServer(); - - const requestHandler = jest.fn((req, res) => { - res.stream._writableState.highWaterMark = testString.length; - expect(res.write(testString)).toBe(false); - res.on('drain', jest.fn(() => res.end(testString))); - }); - - server.on('request', requestHandler); - - await new Promise(resolve => server.listen(0, resolve)); - const port = server.address().port; - - const client = h2.connect(`http://localhost:${port}`); - const request = client.request({ - ':path': '/foobar', - ':method': 'POST', - ':scheme': 'http', - ':authority': `localhost:${port}` - }); - request.resume(); - request.end(); - - let data = ''; - request.setEncoding('utf8'); - request.on('data', (chunk) => (data += chunk)); - - await new Promise(resolve => request.on('end', resolve)); - - expect(data).toBe(testString.repeat(2)); - expect(requestHandler).toHaveBeenCalled(); - - client.close(); - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http2-compat-serverresponse-drain.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-end-after-statuses-without-body.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-end-after-statuses-without-body.test.js deleted file mode 100644 index 2dd0f00dd3..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-end-after-statuses-without-body.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-end-after-statuses-without-body.js -//#SHA1: c4a4b76e1b04b7e6779f80f7077758dfab0e8b80 -//----------------- -"use strict"; - -const h2 = require("http2"); - -const { HTTP_STATUS_NO_CONTENT, HTTP_STATUS_RESET_CONTENT, HTTP_STATUS_NOT_MODIFIED } = h2.constants; - -const statusWithoutBody = [HTTP_STATUS_NO_CONTENT, HTTP_STATUS_RESET_CONTENT, HTTP_STATUS_NOT_MODIFIED]; -const STATUS_CODES_COUNT = statusWithoutBody.length; - -describe("HTTP/2 server response end after statuses without body", () => { - let server; - let url; - - beforeAll(done => { - server = h2.createServer((req, res) => { - res.writeHead(statusWithoutBody.pop()); - res.end(); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - it("should handle end() after sending statuses without body", done => { - const client = h2.connect(url, () => { - let responseCount = 0; - const closeAfterResponse = () => { - if (STATUS_CODES_COUNT === ++responseCount) { - client.destroy(); - done(); - } - }; - - for (let i = 0; i < STATUS_CODES_COUNT; i++) { - const request = client.request(); - request.on("response", closeAfterResponse); - } - }); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-end-after-statuses-without-body.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-end.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-end.test.js deleted file mode 100644 index 27b1f393db..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-end.test.js +++ /dev/null @@ -1,80 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-end.js -//#SHA1: 672da69abcb0b86d5234556e692949ac36ef6395 -//----------------- -'use strict'; - -const http2 = require('http2'); -const { promisify } = require('util'); - -// Mock the common module functions -const mustCall = (fn) => jest.fn(fn); -const mustNotCall = () => jest.fn().mockImplementation(() => { - throw new Error('This function should not have been called'); -}); - -const { - HTTP2_HEADER_STATUS, - HTTP_STATUS_OK -} = http2.constants; - -// Helper function to create a server and get its port -const createServerAndGetPort = async (requestListener) => { - const server = http2.createServer(requestListener); - await promisify(server.listen.bind(server))(0); - const { port } = server.address(); - return { server, port }; -}; - -// Helper function to create a client -const createClient = (port) => { - const url = `http://localhost:${port}`; - return http2.connect(url); -}; - -describe('Http2ServerResponse.end', () => { - test('accepts chunk, encoding, cb as args and can be called multiple times', async () => { - const { server, port } = await createServerAndGetPort((request, response) => { - const endCallback = jest.fn(() => { - response.end(jest.fn()); - process.nextTick(() => { - response.end(jest.fn()); - server.close(); - }); - }); - - response.end('end', 'utf8', endCallback); - response.on('finish', () => { - response.end(jest.fn()); - }); - response.end(jest.fn()); - }); - - const client = createClient(port); - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - - let data = ''; - const request = client.request(headers); - request.setEncoding('utf8'); - request.on('data', (chunk) => (data += chunk)); - await new Promise(resolve => { - request.on('end', () => { - expect(data).toBe('end'); - client.close(); - resolve(); - }); - request.end(); - request.resume(); - }); - }); - - // Add more tests here... -}); - -// More test blocks for other scenarios... - -//<#END_FILE: test-http2-compat-serverresponse-end.test.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-finished.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-finished.test.js deleted file mode 100644 index fb6f9c2b52..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-finished.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-finished.js -//#SHA1: 6ef7a05f30923975d7a267cee54aafae1bfdbc7d -//----------------- -'use strict'; - -const h2 = require('http2'); -const net = require('net'); - -let server; - -beforeAll(() => { - // Skip the test if crypto is not available - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (server) { - server.close(); - } -}); - -test('Http2ServerResponse.finished', (done) => { - server = h2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - expect(response.socket).toBeInstanceOf(net.Socket); - expect(response.connection).toBeInstanceOf(net.Socket); - expect(response.socket).toBe(response.connection); - - response.on('finish', () => { - expect(response.socket).toBeUndefined(); - expect(response.connection).toBeUndefined(); - process.nextTick(() => { - expect(response.stream).toBeDefined(); - done(); - }); - }); - - expect(response.finished).toBe(false); - expect(response.writableEnded).toBe(false); - response.end(); - expect(response.finished).toBe(true); - expect(response.writableEnded).toBe(true); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - const request = client.request(headers); - request.on('end', () => { - client.close(); - }); - request.end(); - request.resume(); - }); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-finished.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-flushheaders.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-flushheaders.test.js deleted file mode 100644 index 6d0864b507..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-flushheaders.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-flushheaders.js -//#SHA1: ea772e05a29f43bd7b61e4d70f24b94c1e1e201c -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let serverResponse; - -beforeAll(done => { - server = h2.createServer(); - server.listen(0, () => { - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("Http2ServerResponse.flushHeaders", done => { - const port = server.address().port; - - server.once("request", (request, response) => { - expect(response.headersSent).toBe(false); - expect(response._header).toBe(false); // Alias for headersSent - response.flushHeaders(); - expect(response.headersSent).toBe(true); - expect(response._header).toBe(true); - response.flushHeaders(); // Idempotent - - expect(() => { - response.writeHead(400, { "foo-bar": "abc123" }); - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_HEADERS_SENT", - }), - ); - response.on("finish", () => { - process.nextTick(() => { - response.flushHeaders(); // Idempotent - done(); - }); - }); - serverResponse = response; - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }; - const request = client.request(headers); - request.on("response", (headers, flags) => { - expect(headers["foo-bar"]).toBeUndefined(); - expect(headers[":status"]).toBe(200); - serverResponse.end(); - }); - request.on("end", () => { - client.close(); - }); - request.end(); - request.resume(); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-flushheaders.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-headers-send-date.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-headers-send-date.test.js deleted file mode 100644 index 6f410d12f1..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-headers-send-date.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-headers-send-date.js -//#SHA1: 1ed6319986a3bb9bf58709d9577d03407fdde3f2 -//----------------- -"use strict"; -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - server = http2.createServer((request, response) => { - response.sendDate = false; - response.writeHead(200); - response.end(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("HTTP/2 server response should not send Date header when sendDate is false", done => { - const session = http2.connect(`http://localhost:${port}`); - const req = session.request(); - - req.on("response", (headers, flags) => { - expect(headers).not.toHaveProperty("Date"); - expect(headers).not.toHaveProperty("date"); - }); - - req.on("end", () => { - session.close(); - done(); - }); - - req.end(); -}); - -//<#END_FILE: test-http2-compat-serverresponse-headers-send-date.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-settimeout.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-settimeout.test.js deleted file mode 100644 index 305f398176..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-settimeout.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-settimeout.js -//#SHA1: fe2e0371e885463968a268362464724494b758a6 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const msecs = 1000; // Assuming a reasonable timeout for all platforms - -let server; -let client; - -beforeAll(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - server = http2.createServer(); - server.listen(0, () => { - done(); - }); -}); - -afterAll(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test("HTTP2 ServerResponse setTimeout", done => { - const timeoutCallback = jest.fn(); - const onTimeout = jest.fn(); - const onFinish = jest.fn(); - - server.on("request", (req, res) => { - res.setTimeout(msecs, timeoutCallback); - res.on("timeout", onTimeout); - res.on("finish", () => { - onFinish(); - res.setTimeout(msecs, jest.fn()); - process.nextTick(() => { - res.setTimeout(msecs, jest.fn()); - }); - }); - - // Explicitly end the response after a short delay - setTimeout(() => { - res.end(); - }, 100); - }); - - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - const req = client.request({ - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }); - - req.on("end", () => { - client.close(); - - // Move assertions here to ensure they run after the response has finished - expect(timeoutCallback).not.toHaveBeenCalled(); - expect(onTimeout).not.toHaveBeenCalled(); - expect(onFinish).toHaveBeenCalledTimes(1); - - done(); - }); - - req.resume(); - req.end(); -}, 10000); // Increase the timeout to 10 seconds - -//<#END_FILE: test-http2-compat-serverresponse-settimeout.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-statuscode.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-statuscode.test.js deleted file mode 100644 index 8845f6c532..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-statuscode.test.js +++ /dev/null @@ -1,95 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-statuscode.js -//#SHA1: 10cb487c1fd9e256f807319b84c426b356be443f -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let port; - -beforeAll(async () => { - server = h2.createServer(); - await new Promise(resolve => server.listen(0, resolve)); - port = server.address().port; -}); - -afterAll(async () => { - server.close(); -}); - -test("Http2ServerResponse should have a statusCode property", async () => { - const responsePromise = new Promise(resolve => { - server.once("request", (request, response) => { - const expectedDefaultStatusCode = 200; - const realStatusCodes = { - continue: 100, - ok: 200, - multipleChoices: 300, - badRequest: 400, - internalServerError: 500, - }; - const fakeStatusCodes = { - tooLow: 99, - tooHigh: 600, - }; - - expect(response.statusCode).toBe(expectedDefaultStatusCode); - - // Setting the response.statusCode should not throw. - response.statusCode = realStatusCodes.ok; - response.statusCode = realStatusCodes.multipleChoices; - response.statusCode = realStatusCodes.badRequest; - response.statusCode = realStatusCodes.internalServerError; - - expect(() => { - response.statusCode = realStatusCodes.continue; - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_INFO_STATUS_NOT_ALLOWED", - name: "RangeError", - }), - ); - - expect(() => { - response.statusCode = fakeStatusCodes.tooLow; - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_STATUS_INVALID", - name: "RangeError", - }), - ); - - expect(() => { - response.statusCode = fakeStatusCodes.tooHigh; - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_STATUS_INVALID", - name: "RangeError", - }), - ); - - response.on("finish", resolve); - response.end(); - }); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url); - - const headers = { - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }; - - const request = client.request(headers); - request.end(); - await new Promise(resolve => request.resume().on("end", resolve)); - - await responsePromise; - client.close(); -}); - -//<#END_FILE: test-http2-compat-serverresponse-statuscode.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-writehead-array.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-writehead-array.test.js deleted file mode 100644 index 2b1ca358a9..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-writehead-array.test.js +++ /dev/null @@ -1,114 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-writehead-array.js -//#SHA1: e43a5a9f99ddad68b313e15fbb69839cca6d0775 -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - describe('Http2ServerResponse.writeHead with arrays', () => { - test('should support nested arrays', (done) => { - const server = http2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - const returnVal = response.writeHead(200, [ - ['foo', 'bar'], - ['foo', 'baz'], - ['ABC', 123], - ]); - expect(returnVal).toBe(response); - response.end(() => { server.close(); }); - }); - - const client = http2.connect(`http://localhost:${port}`, () => { - const request = client.request(); - - request.on('response', (headers) => { - expect(headers.foo).toBe('bar, baz'); - expect(headers.abc).toBe('123'); - expect(headers[':status']).toBe(200); - }); - request.on('end', () => { - client.close(); - done(); - }); - request.end(); - request.resume(); - }); - }); - }); - - test('should support flat arrays', (done) => { - const server = http2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - const returnVal = response.writeHead(200, ['foo', 'bar', 'foo', 'baz', 'ABC', 123]); - expect(returnVal).toBe(response); - response.end(() => { server.close(); }); - }); - - const client = http2.connect(`http://localhost:${port}`, () => { - const request = client.request(); - - request.on('response', (headers) => { - expect(headers.foo).toBe('bar, baz'); - expect(headers.abc).toBe('123'); - expect(headers[':status']).toBe(200); - }); - request.on('end', () => { - client.close(); - done(); - }); - request.end(); - request.resume(); - }); - }); - }); - - test('should throw ERR_INVALID_ARG_VALUE for invalid array', (done) => { - const server = http2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - expect(() => { - response.writeHead(200, ['foo', 'bar', 'ABC', 123, 'extra']); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE' - })); - - response.end(() => { server.close(); }); - }); - - const client = http2.connect(`http://localhost:${port}`, () => { - const request = client.request(); - - request.on('end', () => { - client.close(); - done(); - }); - request.end(); - request.resume(); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-compat-serverresponse-writehead-array.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-writehead.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-writehead.test.js deleted file mode 100644 index 296a1e1a73..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-writehead.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-writehead.js -//#SHA1: fa267d5108f95ba69583bc709a82185ee9d18e76 -//----------------- -'use strict'; - -const h2 = require('http2'); - -// Http2ServerResponse.writeHead should override previous headers - -test('Http2ServerResponse.writeHead overrides previous headers', (done) => { - const server = h2.createServer(); - server.listen(0, () => { - const port = server.address().port; - server.once('request', (request, response) => { - response.setHeader('foo-bar', 'def456'); - - // Override - const returnVal = response.writeHead(418, { 'foo-bar': 'abc123' }); - - expect(returnVal).toBe(response); - - expect(() => { response.writeHead(300); }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_HEADERS_SENT' - })); - - response.on('finish', () => { - server.close(); - process.nextTick(() => { - // The stream is invalid at this point, - // and this line verifies this does not throw. - response.writeHead(300); - done(); - }); - }); - response.end(); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - const request = client.request(headers); - request.on('response', (headers) => { - expect(headers['foo-bar']).toBe('abc123'); - expect(headers[':status']).toBe(418); - }); - request.on('end', () => { - client.close(); - }); - request.end(); - request.resume(); - }); - }); -}); - -// Skip the test if crypto is not available -if (!process.versions.openssl) { - test.skip('missing crypto', () => {}); -} - -//<#END_FILE: test-http2-compat-serverresponse-writehead.js diff --git a/test/js/node/test/parallel/http2-compat-socket-destroy-delayed.test.js b/test/js/node/test/parallel/http2-compat-socket-destroy-delayed.test.js deleted file mode 100644 index 10e6afe2bc..0000000000 --- a/test/js/node/test/parallel/http2-compat-socket-destroy-delayed.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http2-compat-socket-destroy-delayed.js -//#SHA1: c7b5b8b5de4667a89e0e261e36098f617d411ed2 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const { HTTP2_HEADER_PATH, HTTP2_HEADER_METHOD } = http2.constants; - -// Skip the test if crypto is not available -if (!process.versions.openssl) { - test.skip("missing crypto", () => {}); -} else { - test("HTTP/2 socket destroy delayed", done => { - const app = http2.createServer((req, res) => { - res.end("hello"); - setImmediate(() => req.socket?.destroy()); - }); - - app.listen(0, () => { - const session = http2.connect(`http://localhost:${app.address().port}`); - const request = session.request({ - [HTTP2_HEADER_PATH]: "/", - [HTTP2_HEADER_METHOD]: "get", - }); - request.once("response", (headers, flags) => { - let data = ""; - request.on("data", chunk => { - data += chunk; - }); - request.on("end", () => { - expect(data).toBe("hello"); - session.close(); - app.close(); - done(); - }); - }); - request.end(); - }); - }); -} - -// This tests verifies that calling `req.socket.destroy()` via -// setImmediate does not crash. -// Fixes https://github.com/nodejs/node/issues/22855. - -//<#END_FILE: test-http2-compat-socket-destroy-delayed.js diff --git a/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js b/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js deleted file mode 100644 index 0ab3a588a3..0000000000 --- a/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-http2-compat-write-early-hints-invalid-argument-type.js -//#SHA1: 8ae2eba59668a38b039a100d3ad26f88e54be806 -//----------------- -"use strict"; - -const http2 = require("node:http2"); -const util = require("node:util"); -const debug = util.debuglog("test"); - -const testResBody = "response content"; - -// Check if crypto is available -let hasCrypto = false; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - // crypto not available -} - -(hasCrypto ? describe : describe.skip)("HTTP2 compat writeEarlyHints invalid argument type", () => { - let server; - let client; - - beforeAll(done => { - server = http2.createServer(); - server.listen(0, () => { - done(); - }); - }); - - afterAll(() => { - if (client) { - client.close(); - } - server.close(); - }); - - test("should throw ERR_INVALID_ARG_TYPE for invalid object value", done => { - server.on("request", (req, res) => { - debug("Server sending early hints..."); - expect(() => { - res.writeEarlyHints("this should not be here"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - debug("Server sending full response..."); - res.end(testResBody); - }); - - client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug("Client sending request..."); - - req.on("headers", () => { - done(new Error("Should not receive headers")); - }); - - req.on("response", () => { - done(); - }); - - req.end(); - }); -}); - -//<#END_FILE: test-http2-compat-write-early-hints-invalid-argument-type.js diff --git a/test/js/node/test/parallel/http2-compat-write-early-hints.test.js b/test/js/node/test/parallel/http2-compat-write-early-hints.test.js deleted file mode 100644 index c3d8fb4e15..0000000000 --- a/test/js/node/test/parallel/http2-compat-write-early-hints.test.js +++ /dev/null @@ -1,146 +0,0 @@ -//#FILE: test-http2-compat-write-early-hints.js -//#SHA1: 0ed18263958421cde07c37b8ec353005b7477499 -//----------------- -'use strict'; - -const http2 = require('node:http2'); -const util = require('node:util'); -const debug = util.debuglog('test'); - -const testResBody = 'response content'; - -describe('HTTP/2 Early Hints', () => { - test('Happy flow - string argument', async () => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints({ - link: '; rel=preload; as=style' - }); - - debug('Server sending full response...'); - res.end(testResBody); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug('Client sending request...'); - - await new Promise(resolve => { - req.on('headers', (headers) => { - expect(headers).toBeDefined(); - expect(headers[':status']).toBe(103); - expect(headers.link).toBe('; rel=preload; as=style'); - }); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - }); - - let data = ''; - req.on('data', (d) => data += d); - - req.on('end', () => { - debug('Got full response.'); - expect(data).toBe(testResBody); - client.close(); - server.close(resolve); - }); - }); - }); - - test('Happy flow - array argument', async () => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints({ - link: [ - '; rel=preload; as=style', - '; rel=preload; as=script', - ] - }); - - debug('Server sending full response...'); - res.end(testResBody); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug('Client sending request...'); - - await new Promise(resolve => { - req.on('headers', (headers) => { - expect(headers).toBeDefined(); - expect(headers[':status']).toBe(103); - expect(headers.link).toBe( - '; rel=preload; as=style, ; rel=preload; as=script' - ); - }); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - }); - - let data = ''; - req.on('data', (d) => data += d); - - req.on('end', () => { - debug('Got full response.'); - expect(data).toBe(testResBody); - client.close(); - server.close(resolve); - }); - }); - }); - - test('Happy flow - empty array', async () => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints({ - link: [] - }); - - debug('Server sending full response...'); - res.end(testResBody); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug('Client sending request...'); - - await new Promise(resolve => { - const headersListener = jest.fn(); - req.on('headers', headersListener); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - expect(headersListener).not.toHaveBeenCalled(); - }); - - let data = ''; - req.on('data', (d) => data += d); - - req.on('end', () => { - debug('Got full response.'); - expect(data).toBe(testResBody); - client.close(); - server.close(resolve); - }); - }); - }); -}); - -//<#END_FILE: test-http2-compat-write-early-hints.js diff --git a/test/js/node/test/parallel/http2-compat-write-head-destroyed.test.js b/test/js/node/test/parallel/http2-compat-write-head-destroyed.test.js deleted file mode 100644 index 601f47928e..0000000000 --- a/test/js/node/test/parallel/http2-compat-write-head-destroyed.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http2-compat-write-head-destroyed.js -//#SHA1: 29f693f49912d4621c1a19ab7412b1b318d55d8e -//----------------- -"use strict"; - -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - if (!process.versions.openssl) { - done(); - return; - } - - server = http2.createServer((req, res) => { - // Destroy the stream first - req.stream.destroy(); - - res.writeHead(200); - res.write("hello "); - res.end("world"); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("writeHead, write and end do not crash in compatibility mode", done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - const client = http2.connect(`http://localhost:${port}`); - - const req = client.request(); - - req.on("response", () => { - done.fail("Should not receive response"); - }); - - req.on("close", () => { - client.close(); - done(); - }); - - req.resume(); -}); - -//<#END_FILE: test-http2-compat-write-head-destroyed.js diff --git a/test/js/node/test/parallel/http2-connect-tls-with-delay.test.js b/test/js/node/test/parallel/http2-connect-tls-with-delay.test.js deleted file mode 100644 index 1161272cab..0000000000 --- a/test/js/node/test/parallel/http2-connect-tls-with-delay.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http2-connect-tls-with-delay.js -//#SHA1: 8c5489e025ec14c2cc53788b27fde11a11990e42 -//----------------- -"use strict"; - -const http2 = require("http2"); -const tls = require("tls"); -const fs = require("fs"); -const path = require("path"); - -const serverOptions = { - key: fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "agent1-key.pem")), - cert: fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "agent1-cert.pem")), -}; - -let server; - -beforeAll(done => { - server = http2.createSecureServer(serverOptions, (req, res) => { - res.end(); - }); - - server.listen(0, "127.0.0.1", done); -}); - -afterAll(() => { - server.close(); -}); - -test("HTTP/2 connect with TLS and delay", done => { - const options = { - ALPNProtocols: ["h2"], - host: "127.0.0.1", - servername: "localhost", - port: server.address().port, - rejectUnauthorized: false, - }; - - const socket = tls.connect(options, async () => { - socket.once("readable", () => { - const client = http2.connect("https://localhost:" + server.address().port, { - ...options, - createConnection: () => socket, - }); - - client.once("remoteSettings", () => { - const req = client.request({ - ":path": "/", - }); - req.on("data", () => req.resume()); - req.on("end", () => { - client.close(); - req.close(); - done(); - }); - req.end(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-connect-tls-with-delay.js diff --git a/test/js/node/test/parallel/http2-cookies.test.js b/test/js/node/test/parallel/http2-cookies.test.js deleted file mode 100644 index c906992d71..0000000000 --- a/test/js/node/test/parallel/http2-cookies.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-http2-cookies.js -//#SHA1: 91bdbacba9eb8ebd9dddd43327aa2271dc00c271 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - test('HTTP/2 cookies', async () => { - const server = h2.createServer(); - - const setCookie = [ - 'a=b', - 'c=d; Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly', - 'e=f', - ]; - - server.on('stream', (stream, headers) => { - expect(typeof headers.abc).toBe('string'); - expect(headers.abc).toBe('1, 2, 3'); - expect(typeof headers.cookie).toBe('string'); - expect(headers.cookie).toBe('a=b; c=d; e=f'); - - stream.respond({ - 'content-type': 'text/html', - ':status': 200, - 'set-cookie': setCookie - }); - - stream.end('hello world'); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = h2.connect(`http://localhost:${server.address().port}`); - - const req = client.request({ - ':path': '/', - 'abc': [1, 2, 3], - 'cookie': ['a=b', 'c=d', 'e=f'], - }); - - await new Promise((resolve, reject) => { - req.on('response', (headers) => { - expect(Array.isArray(headers['set-cookie'])).toBe(true); - expect(headers['set-cookie']).toEqual(setCookie); - }); - - req.on('end', resolve); - req.on('error', reject); - req.end(); - req.resume(); - }); - - server.close(); - client.close(); - }); -} - -//<#END_FILE: test-http2-cookies.js diff --git a/test/js/node/test/parallel/http2-createwritereq.test.js b/test/js/node/test/parallel/http2-createwritereq.test.js deleted file mode 100644 index 2c768f880a..0000000000 --- a/test/js/node/test/parallel/http2-createwritereq.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-http2-createwritereq.js -//#SHA1: 8b0d2399fb8a26ce6cc76b9f338be37a7ff08ca5 -//----------------- -"use strict"; - -const http2 = require("http2"); - -// Mock the gc function -global.gc = jest.fn(); - -const testString = "a\u00A1\u0100\uD83D\uDE00"; - -const encodings = { - // "buffer": "utf8", - "ascii": "ascii", - // "latin1": "latin1", - // "binary": "latin1", - // "utf8": "utf8", - // "utf-8": "utf8", - // "ucs2": "ucs2", - // "ucs-2": "ucs2", - // "utf16le": "ucs2", - // "utf-16le": "ucs2", - // "UTF8": "utf8", -}; - -describe("http2 createWriteReq", () => { - let server; - let serverAddress; - - beforeAll(done => { - server = http2.createServer((req, res) => { - const testEncoding = encodings[req.url.slice(1)]; - - req.on("data", chunk => { - // console.error(testEncoding, chunk, Buffer.from(testString, testEncoding)); - expect(Buffer.from(testString, testEncoding).equals(chunk)).toBe(true); - }); - - req.on("end", () => res.end()); - }); - - server.listen(0, () => { - serverAddress = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - Object.keys(encodings).forEach(writeEncoding => { - test(`should handle ${writeEncoding} encoding`, done => { - const client = http2.connect(serverAddress); - const req = client.request({ - ":path": `/${writeEncoding}`, - ":method": "POST", - }); - - expect(req._writableState.decodeStrings).toBe(false); - - req.write( - writeEncoding !== "buffer" ? testString : Buffer.from(testString), - writeEncoding !== "buffer" ? writeEncoding : undefined, - ); - req.resume(); - - req.on("end", () => { - client.close(); - done(); - }); - - // Ref: https://github.com/nodejs/node/issues/17840 - const origDestroy = req.destroy; - req.destroy = function (...args) { - // Schedule a garbage collection event at the end of the current - // MakeCallback() run. - process.nextTick(global.gc); - return origDestroy.call(this, ...args); - }; - - req.end(); - }); - }); -}); - -//<#END_FILE: test-http2-createwritereq.test.js diff --git a/test/js/node/test/parallel/http2-destroy-after-write.test.js b/test/js/node/test/parallel/http2-destroy-after-write.test.js deleted file mode 100644 index c3303887ac..0000000000 --- a/test/js/node/test/parallel/http2-destroy-after-write.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http2-destroy-after-write.js -//#SHA1: 193688397df0b891b9286ff825ca873935d30e04 -//----------------- -"use strict"; - -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - server = http2.createServer(); - - server.on("session", session => { - session.on("stream", stream => { - stream.on("end", function () { - this.respond({ - ":status": 200, - }); - this.write("foo"); - this.destroy(); - }); - stream.resume(); - }); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("http2 destroy after write", done => { - const client = http2.connect(`http://localhost:${port}`); - const stream = client.request({ ":method": "POST" }); - - stream.on("response", headers => { - expect(headers[":status"]).toBe(200); - }); - - stream.on("close", () => { - client.close(); - done(); - }); - - stream.resume(); - stream.end(); -}); - -//<#END_FILE: test-http2-destroy-after-write.js diff --git a/test/js/node/test/parallel/http2-dont-override.test.js b/test/js/node/test/parallel/http2-dont-override.test.js deleted file mode 100644 index ea465da5a3..0000000000 --- a/test/js/node/test/parallel/http2-dont-override.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-dont-override.js -//#SHA1: d295b8c4823cc34c03773eb08bf0393fca541694 -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip test if crypto is not available -if (!process.versions.openssl) { - test.skip('missing crypto', () => {}); -} else { - test('http2 should not override options', (done) => { - const options = {}; - - const server = http2.createServer(options); - - // Options are defaulted but the options are not modified - expect(Object.keys(options)).toEqual([]); - - server.on('stream', (stream) => { - const headers = {}; - const options = {}; - stream.respond(headers, options); - - // The headers are defaulted but the original object is not modified - expect(Object.keys(headers)).toEqual([]); - - // Options are defaulted but the original object is not modified - expect(Object.keys(options)).toEqual([]); - - stream.end(); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - const headers = {}; - const options = {}; - - const req = client.request(headers, options); - - // The headers are defaulted but the original object is not modified - expect(Object.keys(headers)).toEqual([]); - - // Options are defaulted but the original object is not modified - expect(Object.keys(options)).toEqual([]); - - req.resume(); - req.on('end', () => { - server.close(); - client.close(); - done(); - }); - }); - }); -} - -//<#END_FILE: test-http2-dont-override.js diff --git a/test/js/node/test/parallel/http2-forget-closed-streams.test.js b/test/js/node/test/parallel/http2-forget-closed-streams.test.js deleted file mode 100644 index b21280b343..0000000000 --- a/test/js/node/test/parallel/http2-forget-closed-streams.test.js +++ /dev/null @@ -1,85 +0,0 @@ -//#FILE: test-http2-forget-closed-streams.js -//#SHA1: 2f917924c763cc220e68ce2b829c63dc03a836ab -//----------------- -"use strict"; -const http2 = require("http2"); - -// Skip test if crypto is not available -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -(hasCrypto ? describe : describe.skip)("http2 forget closed streams", () => { - let server; - - beforeAll(() => { - server = http2.createServer({ maxSessionMemory: 1 }); - - server.on("session", session => { - session.on("stream", stream => { - stream.on("end", () => { - stream.respond( - { - ":status": 200, - }, - { - endStream: true, - }, - ); - }); - stream.resume(); - }); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should handle 10000 requests without memory issues", done => { - const listenPromise = new Promise(resolve => { - server.listen(0, () => { - resolve(server.address().port); - }); - }); - - listenPromise.then(port => { - const client = http2.connect(`http://localhost:${port}`); - - function makeRequest(i) { - return new Promise(resolve => { - const stream = client.request({ ":method": "POST" }); - stream.on("response", headers => { - expect(headers[":status"]).toBe(200); - stream.on("close", resolve); - }); - stream.end(); - }); - } - - async function runRequests() { - for (let i = 0; i < 10000; i++) { - await makeRequest(i); - } - client.close(); - } - - runRequests() - .then(() => { - // If we've reached here without errors, the test has passed - expect(true).toBe(true); - done(); - }) - .catch(err => { - done(err); - }); - }); - }, 30000); // Increase timeout to 30 seconds -}); - -//<#END_FILE: test-http2-forget-closed-streams.js diff --git a/test/js/node/test/parallel/http2-goaway-opaquedata.test.js b/test/js/node/test/parallel/http2-goaway-opaquedata.test.js deleted file mode 100644 index 7de3263266..0000000000 --- a/test/js/node/test/parallel/http2-goaway-opaquedata.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-goaway-opaquedata.js -//#SHA1: 5ad5b6a64cb0e7419753dcd88d59692eb97973ed -//----------------- -'use strict'; - -const http2 = require('http2'); - -let server; -let serverPort; - -beforeAll((done) => { - server = http2.createServer(); - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('HTTP/2 GOAWAY with opaque data', (done) => { - const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); - let session; - - server.once('stream', (stream) => { - session = stream.session; - session.on('close', () => { - expect(true).toBe(true); // Session closed - }); - session.goaway(0, 0, data); - stream.respond(); - stream.end(); - }); - - const client = http2.connect(`http://localhost:${serverPort}`); - client.once('goaway', (code, lastStreamID, buf) => { - expect(code).toBe(0); - expect(lastStreamID).toBe(1); - expect(buf).toEqual(data); - session.close(); - client.close(); - done(); - }); - - const req = client.request(); - req.resume(); - req.on('end', () => { - expect(true).toBe(true); // Request ended - }); - req.on('close', () => { - expect(true).toBe(true); // Request closed - }); - req.end(); -}); - -//<#END_FILE: test-http2-goaway-opaquedata.js diff --git a/test/js/node/test/parallel/http2-large-write-close.test.js b/test/js/node/test/parallel/http2-large-write-close.test.js deleted file mode 100644 index f50a3b581f..0000000000 --- a/test/js/node/test/parallel/http2-large-write-close.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http2-large-write-close.js -//#SHA1: 66ad4345c0888700887c23af455fdd9ff49721d9 -//----------------- -"use strict"; -const fixtures = require("../common/fixtures"); -const http2 = require("http2"); - -const { beforeEach, afterEach, test, expect } = require("bun:test"); -const { isWindows } = require("harness"); -const content = Buffer.alloc(1e5, 0x44); - -let server; -let port; - -beforeEach(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - server = http2.createSecureServer({ - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }); - - server.on("stream", stream => { - stream.respond({ - "Content-Type": "application/octet-stream", - "Content-Length": content.byteLength.toString() * 2, - "Vary": "Accept-Encoding", - }); - - stream.write(content); - stream.write(content); - stream.end(); - stream.close(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterEach(() => { - server.close(); -}); - -test.todoIf(isWindows)( - "HTTP/2 large write and close", - done => { - const client = http2.connect(`https://localhost:${port}`, { rejectUnauthorized: false }); - - const req = client.request({ ":path": "/" }); - req.end(); - - let receivedBufferLength = 0; - req.on("data", buf => { - receivedBufferLength += buf.byteLength; - }); - - req.on("close", () => { - expect(receivedBufferLength).toBe(content.byteLength * 2); - client.close(); - done(); - }); - }, - 5000, -); - -//<#END_FILE: test-http2-large-write-close.js diff --git a/test/js/node/test/parallel/http2-large-write-destroy.test.js b/test/js/node/test/parallel/http2-large-write-destroy.test.js deleted file mode 100644 index b9d7679961..0000000000 --- a/test/js/node/test/parallel/http2-large-write-destroy.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-http2-large-write-destroy.js -//#SHA1: 0c76344570b21b6ed78f12185ddefde59a9b2914 -//----------------- -'use strict'; - -const http2 = require('http2'); - -const content = Buffer.alloc(60000, 0x44); - -let server; - -afterEach(() => { - if (server) { - server.close(); - } -}); - -test('HTTP/2 large write and destroy', (done) => { - server = http2.createServer(); - - server.on('stream', (stream) => { - stream.respond({ - 'Content-Type': 'application/octet-stream', - 'Content-Length': (content.length.toString() * 2), - 'Vary': 'Accept-Encoding' - }, { waitForTrailers: true }); - - stream.write(content); - stream.destroy(); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - const req = client.request({ ':path': '/' }); - req.end(); - req.resume(); // Otherwise close won't be emitted if there's pending data. - - req.on('close', () => { - client.close(); - done(); - }); - - req.on('error', (err) => { - // We expect an error due to the stream being destroyed - expect(err.code).toBe('ECONNRESET'); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-large-write-destroy.js diff --git a/test/js/node/test/parallel/http2-large-writes-session-memory-leak.test.js b/test/js/node/test/parallel/http2-large-writes-session-memory-leak.test.js deleted file mode 100644 index e718f292ff..0000000000 --- a/test/js/node/test/parallel/http2-large-writes-session-memory-leak.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-http2-large-writes-session-memory-leak.js -//#SHA1: 8f39b92e38fac58143b4d50534b2f6a171fb1a1b -//----------------- -'use strict'; - -const http2 = require('http2'); - -test.skip('HTTP/2 large writes should not cause session memory leak', () => { - console.log('This test is skipped because it requires specific Node.js internals and fixtures.'); - console.log('Original test description:'); - console.log('Regression test for https://github.com/nodejs/node/issues/29223.'); - console.log('There was a "leak" in the accounting of session memory leading'); - console.log('to streams eventually failing with NGHTTP2_ENHANCE_YOUR_CALM.'); - - // Original test logic preserved for reference: - /* - const server = http2.createSecureServer({ - key: fixtures.readKey('agent2-key.pem'), - cert: fixtures.readKey('agent2-cert.pem'), - }); - - const data200k = 'a'.repeat(200 * 1024); - server.on('stream', (stream) => { - stream.write(data200k); - stream.end(); - }); - - server.listen(0, () => { - const client = http2.connect(`https://localhost:${server.address().port}`, { - ca: fixtures.readKey('agent2-cert.pem'), - servername: 'agent2', - maxSessionMemory: 1 - }); - - let streamsLeft = 50; - function newStream() { - const stream = client.request({ ':path': '/' }); - stream.on('data', () => { }); - stream.on('close', () => { - if (streamsLeft-- > 0) { - newStream(); - } else { - client.destroy(); - server.close(); - } - }); - } - newStream(); - }); - */ -}); - -//<#END_FILE: test-http2-large-writes-session-memory-leak.js diff --git a/test/js/node/test/parallel/http2-many-writes-and-destroy.test.js b/test/js/node/test/parallel/http2-many-writes-and-destroy.test.js deleted file mode 100644 index 503419d879..0000000000 --- a/test/js/node/test/parallel/http2-many-writes-and-destroy.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http2-many-writes-and-destroy.js -//#SHA1: b4a66fa27d761038f79e0eb3562f521724887db4 -//----------------- -"use strict"; - -const http2 = require("http2"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -(hasCrypto ? describe : describe.skip)("HTTP/2 many writes and destroy", () => { - let server; - let url; - - beforeAll(done => { - server = http2.createServer((req, res) => { - req.pipe(res); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should handle many writes and destroy", done => { - const client = http2.connect(url); - const req = client.request({ ":method": "POST" }); - - for (let i = 0; i < 4000; i++) { - req.write(Buffer.alloc(6)); - } - - req.on("close", () => { - console.log("(req onclose)"); - client.close(); - done(); - }); - - req.once("data", () => { - req.destroy(); - }); - }); -}); - -//<#END_FILE: test-http2-many-writes-and-destroy.js diff --git a/test/js/node/test/parallel/http2-misc-util.test.js b/test/js/node/test/parallel/http2-misc-util.test.js deleted file mode 100644 index 0af25ec564..0000000000 --- a/test/js/node/test/parallel/http2-misc-util.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-http2-misc-util.js -//#SHA1: 0fa21e185faeff6ee5b1d703d9a998bf98d6b229 -//----------------- -const http2 = require("http2"); - -describe("HTTP/2 Misc Util", () => { - test("HTTP2 constants are defined", () => { - expect(http2.constants).toBeDefined(); - expect(http2.constants.NGHTTP2_SESSION_SERVER).toBe(0); - expect(http2.constants.NGHTTP2_SESSION_CLIENT).toBe(1); - }); - // make it not fail after re-enabling push - test.todo("HTTP2 default settings are within valid ranges", () => { - const defaultSettings = http2.getDefaultSettings(); - expect(defaultSettings).toBeDefined(); - expect(defaultSettings.headerTableSize).toBeGreaterThanOrEqual(0); - expect(defaultSettings.enablePush).toBe(true); // push is disabled because is not implemented yet - expect(defaultSettings.initialWindowSize).toBeGreaterThanOrEqual(0); - expect(defaultSettings.maxFrameSize).toBeGreaterThanOrEqual(16384); - expect(defaultSettings.maxConcurrentStreams).toBeGreaterThanOrEqual(0); - expect(defaultSettings.maxHeaderListSize).toBeGreaterThanOrEqual(0); - }); - - test("HTTP2 getPackedSettings and getUnpackedSettings", () => { - const settings = { - headerTableSize: 4096, - enablePush: true, - initialWindowSize: 65535, - maxFrameSize: 16384, - }; - const packed = http2.getPackedSettings(settings); - expect(packed).toBeInstanceOf(Buffer); - - const unpacked = http2.getUnpackedSettings(packed); - expect(unpacked).toEqual(expect.objectContaining(settings)); - }); -}); - -//<#END_FILE: test-http2-misc-util.js diff --git a/test/js/node/test/parallel/http2-multistream-destroy-on-read-tls.test.js b/test/js/node/test/parallel/http2-multistream-destroy-on-read-tls.test.js deleted file mode 100644 index 5e27b6472c..0000000000 --- a/test/js/node/test/parallel/http2-multistream-destroy-on-read-tls.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-http2-multistream-destroy-on-read-tls.js -//#SHA1: bf3869a9f8884210710d41c0fb1f54d2112e9af5 -//----------------- -"use strict"; -const http2 = require("http2"); - -describe("HTTP2 multistream destroy on read", () => { - let server; - const filenames = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - - beforeAll(done => { - server = http2.createServer(); - - server.on("stream", stream => { - function write() { - stream.write("a".repeat(10240)); - stream.once("drain", write); - } - write(); - }); - - server.listen(0, done); - }); - - afterAll(() => { - if (server) { - server.close(); - } else { - done(); - } - }); - - test("should handle multiple stream destructions", done => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - let destroyed = 0; - for (const entry of filenames) { - const stream = client.request({ - ":path": `/${entry}`, - }); - stream.once("data", () => { - stream.destroy(); - - if (++destroyed === filenames.length) { - client.close(); - done(); - } - }); - } - }); -}); - -//<#END_FILE: test-http2-multistream-destroy-on-read-tls.js diff --git a/test/js/node/test/parallel/http2-no-wanttrailers-listener.test.js b/test/js/node/test/parallel/http2-no-wanttrailers-listener.test.js deleted file mode 100644 index b7aa239af9..0000000000 --- a/test/js/node/test/parallel/http2-no-wanttrailers-listener.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-http2-no-wanttrailers-listener.js -//#SHA1: a5297c0a1ed58f7d2d0a13bc4eaaa198a7ab160e -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let client; - -beforeAll(() => { - // Check if crypto is available - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test("HTTP/2 server should not hang without wantTrailers listener", done => { - server = h2.createServer(); - - server.on("stream", (stream, headers, flags) => { - stream.respond(undefined, { waitForTrailers: true }); - stream.end("ok"); - }); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - req.resume(); - - req.on("trailers", () => { - throw new Error("Unexpected trailers event"); - }); - - req.on("close", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http2-no-wanttrailers-listener.js diff --git a/test/js/node/test/parallel/http2-options-server-response.test.js b/test/js/node/test/parallel/http2-options-server-response.test.js deleted file mode 100644 index 4ad8e33898..0000000000 --- a/test/js/node/test/parallel/http2-options-server-response.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http2-options-server-response.js -//#SHA1: 66736f340efdbdf2e20a79a3dffe75f499e65d89 -//----------------- -'use strict'; - -const h2 = require('http2'); - -class MyServerResponse extends h2.Http2ServerResponse { - status(code) { - return this.writeHead(code, { 'Content-Type': 'text/plain' }); - } -} - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterAll(() => { - if (server) server.close(); - if (client) client.destroy(); -}); - -test('http2 server with custom ServerResponse', (done) => { - server = h2.createServer({ - Http2ServerResponse: MyServerResponse - }, (req, res) => { - res.status(200); - res.end(); - }); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - const req = client.request({ ':path': '/' }); - - const responseHandler = jest.fn(); - req.on('response', responseHandler); - - const endHandler = jest.fn(() => { - expect(responseHandler).toHaveBeenCalled(); - done(); - }); - - req.resume(); - req.on('end', endHandler); - }); -}); - -//<#END_FILE: test-http2-options-server-response.js diff --git a/test/js/node/test/parallel/http2-perf_hooks.test.js b/test/js/node/test/parallel/http2-perf_hooks.test.js deleted file mode 100644 index b45b8d48c7..0000000000 --- a/test/js/node/test/parallel/http2-perf_hooks.test.js +++ /dev/null @@ -1,124 +0,0 @@ -//#FILE: test-http2-perf_hooks.js -//#SHA1: a759a55527c8587bdf272da00c6597d93aa36da0 -//----------------- -'use strict'; - -const h2 = require('http2'); -const { PerformanceObserver } = require('perf_hooks'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (client) client.close(); - if (server) server.close(); -}); - -test('HTTP/2 performance hooks', (done) => { - const obs = new PerformanceObserver((items) => { - const entry = items.getEntries()[0]; - expect(entry.entryType).toBe('http2'); - expect(typeof entry.startTime).toBe('number'); - expect(typeof entry.duration).toBe('number'); - - switch (entry.name) { - case 'Http2Session': - expect(typeof entry.pingRTT).toBe('number'); - expect(typeof entry.streamAverageDuration).toBe('number'); - expect(typeof entry.streamCount).toBe('number'); - expect(typeof entry.framesReceived).toBe('number'); - expect(typeof entry.framesSent).toBe('number'); - expect(typeof entry.bytesWritten).toBe('number'); - expect(typeof entry.bytesRead).toBe('number'); - expect(typeof entry.maxConcurrentStreams).toBe('number'); - expect(typeof entry.detail.pingRTT).toBe('number'); - expect(typeof entry.detail.streamAverageDuration).toBe('number'); - expect(typeof entry.detail.streamCount).toBe('number'); - expect(typeof entry.detail.framesReceived).toBe('number'); - expect(typeof entry.detail.framesSent).toBe('number'); - expect(typeof entry.detail.bytesWritten).toBe('number'); - expect(typeof entry.detail.bytesRead).toBe('number'); - expect(typeof entry.detail.maxConcurrentStreams).toBe('number'); - switch (entry.type) { - case 'server': - expect(entry.detail.streamCount).toBe(1); - expect(entry.detail.framesReceived).toBeGreaterThanOrEqual(3); - break; - case 'client': - expect(entry.detail.streamCount).toBe(1); - expect(entry.detail.framesReceived).toBe(7); - break; - default: - fail('invalid Http2Session type'); - } - break; - case 'Http2Stream': - expect(typeof entry.timeToFirstByte).toBe('number'); - expect(typeof entry.timeToFirstByteSent).toBe('number'); - expect(typeof entry.timeToFirstHeader).toBe('number'); - expect(typeof entry.bytesWritten).toBe('number'); - expect(typeof entry.bytesRead).toBe('number'); - expect(typeof entry.detail.timeToFirstByte).toBe('number'); - expect(typeof entry.detail.timeToFirstByteSent).toBe('number'); - expect(typeof entry.detail.timeToFirstHeader).toBe('number'); - expect(typeof entry.detail.bytesWritten).toBe('number'); - expect(typeof entry.detail.bytesRead).toBe('number'); - break; - default: - fail('invalid entry name'); - } - }); - - obs.observe({ type: 'http2' }); - - const body = '

this is some data

'; - - server = h2.createServer(); - - server.on('stream', (stream, headers, flags) => { - expect(headers[':scheme']).toBe('http'); - expect(headers[':authority']).toBeTruthy(); - expect(headers[':method']).toBe('GET'); - expect(flags).toBe(5); - stream.respond({ - 'content-type': 'text/html', - ':status': 200 - }); - stream.write(body.slice(0, 20)); - stream.end(body.slice(20)); - }); - - server.on('session', (session) => { - session.ping(jest.fn()); - }); - - server.listen(0, () => { - client = h2.connect(`http://localhost:${server.address().port}`); - - client.on('connect', () => { - client.ping(jest.fn()); - }); - - const req = client.request(); - - req.on('response', jest.fn()); - - let data = ''; - req.setEncoding('utf8'); - req.on('data', (d) => data += d); - req.on('end', () => { - expect(body).toBe(data); - }); - req.on('close', () => { - obs.disconnect(); - done(); - }); - }); -}); -//<#END_FILE: test-http2-perf_hooks.js diff --git a/test/js/node/test/parallel/http2-pipe.test.js b/test/js/node/test/parallel/http2-pipe.test.js deleted file mode 100644 index 0f852cef61..0000000000 --- a/test/js/node/test/parallel/http2-pipe.test.js +++ /dev/null @@ -1,83 +0,0 @@ -//#FILE: test-http2-pipe.js -//#SHA1: bb970b612d495580b8c216a1b202037e5eb0721e -//----------------- -"use strict"; - -import { afterEach, beforeEach, test, expect, describe, mock } from "bun:test"; - -const http2 = require("http2"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -const testIfCrypto = hasCrypto ? test : test.skip; - -describe("HTTP2 Pipe", () => { - let server; - let serverPort; - let tmpdir; - const fixturesDir = path.join(__dirname, "..", "fixtures"); - const loc = path.join(fixturesDir, "person-large.jpg"); - let fn; - - beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "http2-test-")); - fn = path.join(tmpdir, "http2-url-tests.js"); - }); - - afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - testIfCrypto("Piping should work as expected with createWriteStream", done => { - server = http2.createServer(); - - server.on("stream", stream => { - const dest = stream.pipe(fs.createWriteStream(fn)); - - dest.on("finish", () => { - expect(fs.readFileSync(loc).length).toBe(fs.readFileSync(fn).length); - }); - stream.respond(); - stream.end(); - }); - - server.listen(0, () => { - serverPort = server.address().port; - const client = http2.connect(`http://localhost:${serverPort}`); - - const req = client.request({ ":method": "POST" }); - - const responseHandler = mock(() => {}); - req.on("response", responseHandler); - req.resume(); - - req.on("close", () => { - expect(responseHandler).toHaveBeenCalled(); - server.close(); - client.close(); - done(); - }); - - const str = fs.createReadStream(loc); - const strEndHandler = mock(() => {}); - str.on("end", strEndHandler); - str.pipe(req); - - req.on("finish", () => { - expect(strEndHandler).toHaveBeenCalled(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-pipe.js diff --git a/test/js/node/test/parallel/http2-priority-cycle-.test.js b/test/js/node/test/parallel/http2-priority-cycle-.test.js deleted file mode 100644 index 61bab1f9cd..0000000000 --- a/test/js/node/test/parallel/http2-priority-cycle-.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-http2-priority-cycle-.js -//#SHA1: 32c70d0d1e4be42834f071fa3d9bb529aa4ea1c1 -//----------------- -'use strict'; - -const http2 = require('http2'); - -const largeBuffer = Buffer.alloc(1e4); - -class Countdown { - constructor(count, done) { - this.count = count; - this.done = done; - } - - dec() { - this.count--; - if (this.count === 0) this.done(); - } -} - -test('HTTP/2 priority cycle', (done) => { - const server = http2.createServer(); - - server.on('stream', (stream) => { - stream.respond(); - setImmediate(() => { - stream.end(largeBuffer); - }); - }); - - server.on('session', (session) => { - session.on('priority', (id, parent, weight, exclusive) => { - expect(weight).toBe(16); - expect(exclusive).toBe(false); - switch (id) { - case 1: - expect(parent).toBe(5); - break; - case 3: - expect(parent).toBe(1); - break; - case 5: - expect(parent).toBe(3); - break; - default: - fail('should not happen'); - } - }); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - const countdown = new Countdown(3, () => { - client.close(); - server.close(); - done(); - }); - - { - const req = client.request(); - req.priority({ parent: 5 }); - req.resume(); - req.on('close', () => countdown.dec()); - } - - { - const req = client.request(); - req.priority({ parent: 1 }); - req.resume(); - req.on('close', () => countdown.dec()); - } - - { - const req = client.request(); - req.priority({ parent: 3 }); - req.resume(); - req.on('close', () => countdown.dec()); - } - }); -}); - -//<#END_FILE: test-http2-priority-cycle-.js diff --git a/test/js/node/test/parallel/http2-removed-header-stays-removed.test.js b/test/js/node/test/parallel/http2-removed-header-stays-removed.test.js deleted file mode 100644 index a996aabc1c..0000000000 --- a/test/js/node/test/parallel/http2-removed-header-stays-removed.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http2-removed-header-stays-removed.js -//#SHA1: f8bc3d1be9927b83a02492d9cb44c803c337e3c1 -//----------------- -"use strict"; -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - server = http2.createServer((request, response) => { - response.setHeader("date", "snacks o clock"); - response.end(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("HTTP/2 removed header stays removed", done => { - const session = http2.connect(`http://localhost:${port}`); - const req = session.request(); - - req.on("response", (headers, flags) => { - expect(headers.date).toBe("snacks o clock"); - }); - - req.on("end", () => { - session.close(); - done(); - }); -}); - -// Conditional skip if crypto is not available -try { - require("crypto"); -} catch (err) { - test.skip("missing crypto", () => {}); -} - -//<#END_FILE: test-http2-removed-header-stays-removed.js diff --git a/test/js/node/test/parallel/http2-request-remove-connect-listener.test.js b/test/js/node/test/parallel/http2-request-remove-connect-listener.test.js deleted file mode 100644 index 85bcbf502c..0000000000 --- a/test/js/node/test/parallel/http2-request-remove-connect-listener.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-http2-request-remove-connect-listener.js -//#SHA1: 28cbc334f4429a878522e1e78eac56d13fb0c916 -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -let cryptoAvailable = true; -try { - require('crypto'); -} catch (err) { - cryptoAvailable = false; -} - -test('HTTP/2 request removes connect listener', (done) => { - if (!cryptoAvailable) { - console.log('Skipping test: missing crypto'); - return done(); - } - - const server = http2.createServer(); - const streamHandler = jest.fn((stream) => { - stream.respond(); - stream.end(); - }); - server.on('stream', streamHandler); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - const connectHandler = jest.fn(); - client.once('connect', connectHandler); - - const req = client.request(); - - req.on('response', () => { - expect(client.listenerCount('connect')).toBe(0); - expect(streamHandler).toHaveBeenCalled(); - expect(connectHandler).toHaveBeenCalled(); - }); - - req.on('close', () => { - server.close(); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-request-remove-connect-listener.js diff --git a/test/js/node/test/parallel/http2-request-response-proto.test.js b/test/js/node/test/parallel/http2-request-response-proto.test.js deleted file mode 100644 index 5ed889e51a..0000000000 --- a/test/js/node/test/parallel/http2-request-response-proto.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-http2-request-response-proto.js -//#SHA1: ffffac0d4d11b6a77ddbfce366c206de8db99446 -//----------------- -'use strict'; - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -let http2; - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - http2 = require('http2'); - - const { - Http2ServerRequest, - Http2ServerResponse, - } = http2; - - describe('Http2ServerRequest and Http2ServerResponse prototypes', () => { - test('protoRequest should be instance of Http2ServerRequest', () => { - const protoRequest = { __proto__: Http2ServerRequest.prototype }; - expect(protoRequest instanceof Http2ServerRequest).toBe(true); - }); - - test('protoResponse should be instance of Http2ServerResponse', () => { - const protoResponse = { __proto__: Http2ServerResponse.prototype }; - expect(protoResponse instanceof Http2ServerResponse).toBe(true); - }); - }); -} - -//<#END_FILE: test-http2-request-response-proto.js diff --git a/test/js/node/test/parallel/http2-res-corked.test.js b/test/js/node/test/parallel/http2-res-corked.test.js deleted file mode 100644 index 0da21d6cc4..0000000000 --- a/test/js/node/test/parallel/http2-res-corked.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-http2-res-corked.js -//#SHA1: a6c5da9f22eae611c043c6d177d63c0eaca6e02e -//----------------- -"use strict"; -const http2 = require("http2"); - -// Skip the test if crypto is not available -let hasCrypto = false; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - // crypto not available -} - -(hasCrypto ? describe : describe.skip)("Http2ServerResponse#[writableCorked,cork,uncork]", () => { - let server; - let client; - let corksLeft = 0; - - beforeAll(done => { - server = http2.createServer((req, res) => { - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.end(); - }); - - server.listen(0, () => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - done(); - }); - }); - - afterAll(() => { - client.close(); - server.close(); - }); - - test("cork and uncork operations", done => { - const req = client.request(); - let dataCallCount = 0; - req.on("data", () => { - dataCallCount++; - }); - req.on("end", () => { - expect(dataCallCount).toBe(2); - done(); - }); - }); -}); -//<#END_FILE: test-http2-res-corked.js diff --git a/test/js/node/test/parallel/http2-respond-file-compat.test.js b/test/js/node/test/parallel/http2-respond-file-compat.test.js deleted file mode 100644 index 7d05c6e8f0..0000000000 --- a/test/js/node/test/parallel/http2-respond-file-compat.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http2-respond-file-compat.js -//#SHA1: fac1eb9c2e4f7a75e9c7605abc64fc9c6e6f7f14 -//----------------- -'use strict'; - -const http2 = require('http2'); -const fs = require('fs'); -const path = require('path'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -const fname = path.join(__dirname, '..', 'fixtures', 'elipses.txt'); - -describe('HTTP/2 respondWithFile', () => { - let server; - - beforeAll(() => { - if (!hasCrypto) { - return; - } - // Ensure the file exists - if (!fs.existsSync(fname)) { - fs.writeFileSync(fname, '...'); - } - }); - - afterAll(() => { - if (server) { - server.close(); - } - }); - - test('should respond with file', (done) => { - if (!hasCrypto) { - done(); - return; - } - - const requestHandler = jest.fn((request, response) => { - response.stream.respondWithFile(fname); - }); - - server = http2.createServer(requestHandler); - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - const responseHandler = jest.fn(); - req.on('response', responseHandler); - - req.on('end', () => { - expect(requestHandler).toHaveBeenCalled(); - expect(responseHandler).toHaveBeenCalled(); - client.close(); - server.close(() => { - done(); - }); - }); - - req.end(); - req.resume(); - }); - }); -}); - -//<#END_FILE: test-http2-respond-file-compat.js diff --git a/test/js/node/test/parallel/http2-respond-file-error-dir.test.js b/test/js/node/test/parallel/http2-respond-file-error-dir.test.js deleted file mode 100644 index b3b9e7a592..0000000000 --- a/test/js/node/test/parallel/http2-respond-file-error-dir.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http2-respond-file-error-dir.js -//#SHA1: 61f98e2ad2c69302fe84383e1dec1118edaa70e1 -//----------------- -'use strict'; - -const http2 = require('http2'); -const path = require('path'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test('http2 respondWithFile with directory should fail', (done) => { - server = http2.createServer(); - server.on('stream', (stream) => { - stream.respondWithFile(process.cwd(), { - 'content-type': 'text/plain' - }, { - onError(err) { - expect(err).toMatchObject({ - code: 'ERR_HTTP2_SEND_FILE', - name: 'Error', - message: 'Directories cannot be sent' - }); - - stream.respond({ ':status': 404 }); - stream.end(); - }, - statCheck: jest.fn() - }); - }); - - server.listen(0, () => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - const req = client.request(); - - const responseHandler = jest.fn((headers) => { - expect(headers[':status']).toBe(404); - }); - - const dataHandler = jest.fn(); - const endHandler = jest.fn(() => { - expect(responseHandler).toHaveBeenCalled(); - expect(dataHandler).not.toHaveBeenCalled(); - done(); - }); - - req.on('response', responseHandler); - req.on('data', dataHandler); - req.on('end', endHandler); - req.end(); - }); -}); - -//<#END_FILE: test-http2-respond-file-error-dir.js diff --git a/test/js/node/test/parallel/http2-respond-file-filehandle.test.js b/test/js/node/test/parallel/http2-respond-file-filehandle.test.js deleted file mode 100644 index 297f53d852..0000000000 --- a/test/js/node/test/parallel/http2-respond-file-filehandle.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http2-respond-file-filehandle.js -//#SHA1: c80cf9e1a4a879a73d275616e0604e56ac7756bb -//----------------- -'use strict'; - -const http2 = require('http2'); -const fs = require('fs'); -const path = require('path'); - -const { - HTTP2_HEADER_CONTENT_TYPE, - HTTP2_HEADER_CONTENT_LENGTH -} = http2.constants; - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); -const fname = path.join(fixturesPath, 'elipses.txt'); - -test('http2 respond with file handle', async () => { - // Skip test if running in Bun - if (process.versions.bun) { - return; - } - - const data = await fs.promises.readFile(fname); - const stat = await fs.promises.stat(fname); - - const fileHandle = await fs.promises.open(fname, 'r'); - - const server = http2.createServer(); - server.on('stream', (stream) => { - stream.respondWithFD(fileHandle, { - [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain', - [HTTP2_HEADER_CONTENT_LENGTH]: stat.size, - }); - }); - - const serverCloseHandler = jest.fn(); - server.on('close', serverCloseHandler); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - const responseHandler = jest.fn((headers) => { - expect(headers[HTTP2_HEADER_CONTENT_TYPE]).toBe('text/plain'); - expect(Number(headers[HTTP2_HEADER_CONTENT_LENGTH])).toBe(data.length); - }); - req.on('response', responseHandler); - - req.setEncoding('utf8'); - let check = ''; - req.on('data', (chunk) => check += chunk); - - await new Promise(resolve => { - req.on('end', () => { - expect(check).toBe(data.toString('utf8')); - client.close(); - server.close(); - resolve(); - }); - req.end(); - }); - - await new Promise(resolve => server.on('close', resolve)); - - expect(responseHandler).toHaveBeenCalled(); - expect(serverCloseHandler).toHaveBeenCalled(); - - await fileHandle.close(); -}); - -//<#END_FILE: test-http2-respond-file-filehandle.js diff --git a/test/js/node/test/parallel/http2-sent-headers.test.js b/test/js/node/test/parallel/http2-sent-headers.test.js deleted file mode 100644 index 21a5c36ad1..0000000000 --- a/test/js/node/test/parallel/http2-sent-headers.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-http2-sent-headers.js -//#SHA1: cbc2db06925ef62397fd91d70872b787363cd96c -//----------------- -"use strict"; - -const h2 = require("http2"); - -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -(hasCrypto ? describe : describe.skip)("http2 sent headers", () => { - let server; - let client; - let port; - - beforeAll(done => { - server = h2.createServer(); - - server.on("stream", stream => { - stream.additionalHeaders({ ":status": 102 }); - expect(stream.sentInfoHeaders[0][":status"]).toBe(102); - - stream.respond({ abc: "xyz" }, { waitForTrailers: true }); - stream.on("wantTrailers", () => { - stream.sendTrailers({ xyz: "abc" }); - }); - expect(stream.sentHeaders.abc).toBe("xyz"); - expect(stream.sentHeaders[":status"]).toBe(200); - expect(stream.sentHeaders.date).toBeDefined(); - stream.end(); - stream.on("close", () => { - expect(stream.sentTrailers.xyz).toBe("abc"); - }); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("client request headers", done => { - client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - - req.on("headers", (headers, flags) => { - expect(headers[":status"]).toBe(102); - expect(typeof flags).toBe("number"); - }); - - expect(req.sentHeaders[":method"]).toBe("GET"); - expect(req.sentHeaders[":authority"]).toBe(`localhost:${port}`); - expect(req.sentHeaders[":scheme"]).toBe("http"); - expect(req.sentHeaders[":path"]).toBe("/"); - - req.resume(); - req.on("close", () => { - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-sent-headers.js diff --git a/test/js/node/test/parallel/http2-server-async-dispose.test.js b/test/js/node/test/parallel/http2-server-async-dispose.test.js deleted file mode 100644 index bdf5282129..0000000000 --- a/test/js/node/test/parallel/http2-server-async-dispose.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-http2-server-async-dispose.js -//#SHA1: 3f26a183d15534b5f04c61836e718ede1726834f -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Check if crypto is available -let hasCrypto = false; -try { - require('crypto'); - hasCrypto = true; -} catch (err) { - // crypto is not available -} - -(hasCrypto ? test : test.skip)('http2 server async close', (done) => { - const server = http2.createServer(); - - const closeHandler = jest.fn(); - server.on('close', closeHandler); - - server.listen(0, () => { - // Use the close method instead of Symbol.asyncDispose - server.close(() => { - expect(closeHandler).toHaveBeenCalled(); - done(); - }); - }); -}, 10000); // Increase timeout to 10 seconds - -//<#END_FILE: test-http2-server-async-dispose.js diff --git a/test/js/node/test/parallel/http2-server-push-stream-errors.test.js b/test/js/node/test/parallel/http2-server-push-stream-errors.test.js deleted file mode 100644 index 7c9aa34bcc..0000000000 --- a/test/js/node/test/parallel/http2-server-push-stream-errors.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-http2-server-push-stream-errors.js -//#SHA1: e0c43917d2cc3edee06a7d89fb1cbeff9c81fb08 -//----------------- -'use strict'; - -test.skip('HTTP/2 server push stream errors', () => { - console.log('This test is skipped because it relies on Node.js internals that are not accessible in Jest.'); -}); - -// Original test code (commented out for reference) -/* -const http2 = require('http2'); -const { internalBinding } = require('internal/test/binding'); -const { - constants, - Http2Stream, - nghttp2ErrorString -} = internalBinding('http2'); -const { NghttpError } = require('internal/http2/util'); - -// ... rest of the original test code ... -*/ - -//<#END_FILE: test-http2-server-push-stream-errors.test.js diff --git a/test/js/node/test/parallel/http2-server-rst-before-respond.test.js b/test/js/node/test/parallel/http2-server-rst-before-respond.test.js deleted file mode 100644 index 9280ea17eb..0000000000 --- a/test/js/node/test/parallel/http2-server-rst-before-respond.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http2-server-rst-before-respond.js -//#SHA1: 67d0d7c2fdd32d5eb050bf8473a767dbf24d158a -//----------------- -'use strict'; - -const h2 = require('http2'); - -let server; -let client; - -beforeEach(() => { - server = h2.createServer(); -}); - -afterEach(() => { - if (server) server.close(); - if (client) client.close(); -}); - -test('HTTP/2 server reset stream before respond', (done) => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - return; - } - - const onStream = jest.fn((stream, headers, flags) => { - stream.close(); - - expect(() => { - stream.additionalHeaders({ - ':status': 123, - 'abc': 123 - }); - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_INVALID_STREAM' - })); - }); - - server.on('stream', onStream); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - - const onHeaders = jest.fn(); - req.on('headers', onHeaders); - - const onResponse = jest.fn(); - req.on('response', onResponse); - - req.on('close', () => { - expect(req.rstCode).toBe(h2.constants.NGHTTP2_NO_ERROR); - expect(onStream).toHaveBeenCalledTimes(1); - expect(onHeaders).not.toHaveBeenCalled(); - expect(onResponse).not.toHaveBeenCalled(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-server-rst-before-respond.js diff --git a/test/js/node/test/parallel/http2-server-set-header.test.js b/test/js/node/test/parallel/http2-server-set-header.test.js deleted file mode 100644 index 8f63781248..0000000000 --- a/test/js/node/test/parallel/http2-server-set-header.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http2-server-set-header.js -//#SHA1: d4ba0042eab7b4ef4927f3aa3e344f4b5e04f935 -//----------------- -'use strict'; - -const http2 = require('http2'); - -const body = '

this is some data

'; - -let server; -let port; - -beforeAll((done) => { - server = http2.createServer((req, res) => { - res.setHeader('foobar', 'baz'); - res.setHeader('X-POWERED-BY', 'node-test'); - res.setHeader('connection', 'connection-test'); - res.end(body); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('HTTP/2 server set header', (done) => { - const client = http2.connect(`http://localhost:${port}`); - const headers = { ':path': '/' }; - const req = client.request(headers); - req.setEncoding('utf8'); - - req.on('response', (headers) => { - expect(headers.foobar).toBe('baz'); - expect(headers['x-powered-by']).toBe('node-test'); - // The 'connection' header should not be present in HTTP/2 - expect(headers.connection).toBeUndefined(); - }); - - let data = ''; - req.on('data', (d) => data += d); - req.on('end', () => { - expect(data).toBe(body); - client.close(); - done(); - }); - req.end(); -}); - -test('Setting connection header should not throw', () => { - const res = { - setHeader: jest.fn() - }; - - expect(() => { - res.setHeader('connection', 'test'); - }).not.toThrow(); - - expect(res.setHeader).toHaveBeenCalledWith('connection', 'test'); -}); - -test('Server should not emit error', (done) => { - const errorListener = jest.fn(); - server.on('error', errorListener); - - setTimeout(() => { - expect(errorListener).not.toHaveBeenCalled(); - server.removeListener('error', errorListener); - done(); - }, 100); -}); - -//<#END_FILE: test-http2-server-set-header.js diff --git a/test/js/node/test/parallel/http2-session-timeout.test.js b/test/js/node/test/parallel/http2-session-timeout.test.js deleted file mode 100644 index 08b4a07c34..0000000000 --- a/test/js/node/test/parallel/http2-session-timeout.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-session-timeout.js -//#SHA1: 8a03d5dc642f9d07faac7b4a44caa0e02b625339 -//----------------- -'use strict'; - -const http2 = require('http2'); -const { hrtime } = process; -const NS_PER_MS = 1_000_000n; - -let requests = 0; - -test('HTTP/2 session timeout', (done) => { - const server = http2.createServer(); - server.timeout = 0n; - - server.on('request', (req, res) => res.end()); - server.on('timeout', () => { - throw new Error(`Timeout after ${requests} request(s)`); - }); - - server.listen(0, () => { - const port = server.address().port; - const url = `http://localhost:${port}`; - const client = http2.connect(url); - let startTime = hrtime.bigint(); - - function makeReq() { - const request = client.request({ - ':path': '/foobar', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}`, - }); - request.resume(); - request.end(); - - requests += 1; - - request.on('end', () => { - const diff = hrtime.bigint() - startTime; - const milliseconds = diff / NS_PER_MS; - if (server.timeout === 0n) { - server.timeout = milliseconds * 2n; - startTime = hrtime.bigint(); - makeReq(); - } else if (milliseconds < server.timeout * 2n) { - makeReq(); - } else { - server.close(); - client.close(); - expect(requests).toBeGreaterThan(1); - done(); - } - }); - } - - makeReq(); - }); -}); - -//<#END_FILE: test-http2-session-timeout.js diff --git a/test/js/node/test/parallel/http2-socket-proxy.test.js b/test/js/node/test/parallel/http2-socket-proxy.test.js deleted file mode 100644 index 3e6122df11..0000000000 --- a/test/js/node/test/parallel/http2-socket-proxy.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-socket-proxy.js -//#SHA1: c5158fe06db7a7572dc5f7a52c23f019d16fb8ce -//----------------- -'use strict'; - -const h2 = require('http2'); -const net = require('net'); - -let server; -let port; - -beforeAll(async () => { - server = h2.createServer(); - await new Promise(resolve => server.listen(0, () => { - port = server.address().port; - resolve(); - })); -}); - -afterAll(async () => { - await new Promise(resolve => server.close(resolve)); -}); - -describe('HTTP/2 Socket Proxy', () => { - test('Socket behavior on Http2Session', async () => { - expect.assertions(5); - - server.once('stream', (stream, headers) => { - const socket = stream.session.socket; - const session = stream.session; - - expect(socket).toBeInstanceOf(net.Socket); - expect(socket.writable).toBe(true); - expect(socket.readable).toBe(true); - expect(typeof socket.address()).toBe('object'); - - // Test that setting a property on socket affects the session - const fn = jest.fn(); - socket.setTimeout = fn; - expect(session.setTimeout).toBe(fn); - - stream.respond({ ':status': 200 }); - stream.end('OK'); - }); - - const client = h2.connect(`http://localhost:${port}`); - const req = client.request({ ':path': '/' }); - - await new Promise(resolve => { - req.on('response', () => { - req.on('data', () => {}); - req.on('end', () => { - client.close(); - resolve(); - }); - }); - }); - }, 10000); // Increase timeout to 10 seconds -}); - -//<#END_FILE: test-http2-socket-proxy.js diff --git a/test/js/node/test/parallel/http2-status-code.test.js b/test/js/node/test/parallel/http2-status-code.test.js deleted file mode 100644 index ec02531975..0000000000 --- a/test/js/node/test/parallel/http2-status-code.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-status-code.js -//#SHA1: 53911ac66c46f57bca1d56cdaf76e46d61c957d8 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const codes = [200, 202, 300, 400, 404, 451, 500]; -let server; -let client; - -beforeAll(done => { - server = http2.createServer(); - - let testIndex = 0; - server.on("stream", stream => { - const status = codes[testIndex++]; - stream.respond({ ":status": status }, { endStream: true }); - }); - - server.listen(0, () => { - done(); - }); -}); - -afterAll(() => { - client.close(); - server.close(); -}); - -test("HTTP/2 status codes", done => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - - let remaining = codes.length; - function maybeClose() { - if (--remaining === 0) { - done(); - } - } - - function doTest(expected) { - return new Promise(resolve => { - const req = client.request(); - req.on("response", headers => { - expect(headers[":status"]).toBe(expected); - }); - req.resume(); - req.on("end", () => { - maybeClose(); - resolve(); - }); - }); - } - - Promise.all(codes.map(doTest)).then(() => { - // All tests completed - }); -}); - -//<#END_FILE: test-http2-status-code.js diff --git a/test/js/node/test/parallel/http2-tls-disconnect.test.js b/test/js/node/test/parallel/http2-tls-disconnect.test.js deleted file mode 100644 index f26b325906..0000000000 --- a/test/js/node/test/parallel/http2-tls-disconnect.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-http2-tls-disconnect.js -//#SHA1: 0673265638d2f040031cb9fbc7a1fefda23ba0e1 -//----------------- -'use strict'; - -test.skip('http2 TLS disconnect', () => { - console.log('This test is skipped because:'); - console.log('1. It requires specific SSL certificate files (agent8-key.pem and agent8-cert.pem) which are not available in the current test environment.'); - console.log('2. It relies on an external tool (h2load) which may not be installed on all systems.'); - console.log('3. The test involves creating a real HTTPS server and spawning a child process, which is not ideal for unit testing.'); - console.log('To properly test this functionality, consider:'); - console.log('- Mocking the SSL certificates and http2 server creation'); - console.log('- Replacing the h2load functionality with a simulated load using pure JavaScript'); - console.log('- Focusing on testing the specific behavior (TLS disconnect handling) without relying on external tools'); -}); - -//<#END_FILE: test-http2-tls-disconnect.js diff --git a/test/js/node/test/parallel/http2-trailers.test.js b/test/js/node/test/parallel/http2-trailers.test.js deleted file mode 100644 index 63666b1966..0000000000 --- a/test/js/node/test/parallel/http2-trailers.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-http2-trailers.js -//#SHA1: 1e3d42d5008cf87fa8bf557b38f4fd00b4dbd712 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const body = - '

this is some data

'; -const trailerKey = 'test-trailer'; -const trailerValue = 'testing'; - -let server; - -beforeAll(() => { - server = h2.createServer(); - server.on('stream', onStream); -}); - -afterAll(() => { - server.close(); -}); - -function onStream(stream, headers, flags) { - stream.on('trailers', (headers) => { - expect(headers[trailerKey]).toBe(trailerValue); - stream.end(body); - }); - stream.respond({ - 'content-type': 'text/html', - ':status': 200 - }, { waitForTrailers: true }); - stream.on('wantTrailers', () => { - stream.sendTrailers({ [trailerKey]: trailerValue }); - expect(() => stream.sendTrailers({})).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_TRAILERS_ALREADY_SENT', - name: 'Error' - })); - }); - - expect(() => stream.sendTrailers({})).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_TRAILERS_NOT_READY', - name: 'Error' - })); -} - -test('HTTP/2 trailers', (done) => { - server.listen(0, () => { - const client = h2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ ':path': '/', ':method': 'POST' }, - { waitForTrailers: true }); - req.on('wantTrailers', () => { - req.sendTrailers({ [trailerKey]: trailerValue }); - }); - req.on('data', () => {}); - req.on('trailers', (headers) => { - expect(headers[trailerKey]).toBe(trailerValue); - }); - req.on('close', () => { - expect(() => req.sendTrailers({})).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_INVALID_STREAM', - name: 'Error' - })); - client.close(); - done(); - }); - req.end('data'); - }); -}); - -//<#END_FILE: test-http2-trailers.js diff --git a/test/js/node/test/parallel/http2-unbound-socket-proxy.test.js b/test/js/node/test/parallel/http2-unbound-socket-proxy.test.js deleted file mode 100644 index c4c0635240..0000000000 --- a/test/js/node/test/parallel/http2-unbound-socket-proxy.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http2-unbound-socket-proxy.js -//#SHA1: bcb8a31b2f29926a8e8d9a3bb5f23d09bfa5e805 -//----------------- -'use strict'; - -const http2 = require('http2'); -const net = require('net'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test('http2 unbound socket proxy', (done) => { - server = http2.createServer(); - const streamHandler = jest.fn((stream) => { - stream.respond(); - stream.end('ok'); - }); - server.on('stream', streamHandler); - - server.listen(0, () => { - client = http2.connect(`http://localhost:${server.address().port}`); - const socket = client.socket; - const req = client.request(); - req.resume(); - req.on('close', () => { - client.close(); - server.close(); - - // Tests to make sure accessing the socket proxy fails with an - // informative error. - setImmediate(() => { - expect(() => { - socket.example; // eslint-disable-line no-unused-expressions - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_SOCKET_UNBOUND' - })); - - expect(() => { - socket.example = 1; - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_SOCKET_UNBOUND' - })); - - expect(() => { - // eslint-disable-next-line no-unused-expressions - socket instanceof net.Socket; - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_SOCKET_UNBOUND' - })); - - expect(streamHandler).toHaveBeenCalled(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-unbound-socket-proxy.js diff --git a/test/js/node/test/parallel/http2-util-assert-valid-pseudoheader.test.js b/test/js/node/test/parallel/http2-util-assert-valid-pseudoheader.test.js deleted file mode 100644 index 42f0ccf3c2..0000000000 --- a/test/js/node/test/parallel/http2-util-assert-valid-pseudoheader.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-http2-util-assert-valid-pseudoheader.js -//#SHA1: 765cdbf9a64c432ef1706fb7b24ab35d926cda3b -//----------------- -'use strict'; - -let mapToHeaders; - -beforeAll(() => { - try { - // Try to require the internal module - ({ mapToHeaders } = require('internal/http2/util')); - } catch (error) { - // If the internal module is not available, mock it - mapToHeaders = jest.fn((headers) => { - const validPseudoHeaders = [':status', ':path', ':authority', ':scheme', ':method']; - for (const key in headers) { - if (key.startsWith(':') && !validPseudoHeaders.includes(key)) { - throw new TypeError(`"${key}" is an invalid pseudoheader or is used incorrectly`); - } - } - }); - } -}); - -describe('HTTP/2 Util - assertValidPseudoHeader', () => { - test('should not throw for valid pseudo-headers', () => { - expect(() => mapToHeaders({ ':status': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':path': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':authority': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':scheme': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':method': 'a' })).not.toThrow(); - }); - - test('should throw for invalid pseudo-headers', () => { - expect(() => mapToHeaders({ ':foo': 'a' })).toThrow(expect.objectContaining({ - name: 'TypeError', - message: expect.stringContaining('is an invalid pseudoheader or is used incorrectly') - })); - }); -}); - -//<#END_FILE: test-http2-util-assert-valid-pseudoheader.js diff --git a/test/js/node/test/parallel/http2-util-update-options-buffer.test.js b/test/js/node/test/parallel/http2-util-update-options-buffer.test.js deleted file mode 100644 index d83855aa28..0000000000 --- a/test/js/node/test/parallel/http2-util-update-options-buffer.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-http2-util-update-options-buffer.js -//#SHA1: f1d75eaca8be74152cd7eafc114815b5d59d7f0c -//----------------- -'use strict'; - -test('Skip: HTTP/2 util update options buffer test', () => { - console.log('This test is skipped because it relies on Node.js internals that are not easily accessible in a Jest environment.'); - expect(true).toBe(true); -}); - -//<#END_FILE: test-http2-util-update-options-buffer.js diff --git a/test/js/node/test/parallel/http2-write-callbacks.test.js b/test/js/node/test/parallel/http2-write-callbacks.test.js deleted file mode 100644 index 2aa826a373..0000000000 --- a/test/js/node/test/parallel/http2-write-callbacks.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-http2-write-callbacks.js -//#SHA1: 4ad84acd162dcde6c2fbe344e6da2a3ec225edc1 -//----------------- -"use strict"; - -const http2 = require("http2"); - -// Mock for common.mustCall -const mustCall = fn => { - const wrappedFn = jest.fn(fn); - return wrappedFn; -}; - -describe("HTTP/2 write callbacks", () => { - let server; - let client; - let port; - - beforeAll(done => { - server = http2.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("write callbacks are called", done => { - const serverWriteCallback = mustCall(() => {}); - const clientWriteCallback = mustCall(() => {}); - - server.once("stream", stream => { - stream.write("abc", serverWriteCallback); - stream.end("xyz"); - - let actual = ""; - stream.setEncoding("utf8"); - stream.on("data", chunk => (actual += chunk)); - stream.on("end", () => { - expect(actual).toBe("abcxyz"); - }); - }); - - client = http2.connect(`http://localhost:${port}`); - const req = client.request({ ":method": "POST" }); - - req.write("abc", clientWriteCallback); - req.end("xyz"); - - let actual = ""; - req.setEncoding("utf8"); - req.on("data", chunk => (actual += chunk)); - req.on("end", () => { - expect(actual).toBe("abcxyz"); - }); - - req.on("close", () => { - client.close(); - - // Check if callbacks were called - expect(serverWriteCallback).toHaveBeenCalled(); - expect(clientWriteCallback).toHaveBeenCalled(); - - done(); - }); - }); -}); - -//<#END_FILE: test-http2-write-callbacks.js diff --git a/test/js/node/test/parallel/http2-write-empty-string.test.js b/test/js/node/test/parallel/http2-write-empty-string.test.js deleted file mode 100644 index ca1e65b234..0000000000 --- a/test/js/node/test/parallel/http2-write-empty-string.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-http2-write-empty-string.js -//#SHA1: 59ba4a8a3c63aad827770d96f668922107ed2f2f -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -let http2Server; -beforeAll(() => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - } -}); - -afterAll(() => { - if (http2Server) { - http2Server.close(); - } -}); - -test('HTTP/2 server writes empty strings correctly', async () => { - http2Server = http2.createServer((request, response) => { - response.writeHead(200, { 'Content-Type': 'text/plain' }); - response.write('1\n'); - response.write(''); - response.write('2\n'); - response.write(''); - response.end('3\n'); - }); - - await new Promise(resolve => { - http2Server.listen(0, resolve); - }); - - const port = http2Server.address().port; - const client = http2.connect(`http://localhost:${port}`); - const headers = { ':path': '/' }; - - const responsePromise = new Promise((resolve, reject) => { - const req = client.request(headers); - - let res = ''; - req.setEncoding('ascii'); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - }); - - req.on('data', (chunk) => { - res += chunk; - }); - - req.on('end', () => { - resolve(res); - }); - - req.on('error', reject); - - req.end(); - }); - - const response = await responsePromise; - expect(response).toBe('1\n2\n3\n'); - - await new Promise(resolve => client.close(resolve)); -}); - -//<#END_FILE: test-http2-write-empty-string.js diff --git a/test/js/node/test/parallel/http2-zero-length-header.test.js b/test/js/node/test/parallel/http2-zero-length-header.test.js deleted file mode 100644 index aef1d62dbf..0000000000 --- a/test/js/node/test/parallel/http2-zero-length-header.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http2-zero-length-header.js -//#SHA1: 65bd4ca954be7761c2876b26c6ac5d3f0e5c98e4 -//----------------- -"use strict"; -const http2 = require("http2"); - -// Skip test if crypto is not available -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -(hasCrypto ? describe : describe.skip)("http2 zero length header", () => { - let server; - let port; - - beforeAll(async () => { - server = http2.createServer(); - await new Promise(resolve => server.listen(0, resolve)); - port = server.address().port; - }); - - afterAll(() => { - server.close(); - }); - - test("server receives correct headers", async () => { - const serverPromise = new Promise(resolve => { - server.once("stream", (stream, headers) => { - expect(headers).toEqual({ - ":scheme": "http", - ":authority": `localhost:${port}`, - ":method": "GET", - ":path": "/", - "bar": "", - "__proto__": null, - [http2.sensitiveHeaders]: [], - }); - stream.session.destroy(); - resolve(); - }); - }); - - const client = http2.connect(`http://localhost:${port}/`); - client.request({ ":path": "/", "": "foo", "bar": "" }).end(); - - await serverPromise; - client.close(); - }); -}); - -//<#END_FILE: test-http2-zero-length-header.js diff --git a/test/js/node/test/parallel/http2-zero-length-write.test.js b/test/js/node/test/parallel/http2-zero-length-write.test.js deleted file mode 100644 index dbd25616c5..0000000000 --- a/test/js/node/test/parallel/http2-zero-length-write.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-http2-zero-length-write.js -//#SHA1: a948a83af3675490313ff7b33a36d2c12cdd2837 -//----------------- -"use strict"; - -const http2 = require("http2"); -const { Readable } = require("stream"); - -function getSrc() { - const chunks = ["", "asdf", "", "foo", "", "bar", ""]; - return new Readable({ - read() { - const chunk = chunks.shift(); - if (chunk !== undefined) this.push(chunk); - else this.push(null); - }, - }); -} - -const expectedOutput = "asdffoobar"; - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - test.skip("missing crypto"); - } -}); - -afterEach(() => { - if (client) client.close(); - if (server) server.close(); -}); - -test("HTTP/2 zero length write", async () => { - return new Promise((resolve, reject) => { - server = http2.createServer(); - server.on("stream", stream => { - let actual = ""; - stream.respond(); - stream.resume(); - stream.setEncoding("utf8"); - stream.on("data", chunk => (actual += chunk)); - stream.on("end", () => { - getSrc().pipe(stream); - expect(actual).toBe(expectedOutput); - }); - }); - - server.listen(0, () => { - client = http2.connect(`http://localhost:${server.address().port}`); - let actual = ""; - const req = client.request({ ":method": "POST" }); - req.on("response", () => {}); - req.setEncoding("utf8"); - req.on("data", chunk => (actual += chunk)); - - req.on("end", () => { - expect(actual).toBe(expectedOutput); - resolve(); - }); - getSrc().pipe(req); - }); - }); -}, 10000); // Increase timeout to 10 seconds - -//<#END_FILE: test-http2-zero-length-write.js diff --git a/test/js/node/test/parallel/https-agent-constructor.test.js b/test/js/node/test/parallel/https-agent-constructor.test.js deleted file mode 100644 index dc3079eddd..0000000000 --- a/test/js/node/test/parallel/https-agent-constructor.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-https-agent-constructor.js -//#SHA1: 6b63dcb4d1a1a60f19fbb26cb555013821af5791 -//----------------- -"use strict"; - -if (!process.versions.openssl) { - test.skip("missing crypto"); -} - -const https = require("https"); - -test("https.Agent constructor", () => { - expect(new https.Agent()).toBeInstanceOf(https.Agent); - expect(https.Agent()).toBeInstanceOf(https.Agent); -}); - -//<#END_FILE: test-https-agent-constructor.js diff --git a/test/js/node/test/parallel/https-agent.test.js b/test/js/node/test/parallel/https-agent.test.js deleted file mode 100644 index 31b1e0ee25..0000000000 --- a/test/js/node/test/parallel/https-agent.test.js +++ /dev/null @@ -1,106 +0,0 @@ -//#FILE: test-https-agent.js -//#SHA1: 1348abc863ae99725dd893838c95b42c5120a052 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const https = require("https"); -const { readKey } = require("../common/fixtures"); - -const options = { - key: readKey("agent1-key.pem"), - cert: readKey("agent1-cert.pem"), -}; - -const N = 4; -const M = 4; - -let server; -let responses = 0; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } -}); - -beforeEach(() => { - return new Promise(resolve => { - server = https.createServer(options, (req, res) => { - res.writeHead(200); - res.end("hello world\n"); - }); - - server.listen(0, () => { - resolve(); - }); - }); -}); - -afterEach(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -test("HTTPS Agent handles multiple concurrent requests", async () => { - const makeRequests = i => { - return new Promise(resolve => { - setTimeout(() => { - const requests = Array.from( - { length: M }, - () => - new Promise(innerResolve => { - https - .get( - { - path: "/", - port: server.address().port, - rejectUnauthorized: false, - }, - function (res) { - res.resume(); - expect(res.statusCode).toBe(200); - responses++; - innerResolve(); - }, - ) - .on("error", e => { - throw e; - }); - }), - ); - Promise.all(requests).then(resolve); - }, i); - }); - }; - - const allRequests = Array.from({ length: N }, (_, i) => makeRequests(i)); - await Promise.all(allRequests); - - expect(responses).toBe(N * M); -}); - -//<#END_FILE: test-https-agent.js diff --git a/test/js/node/test/parallel/https-foafssl.test.js b/test/js/node/test/parallel/https-foafssl.test.js deleted file mode 100644 index aac467af04..0000000000 --- a/test/js/node/test/parallel/https-foafssl.test.js +++ /dev/null @@ -1,114 +0,0 @@ -//#FILE: test-https-foafssl.js -//#SHA1: 07ac711f5948207540af7366d06803f2675f04c7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { skip } = require("../common"); -const fixtures = require("../common/fixtures"); -const https = require("https"); -const { spawn } = require("child_process"); - -if (!process.versions.openssl) { - skip("missing crypto"); -} - -if (!process.env.NODE_OPENSSL_CERT) { - skip("node compiled without OpenSSL CLI."); -} - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), - requestCert: true, - rejectUnauthorized: false, -}; - -const webIdUrl = "URI:http://example.com/#me"; -const modulus = fixtures.readKey("rsa_cert_foafssl_b.modulus", "ascii").replace(/\n/g, ""); -const exponent = fixtures.readKey("rsa_cert_foafssl_b.exponent", "ascii").replace(/\n/g, ""); - -const CRLF = "\r\n"; -const body = "hello world\n"; -let cert; - -test("HTTPS FOAFSSL", async () => { - const serverHandler = jest.fn((req, res) => { - console.log("got request"); - - cert = req.connection.getPeerCertificate(); - - expect(cert.subjectaltname).toBe(webIdUrl); - expect(cert.exponent).toBe(exponent); - expect(cert.modulus).toBe(modulus); - res.writeHead(200, { "content-type": "text/plain" }); - res.end(body, () => { - console.log("stream finished"); - }); - console.log("sent response"); - }); - - const server = https.createServer(options, serverHandler); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - const args = [ - "s_client", - "-quiet", - "-connect", - `127.0.0.1:${port}`, - "-cert", - fixtures.path("keys/rsa_cert_foafssl_b.crt"), - "-key", - fixtures.path("keys/rsa_private_b.pem"), - ]; - - const client = spawn(process.env.NODE_OPENSSL_CERT, args); - - client.stdout.on("data", data => { - console.log("response received"); - const message = data.toString(); - const contents = message.split(CRLF + CRLF).pop(); - expect(contents).toBe(body); - server.close(e => { - expect(e).toBeFalsy(); - console.log("server closed"); - }); - console.log("server.close() called"); - }); - - client.stdin.write("GET /\r\n\r\n"); - - await new Promise((resolve, reject) => { - client.on("error", reject); - client.on("close", resolve); - }); - - expect(serverHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-https-foafssl.js diff --git a/test/js/node/test/parallel/https-socket-options.test.js b/test/js/node/test/parallel/https-socket-options.test.js deleted file mode 100644 index 5e325acc5d..0000000000 --- a/test/js/node/test/parallel/https-socket-options.test.js +++ /dev/null @@ -1,102 +0,0 @@ -//#FILE: test-https-socket-options.js -//#SHA1: 8f63b3c65f69e8b766b159d148e681984c134477 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fixtures = require("../common/fixtures"); -const https = require("https"); -const http = require("http"); - -const options = { - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), -}; - -const body = "hello world\n"; - -test("HTTP server socket options", async () => { - const server_http = http.createServer((req, res) => { - console.log("got HTTP request"); - res.writeHead(200, { "content-type": "text/plain" }); - res.end(body); - }); - - await new Promise(resolve => { - server_http.listen(0, () => { - const req = http.request( - { - port: server_http.address().port, - rejectUnauthorized: false, - }, - res => { - server_http.close(); - res.resume(); - resolve(); - }, - ); - // These methods should exist on the request and get passed down to the socket - expect(req.setNoDelay).toBeDefined(); - expect(req.setTimeout).toBeDefined(); - expect(req.setSocketKeepAlive).toBeDefined(); - req.setNoDelay(true); - req.setTimeout(1000, () => {}); - req.setSocketKeepAlive(true, 1000); - req.end(); - }); - }); -}); - -test("HTTPS server socket options", async () => { - const server_https = https.createServer(options, (req, res) => { - console.log("got HTTPS request"); - res.writeHead(200, { "content-type": "text/plain" }); - res.end(body); - }); - - await new Promise(resolve => { - server_https.listen(0, () => { - const req = https.request( - { - port: server_https.address().port, - rejectUnauthorized: false, - }, - res => { - server_https.close(); - res.resume(); - resolve(); - }, - ); - // These methods should exist on the request and get passed down to the socket - expect(req.setNoDelay).toBeDefined(); - expect(req.setTimeout).toBeDefined(); - expect(req.setSocketKeepAlive).toBeDefined(); - req.setNoDelay(true); - req.setTimeout(1000, () => {}); - req.setSocketKeepAlive(true, 1000); - req.end(); - }); - }); -}); - -//<#END_FILE: test-https-socket-options.js diff --git a/test/js/node/test/parallel/inspect-publish-uid.test.js b/test/js/node/test/parallel/inspect-publish-uid.test.js deleted file mode 100644 index 09ec36dcd3..0000000000 --- a/test/js/node/test/parallel/inspect-publish-uid.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-inspect-publish-uid.js -//#SHA1: cd6577ea81261e5e89b5ec6272e27f5a0614ffcf -//----------------- -"use strict"; - -const { spawnSync } = require("child_process"); -const inspector = require("inspector"); -const http = require("http"); -const url = require("url"); - -// Skip the test if inspector is disabled -if (!inspector.url()) { - test.skip("Inspector is disabled", () => {}); -} else { - test("Checks stderr", async () => { - await testArg("stderr"); - }); - - test("Checks http", async () => { - await testArg("http"); - }); - - test("Checks http,stderr", async () => { - await testArg("http,stderr"); - }); -} - -async function testArg(argValue) { - console.log("Checks " + argValue + ".."); - const hasHttp = argValue.split(",").includes("http"); - const hasStderr = argValue.split(",").includes("stderr"); - - const nodeProcess = spawnSync(process.execPath, [ - "--inspect=0", - `--inspect-publish-uid=${argValue}`, - "-e", - `(${scriptMain.toString()})(${hasHttp ? 200 : 404})`, - ]); - const hasWebSocketInStderr = checkStdError(nodeProcess.stderr.toString("utf8")); - expect(hasWebSocketInStderr).toBe(hasStderr); -} - -function checkStdError(data) { - const matches = data.toString("utf8").match(/ws:\/\/.+:(\d+)\/.+/); - return !!matches; -} - -function scriptMain(code) { - const inspectorUrl = inspector.url(); - const { host } = url.parse(inspectorUrl); - http.get("http://" + host + "/json/list", response => { - expect(response.statusCode).toBe(code); - response.destroy(); - }); -} - -//<#END_FILE: test-inspect-publish-uid.js diff --git a/test/js/node/test/parallel/inspect-support-for-node_options.test.js b/test/js/node/test/parallel/inspect-support-for-node_options.test.js deleted file mode 100644 index 77187f7aa6..0000000000 --- a/test/js/node/test/parallel/inspect-support-for-node_options.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-inspect-support-for-node_options.js -//#SHA1: 622a8cb07922833373b0d88c42c2a7bbfdc7d58e -//----------------- -"use strict"; - -const cluster = require("cluster"); - -// Skip if inspector is disabled -if (process.config.variables.v8_enable_inspector === 0) { - test.skip("Inspector is disabled", () => {}); -} else { - checkForInspectSupport("--inspect"); -} - -function checkForInspectSupport(flag) { - const nodeOptions = JSON.stringify(flag); - const numWorkers = 2; - process.env.NODE_OPTIONS = flag; - - test(`Cluster support for NODE_OPTIONS ${nodeOptions}`, () => { - if (cluster.isPrimary) { - const workerExitPromises = []; - - for (let i = 0; i < numWorkers; i++) { - const worker = cluster.fork(); - - worker.on("online", () => { - worker.disconnect(); - }); - - workerExitPromises.push( - new Promise(resolve => { - worker.on("exit", (code, signal) => { - expect(worker.exitedAfterDisconnect).toBe(true); - resolve(); - }); - }), - ); - } - - return Promise.all(workerExitPromises); - } - }); -} - -//<#END_FILE: test-inspect-support-for-node_options.js diff --git a/test/js/node/test/parallel/internal-fs.test.js b/test/js/node/test/parallel/internal-fs.test.js deleted file mode 100644 index 7b84ec5156..0000000000 --- a/test/js/node/test/parallel/internal-fs.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-internal-fs.js -//#SHA1: 47b2f898d6c0cdfba71a1f82b7617f466eb475c9 -//----------------- -'use strict'; - -const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); - -// We can't use internal modules in this test, so we'll mock the necessary functions -const mockFs = { - assertEncoding: (encoding) => { - if (encoding && !['utf8', 'utf-8', 'ascii', 'utf16le', 'ucs2', 'ucs-2', 'base64', 'base64url', 'latin1', 'binary', 'hex'].includes(encoding.toLowerCase())) { - throw new TypeError('ERR_INVALID_ARG_VALUE'); - } - }, - preprocessSymlinkDestination: (pathString, type, linkPathString) => { - if (process.platform === 'win32' && type === 'junction') { - return path.join('\\\\?\\', pathString); - } - return pathString; - } -}; - -test('assertEncoding should not throw for valid encodings', () => { - expect(() => mockFs.assertEncoding()).not.toThrow(); - expect(() => mockFs.assertEncoding('utf8')).not.toThrow(); -}); - -test('assertEncoding should throw for invalid encodings', () => { - expect(() => mockFs.assertEncoding('foo')).toThrow(expect.objectContaining({ - name: 'TypeError', - message: expect.stringContaining('ERR_INVALID_ARG_VALUE') - })); -}); - -test('preprocessSymlinkDestination for junction symlinks', () => { - const pathString = 'c:\\test1'; - const linkPathString = '\\test2'; - - const preprocessSymlinkDestination = mockFs.preprocessSymlinkDestination( - pathString, - 'junction', - linkPathString - ); - - if (process.platform === 'win32') { - expect(preprocessSymlinkDestination).toMatch(/^\\\\\?\\/); - } else { - expect(preprocessSymlinkDestination).toBe(pathString); - } -}); - -test('preprocessSymlinkDestination for non-junction symlinks', () => { - const pathString = 'c:\\test1'; - const linkPathString = '\\test2'; - - const preprocessSymlinkDestination = mockFs.preprocessSymlinkDestination( - pathString, - undefined, - linkPathString - ); - - if (process.platform === 'win32') { - expect(preprocessSymlinkDestination).not.toMatch(/\//); - } else { - expect(preprocessSymlinkDestination).toBe(pathString); - } -}); - -//<#END_FILE: test-internal-fs.js diff --git a/test/js/node/test/parallel/internal-process-binding.test.js b/test/js/node/test/parallel/internal-process-binding.test.js deleted file mode 100644 index e3b90d28a4..0000000000 --- a/test/js/node/test/parallel/internal-process-binding.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-internal-process-binding.js -//#SHA1: e14c48cb6cd21ab499bd5d72cf8c8d0cddccf767 -//----------------- -"use strict"; - -test("process internal binding", () => { - expect(process._internalBinding).toBeUndefined(); - expect(process.internalBinding).toBeUndefined(); - expect(() => { - process.binding("module_wrap"); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-internal-process-binding.js diff --git a/test/js/node/test/parallel/intl-v8breakiterator.test.js b/test/js/node/test/parallel/intl-v8breakiterator.test.js deleted file mode 100644 index 7bf86915d2..0000000000 --- a/test/js/node/test/parallel/intl-v8breakiterator.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-intl-v8BreakIterator.js -//#SHA1: c1592e4a1a3d4971e70d4b2bc30e31bb157f8646 -//----------------- -"use strict"; - -if (!globalThis.Intl) { - test.skip("missing Intl"); -} - -test("v8BreakIterator is not in Intl", () => { - expect("v8BreakIterator" in Intl).toBe(false); -}); - -test("v8BreakIterator is not in Intl in a new context", () => { - const vm = require("vm"); - expect(vm.runInNewContext('"v8BreakIterator" in Intl')).toBe(false); -}); - -//<#END_FILE: test-intl-v8BreakIterator.js diff --git a/test/js/node/test/parallel/jest.config.js b/test/js/node/test/parallel/jest.config.js deleted file mode 100644 index 7f2c94ff64..0000000000 --- a/test/js/node/test/parallel/jest.config.js +++ /dev/null @@ -1,2 +0,0 @@ -// So jest doesn't try to look up. -module.exports = {}; diff --git a/test/js/node/test/parallel/js-stream-call-properties.test.js b/test/js/node/test/parallel/js-stream-call-properties.test.js deleted file mode 100644 index 070bd47e99..0000000000 --- a/test/js/node/test/parallel/js-stream-call-properties.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-js-stream-call-properties.js -//#SHA1: 491c3447495fadda8e713d00b9621ea31d8fb27d -//----------------- -"use strict"; - -test("JSStream properties can be inspected", () => { - // We can't use internal bindings in Jest, so we'll mock the JSStream - class MockJSStream { - constructor() { - // Add some properties that might be inspected - this.readableFlowing = null; - this.writableFinished = false; - // Add more properties as needed - } - } - - // Mock util.inspect to ensure it's called - const mockInspect = jest.fn(); - jest.spyOn(console, "log").mockImplementation(mockInspect); - - // Create an instance of our mock JSStream - const jsStream = new MockJSStream(); - - // Call console.log, which will internally call util.inspect - console.log(jsStream); - - // Verify that inspect was called - expect(mockInspect).toHaveBeenCalledTimes(1); - expect(mockInspect).toHaveBeenCalledWith(expect.any(MockJSStream)); - - // Clean up - console.log.mockRestore(); -}); - -//<#END_FILE: test-js-stream-call-properties.js diff --git a/test/js/node/test/parallel/kill-segfault-freebsd.test.js b/test/js/node/test/parallel/kill-segfault-freebsd.test.js deleted file mode 100644 index 786d3f4dbf..0000000000 --- a/test/js/node/test/parallel/kill-segfault-freebsd.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-kill-segfault-freebsd.js -//#SHA1: ac3b65d8e5e92ffb9714307d2249b4c3b9240ed9 -//----------------- -"use strict"; - -// This test ensures Node.js doesn't crash on hitting Ctrl+C in order to -// terminate the currently running process (especially on FreeBSD). -// https://github.com/nodejs/node-v0.x-archive/issues/9326 - -const child_process = require("child_process"); - -test("Node.js doesn't crash on SIGINT (FreeBSD issue)", done => { - // NOTE: Was crashing on FreeBSD - const cp = child_process.spawn(process.execPath, ["-e", 'process.kill(process.pid, "SIGINT")']); - - cp.on("exit", function (code) { - expect(code).not.toBe(0); - done(); - }); -}); - -//<#END_FILE: test-kill-segfault-freebsd.js diff --git a/test/js/node/test/parallel/listen-fd-detached-inherit.test.js b/test/js/node/test/parallel/listen-fd-detached-inherit.test.js deleted file mode 100644 index ba5323fbdc..0000000000 --- a/test/js/node/test/parallel/listen-fd-detached-inherit.test.js +++ /dev/null @@ -1,137 +0,0 @@ -//#FILE: test-listen-fd-detached-inherit.js -//#SHA1: 4aab69e9262c853cc790bd9de1fc3cf9529baa01 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const http = require("http"); -const net = require("net"); -const { spawn } = require("child_process"); - -if (process.platform === "win32") { - test.skip("This test is disabled on windows."); -} - -switch (process.argv[2]) { - case "child": - child(); - break; - case "parent": - parent(); - break; - default: - runTest(); -} - -// Spawn the parent, and listen for it to tell us the pid of the child. -// WARNING: This is an example of listening on some arbitrary FD number -// that has already been bound elsewhere in advance. However, binding -// server handles to stdio fd's is NOT a good or reliable way to do -// concurrency in HTTP servers! Use the cluster module, or if you want -// a more low-level approach, use child process IPC manually. -async function runTest() { - const parent = spawn(process.execPath, [__filename, "parent"], { - stdio: [0, "pipe", 2], - }); - - let json = ""; - for await (const chunk of parent.stdout) { - json += chunk.toString(); - if (json.includes("\n")) break; - } - - console.error("output from parent = %s", json); - const child = JSON.parse(json); - - // Now make sure that we can request to the subprocess, then kill it. - const response = await new Promise(resolve => { - http.get( - { - hostname: "localhost", - port: child.port, - path: "/", - }, - resolve, - ); - }); - - let responseBody = ""; - for await (const chunk of response) { - responseBody += chunk.toString(); - } - - // Kill the subprocess before we start doing asserts. - // It's really annoying when tests leave orphans! - process.kill(child.pid, "SIGKILL"); - try { - parent.kill(); - } catch { - // Continue regardless of error. - } - - expect(responseBody).toBe("hello from child\n"); - expect(response.statusCode).toBe(200); -} - -// Listen on port, and then pass the handle to the detached child. -// Then output the child's pid, and immediately exit. -function parent() { - const server = net - .createServer(conn => { - conn.end("HTTP/1.1 403 Forbidden\r\n\r\nI got problems.\r\n"); - throw new Error("Should not see connections on parent"); - }) - .listen(0, function () { - console.error("server listening on %d", this.address().port); - - const child = spawn(process.execPath, [__filename, "child"], { - stdio: [0, 1, 2, server._handle], - detached: true, - }); - - console.log("%j\n", { pid: child.pid, port: this.address().port }); - - // Now close the parent, so that the child is the only thing - // referencing that handle. Note that connections will still - // be accepted, because the child has the fd open, but the parent - // will exit gracefully. - server.close(); - child.unref(); - }); -} - -// Run as a child of the parent() mode. -function child() { - // Start a server on fd=3 - http - .createServer((req, res) => { - console.error("request on child"); - console.error("%s %s", req.method, req.url, req.headers); - res.end("hello from child\n"); - }) - .listen({ fd: 3 }, () => { - console.error("child listening on fd=3"); - }); -} - -//<#END_FILE: test-listen-fd-detached-inherit.js diff --git a/test/js/node/test/parallel/memory-usage-emfile.test.js b/test/js/node/test/parallel/memory-usage-emfile.test.js deleted file mode 100644 index 6e8cb22c8d..0000000000 --- a/test/js/node/test/parallel/memory-usage-emfile.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-memory-usage-emfile.js -//#SHA1: 062c0483d1da90d9e08bab6a7d006d2da5861bd9 -//----------------- -"use strict"; - -// On IBMi, the rss memory always returns zero -if (process.platform === "os400") { - test.skip("On IBMi, the rss memory always returns zero", () => {}); -} else { - const fs = require("fs"); - - test("memory usage with many open files", () => { - const files = []; - - while (files.length < 256) files.push(fs.openSync(__filename, "r")); - - const r = process.memoryUsage.rss(); - expect(r).toBeGreaterThan(0); - - // Clean up opened files - files.forEach(fd => fs.closeSync(fd)); - }); -} - -//<#END_FILE: test-memory-usage-emfile.js diff --git a/test/js/node/test/parallel/memory-usage.test.js b/test/js/node/test/parallel/memory-usage.test.js deleted file mode 100644 index 74c73896df..0000000000 --- a/test/js/node/test/parallel/memory-usage.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-memory-usage.js -//#SHA1: fffba1b4ff9ad7092d9a8f51b2799a0606d769eb -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Flags: --predictable-gc-schedule -"use strict"; - -const isIBMi = process.platform === "aix" && process.execPath.includes("powerpc"); - -test("memory usage", () => { - const r = process.memoryUsage(); - // On IBMi, the rss memory always returns zero - if (!isIBMi) { - expect(r.rss).toBeGreaterThan(0); - expect(process.memoryUsage.rss()).toBeGreaterThan(0); - } - - expect(r.heapTotal).toBeGreaterThan(0); - expect(r.heapUsed).toBeGreaterThan(0); - expect(r.external).toBeGreaterThan(0); - - expect(typeof r.arrayBuffers).toBe("number"); - if (r.arrayBuffers > 0) { - const size = 10 * 1024 * 1024; - // eslint-disable-next-line no-unused-vars - const ab = new ArrayBuffer(size); - - const after = process.memoryUsage(); - expect(after.external - r.external).toBeGreaterThanOrEqual(size); - expect(after.arrayBuffers - r.arrayBuffers).toBe(size); - } -}); - -//<#END_FILE: test-memory-usage.js diff --git a/test/js/node/test/parallel/messageevent-brandcheck.test.js b/test/js/node/test/parallel/messageevent-brandcheck.test.js deleted file mode 100644 index ba2dd9c11a..0000000000 --- a/test/js/node/test/parallel/messageevent-brandcheck.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-messageevent-brandcheck.js -//#SHA1: 1b04c8b7c45fe0f2fe12018ca10137eefa892b4c -//----------------- -"use strict"; - -test("MessageEvent brand checks", () => { - ["data", "origin", "lastEventId", "source", "ports"].forEach(prop => { - expect(() => Reflect.get(MessageEvent.prototype, prop, {})).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-messageevent-brandcheck.js diff --git a/test/js/node/test/parallel/module-builtin.test.js b/test/js/node/test/parallel/module-builtin.test.js deleted file mode 100644 index cc9f208d38..0000000000 --- a/test/js/node/test/parallel/module-builtin.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-module-builtin.js -//#SHA1: 18114886f66eccc937942a815feca25d9b324a37 -//----------------- -"use strict"; - -test("builtinModules", () => { - const { builtinModules } = require("module"); - - // Includes modules in lib/ (even deprecated ones) - expect(builtinModules).toContain("http"); - expect(builtinModules).toContain("sys"); - - // Does not include internal modules - expect(builtinModules.filter(mod => mod.startsWith("internal/"))).toEqual([]); -}); - -//<#END_FILE: test-module-builtin.js diff --git a/test/js/node/test/parallel/module-cache.test.js b/test/js/node/test/parallel/module-cache.test.js deleted file mode 100644 index 605b768808..0000000000 --- a/test/js/node/test/parallel/module-cache.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-module-cache.js -//#SHA1: ff0f4c6ca37e23c009f98bba966e9daee2dcaef6 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-module-cache-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("throws MODULE_NOT_FOUND when file does not exist", () => { - const filePath = path.join(tmpdir, "test-module-cache.json"); - expect(() => require(filePath)).toThrow( - expect.objectContaining({ - code: "MODULE_NOT_FOUND", - message: expect.any(String), - }), - ); -}); - -test("requires JSON file successfully after creation", () => { - const filePath = path.join(tmpdir, "test-module-cache.json"); - fs.writeFileSync(filePath, "[]"); - - const content = require(filePath); - expect(Array.isArray(content)).toBe(true); - expect(content.length).toBe(0); -}); - -//<#END_FILE: test-module-cache.js diff --git a/test/js/node/test/parallel/module-main-extension-lookup.test.js b/test/js/node/test/parallel/module-main-extension-lookup.test.js deleted file mode 100644 index 741f721bc6..0000000000 --- a/test/js/node/test/parallel/module-main-extension-lookup.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-module-main-extension-lookup.js -//#SHA1: d50be34ba21e1e14de5225ac5d93b6fe20505014 -//----------------- -"use strict"; - -const path = require("path"); -const { execFileSync } = require("child_process"); - -const node = process.argv[0]; - -test("ES modules extension lookup", () => { - const fixturesPath = path.resolve(__dirname, "..", "fixtures"); - - expect(() => { - execFileSync(node, [path.join(fixturesPath, "es-modules", "test-esm-ok.mjs")]); - }).not.toThrow(); - - expect(() => { - execFileSync(node, [path.join(fixturesPath, "es-modules", "noext")]); - }).not.toThrow(); -}); - -//<#END_FILE: test-module-main-extension-lookup.js diff --git a/test/js/node/test/parallel/net-after-close.test.js b/test/js/node/test/parallel/net-after-close.test.js deleted file mode 100644 index 5d2248cc5e..0000000000 --- a/test/js/node/test/parallel/net-after-close.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-net-after-close.js -//#SHA1: 5b16857d2580262739b7c74c87a520ee6fc974c9 -//----------------- -"use strict"; -const net = require("net"); - -let server; -let serverPort; - -beforeAll(done => { - server = net.createServer(s => { - s.end(); - }); - - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); -}); - -afterAll(done => { - server.close(done); -}); - -test("net socket behavior after close", done => { - const c = net.createConnection(serverPort); - - c.on("close", () => { - expect(c._handle).toBeNull(); - - // Calling functions / accessing properties of a closed socket should not throw. - expect(() => { - c.setNoDelay(); - c.setKeepAlive(); - c.bufferSize; - c.pause(); - c.resume(); - c.address(); - c.remoteAddress; - c.remotePort; - }).not.toThrow(); - - done(); - }); -}); - -//<#END_FILE: test-net-after-close.js diff --git a/test/js/node/test/parallel/net-allow-half-open.test.js b/test/js/node/test/parallel/net-allow-half-open.test.js deleted file mode 100644 index 0b05942eeb..0000000000 --- a/test/js/node/test/parallel/net-allow-half-open.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-net-allow-half-open.js -//#SHA1: 713191e6681104ac9709a51cbe5dc881f7a7fa89 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net allow half open', () => { - test('Socket not destroyed immediately after end', (done) => { - const server = net.createServer((socket) => { - socket.end(Buffer.alloc(1024)); - }); - - server.listen(0, () => { - const socket = net.connect(server.address().port); - expect(socket.allowHalfOpen).toBe(false); - socket.resume(); - - socket.on('end', () => { - process.nextTick(() => { - // Ensure socket is not destroyed straight away - // without proper shutdown. - expect(socket.destroyed).toBe(false); - server.close(); - done(); - }); - }); - - socket.on('finish', () => { - expect(socket.destroyed).toBe(false); - }); - - socket.on('close', () => {}); - }); - }); - - test('Socket not destroyed after end and write', (done) => { - const server = net.createServer((socket) => { - socket.end(Buffer.alloc(1024)); - }); - - server.listen(0, () => { - const socket = net.connect(server.address().port); - expect(socket.allowHalfOpen).toBe(false); - socket.resume(); - - socket.on('end', () => { - expect(socket.destroyed).toBe(false); - }); - - socket.end('asd'); - - socket.on('finish', () => { - expect(socket.destroyed).toBe(false); - }); - - socket.on('close', () => { - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-allow-half-open.js diff --git a/test/js/node/test/parallel/net-autoselectfamily-attempt-timeout-default-value.test.js b/test/js/node/test/parallel/net-autoselectfamily-attempt-timeout-default-value.test.js deleted file mode 100644 index 5b33636b4d..0000000000 --- a/test/js/node/test/parallel/net-autoselectfamily-attempt-timeout-default-value.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-net-autoselectfamily-attempt-timeout-default-value.js -//#SHA1: 028b16515c47d987e68ca138e753ed4d255f179c -//----------------- -"use strict"; - -const { platformTimeout } = require("../common"); -const { getDefaultAutoSelectFamilyAttemptTimeout } = require("net"); - -test("getDefaultAutoSelectFamilyAttemptTimeout returns the correct default value", () => { - expect(getDefaultAutoSelectFamilyAttemptTimeout()).toBe(platformTimeout(2500)); -}); - -//<#END_FILE: test-net-autoselectfamily-attempt-timeout-default-value.js diff --git a/test/js/node/test/parallel/net-bind-twice-exclusive.test.js b/test/js/node/test/parallel/net-bind-twice-exclusive.test.js deleted file mode 100644 index 9854878479..0000000000 --- a/test/js/node/test/parallel/net-bind-twice-exclusive.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-net-bind-twice.js -//#SHA1: 432eb9529d0affc39c8af9ebc1147528d96305c9 -//----------------- -"use strict"; -const net = require("net"); - -test("net.Server should not allow binding to the same port twice", done => { - const server1 = net.createServer(() => { - throw new Error("Server1 should not receive connections"); - }); - - server1.listen( - { - exclusive: true, - port: 0, - host: "127.0.0.1", - }, - () => { - const server2 = net.createServer(() => { - throw new Error("Server2 should not receive connections"); - }); - - const port = server1.address().port; - server2.listen(port, "127.0.0.1", () => { - throw new Error("Server2 should not be able to listen"); - }); - - server2.on("error", e => { - console.error(e); - expect(e.code).toBe("EADDRINUSE"); - server1.close(() => { - done(); - }); - }); - }, - ); -}); - -//<#END_FILE: test-net-bind-twice.js diff --git a/test/js/node/test/parallel/net-bind-twice-reuseport.test.js b/test/js/node/test/parallel/net-bind-twice-reuseport.test.js deleted file mode 100644 index 55067ce9d6..0000000000 --- a/test/js/node/test/parallel/net-bind-twice-reuseport.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-net-bind-twice.js -//#SHA1: 432eb9529d0affc39c8af9ebc1147528d96305c9 -//----------------- -"use strict"; - -import { test } from "bun:test"; -import net from "node:net"; -import { isWindows } from "harness"; - -test.skipIf(isWindows)("net.Server should not allow binding to the same port twice", done => { - const server1 = net.createServer(() => { - throw new Error("Server1 should not receive connections"); - }); - - const options = { - reusePort: true, - port: 0, - host: "127.0.0.1", - }; - server1.listen(options, () => { - const server2 = net.createServer(() => { - throw new Error("Server2 should not receive connections"); - }); - - const port = server1.address().port; - server2.listen({ ...options, port }, () => { - server2.close(() => { - server1.close(() => { - done(); - }); - }); - }); - - server2.on("error", e => { - server1.close(() => { - done(e); - }); - }); - }); -}); - -//<#END_FILE: test-net-bind-twice.js diff --git a/test/js/node/test/parallel/net-bind-twice.test.js b/test/js/node/test/parallel/net-bind-twice.test.js deleted file mode 100644 index 56454d2aab..0000000000 --- a/test/js/node/test/parallel/net-bind-twice.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-net-bind-twice.js -//#SHA1: 432eb9529d0affc39c8af9ebc1147528d96305c9 -//----------------- -"use strict"; -const net = require("net"); - -test("net.Server should not allow binding to the same port twice", done => { - const server1 = net.createServer(() => { - throw new Error("Server1 should not receive connections"); - }); - - server1.listen(0, "127.0.0.1", () => { - const server2 = net.createServer(() => { - throw new Error("Server2 should not receive connections"); - }); - - const port = server1.address().port; - server2.listen(port, "127.0.0.1", () => { - throw new Error("Server2 should not be able to listen"); - }); - - server2.on("error", e => { - expect(e.code).toBe("EADDRINUSE"); - server1.close(() => { - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-bind-twice.js diff --git a/test/js/node/test/parallel/net-bytes-written-large.test.js b/test/js/node/test/parallel/net-bytes-written-large.test.js deleted file mode 100644 index 715af6ecc7..0000000000 --- a/test/js/node/test/parallel/net-bytes-written-large.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-net-bytes-written-large.js -//#SHA1: 9005801147f80a8058f1b2126d772e52abd1f237 -//----------------- -"use strict"; -const net = require("net"); - -const N = 10000000; - -describe("Net bytes written large", () => { - test("Write a Buffer", done => { - const server = net - .createServer(socket => { - socket.end(Buffer.alloc(N), () => { - expect(socket.bytesWritten).toBe(N); - }); - expect(socket.bytesWritten).toBe(N); - }) - .listen(0, () => { - const client = net.connect(server.address().port); - client.resume(); - client.on("close", () => { - expect(client.bytesRead).toBe(N); - server.close(); - done(); - }); - }); - }); - - test("Write a string", done => { - const server = net - .createServer(socket => { - socket.end("a".repeat(N), () => { - expect(socket.bytesWritten).toBe(N); - }); - expect(socket.bytesWritten).toBe(N); - }) - .listen(0, () => { - const client = net.connect(server.address().port); - client.resume(); - client.on("close", () => { - expect(client.bytesRead).toBe(N); - server.close(); - done(); - }); - }); - }); - - test("writev() with mixed data", done => { - const server = net - .createServer(socket => { - socket.cork(); - socket.write("a".repeat(N)); - expect(socket.bytesWritten).toBe(N); - socket.write(Buffer.alloc(N)); - expect(socket.bytesWritten).toBe(2 * N); - socket.end("", () => { - expect(socket.bytesWritten).toBe(2 * N); - }); - socket.uncork(); - }) - .listen(0, () => { - const client = net.connect(server.address().port); - client.resume(); - client.on("close", () => { - expect(client.bytesRead).toBe(2 * N); - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-bytes-written-large.js diff --git a/test/js/node/test/parallel/net-can-reset-timeout.test.js b/test/js/node/test/parallel/net-can-reset-timeout.test.js deleted file mode 100644 index 1bb7e8e6a8..0000000000 --- a/test/js/node/test/parallel/net-can-reset-timeout.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-can-reset-timeout.js -//#SHA1: 871319149db929419e14ba7f08e5d0c878222a93 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net can reset timeout', () => { - let server; - let port; - - beforeAll((done) => { - server = net.createServer((stream) => { - stream.setTimeout(100); - - stream.resume(); - - stream.once('timeout', () => { - console.log('timeout'); - // Try to reset the timeout. - stream.write('WHAT.'); - }); - - stream.on('end', () => { - console.log('server side end'); - stream.end(); - }); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test('should handle timeout and reset', (done) => { - const c = net.createConnection(port, "127.0.0.1"); - - c.on('data', () => { - c.end(); - }); - - c.on('end', () => { - console.log('client side end'); - done(); - }); - }); -}); - -//<#END_FILE: test-net-can-reset-timeout.js diff --git a/test/js/node/test/parallel/net-connect-after-destroy.test.js b/test/js/node/test/parallel/net-connect-after-destroy.test.js deleted file mode 100644 index 013f7cd0da..0000000000 --- a/test/js/node/test/parallel/net-connect-after-destroy.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-net-connect-after-destroy.js -//#SHA1: 9341bea710601b5a3a8e823f4847396b210a855c -//----------------- -'use strict'; - -const net = require('net'); - -test('net.createConnection after destroy', () => { - // Connect to something that we need to DNS resolve - const c = net.createConnection(80, 'google.com'); - - // The test passes if this doesn't throw an error - expect(() => { - c.destroy(); - }).not.toThrow(); -}); - -//<#END_FILE: test-net-connect-after-destroy.js diff --git a/test/js/node/test/parallel/net-connect-call-socket-connect.test.js b/test/js/node/test/parallel/net-connect-call-socket-connect.test.js deleted file mode 100644 index 8a74641b74..0000000000 --- a/test/js/node/test/parallel/net-connect-call-socket-connect.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-net-connect-call-socket-connect.js -//#SHA1: 953a427c411633a029dcf978ea07fb701ae5ed9a -//----------------- -"use strict"; - -const net = require("net"); -const Socket = net.Socket; - -// This test checks that calling `net.connect` internally calls -// `Socket.prototype.connect`. -// -// This is important for people who monkey-patch `Socket.prototype.connect` -// since it's not possible to monkey-patch `net.connect` directly (as the core -// `connect` function is called internally in Node instead of calling the -// `exports.connect` function). -// -// Monkey-patching of `Socket.prototype.connect` is done by - among others - -// most APM vendors, the async-listener module and the -// continuation-local-storage module. -// -// Related: -// - https://github.com/nodejs/node/pull/12342 -// - https://github.com/nodejs/node/pull/12852 - -test("net.connect calls Socket.prototype.connect", async () => { - // Monkey patch Socket.prototype.connect to check that it's called. - const orig = Socket.prototype.connect; - const connectMock = jest.fn(function () { - return orig.apply(this, arguments); - }); - Socket.prototype.connect = connectMock; - - const server = net.createServer(); - - await new Promise(resolve => { - server.listen(() => { - const port = server.address().port; - const client = net.connect({ port }, () => { - client.end(); - }); - client.on("end", () => { - server.close(resolve); - }); - }); - }); - - expect(connectMock).toHaveBeenCalledTimes(1); - - // Restore original Socket.prototype.connect - Socket.prototype.connect = orig; -}); - -//<#END_FILE: test-net-connect-call-socket-connect.js diff --git a/test/js/node/test/parallel/net-connect-destroy.test.js b/test/js/node/test/parallel/net-connect-destroy.test.js deleted file mode 100644 index 358d9495a9..0000000000 --- a/test/js/node/test/parallel/net-connect-destroy.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-net-connect-destroy.js -//#SHA1: a185f5169d7b2988a09b74d9524743beda08dcff -//----------------- -'use strict'; -const net = require('net'); - -test('Socket is destroyed and emits close event', (done) => { - const socket = new net.Socket(); - - socket.on('close', () => { - // The close event was emitted - expect(true).toBe(true); - done(); - }); - - socket.destroy(); -}); - -//<#END_FILE: test-net-connect-destroy.js diff --git a/test/js/node/test/parallel/net-connect-immediate-destroy.test.js b/test/js/node/test/parallel/net-connect-immediate-destroy.test.js deleted file mode 100644 index 055b18bae4..0000000000 --- a/test/js/node/test/parallel/net-connect-immediate-destroy.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-net-connect-immediate-destroy.js -//#SHA1: 28ba78fafba37cb07a2ec4b18e3e35bbb78ef699 -//----------------- -"use strict"; -const net = require("net"); - -test("net.connect immediate destroy", done => { - const server = net.createServer(); - server.listen(0, () => { - const port = server.address().port; - const socket = net.connect(port, "127.0.0.1"); - - socket.on("connect", () => { - throw new Error("Socket should not connect"); - }); - - socket.on("error", () => { - throw new Error("Socket should not emit error"); - }); - - server.close(() => { - socket.destroy(); - done(); - }); - }); -}); - -//<#END_FILE: test-net-connect-immediate-destroy.js diff --git a/test/js/node/test/parallel/net-connect-options-allowhalfopen.test.js b/test/js/node/test/parallel/net-connect-options-allowhalfopen.test.js deleted file mode 100644 index e0cdeb1803..0000000000 --- a/test/js/node/test/parallel/net-connect-options-allowhalfopen.test.js +++ /dev/null @@ -1,112 +0,0 @@ -//#FILE: test-net-connect-options-allowhalfopen.js -//#SHA1: 9ba18563d747b3ebfa63f8f54468b62526224ec6 -//----------------- -"use strict"; -const net = require("net"); - -describe("Net connect options allowHalfOpen", () => { - let server; - let clientReceivedFIN = 0; - let serverConnections = 0; - let clientSentFIN = 0; - let serverReceivedFIN = 0; - const host = "127.0.0.1"; - const CLIENT_VARIANTS = 6; - - function serverOnConnection(socket) { - console.log(`'connection' ${++serverConnections} emitted on server`); - const srvConn = serverConnections; - socket.resume(); - socket.on("data", data => { - socket.clientId = data.toString(); - console.log(`server connection ${srvConn} is started by client ${socket.clientId}`); - }); - - socket.on("end", () => { - console.log(`Server received FIN sent by client ${socket.clientId}`); - if (++serverReceivedFIN < CLIENT_VARIANTS) return; - setTimeout(() => { - server.close(); - console.log( - `connection ${socket.clientId} is closing the server: - FIN ${serverReceivedFIN} received by server, - FIN ${clientReceivedFIN} received by client - FIN ${clientSentFIN} sent by client, - FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ""), - ); - }, 50); - }); - socket.end(); - console.log(`Server has sent ${serverConnections} FIN`); - } - - function serverOnClose() { - console.log( - `Server has been closed: - FIN ${serverReceivedFIN} received by server - FIN ${clientReceivedFIN} received by client - FIN ${clientSentFIN} sent by client - FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ""), - ); - } - - beforeAll(done => { - server = net - .createServer({ allowHalfOpen: true }) - .on("connection", serverOnConnection) - .on("close", serverOnClose) - .listen(0, host, () => { - console.log(`Server started listening at ${host}:${server.address().port}`); - done(); - }); - }); - - afterAll(() => { - if (server) { - server.close(); - } else { - done(); - } - }); - - test("should handle allowHalfOpen connections correctly", done => { - function clientOnConnect(index) { - return function clientOnConnectInner() { - const client = this; - console.log(`'connect' emitted on Client ${index}`); - client.resume(); - client.on("end", () => { - setTimeout(() => { - console.log(`client ${index} received FIN`); - expect(client.readable).toBe(false); - expect(client.writable).toBe(true); - expect(client.write(String(index))).toBeTruthy(); - client.end(); - clientSentFIN++; - console.log(`client ${index} sent FIN, ${clientSentFIN} have been sent`); - }, 50); - }); - client.on("close", () => { - clientReceivedFIN++; - console.log( - `connection ${index} has been closed by both sides,` + ` ${clientReceivedFIN} clients have closed`, - ); - if (clientReceivedFIN === CLIENT_VARIANTS) { - done(); - } - }); - }; - } - - const port = server.address().port; - const opts = { allowHalfOpen: true, host, port }; - net.connect(opts, clientOnConnect(1)); - net.connect(opts).on("connect", clientOnConnect(2)); - net.createConnection(opts, clientOnConnect(3)); - net.createConnection(opts).on("connect", clientOnConnect(4)); - new net.Socket(opts).connect(opts, clientOnConnect(5)); - new net.Socket(opts).connect(opts).on("connect", clientOnConnect(6)); - }); -}); - -//<#END_FILE: test-net-connect-options-allowhalfopen.js diff --git a/test/js/node/test/parallel/net-connect-options-fd.test.js b/test/js/node/test/parallel/net-connect-options-fd.test.js deleted file mode 100644 index a685b4a0e6..0000000000 --- a/test/js/node/test/parallel/net-connect-options-fd.test.js +++ /dev/null @@ -1,12 +0,0 @@ -//#FILE: test-net-connect-options-fd.js -//#SHA1: 3933f2a09469bfaad999b5ba483bde9c6255cb35 -//----------------- -'use strict'; - -// This test requires internal Node.js modules and cannot be run in a standard Jest environment -test('net connect options fd', () => { - console.log('This test requires internal Node.js modules and cannot be run in a standard Jest environment'); - expect(true).toBe(true); -}); - -//<#END_FILE: test-net-connect-options-fd.js diff --git a/test/js/node/test/parallel/net-connect-options-path.test.js b/test/js/node/test/parallel/net-connect-options-path.test.js deleted file mode 100644 index 446200036b..0000000000 --- a/test/js/node/test/parallel/net-connect-options-path.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-net-connect-options-path.js -//#SHA1: 03b1a7de04f689c6429298b553a49478321b4adb -//----------------- -'use strict'; -const net = require('net'); -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const CLIENT_VARIANTS = 12; - -describe('net.connect options path', () => { - let serverPath; - let server; - - beforeAll(() => { - const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'net-connect-options-path-')); - serverPath = path.join(tmpdir, 'server'); - }); - - afterAll(() => { - fs.rmdirSync(path.dirname(serverPath), { recursive: true }); - }); - - test('connect with various options', (done) => { - let connectionsCount = 0; - - server = net.createServer((socket) => { - socket.end('ok'); - }); - - server.listen(serverPath, () => { - const connectAndTest = (connectFn) => { - return new Promise((resolve) => { - const socket = connectFn(); - socket.on('data', (data) => { - expect(data.toString()).toBe('ok'); - socket.end(); - }); - socket.on('end', () => { - connectionsCount++; - resolve(); - }); - }); - }; - - const connectPromises = [ - () => net.connect(serverPath), - () => net.createConnection(serverPath), - () => new net.Socket().connect(serverPath), - () => net.connect({ path: serverPath }), - () => net.createConnection({ path: serverPath }), - () => new net.Socket().connect({ path: serverPath }) - ]; - - Promise.all(connectPromises.map(connectAndTest)) - .then(() => { - expect(connectionsCount).toBe(CLIENT_VARIANTS / 2); // We're testing 6 variants instead of 12 - server.close(() => { - done(); - }); - }) - .catch((err) => { - done(err); - }); - }); - }); -}); - -//<#END_FILE: test-net-connect-options-path.js diff --git a/test/js/node/test/parallel/net-connect-paused-connection.test.js b/test/js/node/test/parallel/net-connect-paused-connection.test.js deleted file mode 100644 index 6bfc5f554a..0000000000 --- a/test/js/node/test/parallel/net-connect-paused-connection.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-connect-paused-connection.js -//#SHA1: ab2fae629f3abb5fc4d5e59dd7d1dd2e09b9eb48 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net connect with paused connection", done => { - const server = net.createServer(conn => { - conn.unref(); - }); - - server.listen(0, () => { - const connection = net.connect(server.address().port, "localhost"); - connection.pause(); - - const timeoutId = setTimeout(() => { - done.fail("Should not have called timeout"); - }, 1000); - - // Unref the timeout to allow the process to exit - timeoutId.unref(); - - // Allow some time for the test to potentially fail - setTimeout(() => { - server.close(); - done(); - }, 500); - }); - - server.unref(); -}); - -//<#END_FILE: test-net-connect-paused-connection.js diff --git a/test/js/node/test/parallel/net-connect-reset-until-connected.test.js b/test/js/node/test/parallel/net-connect-reset-until-connected.test.js deleted file mode 100644 index 7e2e77698b..0000000000 --- a/test/js/node/test/parallel/net-connect-reset-until-connected.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-net-connect-reset-until-connected.js -//#SHA1: b0170103868d4f693e9afda7923e021758393a39 -//----------------- -"use strict"; - -const net = require("net"); - -function barrier(count, cb) { - return function () { - if (--count === 0) cb(); - }; -} - -test("net connection reset until connected", done => { - const server = net.createServer(); - server.listen(0, () => { - const port = server.address().port; - const conn = net.createConnection(port); - const connok = barrier(2, () => conn.resetAndDestroy()); - - conn.on("close", () => { - expect(true).toBe(true); // Ensure 'close' event is called - }); - - server.on("connection", socket => { - connok(); - socket.on("error", err => { - expect(err).toEqual( - expect.objectContaining({ - code: "ECONNRESET", - name: "Error", - message: expect.any(String), - }), - ); - }); - server.close(); - }); - - conn.on("connect", connok); - }); - - // Ensure the test completes - setTimeout(() => { - done(); - }, 1000); -}); - -//<#END_FILE: test-net-connect-reset-until-connected.js diff --git a/test/js/node/test/parallel/net-dns-lookup-skip.test.js b/test/js/node/test/parallel/net-dns-lookup-skip.test.js deleted file mode 100644 index b75771a6cf..0000000000 --- a/test/js/node/test/parallel/net-dns-lookup-skip.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-net-dns-lookup-skip.js -//#SHA1: 023bfbaa998480ab732d83d4bf8efb68ad4fe5db -//----------------- -'use strict'; -const net = require('net'); - -async function checkDnsLookupSkip(addressType) { - return new Promise((resolve, reject) => { - const server = net.createServer((client) => { - client.end(); - server.close(); - }); - - const address = addressType === 4 ? '127.0.0.1' : '::1'; - const lookupSpy = jest.fn(); - - server.listen(0, address, () => { - net.connect(server.address().port, address) - .on('lookup', lookupSpy) - .on('connect', () => { - expect(lookupSpy).not.toHaveBeenCalled(); - resolve(); - }) - .on('error', reject); - }); - }); -} - -test('DNS lookup should be skipped for IPv4', async () => { - await checkDnsLookupSkip(4); -}); - -// Check if the environment supports IPv6 -const hasIPv6 = (() => { - try { - net.createServer().listen(0, '::1').close(); - return true; - } catch { - return false; - } -})(); - -(hasIPv6 ? test : test.skip)('DNS lookup should be skipped for IPv6', async () => { - await checkDnsLookupSkip(6); -}); - -//<#END_FILE: test-net-dns-lookup-skip.js diff --git a/test/js/node/test/parallel/net-end-close.test.js b/test/js/node/test/parallel/net-end-close.test.js deleted file mode 100644 index 10d17c8c07..0000000000 --- a/test/js/node/test/parallel/net-end-close.test.js +++ /dev/null @@ -1,12 +0,0 @@ -//#FILE: test-net-end-close.js -//#SHA1: 01ac4a26e7cb4d477e547f9e6bd2f52a3b0d9277 -//----------------- -"use strict"; - -test.skip("net Socket end and close events", () => { - console.log( - "This test relies on internal Node.js APIs and cannot be accurately replicated in a cross-platform manner.", - ); -}); - -//<#END_FILE: test-net-end-close.js diff --git a/test/js/node/test/parallel/net-end-destroyed.test.js b/test/js/node/test/parallel/net-end-destroyed.test.js deleted file mode 100644 index 0ad62bbf17..0000000000 --- a/test/js/node/test/parallel/net-end-destroyed.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-net-end-destroyed.js -//#SHA1: cf219496c5dd2cb11f3d01750692b61791c7e2f9 -//----------------- -"use strict"; - -const net = require("net"); - -test('socket is not destroyed when the "end" event is emitted', done => { - const server = net.createServer(); - - server.on("connection", () => { - // Connection event handler - }); - - // Ensure that the socket is not destroyed when the 'end' event is emitted. - - server.listen(() => { - const socket = net.createConnection({ - port: server.address().port, - }); - - socket.on("connect", () => { - socket.on("end", () => { - expect(socket.destroyed).toBe(false); - server.close(); - done(); - }); - - socket.end(); - }); - }); -}); - -//<#END_FILE: test-net-end-destroyed.js diff --git a/test/js/node/test/parallel/net-isip.test.js b/test/js/node/test/parallel/net-isip.test.js deleted file mode 100644 index d30b722c22..0000000000 --- a/test/js/node/test/parallel/net-isip.test.js +++ /dev/null @@ -1,107 +0,0 @@ -//#FILE: test-net-isip.js -//#SHA1: 5fb15ec330f4e7489c3e7eb2a74547c44aa5a4dc -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net.isIP", () => { - expect(net.isIP("127.0.0.1")).toBe(4); - expect(net.isIP("x127.0.0.1")).toBe(0); - expect(net.isIP("example.com")).toBe(0); - expect(net.isIP("0000:0000:0000:0000:0000:0000:0000:0000")).toBe(6); - expect(net.isIP("0000:0000:0000:0000:0000:0000:0000:0000::0000")).toBe(0); - expect(net.isIP("1050:0:0:0:5:600:300c:326b")).toBe(6); - expect(net.isIP("2001:252:0:1::2008:6")).toBe(6); - expect(net.isIP("2001:dead:beef:1::2008:6")).toBe(6); - expect(net.isIP("2001::")).toBe(6); - expect(net.isIP("2001:dead::")).toBe(6); - expect(net.isIP("2001:dead:beef::")).toBe(6); - expect(net.isIP("2001:dead:beef:1::")).toBe(6); - expect(net.isIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")).toBe(6); - expect(net.isIP(":2001:252:0:1::2008:6:")).toBe(0); - expect(net.isIP(":2001:252:0:1::2008:6")).toBe(0); - expect(net.isIP("2001:252:0:1::2008:6:")).toBe(0); - expect(net.isIP("2001:252::1::2008:6")).toBe(0); - expect(net.isIP("::2001:252:1:2008:6")).toBe(6); - expect(net.isIP("::2001:252:1:1.1.1.1")).toBe(6); - expect(net.isIP("::2001:252:1:255.255.255.255")).toBe(6); - expect(net.isIP("::2001:252:1:255.255.255.255.76")).toBe(0); - expect(net.isIP("fe80::2008%eth0")).toBe(6); - expect(net.isIP("fe80::2008%eth0.0")).toBe(6); - expect(net.isIP("fe80::2008%eth0@1")).toBe(0); - expect(net.isIP("::anything")).toBe(0); - expect(net.isIP("::1")).toBe(6); - expect(net.isIP("::")).toBe(6); - expect(net.isIP("0000:0000:0000:0000:0000:0000:12345:0000")).toBe(0); - expect(net.isIP("0")).toBe(0); - expect(net.isIP()).toBe(0); - expect(net.isIP("")).toBe(0); - expect(net.isIP(null)).toBe(0); - expect(net.isIP(123)).toBe(0); - expect(net.isIP(true)).toBe(0); - expect(net.isIP({})).toBe(0); - expect(net.isIP({ toString: () => "::2001:252:1:255.255.255.255" })).toBe(6); - expect(net.isIP({ toString: () => "127.0.0.1" })).toBe(4); - expect(net.isIP({ toString: () => "bla" })).toBe(0); -}); - -test("net.isIPv4", () => { - expect(net.isIPv4("127.0.0.1")).toBe(true); - expect(net.isIPv4("example.com")).toBe(false); - expect(net.isIPv4("2001:252:0:1::2008:6")).toBe(false); - expect(net.isIPv4()).toBe(false); - expect(net.isIPv4("")).toBe(false); - expect(net.isIPv4(null)).toBe(false); - expect(net.isIPv4(123)).toBe(false); - expect(net.isIPv4(true)).toBe(false); - expect(net.isIPv4({})).toBe(false); - expect( - net.isIPv4({ - toString: () => "::2001:252:1:255.255.255.255", - }), - ).toBe(false); - expect(net.isIPv4({ toString: () => "127.0.0.1" })).toBe(true); - expect(net.isIPv4({ toString: () => "bla" })).toBe(false); -}); - -test("net.isIPv6", () => { - expect(net.isIPv6("127.0.0.1")).toBe(false); - expect(net.isIPv6("example.com")).toBe(false); - expect(net.isIPv6("2001:252:0:1::2008:6")).toBe(true); - expect(net.isIPv6()).toBe(false); - expect(net.isIPv6("")).toBe(false); - expect(net.isIPv6(null)).toBe(false); - expect(net.isIPv6(123)).toBe(false); - expect(net.isIPv6(true)).toBe(false); - expect(net.isIPv6({})).toBe(false); - expect( - net.isIPv6({ - toString: () => "::2001:252:1:255.255.255.255", - }), - ).toBe(true); - expect(net.isIPv6({ toString: () => "127.0.0.1" })).toBe(false); - expect(net.isIPv6({ toString: () => "bla" })).toBe(false); -}); - -//<#END_FILE: test-net-isip.js diff --git a/test/js/node/test/parallel/net-isipv4.test.js b/test/js/node/test/parallel/net-isipv4.test.js deleted file mode 100644 index d76f97e740..0000000000 --- a/test/js/node/test/parallel/net-isipv4.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-isipv4.js -//#SHA1: 2f26a4e5d952b01d6fe2143c1e8f65a19997c974 -//----------------- -"use strict"; - -const net = require("net"); - -const v4 = [ - "0.0.0.0", - "8.8.8.8", - "127.0.0.1", - "100.100.100.100", - "192.168.0.1", - "18.101.25.153", - "123.23.34.2", - "172.26.168.134", - "212.58.241.131", - "128.0.0.0", - "23.71.254.72", - "223.255.255.255", - "192.0.2.235", - "99.198.122.146", - "46.51.197.88", - "173.194.34.134", -]; - -const v4not = [ - ".100.100.100.100", - "100..100.100.100.", - "100.100.100.100.", - "999.999.999.999", - "256.256.256.256", - "256.100.100.100.100", - "123.123.123", - "http://123.123.123", - "1000.2.3.4", - "999.2.3.4", - "0000000192.168.0.200", - "192.168.0.2000000000", -]; - -test("net.isIPv4 correctly identifies valid IPv4 addresses", () => { - for (const ip of v4) { - expect(net.isIPv4(ip)).toBe(true); - } -}); - -test("net.isIPv4 correctly identifies invalid IPv4 addresses", () => { - for (const ip of v4not) { - expect(net.isIPv4(ip)).toBe(false); - } -}); - -//<#END_FILE: test-net-isipv4.js diff --git a/test/js/node/test/parallel/net-isipv6.test.js b/test/js/node/test/parallel/net-isipv6.test.js deleted file mode 100644 index 62d06daf6b..0000000000 --- a/test/js/node/test/parallel/net-isipv6.test.js +++ /dev/null @@ -1,254 +0,0 @@ -//#FILE: test-net-isipv6.js -//#SHA1: 4c6eea8203876ea65c4496732ee169a8fe0eb574 -//----------------- -"use strict"; - -const net = require("net"); - -const v6 = [ - "::", - "1::", - "::1", - "1::8", - "1::7:8", - "1:2:3:4:5:6:7:8", - "1:2:3:4:5:6::8", - "1:2:3:4:5:6:7::", - "1:2:3:4:5::7:8", - "1:2:3:4:5::8", - "1:2:3::8", - "1::4:5:6:7:8", - "1::6:7:8", - "1::3:4:5:6:7:8", - "1:2:3:4::6:7:8", - "1:2::4:5:6:7:8", - "::2:3:4:5:6:7:8", - "1:2::8", - "2001:0000:1234:0000:0000:C1C0:ABCD:0876", - "3ffe:0b00:0000:0000:0001:0000:0000:000a", - "FF02:0000:0000:0000:0000:0000:0000:0001", - "0000:0000:0000:0000:0000:0000:0000:0001", - "0000:0000:0000:0000:0000:0000:0000:0000", - "::ffff:192.168.1.26", - "2::10", - "ff02::1", - "fe80::", - "2002::", - "2001:db8::", - "2001:0db8:1234::", - "::ffff:0:0", - "::ffff:192.168.1.1", - "1:2:3:4::8", - "1::2:3:4:5:6:7", - "1::2:3:4:5:6", - "1::2:3:4:5", - "1::2:3:4", - "1::2:3", - "::2:3:4:5:6:7", - "::2:3:4:5:6", - "::2:3:4:5", - "::2:3:4", - "::2:3", - "::8", - "1:2:3:4:5:6::", - "1:2:3:4:5::", - "1:2:3:4::", - "1:2:3::", - "1:2::", - "1:2:3:4::7:8", - "1:2:3::7:8", - "1:2::7:8", - "1:2:3:4:5:6:1.2.3.4", - "1:2:3:4:5::1.2.3.4", - "1:2:3:4::1.2.3.4", - "1:2:3::1.2.3.4", - "1:2::1.2.3.4", - "1::1.2.3.4", - "1:2:3:4::5:1.2.3.4", - "1:2:3::5:1.2.3.4", - "1:2::5:1.2.3.4", - "1::5:1.2.3.4", - "1::5:11.22.33.44", - "fe80::217:f2ff:254.7.237.98", - "fe80::217:f2ff:fe07:ed62", - "2001:DB8:0:0:8:800:200C:417A", - "FF01:0:0:0:0:0:0:101", - "0:0:0:0:0:0:0:1", - "0:0:0:0:0:0:0:0", - "2001:DB8::8:800:200C:417A", - "FF01::101", - "0:0:0:0:0:0:13.1.68.3", - "0:0:0:0:0:FFFF:129.144.52.38", - "::13.1.68.3", - "::FFFF:129.144.52.38", - "fe80:0000:0000:0000:0204:61ff:fe9d:f156", - "fe80:0:0:0:204:61ff:fe9d:f156", - "fe80::204:61ff:fe9d:f156", - "fe80:0:0:0:204:61ff:254.157.241.86", - "fe80::204:61ff:254.157.241.86", - "fe80::1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", - "2001:db8:85a3:0:0:8a2e:370:7334", - "2001:db8:85a3::8a2e:370:7334", - "2001:0db8:0000:0000:0000:0000:1428:57ab", - "2001:0db8:0000:0000:0000::1428:57ab", - "2001:0db8:0:0:0:0:1428:57ab", - "2001:0db8:0:0::1428:57ab", - "2001:0db8::1428:57ab", - "2001:db8::1428:57ab", - "::ffff:12.34.56.78", - "::ffff:0c22:384e", - "2001:0db8:1234:0000:0000:0000:0000:0000", - "2001:0db8:1234:ffff:ffff:ffff:ffff:ffff", - "2001:db8:a::123", - "::ffff:192.0.2.128", - "::ffff:c000:280", - "a:b:c:d:e:f:f1:f2", - "a:b:c::d:e:f:f1", - "a:b:c::d:e:f", - "a:b:c::d:e", - "a:b:c::d", - "::a", - "::a:b:c", - "::a:b:c:d:e:f:f1", - "a::", - "a:b:c::", - "a:b:c:d:e:f:f1::", - "a:bb:ccc:dddd:000e:00f:0f::", - "0:a:0:a:0:0:0:a", - "0:a:0:0:a:0:0:a", - "2001:db8:1:1:1:1:0:0", - "2001:db8:1:1:1:0:0:0", - "2001:db8:1:1:0:0:0:0", - "2001:db8:1:0:0:0:0:0", - "2001:db8:0:0:0:0:0:0", - "2001:0:0:0:0:0:0:0", - "A:BB:CCC:DDDD:000E:00F:0F::", - "0:0:0:0:0:0:0:a", - "0:0:0:0:a:0:0:0", - "0:0:0:a:0:0:0:0", - "a:0:0:a:0:0:a:a", - "a:0:0:a:0:0:0:a", - "a:0:0:0:a:0:0:a", - "a:0:0:0:a:0:0:0", - "a:0:0:0:0:0:0:0", - "fe80::7:8%eth0", - "fe80::7:8%1", -]; - -const v6not = [ - "", - "1:", - ":1", - "11:36:12", - "02001:0000:1234:0000:0000:C1C0:ABCD:0876", - "2001:0000:1234:0000:00001:C1C0:ABCD:0876", - "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", - "2001:1:1:1:1:1:255Z255X255Y255", - "3ffe:0b00:0000:0001:0000:0000:000a", - "FF02:0000:0000:0000:0000:0000:0000:0000:0001", - "3ffe:b00::1::a", - "::1111:2222:3333:4444:5555:6666::", - "1:2:3::4:5::7:8", - "12345::6:7:8", - "1::5:400.2.3.4", - "1::5:260.2.3.4", - "1::5:256.2.3.4", - "1::5:1.256.3.4", - "1::5:1.2.256.4", - "1::5:1.2.3.256", - "1::5:300.2.3.4", - "1::5:1.300.3.4", - "1::5:1.2.300.4", - "1::5:1.2.3.300", - "1::5:900.2.3.4", - "1::5:1.900.3.4", - "1::5:1.2.900.4", - "1::5:1.2.3.900", - "1::5:300.300.300.300", - "1::5:3000.30.30.30", - "1::400.2.3.4", - "1::260.2.3.4", - "1::256.2.3.4", - "1::1.256.3.4", - "1::1.2.256.4", - "1::1.2.3.256", - "1::300.2.3.4", - "1::1.300.3.4", - "1::1.2.300.4", - "1::1.2.3.300", - "1::900.2.3.4", - "1::1.900.3.4", - "1::1.2.900.4", - "1::1.2.3.900", - "1::300.300.300.300", - "1::3000.30.30.30", - "::400.2.3.4", - "::260.2.3.4", - "::256.2.3.4", - "::1.256.3.4", - "::1.2.256.4", - "::1.2.3.256", - "::300.2.3.4", - "::1.300.3.4", - "::1.2.300.4", - "::1.2.3.300", - "::900.2.3.4", - "::1.900.3.4", - "::1.2.900.4", - "::1.2.3.900", - "::300.300.300.300", - "::3000.30.30.30", - "2001:DB8:0:0:8:800:200C:417A:221", - "FF01::101::2", - "1111:2222:3333:4444::5555:", - "1111:2222:3333::5555:", - "1111:2222::5555:", - "1111::5555:", - "::5555:", - ":::", - "1111:", - ":", - ":1111:2222:3333:4444::5555", - ":1111:2222:3333::5555", - ":1111:2222::5555", - ":1111::5555", - ":::5555", - "1.2.3.4:1111:2222:3333:4444::5555", - "1.2.3.4:1111:2222:3333::5555", - "1.2.3.4:1111:2222::5555", - "1.2.3.4:1111::5555", - "1.2.3.4::5555", - "1.2.3.4::", - "fe80:0000:0000:0000:0204:61ff:254.157.241.086", - "123", - "ldkfj", - "2001::FFD3::57ab", - "2001:db8:85a3::8a2e:37023:7334", - "2001:db8:85a3::8a2e:370k:7334", - "1:2:3:4:5:6:7:8:9", - "1::2::3", - "1:::3:4:5", - "1:2:3::4:5:6:7:8:9", - "::ffff:2.3.4", - "::ffff:257.1.2.3", - "::ffff:12345678901234567890.1.26", - "2001:0000:1234:0000:0000:C1C0:ABCD:0876 0", - "02001:0000:1234:0000:0000:C1C0:ABCD:0876", -]; - -describe("net.isIPv6", () => { - test("valid IPv6 addresses", () => { - for (const ip of v6) { - expect(net.isIPv6(ip)).toBe(true); - } - }); - - test("invalid IPv6 addresses", () => { - for (const ip of v6not) { - expect(net.isIPv6(ip)).toBe(false); - } - }); -}); - -//<#END_FILE: test-net-isipv6.js diff --git a/test/js/node/test/parallel/net-keepalive.test.js b/test/js/node/test/parallel/net-keepalive.test.js deleted file mode 100644 index 2b875ceb20..0000000000 --- a/test/js/node/test/parallel/net-keepalive.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-keepalive.js -//#SHA1: 822f2eb57a17abc64e2664803a4ac69430e5b035 -//----------------- -"use strict"; - -const net = require("net"); - -describe("net keepalive", () => { - test("should maintain connection", async () => { - let serverConnection; - let clientConnection; - - const { promise, resolve, reject } = Promise.withResolvers(); - function done(err) { - clientConnection.destroy(); - echoServer.close(); - if (err) reject(err); - else resolve(); - } - - const echoServer = net.createServer(connection => { - serverConnection = connection; - connection.setTimeout(0); - try { - expect(connection.setKeepAlive).toBeDefined(); - } catch (err) { - done(err); - return; - } - connection.setKeepAlive(true, 50); - connection.on("end", () => { - connection.end(); - }); - }); - - echoServer.listen(0, () => { - clientConnection = net.createConnection(echoServer.address().port, "127.0.0.1"); - clientConnection.setTimeout(0); - clientConnection.on("connect", () => { - setTimeout(() => { - try { - expect(serverConnection.readyState).toBe("open"); - expect(clientConnection.readyState).toBe("open"); - done(); - } catch (err) { - done(err); - } - }, 100); - }); - }); - - await promise; - }); -}); - -//<#END_FILE: test-net-keepalive.js diff --git a/test/js/node/test/parallel/net-large-string.test.js b/test/js/node/test/parallel/net-large-string.test.js deleted file mode 100644 index e69dd073d4..0000000000 --- a/test/js/node/test/parallel/net-large-string.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-large-string.js -//#SHA1: d823932009345f5d651ca02b7ddbba67057a423b -//----------------- -"use strict"; -const net = require("net"); - -const kPoolSize = 40 * 1024; -const data = "あ".repeat(kPoolSize); -const encoding = "UTF-8"; - -test("net large string", done => { - const server = net.createServer(socket => { - let receivedSize = 0; - socket.setEncoding(encoding); - socket.on("data", chunk => { - receivedSize += chunk.length; - }); - socket.on("end", () => { - expect(receivedSize).toBe(kPoolSize); - socket.end(); - }); - }); - - server.listen(0, () => { - // we connect to the server using 127.0.0.1 to avoid happy eyeballs - const client = net.createConnection(server.address().port, "127.0.0.1"); - client.on("end", () => { - server.close(); - done(); - }); - client.write(data, encoding); - client.end(); - }); -}); - -//<#END_FILE: test-net-large-string.js diff --git a/test/js/node/test/parallel/net-listen-after-destroying-stdin.test.js b/test/js/node/test/parallel/net-listen-after-destroying-stdin.test.js deleted file mode 100644 index 3084568eda..0000000000 --- a/test/js/node/test/parallel/net-listen-after-destroying-stdin.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-listen-after-destroying-stdin.js -//#SHA1: 933b6b80e7babac7189e16f85967b75836efea74 -//----------------- -"use strict"; - -// Just test that destroying stdin doesn't mess up listening on a server. -// This is a regression test for -// https://github.com/nodejs/node-v0.x-archive/issues/746. - -const net = require("net"); - -test("destroying stdin does not affect server listening", done => { - process.stdin.destroy(); - - const server = net.createServer(socket => { - console.log("accepted..."); - socket.end(() => { - console.log("finished..."); - expect(true).toBe(true); // Ensure this callback is called - }); - server.close(() => { - console.log("closed"); - expect(true).toBe(true); // Ensure this callback is called - done(); - }); - }); - - server.listen(0, () => { - console.log("listening..."); - expect(server.address().port).toBeGreaterThan(0); - - net.createConnection(server.address().port); - }); -}); - -//<#END_FILE: test-net-listen-after-destroying-stdin.js diff --git a/test/js/node/test/parallel/net-listen-exclusive-random-ports.test.js b/test/js/node/test/parallel/net-listen-exclusive-random-ports.test.js deleted file mode 100644 index 01f8e25506..0000000000 --- a/test/js/node/test/parallel/net-listen-exclusive-random-ports.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-listen-exclusive-random-ports.js -//#SHA1: d125e8ff5fd688b5638099581c08c78d91460c59 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net listen exclusive random ports', () => { - test('should listen on different ports for different servers', async () => { - const createServer = () => { - return new Promise((resolve, reject) => { - const server = net.createServer(() => {}); - server.listen({ - port: 0, - exclusive: true - }, () => { - const port = server.address().port; - resolve({ server, port }); - }); - server.on('error', reject); - }); - }; - - const { server: server1, port: port1 } = await createServer(); - const { server: server2, port: port2 } = await createServer(); - - expect(port1).toBe(port1 | 0); - expect(port2).toBe(port2 | 0); - expect(port1).not.toBe(port2); - - server1.close(); - server2.close(); - }); -}); - -//<#END_FILE: test-net-listen-exclusive-random-ports.js diff --git a/test/js/node/test/parallel/net-listen-handle-in-cluster-2.test.js b/test/js/node/test/parallel/net-listen-handle-in-cluster-2.test.js deleted file mode 100644 index ac5017b087..0000000000 --- a/test/js/node/test/parallel/net-listen-handle-in-cluster-2.test.js +++ /dev/null @@ -1,10 +0,0 @@ -//#FILE: test-net-listen-handle-in-cluster-2.js -//#SHA1: 1902a830aa4f12e7049fc0383e9a919b46aa79dc -//----------------- -'use strict'; - -test.skip('net.listen with handle in cluster (worker)', () => { - console.log('This test is skipped because it relies on Node.js internals and cluster functionality that cannot be accurately replicated in a Jest environment.'); -}); - -//<#END_FILE: test-net-listen-handle-in-cluster-2.js diff --git a/test/js/node/test/parallel/net-listening.test.js b/test/js/node/test/parallel/net-listening.test.js deleted file mode 100644 index ea0b14ab53..0000000000 --- a/test/js/node/test/parallel/net-listening.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-net-listening.js -//#SHA1: 3c2824f7bd90fec69702e096faba0b4f07353a20 -//----------------- -"use strict"; -const net = require("net"); - -test("Server listening state", done => { - const server = net.createServer(); - - expect(server.listening).toBe(false); - - server.listen(0, () => { - expect(server.listening).toBe(true); - - server.close(() => { - expect(server.listening).toBe(false); - done(); - }); - }); -}); - -//<#END_FILE: test-net-listening.js diff --git a/test/js/node/test/parallel/net-local-address-port.test.js b/test/js/node/test/parallel/net-local-address-port.test.js deleted file mode 100644 index a41661e52b..0000000000 --- a/test/js/node/test/parallel/net-local-address-port.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-net-local-address-port.js -//#SHA1: 9fdb2786eb87ca722138e027be5ee72f04b9909c -//----------------- -"use strict"; -const net = require("net"); - -const localhostIPv4 = "127.0.0.1"; - -describe("Net local address and port", () => { - let server; - let client; - - afterEach(() => { - if (client) { - client.destroy(); - } - if (server && server.listening) { - server.close(); - } - }); - - test("should have correct local address, port, and family", done => { - server = net.createServer(socket => { - expect(socket.localAddress).toBe(localhostIPv4); - expect(socket.localPort).toBe(server.address().port); - expect(socket.localFamily).toBe(server.address().family); - - socket.resume(); - }); - - server.listen(0, localhostIPv4, () => { - client = net.createConnection(server.address().port, localhostIPv4); - client.on("connect", () => { - client.end(); - // We'll end the test here instead of waiting for the server to close - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-local-address-port.js diff --git a/test/js/node/test/parallel/net-persistent-keepalive.test.js b/test/js/node/test/parallel/net-persistent-keepalive.test.js deleted file mode 100644 index 86b5fbc054..0000000000 --- a/test/js/node/test/parallel/net-persistent-keepalive.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-persistent-keepalive.js -//#SHA1: 1428cedddea85130590caec6c04b1939c1f614d4 -//----------------- -"use strict"; -const net = require("net"); - -let serverConnection; -let clientConnection; -let echoServer; -let serverPort; - -beforeAll((done) => { - echoServer = net.createServer((connection) => { - serverConnection = connection; - connection.setTimeout(0); - expect(typeof connection.setKeepAlive).toBe("function"); - connection.on("end", () => { - connection.end(); - }); - }); - - echoServer.listen(0, () => { - serverPort = echoServer.address().port; - done(); - }); -}); - -afterAll((done) => { - if (echoServer) { - echoServer.close(done); - } else { - done(); - } -}); - -test("persistent keepalive", (done) => { - clientConnection = new net.Socket(); - // Send a keepalive packet after 400 ms and make sure it persists - const s = clientConnection.setKeepAlive(true, 400); - expect(s).toBeInstanceOf(net.Socket); - - clientConnection.connect(serverPort, "127.0.0.1"); - clientConnection.setTimeout(0); - - setTimeout(() => { - // Make sure both connections are still open - expect(serverConnection.readyState).toBe("open"); - expect(clientConnection.readyState).toBe("open"); - - serverConnection.end(); - clientConnection.end(); - done(); - }, 600); -}); - -//<#END_FILE: test-net-persistent-keepalive.js diff --git a/test/js/node/test/parallel/net-persistent-nodelay.test.js b/test/js/node/test/parallel/net-persistent-nodelay.test.js deleted file mode 100644 index 87e9db3e82..0000000000 --- a/test/js/node/test/parallel/net-persistent-nodelay.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-persistent-nodelay.js -//#SHA1: 48ab228711df09f547aa2ef4abaac2735ef4625b -//----------------- -"use strict"; - -const net = require("net"); - -describe("TCP setNoDelay persistence", () => { - let echoServer; - let originalSetNoDelay; - let callCount; - - beforeAll(() => { - echoServer = net.createServer(connection => { - connection.end(); - }); - }); - - afterAll(() => { - echoServer.close(); - }); - - beforeEach(() => { - callCount = 0; - originalSetNoDelay = net.Socket.prototype.setNoDelay; - net.Socket.prototype.setNoDelay = function (enable) { - const result = originalSetNoDelay.call(this, enable); - callCount++; - return result; - }; - }); - - afterEach(() => { - net.Socket.prototype.setNoDelay = originalSetNoDelay; - }); - - test("setNoDelay is called once when connecting", done => { - echoServer.listen(0, () => { - const sock1 = new net.Socket(); - - // setNoDelay before the handle is created - const s = sock1.setNoDelay(); - expect(s).toBeInstanceOf(net.Socket); - - sock1.connect(echoServer.address().port); - sock1.on("end", () => { - expect(callCount).toBe(1); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-persistent-nodelay.js diff --git a/test/js/node/test/parallel/net-persistent-ref-unref.test.js b/test/js/node/test/parallel/net-persistent-ref-unref.test.js deleted file mode 100644 index 58c2a799bc..0000000000 --- a/test/js/node/test/parallel/net-persistent-ref-unref.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-persistent-ref-unref.js -//#SHA1: 630ad893713b3c13100743b5e5ae46453adc523e -//----------------- -'use strict'; -const net = require('net'); - -// Mock TCPWrap -const TCPWrap = { - prototype: { - ref: jest.fn(), - unref: jest.fn(), - }, -}; - -let refCount = 0; - -describe('Net persistent ref/unref', () => { - let echoServer; - - beforeAll((done) => { - echoServer = net.createServer((conn) => { - conn.end(); - }); - - TCPWrap.prototype.ref = jest.fn().mockImplementation(function() { - TCPWrap.prototype.ref.mockOriginal.call(this); - refCount++; - expect(refCount).toBe(0); - }); - - TCPWrap.prototype.unref = jest.fn().mockImplementation(function() { - TCPWrap.prototype.unref.mockOriginal.call(this); - refCount--; - expect(refCount).toBe(-1); - }); - - echoServer.listen(0, done); - }); - - afterAll((done) => { - echoServer.close(done); - }); - - test('should maintain correct ref count', (done) => { - const sock = new net.Socket(); - sock.unref(); - sock.ref(); - sock.connect(echoServer.address().port); - sock.on('end', () => { - expect(refCount).toBe(0); - done(); - }); - }); -}); - -//<#END_FILE: test-net-persistent-ref-unref.js diff --git a/test/js/node/test/parallel/net-remote-address.test.js b/test/js/node/test/parallel/net-remote-address.test.js deleted file mode 100644 index ea145b43d3..0000000000 --- a/test/js/node/test/parallel/net-remote-address.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-net-remote-address.js -//#SHA1: a4c7e915b2d6465060b3d283c3cc3906ad629531 -//----------------- -"use strict"; - -const net = require("net"); - -test("remote address behavior", done => { - const server = net.createServer(); - - server.listen(() => { - const socket = net.connect({ port: server.address().port }); - - expect(socket.connecting).toBe(true); - expect(socket.remoteAddress).toBeUndefined(); - - socket.on("connect", () => { - expect(socket.remoteAddress).toBeDefined(); - socket.end(); - }); - - socket.on("end", () => { - server.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-net-remote-address.js diff --git a/test/js/node/test/parallel/net-server-close-before-calling-lookup-callback.test.js b/test/js/node/test/parallel/net-server-close-before-calling-lookup-callback.test.js deleted file mode 100644 index dd3c3b91d0..0000000000 --- a/test/js/node/test/parallel/net-server-close-before-calling-lookup-callback.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-net-server-close-before-calling-lookup-callback.js -//#SHA1: 42a269f691f19c53994f939bacf7e0451f065107 -//----------------- -"use strict"; - -const net = require("net"); - -test("server closes before calling lookup callback", () => { - // Process should exit because it does not create a real TCP server. - // Pass localhost to ensure create TCP handle asynchronously because It causes DNS resolution. - const server = net.createServer(); - - expect(() => { - server.listen(0, "localhost", () => { - throw new Error("This callback should not be called"); - }); - server.close(); - }).not.toThrow(); -}); - -//<#END_FILE: test-net-server-close-before-calling-lookup-callback.js diff --git a/test/js/node/test/parallel/net-server-close-before-ipc-response.test.js b/test/js/node/test/parallel/net-server-close-before-ipc-response.test.js deleted file mode 100644 index 95bba271d2..0000000000 --- a/test/js/node/test/parallel/net-server-close-before-ipc-response.test.js +++ /dev/null @@ -1,16 +0,0 @@ -//#FILE: test-net-server-close-before-ipc-response.js -//#SHA1: 540c9049f49219e9dbcbbd053be54cc2cbd332a0 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net server close before IPC response', () => { - test.skip('Process should exit', () => { - console.log('This test is skipped because it requires a complex cluster and IPC setup that is difficult to simulate in a Jest environment.'); - console.log('The original test verified that the process exits correctly when a server is closed before an IPC response is received.'); - console.log('To properly test this, we would need to set up a real cluster environment or use a more sophisticated mocking approach.'); - }); -}); - -//<#END_FILE: test-net-server-close-before-ipc-response.js diff --git a/test/js/node/test/parallel/net-server-listen-remove-callback.test.js b/test/js/node/test/parallel/net-server-listen-remove-callback.test.js deleted file mode 100644 index 0aaff47a52..0000000000 --- a/test/js/node/test/parallel/net-server-listen-remove-callback.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-net-server-listen-remove-callback.js -//#SHA1: 031a06bd108815e34b9ebbc3019044daeb8cf8c8 -//----------------- -'use strict'; - -const net = require('net'); - -let server; - -beforeEach(() => { - server = net.createServer(); -}); - -afterEach((done) => { - if (server.listening) { - server.close(done); - } else { - done(); - } -}); - -test('Server should only fire listen callback once', (done) => { - server.on('close', () => { - const listeners = server.listeners('listening'); - console.log('Closed, listeners:', listeners.length); - expect(listeners.length).toBe(0); - }); - - server.listen(0, () => { - server.close(); - }); - - server.once('close', () => { - server.listen(0, () => { - server.close(done); - }); - }); -}); - -//<#END_FILE: test-net-server-listen-remove-callback.js diff --git a/test/js/node/test/parallel/net-server-try-ports.test.js b/test/js/node/test/parallel/net-server-try-ports.test.js deleted file mode 100644 index 67668a69bc..0000000000 --- a/test/js/node/test/parallel/net-server-try-ports.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-net-server-try-ports.js -//#SHA1: 8f3f2a7c0fcc9b76f2aaf8ac2bb00c81e6a752fa -//----------------- -"use strict"; - -const net = require("net"); - -test("Server should handle EADDRINUSE and bind to another port", done => { - const server1 = new net.Server(); - const server2 = new net.Server(); - - server2.on("error", e => { - expect(e.code).toBe("EADDRINUSE"); - - server2.listen(0, () => { - server1.close(); - server2.close(); - done(); - }); - }); - - server1.listen(0, () => { - // This should make server2 emit EADDRINUSE - server2.listen(server1.address().port); - }); -}); - -//<#END_FILE: test-net-server-try-ports.js diff --git a/test/js/node/test/parallel/net-server-unref-persistent.test.js b/test/js/node/test/parallel/net-server-unref-persistent.test.js deleted file mode 100644 index add3449f2b..0000000000 --- a/test/js/node/test/parallel/net-server-unref-persistent.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-net-server-unref-persistent.js -//#SHA1: 4b518c58827ac05dd5c3746c8a0811181184b945 -//----------------- -'use strict'; -const net = require('net'); - -test.skip('net server unref should be persistent', () => { - // This test is skipped in Jest because it relies on Node.js-specific event loop behavior - // that can't be accurately simulated in a Jest environment. - // The original test should be kept in Node.js's test suite. -}); - -//<#END_FILE: test-net-server-unref-persistent.js diff --git a/test/js/node/test/parallel/net-settimeout.test.js b/test/js/node/test/parallel/net-settimeout.test.js deleted file mode 100644 index b766196ac8..0000000000 --- a/test/js/node/test/parallel/net-settimeout.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-net-settimeout.js -//#SHA1: 24fde10dfba0d555d2a61853374866b370e40edf -//----------------- -'use strict'; - -const net = require('net'); - -const T = 100; - -let server; -let serverPort; - -beforeAll((done) => { - server = net.createServer((c) => { - c.write('hello'); - }); - - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('setTimeout and immediate clearTimeout', (done) => { - const socket = net.createConnection(serverPort, 'localhost'); - - const timeoutCallback = jest.fn(); - const s = socket.setTimeout(T, timeoutCallback); - expect(s).toBeInstanceOf(net.Socket); - - socket.on('data', () => { - setTimeout(() => { - socket.destroy(); - expect(timeoutCallback).not.toHaveBeenCalled(); - done(); - }, T * 2); - }); - - socket.setTimeout(0); -}); - -//<#END_FILE: test-net-settimeout.js diff --git a/test/js/node/test/parallel/net-socket-close-after-end.test.js b/test/js/node/test/parallel/net-socket-close-after-end.test.js deleted file mode 100644 index d0d744a6e3..0000000000 --- a/test/js/node/test/parallel/net-socket-close-after-end.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-net-socket-close-after-end.js -//#SHA1: d3abfad3599a4245fb35f5589c55bb56a43ca3f7 -//----------------- -"use strict"; - -const net = require("net"); - -test('socket emits "end" before "close"', done => { - const server = net.createServer(); - - server.on("connection", socket => { - let endEmitted = false; - - socket.once("readable", () => { - setTimeout(() => { - socket.read(); - }, 100); - }); - - socket.on("end", () => { - endEmitted = true; - }); - - socket.on("close", () => { - expect(endEmitted).toBe(true); - server.close(); - done(); - }); - - socket.end("foo"); - }); - - server.listen(() => { - const socket = net.createConnection(server.address().port, () => { - socket.end("foo"); - }); - }); -}); - -//<#END_FILE: test-net-socket-close-after-end.js diff --git a/test/js/node/test/parallel/net-socket-connect-without-cb.test.js b/test/js/node/test/parallel/net-socket-connect-without-cb.test.js deleted file mode 100644 index 3f47475afc..0000000000 --- a/test/js/node/test/parallel/net-socket-connect-without-cb.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-net-socket-connect-without-cb.js -//#SHA1: 2441c4dfe4351f2e9a02cd08df36e4703096864a -//----------------- -"use strict"; - -const net = require("net"); - -// This test ensures that socket.connect can be called without callback -// which is optional. - -test("socket.connect without callback", done => { - const server = net - .createServer(conn => { - conn.end(); - server.close(); - }) - .listen(0, () => { - const client = new net.Socket(); - - client.on("connect", () => { - client.end(); - done(); - }); - - const address = server.address(); - if (process.version.startsWith("v") && !process.versions.bun && address.family === "IPv6") { - // Necessary to pass CI running inside containers. - client.connect(address.port); - } else { - client.connect(address); - } - }); - - expect(server).toBeDefined(); -}); - -//<#END_FILE: test-net-socket-connect-without-cb.js diff --git a/test/js/node/test/parallel/net-socket-destroy-twice.test.js b/test/js/node/test/parallel/net-socket-destroy-twice.test.js deleted file mode 100644 index cc8a7ecaf2..0000000000 --- a/test/js/node/test/parallel/net-socket-destroy-twice.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-net-socket-destroy-twice.js -//#SHA1: b9066749198a610e24f0b75c017f00abb3c70bfc -//----------------- -"use strict"; - -const net = require("net"); - -describe("Net socket destroy twice", () => { - let server; - let port; - - beforeAll((done) => { - server = net.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should handle destroying a socket twice", (done) => { - const conn = net.createConnection(port, "127.0.0.1"); - - let errorCalled = 0; - conn.on("error", () => { - errorCalled++; - conn.destroy(); - }); - - conn.on("close", () => { - expect(errorCalled).toBe(1); - done(); - }); - - // Trigger an error by closing the server - server.close(); - }); -}); - -//<#END_FILE: test-net-socket-destroy-twice.js diff --git a/test/js/node/test/parallel/net-socket-end-before-connect.test.js b/test/js/node/test/parallel/net-socket-end-before-connect.test.js deleted file mode 100644 index d27dfd7d46..0000000000 --- a/test/js/node/test/parallel/net-socket-end-before-connect.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-net-socket-end-before-connect.js -//#SHA1: e09a7492b07dfa5467171563408395f653e9b032 -//----------------- -'use strict'; - -const net = require('net'); - -test('Socket ends before connect', (done) => { - const server = net.createServer(); - - server.listen(() => { - const socket = net.createConnection(server.address().port, "127.0.0.1"); - - const closeHandler = function() { - server.close(); - done(); - } - socket.on('close', closeHandler); - socket.end(); - }); -}); - -//<#END_FILE: test-net-socket-end-before-connect.js diff --git a/test/js/node/test/parallel/net-socket-ready-without-cb.test.js b/test/js/node/test/parallel/net-socket-ready-without-cb.test.js deleted file mode 100644 index d22eac4d22..0000000000 --- a/test/js/node/test/parallel/net-socket-ready-without-cb.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-net-socket-ready-without-cb.js -//#SHA1: 2f6be9472163372bcd602f547bd709b27a2baad6 -//----------------- -'use strict'; - -const net = require('net'); - -test('socket.connect can be called without callback', (done) => { - const server = net.createServer((conn) => { - conn.end(); - server.close(); - }); - - server.listen(0, 'localhost', () => { - const client = new net.Socket(); - - client.on('ready', () => { - client.end(); - done(); - }); - - client.connect(server.address()); - }); -}); - -//<#END_FILE: test-net-socket-ready-without-cb.js diff --git a/test/js/node/test/parallel/net-socket-reset-twice.test.js b/test/js/node/test/parallel/net-socket-reset-twice.test.js deleted file mode 100644 index 10adfdc49d..0000000000 --- a/test/js/node/test/parallel/net-socket-reset-twice.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-net-socket-reset-twice.js -//#SHA1: 70cb2037a6385ada696f8b9f8fa66a0b111275c4 -//----------------- -"use strict"; -const net = require("net"); - -let server; -let port; - -beforeAll((done) => { - server = net.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("net socket reset twice", (done) => { - const conn = net.createConnection(port, "127.0.0.1"); - - const errorHandler = jest.fn(() => { - conn.resetAndDestroy(); - }); - - conn.on("error", errorHandler); - - const closeHandler = jest.fn(() => { - expect(errorHandler).toHaveBeenCalled(); - expect(closeHandler).toHaveBeenCalled(); - done(); - }); - - conn.on("close", closeHandler); - - // Trigger the error event - server.close(); -}); - -//<#END_FILE: test-net-socket-reset-twice.js diff --git a/test/js/node/test/parallel/net-socket-timeout-unref.test.js b/test/js/node/test/parallel/net-socket-timeout-unref.test.js deleted file mode 100644 index 5e05c5cf1c..0000000000 --- a/test/js/node/test/parallel/net-socket-timeout-unref.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-net-socket-timeout-unref.js -//#SHA1: 1583fd33473989bba11fead2493c70a79d9ff48e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// Test that unref'ed sockets with timeouts do not prevent exit. - -const net = require("net"); - -test("unref'ed sockets with timeouts do not prevent exit", () => { - const server = net.createServer(c => { - c.write("hello"); - c.unref(); - }); - server.listen(0); - server.unref(); - - let connections = 0; - const sockets = []; - const delays = [8, 5, 3, 6, 2, 4]; - - delays.forEach(T => { - const socket = net.createConnection(server.address().port, "localhost"); - socket.on("connect", () => { - if (++connections === delays.length) { - sockets.forEach(s => { - s.socket.setTimeout(s.timeout, () => { - s.socket.destroy(); - throw new Error("socket timed out unexpectedly"); - }); - - s.socket.unref(); - }); - } - }); - - sockets.push({ socket: socket, timeout: T * 1000 }); - }); - - // We don't need to explicitly assert anything here. - // The test will pass if the process exits without throwing an error. -}); - -//<#END_FILE: test-net-socket-timeout-unref.js diff --git a/test/js/node/test/parallel/net-socket-write-error.test.js b/test/js/node/test/parallel/net-socket-write-error.test.js deleted file mode 100644 index 56b8b5634f..0000000000 --- a/test/js/node/test/parallel/net-socket-write-error.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-net-socket-write-error.js -//#SHA1: a69bb02fc98fc265ad23ff03e7ae16e9c984202d -//----------------- -"use strict"; - -const net = require("net"); - -describe("Net Socket Write Error", () => { - let server; - - beforeAll(done => { - server = net.createServer().listen(0, () => { - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should throw TypeError when writing non-string/buffer", done => { - const client = net.createConnection(server.address().port, () => { - client.on("error", () => { - done.fail("Client should not emit error"); - }); - - expect(() => { - client.write(1337); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - client.destroy(); - done(); - }); - - client.on("close", () => { - // This ensures the server closes after the client disconnects - server.close(); - }); - }); -}); - -//<#END_FILE: test-net-socket-write-error.js diff --git a/test/js/node/test/parallel/net-stream.test.js b/test/js/node/test/parallel/net-stream.test.js deleted file mode 100644 index bbfca1ad3e..0000000000 --- a/test/js/node/test/parallel/net-stream.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-net-stream.js -//#SHA1: 3682dee1fcd1fea4f59bbad200ab1476e0f49bda -//----------------- -"use strict"; - -const net = require("net"); -const { once } = require("events"); -const SIZE = 2e6; -const N = 10; -const buf = Buffer.alloc(SIZE, "a"); -//TODO: need to check how to handle error on close events properly -test.skip("net stream behavior", async () => { - let server; - try { - const { promise, resolve: done } = Promise.withResolvers(); - - server = net.createServer(socket => { - socket.setNoDelay(); - - let onErrorCalls = 0; - let onCloseCalls = 0; - socket - .on("error", () => { - onErrorCalls++; - socket.destroy(); - }) - .on("close", () => { - onCloseCalls++; - done({ onErrorCalls, onCloseCalls }); - }); - - for (let i = 0; i < N; ++i) { - socket.write(buf, () => {}); - } - - socket.end(); - }); - await once(server.listen(0), "listening"); - - const conn = net.connect(server.address().port, "127.0.0.1"); - const { promise: dataPromise, resolve: dataResolve } = Promise.withResolvers(); - conn.on("data", buf => { - dataResolve(conn.pause()); - setTimeout(() => { - conn.destroy(); - }, 20); - }); - expect(await dataPromise).toBe(conn); - - const { onCloseCalls, onErrorCalls } = await promise; - expect(onErrorCalls).toBeGreaterThan(0); - expect(onCloseCalls).toBeGreaterThan(0); - } finally { - server.close(); - } -}); - -//<#END_FILE: test-net-stream.js diff --git a/test/js/node/test/parallel/net-sync-cork.test.js b/test/js/node/test/parallel/net-sync-cork.test.js deleted file mode 100644 index bc0c4524fd..0000000000 --- a/test/js/node/test/parallel/net-sync-cork.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-net-sync-cork.js -//#SHA1: baf95df782bcb1c53ea0118e8e47e93d63cf4262 -//----------------- -"use strict"; - -const net = require("net"); - -const N = 100; -const buf = Buffer.alloc(2, "a"); - -let server; - -beforeAll(done => { - server = net.createServer(handle); - server.listen(0, done); -}); - -afterAll(() => { - server.close(); -}); - -test("net sync cork", done => { - const conn = net.connect(server.address().port); - - conn.on("connect", () => { - let res = true; - let i = 0; - for (; i < N && res; i++) { - conn.cork(); - conn.write(buf); - res = conn.write(buf); - conn.uncork(); - } - expect(i).toBe(N); - conn.end(); - }); - - conn.on("close", done); -}); - -function handle(socket) { - socket.resume(); - socket.on("error", () => { - throw new Error("Socket error should not occur"); - }); - socket.on("close", () => { - // This is called when the connection is closed - }); -} - -//<#END_FILE: test-net-sync-cork.js diff --git a/test/js/node/test/parallel/net-throttle.test.js b/test/js/node/test/parallel/net-throttle.test.js deleted file mode 100644 index b33fc01bea..0000000000 --- a/test/js/node/test/parallel/net-throttle.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-net-throttle.js -//#SHA1: 5c09d0b1c174ba1f88acae8d731c039ae7c3fc99 -//----------------- -"use strict"; - -const net = require("net"); -const { debuglog } = require("util"); - -const debug = debuglog("test"); - -let chars_recved = 0; -let npauses = 0; -let totalLength = 0; -let server; - -beforeAll(done => { - server = net.createServer(connection => { - const body = "C".repeat(1024); - let n = 1; - debug("starting write loop"); - while (connection.write(body)) { - n++; - } - debug("ended write loop"); - // Now that we're throttled, do some more writes to make sure the data isn't - // lost. - connection.write(body); - connection.write(body); - n += 2; - totalLength = n * body.length; - expect(connection.bufferSize).toBeGreaterThanOrEqual(0); - expect(connection.writableLength).toBeLessThanOrEqual(totalLength); - connection.end(); - }); - - server.listen(0, () => { - debug(`server started on port ${server.address().port}`); - done(); - }); -}); - -afterAll(done => { - server.close(done); -}); - -test("net throttle", done => { - const port = server.address().port; - let paused = false; - const client = net.createConnection(port, "127.0.0.1"); - client.setEncoding("ascii"); - - client.on("data", d => { - chars_recved += d.length; - debug(`got ${chars_recved}`); - if (!paused) { - client.pause(); - npauses += 1; - paused = true; - debug("pause"); - const x = chars_recved; - setTimeout(() => { - expect(chars_recved).toBe(x); - client.resume(); - debug("resume"); - paused = false; - }, 100); - } - }); - - client.on("end", () => { - client.end(); - expect(chars_recved).toBe(totalLength); - expect(npauses).toBeGreaterThan(1); - done(); - }); -}); - -//<#END_FILE: test-net-throttle.js diff --git a/test/js/node/test/parallel/net-writable.test.js b/test/js/node/test/parallel/net-writable.test.js deleted file mode 100644 index 60f10d743b..0000000000 --- a/test/js/node/test/parallel/net-writable.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-net-writable.js -//#SHA1: dfbbbc883e83311b16b93fc9e06d214552cb6448 -//----------------- -"use strict"; - -const net = require("net"); - -test("net writable after end event", done => { - const server = net.createServer(s => { - server.close(); - s.end(); - }); - - server.listen(0, "127.0.0.1", () => { - const socket = net.connect(server.address().port, "127.0.0.1"); - socket.on("end", () => { - expect(socket.writable).toBe(true); - socket.write("hello world"); - done(); - }); - }); - - expect.assertions(1); -}); - -//<#END_FILE: test-net-writable.js diff --git a/test/js/node/test/parallel/net-write-after-close.test.js b/test/js/node/test/parallel/net-write-after-close.test.js deleted file mode 100644 index 8aacf621b9..0000000000 --- a/test/js/node/test/parallel/net-write-after-close.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-net-write-after-close.js -//#SHA1: fe97d63608f4e6651247e83071c81800a6de2ee6 -//----------------- -"use strict"; - -const net = require("net"); - -test("write after close", async () => { - const { promise, resolve } = Promise.withResolvers(); - const { promise: writePromise, resolve: writeResolve } = Promise.withResolvers(); - let server; - try { - server = net.createServer(socket => { - socket.on("end", () => resolve(socket)); - socket.resume(); - socket.on("error", error => { - throw new Error("Server socket should not emit error"); - }); - }); - - server.listen(0, () => { - const client = net.connect(server.address().port, "127.0.0.1", () => { - client.end(); - }); - }); - (await promise).write("test", writeResolve); - const err = await writePromise; - expect(err).toBeTruthy(); - } finally { - server.close(); - } -}); - -//<#END_FILE: test-net-write-after-close.js diff --git a/test/js/node/test/parallel/net-write-after-end-nt.test.js b/test/js/node/test/parallel/net-write-after-end-nt.test.js deleted file mode 100644 index b3f2e81936..0000000000 --- a/test/js/node/test/parallel/net-write-after-end-nt.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-write-after-end-nt.js -//#SHA1: 086a5699d5eff4953af4e9f19757b8489e915579 -//----------------- -"use strict"; -const net = require("net"); - -describe("net.Socket.write() after end", () => { - let server; - let port; - - beforeAll(done => { - server = net - .createServer(socket => { - socket.end(); - }) - .listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(done => { - server.close(done); - }); - - test("error is emitted in the next tick", done => { - const client = net.connect(port, "127.0.0.1", () => { - let hasError = false; - - client.on("error", err => { - hasError = true; - expect(err).toEqual( - expect.objectContaining({ - code: "EPIPE", - message: "This socket has been ended by the other party", - name: "Error", - }), - ); - done(); - }); - - client.on("end", () => { - const ret = client.write("hello"); - expect(ret).toBe(false); - expect(hasError).toBe(false); - process.nextTick(() => { - expect(hasError).toBe(true); - }); - }); - - client.end(); - }); - }); -}); - -//<#END_FILE: test-net-write-after-end-nt.js diff --git a/test/js/node/test/parallel/net-write-arguments.test.js b/test/js/node/test/parallel/net-write-arguments.test.js deleted file mode 100644 index 347230744d..0000000000 --- a/test/js/node/test/parallel/net-write-arguments.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-net-write-arguments.js -//#SHA1: 2a9ed0086e2675e0e31ef15c1e86b15c47c10c5b -//----------------- -"use strict"; -const net = require("net"); - -test("net.Stream write arguments", () => { - const socket = net.Stream({ highWaterMark: 0 }); - - // Make sure that anything besides a buffer or a string throws. - socket.on("error", jest.fn()); - expect(() => { - socket.write(null); - }).toThrow( - expect.objectContaining({ - code: "ERR_STREAM_NULL_VALUES", - name: "TypeError", - message: expect.any(String), - }), - ); - - [true, false, undefined, 1, 1.0, +Infinity, -Infinity, [], {}].forEach(value => { - const socket = net.Stream({ highWaterMark: 0 }); - // We need to check the callback since 'error' will only - // be emitted once per instance. - expect(() => { - socket.write(value); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-net-write-arguments.js diff --git a/test/js/node/test/parallel/net-write-cb-on-destroy-before-connect.test.js b/test/js/node/test/parallel/net-write-cb-on-destroy-before-connect.test.js deleted file mode 100644 index 5a6b245ff6..0000000000 --- a/test/js/node/test/parallel/net-write-cb-on-destroy-before-connect.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-net-write-cb-on-destroy-before-connect.js -//#SHA1: 49dc0c1780402ca7bc3648f52f821b0ba89eff32 -//----------------- -'use strict'; - -const net = require('net'); - -let server; - -beforeAll((done) => { - server = net.createServer(); - server.listen(0, () => { - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('write callback on destroy before connect', (done) => { - const socket = new net.Socket(); - - socket.on('connect', () => { - done('Socket should not connect'); - }); - - socket.connect({ - port: server.address().port, - }, "127.0.0.1"); - - expect(socket.connecting).toBe(true); - - socket.write('foo', (err) => { - expect(err).toEqual(expect.objectContaining({ - code: 'ERR_SOCKET_CLOSED_BEFORE_CONNECTION', - name: 'Error' - })); - done(); - }); - - socket.destroy(); -}); - -//<#END_FILE: test-net-write-cb-on-destroy-before-connect.js diff --git a/test/js/node/test/parallel/net-write-connect-write.test.js b/test/js/node/test/parallel/net-write-connect-write.test.js deleted file mode 100644 index bc959534eb..0000000000 --- a/test/js/node/test/parallel/net-write-connect-write.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-net-write-connect-write.js -//#SHA1: 8d6e9a30cc58bee105db15dc48c8a13c451629be -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net write connect write", async () => { - const server = net.createServer(socket => { - socket.pipe(socket); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const conn = net.connect(server.address().port); - let received = ""; - - conn.setEncoding("utf8"); - conn.write("before"); - - await new Promise(resolve => { - conn.on("connect", () => { - conn.write(" after"); - resolve(); - }); - }); - - await new Promise(resolve => { - conn.on("data", buf => { - received += buf; - conn.end(); - }); - - conn.on("end", () => { - server.close(); - expect(received).toBe("before after"); - resolve(); - }); - }); -}); - -//<#END_FILE: test-net-write-connect-write.js diff --git a/test/js/node/test/parallel/net-write-fully-async-buffer.test.js b/test/js/node/test/parallel/net-write-fully-async-buffer.test.js deleted file mode 100644 index acd0eeb23c..0000000000 --- a/test/js/node/test/parallel/net-write-fully-async-buffer.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-net-write-fully-async-buffer.js -//#SHA1: b26773ed4c8c5bafaaa8a4513b25d1806a72ae5f -//----------------- -"use strict"; - -const net = require("net"); - -// Note: This test assumes that the --expose-gc flag is available. -// In a Jest environment, you might need to configure this separately. - -const data = Buffer.alloc(1000000); - -let server; - -beforeAll(done => { - server = net - .createServer(conn => { - conn.resume(); - }) - .listen(0, () => { - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("net write fully async buffer", done => { - const conn = net.createConnection(server.address().port, () => { - let count = 0; - - function writeLoop() { - if (count++ === 200) { - conn.destroy(); - done(); - return; - } - - while (conn.write(Buffer.from(data))); - - // Note: global.gc() is not available in standard Jest environments. - // You might need to configure Jest to run with the --expose-gc flag. - // For this test, we'll comment it out, but in a real scenario, you'd need to ensure it's available. - // global.gc({ type: 'minor' }); - // The buffer allocated above should still be alive. - } - - conn.on("drain", writeLoop); - - writeLoop(); - }); -}); - -//<#END_FILE: test-net-write-fully-async-buffer.js diff --git a/test/js/node/test/parallel/net-write-fully-async-hex-string.test.js b/test/js/node/test/parallel/net-write-fully-async-hex-string.test.js deleted file mode 100644 index 64b79e17ed..0000000000 --- a/test/js/node/test/parallel/net-write-fully-async-hex-string.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-net-write-fully-async-hex-string.js -//#SHA1: e5b365bb794f38e7153fc41ebfaf991031f85423 -//----------------- -"use strict"; - -const net = require("net"); - -let server; - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("net write fully async hex string", done => { - const data = Buffer.alloc(1000000).toString("hex"); - - server = net.createServer(conn => { - conn.resume(); - }); - - server.listen(0, () => { - const conn = net.createConnection(server.address().port, () => { - let count = 0; - - function writeLoop() { - if (count++ === 20) { - conn.destroy(); - done(); - return; - } - while (conn.write(data, "hex")); - // Note: We can't use global.gc in Jest, so we'll skip this part - // global.gc({ type: 'minor' }); - // The buffer allocated inside the .write() call should still be alive. - - // Use setImmediate to allow other operations to occur - setImmediate(writeLoop); - } - - conn.on("drain", writeLoop); - - writeLoop(); - }); - }); -}); - -//<#END_FILE: test-net-write-fully-async-hex-string.js diff --git a/test/js/node/test/parallel/net-write-slow.test.js b/test/js/node/test/parallel/net-write-slow.test.js deleted file mode 100644 index 9ce97d8d39..0000000000 --- a/test/js/node/test/parallel/net-write-slow.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-net-write-slow.js -//#SHA1: ef646d024e2dfcfb07b99fcdfb9ccf2bfbcb6487 -//----------------- -'use strict'; -const net = require('net'); - -const SIZE = 2E5; -const N = 10; -let flushed = 0; -let received = 0; -const buf = Buffer.alloc(SIZE, 'a'); - -let server; - -beforeAll(() => { - return new Promise((resolve) => { - server = net.createServer((socket) => { - socket.setNoDelay(); - socket.setTimeout(9999); - socket.on('timeout', () => { - throw new Error(`flushed: ${flushed}, received: ${received}/${SIZE * N}`); - }); - - for (let i = 0; i < N; ++i) { - socket.write(buf, () => { - ++flushed; - if (flushed === N) { - socket.setTimeout(0); - } - }); - } - socket.end(); - }).listen(0, () => { - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise((resolve) => { - server.close(resolve); - }); -}); - -test('net write slow', (done) => { - const conn = net.connect(server.address().port); - - conn.on('data', (buf) => { - received += buf.length; - conn.pause(); - setTimeout(() => { - conn.resume(); - }, 20); - }); - - conn.on('end', () => { - expect(received).toBe(SIZE * N); - done(); - }); -}); - -//<#END_FILE: test-net-write-slow.js diff --git a/test/js/node/test/parallel/next-tick-when-exiting.test.js b/test/js/node/test/parallel/next-tick-when-exiting.test.js deleted file mode 100644 index 1684eebc47..0000000000 --- a/test/js/node/test/parallel/next-tick-when-exiting.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-next-tick-when-exiting.js -//#SHA1: fcca0c205805ee8c1d9e994013d5e3738e3ef6e4 -//----------------- -"use strict"; - -test("process.nextTick should not be called when exiting", () => { - const exitHandler = jest.fn(() => { - expect(process._exiting).toBe(true); - - process.nextTick(jest.fn().mockName("process is exiting, should not be called")); - }); - - process.on("exit", exitHandler); - - process.exit(); - - expect(exitHandler).toHaveBeenCalledTimes(1); - expect(exitHandler.mock.calls[0][0]).toBe(undefined); -}); - -//<#END_FILE: test-next-tick-when-exiting.js diff --git a/test/js/node/test/parallel/no-node-snapshot.test.js b/test/js/node/test/parallel/no-node-snapshot.test.js deleted file mode 100644 index bebaabb1c5..0000000000 --- a/test/js/node/test/parallel/no-node-snapshot.test.js +++ /dev/null @@ -1,14 +0,0 @@ -//#FILE: test-no-node-snapshot.js -//#SHA1: e8e4ecdb2aa4c064a55e1c7b076424d0b0d0a007 -//----------------- -"use strict"; - -// Flags: --no-node-snapshot - -test("--no-node-snapshot flag", () => { - // This test doesn't actually assert anything. - // It's merely checking if the script runs without errors when the flag is set. - expect(true).toBe(true); -}); - -//<#END_FILE: test-no-node-snapshot.js diff --git a/test/js/node/test/parallel/outgoing-message-destroy.test.js b/test/js/node/test/parallel/outgoing-message-destroy.test.js deleted file mode 100644 index 6f61032083..0000000000 --- a/test/js/node/test/parallel/outgoing-message-destroy.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-outgoing-message-destroy.js -//#SHA1: 64f5438a6e8b8315e79f25d8f9e40b7dde6e3c19 -//----------------- -"use strict"; - -// Test that http.OutgoingMessage,prototype.destroy() returns `this`. - -const http = require("http"); - -test("http.OutgoingMessage.prototype.destroy() returns `this`", () => { - const outgoingMessage = new http.OutgoingMessage(); - - expect(outgoingMessage.destroyed).toBe(false); - expect(outgoingMessage.destroy()).toBe(outgoingMessage); - expect(outgoingMessage.destroyed).toBe(true); - expect(outgoingMessage.destroy()).toBe(outgoingMessage); -}); - -//<#END_FILE: test-outgoing-message-destroy.js diff --git a/test/js/node/test/parallel/path-extname.test.js b/test/js/node/test/parallel/path-extname.test.js deleted file mode 100644 index e8a74c6b2e..0000000000 --- a/test/js/node/test/parallel/path-extname.test.js +++ /dev/null @@ -1,115 +0,0 @@ -//#FILE: test-path-extname.js -//#SHA1: 29d676d507ef80d7e5795db0f2a0265dbc7baf1e -//----------------- -"use strict"; -const path = require("path"); - -const slashRE = /\//g; - -const testPaths = [ - [__filename, ".js"], - ["", ""], - ["/path/to/file", ""], - ["/path/to/file.ext", ".ext"], - ["/path.to/file.ext", ".ext"], - ["/path.to/file", ""], - ["/path.to/.file", ""], - ["/path.to/.file.ext", ".ext"], - ["/path/to/f.ext", ".ext"], - ["/path/to/..ext", ".ext"], - ["/path/to/..", ""], - ["file", ""], - ["file.ext", ".ext"], - [".file", ""], - [".file.ext", ".ext"], - ["/file", ""], - ["/file.ext", ".ext"], - ["/.file", ""], - ["/.file.ext", ".ext"], - [".path/file.ext", ".ext"], - ["file.ext.ext", ".ext"], - ["file.", "."], - [".", ""], - ["./", ""], - [".file.ext", ".ext"], - [".file", ""], - [".file.", "."], - [".file..", "."], - ["..", ""], - ["../", ""], - ["..file.ext", ".ext"], - ["..file", ".file"], - ["..file.", "."], - ["..file..", "."], - ["...", "."], - ["...ext", ".ext"], - ["....", "."], - ["file.ext/", ".ext"], - ["file.ext//", ".ext"], - ["file/", ""], - ["file//", ""], - ["file./", "."], - ["file.//", "."], -]; - -describe("path.extname", () => { - test("should return correct extensions for various paths", () => { - const failures = []; - - for (const testPath of testPaths) { - const expected = testPath[1]; - const extNames = [path.posix.extname, path.win32.extname]; - for (const extname of extNames) { - let input = testPath[0]; - let os; - if (extname === path.win32.extname) { - input = input.replace(slashRE, "\\"); - os = "win32"; - } else { - os = "posix"; - } - const actual = extname(input); - const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${JSON.stringify( - expected, - )}\n actual=${JSON.stringify(actual)}`; - if (actual !== expected) failures.push(`\n${message}`); - } - const input = `C:${testPath[0].replace(slashRE, "\\")}`; - const actual = path.win32.extname(input); - const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${JSON.stringify( - expected, - )}\n actual=${JSON.stringify(actual)}`; - if (actual !== expected) failures.push(`\n${message}`); - } - - expect(failures).toHaveLength(0); - }); - - describe("Windows-specific behavior", () => { - test("backslash is a path separator", () => { - expect(path.win32.extname(".\\").toString()).toBe(""); - expect(path.win32.extname("..\\").toString()).toBe(""); - expect(path.win32.extname("file.ext\\").toString()).toBe(".ext"); - expect(path.win32.extname("file.ext\\\\").toString()).toBe(".ext"); - expect(path.win32.extname("file\\").toString()).toBe(""); - expect(path.win32.extname("file\\\\").toString()).toBe(""); - expect(path.win32.extname("file.\\").toString()).toBe("."); - expect(path.win32.extname("file.\\\\").toString()).toBe("."); - }); - }); - - describe("POSIX-specific behavior", () => { - test("backslash is a valid name component", () => { - expect(path.posix.extname(".\\").toString()).toBe(""); - expect(path.posix.extname("..\\").toString()).toBe(".\\"); - expect(path.posix.extname("file.ext\\").toString()).toBe(".ext\\"); - expect(path.posix.extname("file.ext\\\\").toString()).toBe(".ext\\\\"); - expect(path.posix.extname("file\\").toString()).toBe(""); - expect(path.posix.extname("file\\\\").toString()).toBe(""); - expect(path.posix.extname("file.\\").toString()).toBe(".\\"); - expect(path.posix.extname("file.\\\\").toString()).toBe(".\\\\"); - }); - }); -}); - -//<#END_FILE: test-path-extname.js diff --git a/test/js/node/test/parallel/path-isabsolute.test.js b/test/js/node/test/parallel/path-isabsolute.test.js deleted file mode 100644 index 53604b0d51..0000000000 --- a/test/js/node/test/parallel/path-isabsolute.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-path-isabsolute.js -//#SHA1: d0ff051a7934f18aed9c435a823ff688e5f782c1 -//----------------- -"use strict"; - -const path = require("path"); - -test("path.win32.isAbsolute", () => { - expect(path.win32.isAbsolute("/")).toBe(true); - expect(path.win32.isAbsolute("//")).toBe(true); - expect(path.win32.isAbsolute("//server")).toBe(true); - expect(path.win32.isAbsolute("//server/file")).toBe(true); - expect(path.win32.isAbsolute("\\\\server\\file")).toBe(true); - expect(path.win32.isAbsolute("\\\\server")).toBe(true); - expect(path.win32.isAbsolute("\\\\")).toBe(true); - expect(path.win32.isAbsolute("c")).toBe(false); - expect(path.win32.isAbsolute("c:")).toBe(false); - expect(path.win32.isAbsolute("c:\\")).toBe(true); - expect(path.win32.isAbsolute("c:/")).toBe(true); - expect(path.win32.isAbsolute("c://")).toBe(true); - expect(path.win32.isAbsolute("C:/Users/")).toBe(true); - expect(path.win32.isAbsolute("C:\\Users\\")).toBe(true); - expect(path.win32.isAbsolute("C:cwd/another")).toBe(false); - expect(path.win32.isAbsolute("C:cwd\\another")).toBe(false); - expect(path.win32.isAbsolute("directory/directory")).toBe(false); - expect(path.win32.isAbsolute("directory\\directory")).toBe(false); -}); - -test("path.posix.isAbsolute", () => { - expect(path.posix.isAbsolute("/home/foo")).toBe(true); - expect(path.posix.isAbsolute("/home/foo/..")).toBe(true); - expect(path.posix.isAbsolute("bar/")).toBe(false); - expect(path.posix.isAbsolute("./baz")).toBe(false); -}); - -//<#END_FILE: test-path-isabsolute.js diff --git a/test/js/node/test/parallel/path-normalize.test.js b/test/js/node/test/parallel/path-normalize.test.js deleted file mode 100644 index c1a4bee899..0000000000 --- a/test/js/node/test/parallel/path-normalize.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-path-normalize.js -//#SHA1: 94c9aec4a962fc0737d7a88610d3c3e17a3b96b5 -//----------------- -"use strict"; - -const path = require("path"); - -describe("path.normalize", () => { - describe("win32", () => { - test("normalizes various paths correctly", () => { - expect(path.win32.normalize("./fixtures///b/../b/c.js")).toBe("fixtures\\b\\c.js"); - expect(path.win32.normalize("/foo/../../../bar")).toBe("\\bar"); - expect(path.win32.normalize("a//b//../b")).toBe("a\\b"); - expect(path.win32.normalize("a//b//./c")).toBe("a\\b\\c"); - expect(path.win32.normalize("a//b//.")).toBe("a\\b"); - expect(path.win32.normalize("//server/share/dir/file.ext")).toBe("\\\\server\\share\\dir\\file.ext"); - expect(path.win32.normalize("/a/b/c/../../../x/y/z")).toBe("\\x\\y\\z"); - expect(path.win32.normalize("C:")).toBe("C:."); - expect(path.win32.normalize("C:..\\abc")).toBe("C:..\\abc"); - expect(path.win32.normalize("C:..\\..\\abc\\..\\def")).toBe("C:..\\..\\def"); - expect(path.win32.normalize("C:\\.")).toBe("C:\\"); - expect(path.win32.normalize("file:stream")).toBe("file:stream"); - expect(path.win32.normalize("bar\\foo..\\..\\")).toBe("bar\\"); - expect(path.win32.normalize("bar\\foo..\\..")).toBe("bar"); - expect(path.win32.normalize("bar\\foo..\\..\\baz")).toBe("bar\\baz"); - expect(path.win32.normalize("bar\\foo..\\")).toBe("bar\\foo..\\"); - expect(path.win32.normalize("bar\\foo..")).toBe("bar\\foo.."); - expect(path.win32.normalize("..\\foo..\\..\\..\\bar")).toBe("..\\..\\bar"); - expect(path.win32.normalize("..\\...\\..\\.\\...\\..\\..\\bar")).toBe("..\\..\\bar"); - expect(path.win32.normalize("../../../foo/../../../bar")).toBe("..\\..\\..\\..\\..\\bar"); - expect(path.win32.normalize("../../../foo/../../../bar/../../")).toBe("..\\..\\..\\..\\..\\..\\"); - expect(path.win32.normalize("../foobar/barfoo/foo/../../../bar/../../")).toBe("..\\..\\"); - expect(path.win32.normalize("../.../../foobar/../../../bar/../../baz")).toBe("..\\..\\..\\..\\baz"); - expect(path.win32.normalize("foo/bar\\baz")).toBe("foo\\bar\\baz"); - }); - }); - - describe("posix", () => { - test("normalizes various paths correctly", () => { - expect(path.posix.normalize("./fixtures///b/../b/c.js")).toBe("fixtures/b/c.js"); - expect(path.posix.normalize("/foo/../../../bar")).toBe("/bar"); - expect(path.posix.normalize("a//b//../b")).toBe("a/b"); - expect(path.posix.normalize("a//b//./c")).toBe("a/b/c"); - expect(path.posix.normalize("a//b//.")).toBe("a/b"); - expect(path.posix.normalize("/a/b/c/../../../x/y/z")).toBe("/x/y/z"); - expect(path.posix.normalize("///..//./foo/.//bar")).toBe("/foo/bar"); - expect(path.posix.normalize("bar/foo../../")).toBe("bar/"); - expect(path.posix.normalize("bar/foo../..")).toBe("bar"); - expect(path.posix.normalize("bar/foo../../baz")).toBe("bar/baz"); - expect(path.posix.normalize("bar/foo../")).toBe("bar/foo../"); - expect(path.posix.normalize("bar/foo..")).toBe("bar/foo.."); - expect(path.posix.normalize("../foo../../../bar")).toBe("../../bar"); - expect(path.posix.normalize("../.../.././.../../../bar")).toBe("../../bar"); - expect(path.posix.normalize("../../../foo/../../../bar")).toBe("../../../../../bar"); - expect(path.posix.normalize("../../../foo/../../../bar/../../")).toBe("../../../../../../"); - expect(path.posix.normalize("../foobar/barfoo/foo/../../../bar/../../")).toBe("../../"); - expect(path.posix.normalize("../.../../foobar/../../../bar/../../baz")).toBe("../../../../baz"); - expect(path.posix.normalize("foo/bar\\baz")).toBe("foo/bar\\baz"); - }); - }); -}); - -//<#END_FILE: test-path-normalize.js diff --git a/test/js/node/test/parallel/path-posix-exists.test.js b/test/js/node/test/parallel/path-posix-exists.test.js deleted file mode 100644 index 8dfb86ea81..0000000000 --- a/test/js/node/test/parallel/path-posix-exists.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-path-posix-exists.js -//#SHA1: 4bd4c9ef3ffd03623fefbdedd28732c21fd10956 -//----------------- -"use strict"; - -// The original test file used Node.js specific modules and assertions. -// We'll convert this to use Jest's testing framework while maintaining -// the same behavior and allowing it to run in both Node.js and Bun. - -test("path/posix module exists and is identical to path.posix", () => { - // In Jest, we don't need to explicitly require assert - // We'll use Jest's expect API instead - - // We still need to require the path module - const path = require("path"); - - // Check if the path/posix module is the same as path.posix - expect(require("path/posix")).toBe(path.posix); -}); - -//<#END_FILE: test-path-posix-exists.js diff --git a/test/js/node/test/parallel/path-posix-relative-on-windows.test.js b/test/js/node/test/parallel/path-posix-relative-on-windows.test.js deleted file mode 100644 index c1a4e9ea2c..0000000000 --- a/test/js/node/test/parallel/path-posix-relative-on-windows.test.js +++ /dev/null @@ -1,15 +0,0 @@ -//#FILE: test-path-posix-relative-on-windows.js -//#SHA1: 2fb8d86d02eea6a077fbca45828aa2433d6a49e6 -//----------------- -"use strict"; - -const path = require("path"); - -// Refs: https://github.com/nodejs/node/issues/13683 - -test("path.posix.relative on Windows", () => { - const relativePath = path.posix.relative("a/b/c", "../../x"); - expect(relativePath).toMatch(/^(\.\.\/){3,5}x$/); -}); - -//<#END_FILE: test-path-posix-relative-on-windows.js diff --git a/test/js/node/test/parallel/path-relative.test.js b/test/js/node/test/parallel/path-relative.test.js deleted file mode 100644 index 86c50968de..0000000000 --- a/test/js/node/test/parallel/path-relative.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-path-relative.js -//#SHA1: 9f0d03bf451853a369a3b31b94b902ee4f607e51 -//----------------- -"use strict"; - -const path = require("path"); - -describe("path.relative", () => { - const relativeTests = [ - [ - path.win32.relative, - // Arguments result - [ - ["c:/blah\\blah", "d:/games", "d:\\games"], - ["c:/aaaa/bbbb", "c:/aaaa", ".."], - ["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"], - ["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"], - ["c:/aaaa/", "c:/aaaa/cccc", "cccc"], - ["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"], - ["c:/aaaa/bbbb", "d:\\", "d:\\"], - ["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"], - ["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."], - ["C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"], - ["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"], - ["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."], - ["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["C:\\baz-quux", "C:\\baz", "..\\baz"], - ["C:\\baz", "C:\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"], - ["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"], - ["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"], - ["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"], - ], - ], - [ - path.posix.relative, - // Arguments result - [ - ["/var/lib", "/var", ".."], - ["/var/lib", "/bin", "../../bin"], - ["/var/lib", "/var/lib", ""], - ["/var/lib", "/var/apache", "../apache"], - ["/var/", "/var/lib", "lib"], - ["/", "/var/lib", "var/lib"], - ["/foo/test", "/foo/test/bar/package.json", "bar/package.json"], - ["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."], - ["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"], - ["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"], - ["/baz-quux", "/baz", "../baz"], - ["/baz", "/baz-quux", "../baz-quux"], - ["/page1/page2/foo", "/", "../../.."], - ], - ], - ]; - - relativeTests.forEach(test => { - const relative = test[0]; - const os = relative === path.win32.relative ? "win32" : "posix"; - - test[1].forEach(testCase => { - it(`path.${os}.relative(${JSON.stringify(testCase[0])}, ${JSON.stringify(testCase[1])})`, () => { - const actual = relative(testCase[0], testCase[1]); - const expected = testCase[2]; - expect(actual).toBe(expected); - }); - }); - }); -}); - -//<#END_FILE: test-path-relative.js diff --git a/test/js/node/test/parallel/path-win32-exists.test.js b/test/js/node/test/parallel/path-win32-exists.test.js deleted file mode 100644 index b3aae0de87..0000000000 --- a/test/js/node/test/parallel/path-win32-exists.test.js +++ /dev/null @@ -1,10 +0,0 @@ -//#FILE: test-path-win32-exists.js -//#SHA1: 7d5cfa1f0fc5a13f9878eddd3b119b9c488fecc5 -//----------------- -"use strict"; - -test("path/win32 exists and is the same as path.win32", () => { - expect(require("path/win32")).toBe(require("path").win32); -}); - -//<#END_FILE: test-path-win32-exists.js diff --git a/test/js/node/test/parallel/path-zero-length-strings.test.js b/test/js/node/test/parallel/path-zero-length-strings.test.js deleted file mode 100644 index 0c4766ddd6..0000000000 --- a/test/js/node/test/parallel/path-zero-length-strings.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-path-zero-length-strings.js -//#SHA1: 2f55f68499f5dcd0b2cbb43e7793c0f45175402f -//----------------- -"use strict"; - -// These testcases are specific to one uncommon behavior in path module. Few -// of the functions in path module, treat '' strings as current working -// directory. This test makes sure that the behavior is intact between commits. -// See: https://github.com/nodejs/node/pull/2106 - -const path = require("path"); -const pwd = process.cwd(); - -describe("Path module zero-length strings behavior", () => { - test("Join with zero-length strings", () => { - expect(path.posix.join("")).toBe("."); - expect(path.posix.join("", "")).toBe("."); - expect(path.win32.join("")).toBe("."); - expect(path.win32.join("", "")).toBe("."); - expect(path.join(pwd)).toBe(pwd); - expect(path.join(pwd, "")).toBe(pwd); - }); - - test("Normalize with zero-length strings", () => { - expect(path.posix.normalize("")).toBe("."); - expect(path.win32.normalize("")).toBe("."); - expect(path.normalize(pwd)).toBe(pwd); - }); - - test("isAbsolute with zero-length strings", () => { - expect(path.posix.isAbsolute("")).toBe(false); - expect(path.win32.isAbsolute("")).toBe(false); - }); - - test("Resolve with zero-length strings", () => { - expect(path.resolve("")).toBe(pwd); - expect(path.resolve("", "")).toBe(pwd); - }); - - test("Relative with zero-length strings", () => { - expect(path.relative("", pwd)).toBe(""); - expect(path.relative(pwd, "")).toBe(""); - expect(path.relative(pwd, pwd)).toBe(""); - }); -}); - -//<#END_FILE: test-path-zero-length-strings.js diff --git a/test/js/node/test/parallel/path.test.js b/test/js/node/test/parallel/path.test.js deleted file mode 100644 index f358ae1a27..0000000000 --- a/test/js/node/test/parallel/path.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-path.js -//#SHA1: da9d0113e57d9da3983b555973f7835c17657326 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const path = require("path"); - -// Test thrown TypeErrors -const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; - -function fail(fn) { - const args = Array.from(arguments).slice(1); - - expect(() => { - fn.apply(null, args); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); -} - -test("Invalid argument types", () => { - for (const test of typeErrorTests) { - for (const namespace of [path.posix, path.win32]) { - fail(namespace.join, test); - fail(namespace.resolve, test); - fail(namespace.normalize, test); - fail(namespace.isAbsolute, test); - fail(namespace.relative, test, "foo"); - fail(namespace.relative, "foo", test); - fail(namespace.parse, test); - fail(namespace.dirname, test); - fail(namespace.basename, test); - fail(namespace.extname, test); - - // Undefined is a valid value as the second argument to basename - if (test !== undefined) { - fail(namespace.basename, "foo", test); - } - } - } -}); - -test("path.sep tests", () => { - // windows - expect(path.win32.sep).toBe("\\"); - // posix - expect(path.posix.sep).toBe("/"); -}); - -test("path.delimiter tests", () => { - // windows - expect(path.win32.delimiter).toBe(";"); - // posix - expect(path.posix.delimiter).toBe(":"); -}); - -test("path module", () => { - if (process.platform === "win32") { - expect(path).toBe(path.win32); - } else { - expect(path).toBe(path.posix); - } -}); - -//<#END_FILE: test-path.js diff --git a/test/js/node/test/parallel/perf-gc-crash.test.js b/test/js/node/test/parallel/perf-gc-crash.test.js deleted file mode 100644 index 059bff9b3c..0000000000 --- a/test/js/node/test/parallel/perf-gc-crash.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-perf-gc-crash.js -//#SHA1: 376f73db023a0e22edee3e715b8b5999213b5c93 -//----------------- -"use strict"; - -// Refers to https://github.com/nodejs/node/issues/39548 - -// The test fails if this crashes. If it closes normally, -// then all is good. - -const { PerformanceObserver } = require("perf_hooks"); - -test("PerformanceObserver does not crash on multiple observe and disconnect calls", () => { - // We don't actually care if the observer callback is called here. - const gcObserver = new PerformanceObserver(() => {}); - - expect(() => { - gcObserver.observe({ entryTypes: ["gc"] }); - gcObserver.disconnect(); - }).not.toThrow(); - - const gcObserver2 = new PerformanceObserver(() => {}); - - expect(() => { - gcObserver2.observe({ entryTypes: ["gc"] }); - gcObserver2.disconnect(); - }).not.toThrow(); -}); - -//<#END_FILE: test-perf-gc-crash.js diff --git a/test/js/node/test/parallel/performance-measure.test.js b/test/js/node/test/parallel/performance-measure.test.js deleted file mode 100644 index eff0e0d818..0000000000 --- a/test/js/node/test/parallel/performance-measure.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-performance-measure.js -//#SHA1: f049b29e11ba7864ddf502609caf424eccee7ca5 -//----------------- -"use strict"; - -const { PerformanceObserver, performance } = require("perf_hooks"); - -const DELAY = 1000; -const ALLOWED_MARGIN = 10; - -test("performance measures", done => { - const expected = ["Start to Now", "A to Now", "A to B"]; - const obs = new PerformanceObserver(items => { - items.getEntries().forEach(({ name, duration }) => { - expect(duration).toBeGreaterThan(DELAY - ALLOWED_MARGIN); - expect(expected.shift()).toBe(name); - }); - if (expected.length === 0) { - done(); - } - }); - obs.observe({ entryTypes: ["measure"] }); - - performance.mark("A"); - setTimeout(() => { - performance.measure("Start to Now"); - performance.measure("A to Now", "A"); - - performance.mark("B"); - performance.measure("A to B", "A", "B"); - }, DELAY); -}); - -//<#END_FILE: test-performance-measure.js diff --git a/test/js/node/test/parallel/performance-nodetiming.test.js b/test/js/node/test/parallel/performance-nodetiming.test.js deleted file mode 100644 index 9146bc5b05..0000000000 --- a/test/js/node/test/parallel/performance-nodetiming.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-performance-nodetiming.js -//#SHA1: 7b861bd4f2035688c2cb23ff54525d8e039c2d23 -//----------------- -"use strict"; - -const { performance } = require("perf_hooks"); -const { isMainThread } = require("worker_threads"); - -describe("performance.nodeTiming", () => { - const { nodeTiming } = performance; - - test("basic properties", () => { - expect(nodeTiming.name).toBe("node"); - expect(nodeTiming.entryType).toBe("node"); - }); - - test("timing values", () => { - expect(nodeTiming.startTime).toBe(0); - const now = performance.now(); - expect(nodeTiming.duration).toBeGreaterThanOrEqual(now); - }); - - test("milestone values order", () => { - const keys = ["nodeStart", "v8Start", "environment", "bootstrapComplete"]; - for (let idx = 0; idx < keys.length; idx++) { - if (idx === 0) { - expect(nodeTiming[keys[idx]]).toBeGreaterThanOrEqual(0); - continue; - } - expect(nodeTiming[keys[idx]]).toBeGreaterThan(nodeTiming[keys[idx - 1]]); - } - }); - - test("loop milestones", () => { - expect(nodeTiming.idleTime).toBe(0); - if (isMainThread) { - expect(nodeTiming.loopStart).toBe(-1); - } else { - expect(nodeTiming.loopStart).toBeGreaterThanOrEqual(nodeTiming.bootstrapComplete); - } - expect(nodeTiming.loopExit).toBe(-1); - }); - - test("idle time and loop exit", done => { - setTimeout(() => { - expect(nodeTiming.idleTime).toBeGreaterThanOrEqual(0); - expect(nodeTiming.idleTime + nodeTiming.loopExit).toBeLessThanOrEqual(nodeTiming.duration); - expect(nodeTiming.loopStart).toBeGreaterThanOrEqual(nodeTiming.bootstrapComplete); - done(); - }, 1); - }); - - test("loop exit on process exit", done => { - process.on("exit", () => { - expect(nodeTiming.loopExit).toBeGreaterThan(0); - done(); - }); - // Trigger process exit - process.exit(); - }); -}); - -//<#END_FILE: test-performance-nodetiming.js diff --git a/test/js/node/test/parallel/performanceobserver-gc.test.js b/test/js/node/test/parallel/performanceobserver-gc.test.js deleted file mode 100644 index b5e6c913d0..0000000000 --- a/test/js/node/test/parallel/performanceobserver-gc.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-performanceobserver-gc.js -//#SHA1: 2a18df2fa465d96ec5c9ee5d42052c732c777e2b -//----------------- -"use strict"; - -// Verifies that setting up two observers to listen -// to gc performance does not crash. - -const { PerformanceObserver } = require("perf_hooks"); - -test("Setting up two observers to listen to gc performance does not crash", () => { - // We don't actually care if the callback is ever invoked in this test - const obs = new PerformanceObserver(() => {}); - const obs2 = new PerformanceObserver(() => {}); - - expect(() => { - obs.observe({ type: "gc" }); - obs2.observe({ type: "gc" }); - }).not.toThrow(); -}); - -//<#END_FILE: test-performanceobserver-gc.js diff --git a/test/js/node/test/parallel/pipe-abstract-socket.test.js b/test/js/node/test/parallel/pipe-abstract-socket.test.js deleted file mode 100644 index 934108f4c5..0000000000 --- a/test/js/node/test/parallel/pipe-abstract-socket.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-pipe-abstract-socket.js -//#SHA1: 085e51018c26c846b8ea9a2b23da4f45f37fe82f -//----------------- -"use strict"; - -const net = require("net"); - -const isLinux = process.platform === "linux"; - -if (!isLinux) { - it.skip("Skipping test on non-Linux platforms", () => {}); -} else { - describe("Abstract Unix socket tests", () => { - const path = "\0abstract"; - const expectedErrorMessage = "The argument 'options' can not set readableAll or writableAll to true when path is abstract unix socket. Received"; - - test("throws when setting readableAll to true", () => { - const options = { - path, - readableAll: true, - }; - - expect(() => { - const server = net.createServer(jest.fn()); - server.listen(options); - }).toThrow( - expect.objectContaining({ - message: `${expectedErrorMessage} ${JSON.stringify(options)}`, - code: "ERR_INVALID_ARG_VALUE", - }), - ); - }); - - test("throws when setting writableAll to true", () => { - const options = { - path, - writableAll: true, - } ; - - expect(() => { - const server = net.createServer(jest.fn()); - server.listen(options); - }).toThrow( - expect.objectContaining({ - message: `${expectedErrorMessage} ${JSON.stringify(options)}`, - code: "ERR_INVALID_ARG_VALUE", - }), - ); - }); - - test("throws when setting both readableAll and writableAll to true", () => { - const options = { - path, - readableAll: true, - writableAll: true, - }; - - expect(() => { - const server = net.createServer(jest.fn()); - server.listen(options); - }).toThrow( - expect.objectContaining({ - message: `${expectedErrorMessage} ${JSON.stringify(options)}`, - code: "ERR_INVALID_ARG_VALUE", - }), - ); - }); - }); -} - -//<#END_FILE: test-pipe-abstract-socket.js diff --git a/test/js/node/test/parallel/preload-print-process-argv.test.js b/test/js/node/test/parallel/preload-print-process-argv.test.js deleted file mode 100644 index 9f26ef5044..0000000000 --- a/test/js/node/test/parallel/preload-print-process-argv.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-preload-print-process-argv.js -//#SHA1: 071fd007af8342d4f1924da004a6dcc7d419cf9f -//----------------- -"use strict"; - -// This tests that process.argv is the same in the preloaded module -// and the user module. - -const tmpdir = require("../common/tmpdir"); -const { spawnSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); - -beforeAll(() => { - tmpdir.refresh(); -}); - -test("process.argv is the same in preloaded and user module", () => { - const preloadPath = path.join(tmpdir.path, "preload.js"); - const mainPath = path.join(tmpdir.path, "main.js"); - - fs.writeFileSync(preloadPath, "console.log(JSON.stringify(process.argv));", "utf-8"); - - fs.writeFileSync(mainPath, "console.log(JSON.stringify(process.argv));", "utf-8"); - - const child = spawnSync(process.execPath, ["-r", "./preload.js", "main.js"], { cwd: tmpdir.path }); - - expect(child.status).toBe(0); - - if (child.status !== 0) { - console.log(child.stderr.toString()); - } - - const lines = child.stdout.toString().trim().split("\n"); - expect(JSON.parse(lines[0])).toEqual(JSON.parse(lines[1])); -}); - -//<#END_FILE: test-preload-print-process-argv.js diff --git a/test/js/node/test/parallel/preload-self-referential.test.js b/test/js/node/test/parallel/preload-self-referential.test.js deleted file mode 100644 index b050248bd6..0000000000 --- a/test/js/node/test/parallel/preload-self-referential.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-preload-self-referential.js -//#SHA1: 24bb3def0bae68082aee5280abc5116ebd81c972 -//----------------- -"use strict"; - -const path = require("path"); -const { exec } = require("child_process"); - -const nodeBinary = process.argv[0]; - -// Skip test if not in main thread -if (typeof Worker !== "undefined") { - test.skip("process.chdir is not available in Workers", () => {}); -} else { - test("self-referential module preload", done => { - const selfRefModule = path.join(__dirname, "..", "fixtures", "self_ref_module"); - const fixtureA = path.join(__dirname, "..", "fixtures", "printA.js"); - - exec(`"${nodeBinary}" -r self_ref "${fixtureA}"`, { cwd: selfRefModule }, (err, stdout, stderr) => { - expect(err).toBeFalsy(); - expect(stdout).toBe("A\n"); - done(); - }); - }); -} - -//<#END_FILE: test-preload-self-referential.js diff --git a/test/js/node/test/parallel/process-abort.test.js b/test/js/node/test/parallel/process-abort.test.js deleted file mode 100644 index 24731b9cc3..0000000000 --- a/test/js/node/test/parallel/process-abort.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-process-abort.js -//#SHA1: ca6e85cb79ad3e78182547bd6be24625268aced4 -//----------------- -"use strict"; - -// Skip this test in Workers as process.abort() is not available -if (typeof Worker !== "undefined") { - test.skip("process.abort() is not available in Workers", () => {}); -} else { - describe("process.abort", () => { - test("should not have a prototype", () => { - expect(process.abort.prototype).toBeUndefined(); - }); - - test("should throw TypeError when instantiated", () => { - expect(() => new process.abort()).toThrow(TypeError); - }); - }); -} - -//<#END_FILE: test-process-abort.js diff --git a/test/js/node/test/parallel/process-chdir-errormessage.test.js b/test/js/node/test/parallel/process-chdir-errormessage.test.js deleted file mode 100644 index 3703d4e1b9..0000000000 --- a/test/js/node/test/parallel/process-chdir-errormessage.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-process-chdir-errormessage.js -//#SHA1: d0eee0a43892b20221d341b892fa425fe207c506 -//----------------- -"use strict"; - -// Skip test in workers where process.chdir is not available -if (typeof Worker !== "undefined") { - test.skip("process.chdir is not available in Workers"); -} else { - test("process.chdir throws correct error for non-existent directory", () => { - expect(() => { - process.chdir("does-not-exist"); - }).toThrow( - expect.objectContaining({ - name: "Error", - code: "ENOENT", - message: expect.stringMatching(/ENOENT: no such file or directory, chdir .+ -> 'does-not-exist'/), - path: process.cwd(), - syscall: "chdir", - dest: "does-not-exist", - }), - ); - }); -} - -//<#END_FILE: test-process-chdir-errormessage.js diff --git a/test/js/node/test/parallel/process-constants-noatime.test.js b/test/js/node/test/parallel/process-constants-noatime.test.js deleted file mode 100644 index 319d5ff97a..0000000000 --- a/test/js/node/test/parallel/process-constants-noatime.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-process-constants-noatime.js -//#SHA1: cc1fb622e4cb1e217a3e7a0662db5050dc2562c2 -//----------------- -"use strict"; - -const fs = require("fs"); - -test("O_NOATIME constant", () => { - if (process.platform === "linux") { - expect(fs.constants).toHaveProperty("O_NOATIME"); - expect(fs.constants.O_NOATIME).toBe(0x40000); - } else { - expect(fs.constants).not.toHaveProperty("O_NOATIME"); - } -}); - -//<#END_FILE: test-process-constants-noatime.js diff --git a/test/js/node/test/parallel/process-emit.test.js b/test/js/node/test/parallel/process-emit.test.js deleted file mode 100644 index ca5b2c353e..0000000000 --- a/test/js/node/test/parallel/process-emit.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-process-emit.js -//#SHA1: a019bda4bcc14ef2e20bad3cc89cf8676ed5bc49 -//----------------- -"use strict"; - -const sym = Symbol(); - -test("process.emit for normal event", () => { - const listener = jest.fn(); - process.on("normal", listener); - - process.emit("normal", "normalData"); - - expect(listener).toHaveBeenCalledTimes(1); - expect(listener).toHaveBeenCalledWith("normalData"); - - process.removeListener("normal", listener); -}); - -test("process.emit for symbol event", () => { - const listener = jest.fn(); - process.on(sym, listener); - - process.emit(sym, "symbolData"); - - expect(listener).toHaveBeenCalledTimes(1); - expect(listener).toHaveBeenCalledWith("symbolData"); - - process.removeListener(sym, listener); -}); - -test("process.emit for SIGPIPE signal", () => { - const listener = jest.fn(); - process.on("SIGPIPE", listener); - - process.emit("SIGPIPE", "signalData"); - - expect(listener).toHaveBeenCalledTimes(1); - expect(listener).toHaveBeenCalledWith("signalData"); - - process.removeListener("SIGPIPE", listener); -}); - -test("process._eventsCount is not NaN", () => { - expect(Number.isNaN(process._eventsCount)).toBe(false); -}); - -//<#END_FILE: test-process-emit.js diff --git a/test/js/node/test/parallel/process-env-windows-error-reset.test.js b/test/js/node/test/parallel/process-env-windows-error-reset.test.js deleted file mode 100644 index 57c703c828..0000000000 --- a/test/js/node/test/parallel/process-env-windows-error-reset.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-process-env-windows-error-reset.js -//#SHA1: f7e32cc8da8c33ecfa3cddeb13d3dd8689d6af64 -//----------------- -"use strict"; - -// This checks that after accessing a missing env var, a subsequent -// env read will succeed even for empty variables. - -test("empty env var after accessing missing env var", () => { - process.env.FOO = ""; - process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions - const foo = process.env.FOO; - - expect(foo).toBe(""); -}); - -test("env var existence after accessing missing env var", () => { - process.env.FOO = ""; - process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions - const hasFoo = "FOO" in process.env; - - expect(hasFoo).toBe(true); -}); - -//<#END_FILE: test-process-env-windows-error-reset.js diff --git a/test/js/node/test/parallel/process-features.test.js b/test/js/node/test/parallel/process-features.test.js deleted file mode 100644 index ee32335bf1..0000000000 --- a/test/js/node/test/parallel/process-features.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-process-features.js -//#SHA1: 18e2385d3d69890cd826120906ce495a0bc4ba85 -//----------------- -"use strict"; - -test("process.features", () => { - const keys = new Set(Object.keys(process.features)); - - expect(keys).toEqual( - new Set(["inspector", "debug", "uv", "ipv6", "tls_alpn", "tls_sni", "tls_ocsp", "tls", "cached_builtins"]), - ); - - for (const key of keys) { - expect(typeof process.features[key]).toBe("boolean"); - } -}); - -//<#END_FILE: test-process-features.js diff --git a/test/js/node/test/parallel/process-hrtime-bigint.test.js b/test/js/node/test/parallel/process-hrtime-bigint.test.js deleted file mode 100644 index 3e8aa3d6fb..0000000000 --- a/test/js/node/test/parallel/process-hrtime-bigint.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-process-hrtime-bigint.js -//#SHA1: 2fbd7286e22ca2f6d3155f829032524692e4d77c -//----------------- -"use strict"; - -// Tests that process.hrtime.bigint() works. - -test("process.hrtime.bigint() works", () => { - const start = process.hrtime.bigint(); - expect(typeof start).toBe("bigint"); - - const end = process.hrtime.bigint(); - expect(typeof end).toBe("bigint"); - - expect(end - start).toBeGreaterThanOrEqual(0n); -}); - -//<#END_FILE: test-process-hrtime-bigint.js diff --git a/test/js/node/test/parallel/process-initgroups.test.js b/test/js/node/test/parallel/process-initgroups.test.js deleted file mode 100644 index 8dac517f1e..0000000000 --- a/test/js/node/test/parallel/process-initgroups.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-process-initgroups.js -//#SHA1: e7321b3005c066a0b2edbe457e695622b9f2b8e9 -//----------------- -"use strict"; - -if (process.platform === "win32") { - test("process.initgroups is undefined on Windows", () => { - expect(process.initgroups).toBeUndefined(); - }); -} else if (typeof process.initgroups !== "undefined") { - describe("process.initgroups", () => { - test("throws TypeError for invalid user argument", () => { - [undefined, null, true, {}, [], () => {}].forEach(val => { - expect(() => { - process.initgroups(val); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "user" argument must be one of type number or string.'), - }), - ); - }); - }); - - test("throws TypeError for invalid extraGroup argument", () => { - [undefined, null, true, {}, [], () => {}].forEach(val => { - expect(() => { - process.initgroups("foo", val); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "extraGroup" argument must be one of type number or string.'), - }), - ); - }); - }); - - test("throws ERR_UNKNOWN_CREDENTIAL for non-existent group", () => { - expect(() => { - process.initgroups("fhqwhgadshgnsdhjsdbkhsdabkfabkveyb", "fhqwhgadshgnsdhjsdbkhsdabkfabkveyb"); - }).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_CREDENTIAL", - message: expect.stringContaining("Group identifier does not exist"), - }), - ); - }); - }); -} - -//<#END_FILE: test-process-initgroups.js diff --git a/test/js/node/test/parallel/promise-unhandled-issue-43655.test.js b/test/js/node/test/parallel/promise-unhandled-issue-43655.test.js deleted file mode 100644 index cdaa2e24a9..0000000000 --- a/test/js/node/test/parallel/promise-unhandled-issue-43655.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-promise-unhandled-issue-43655.js -//#SHA1: d0726f3e05d7aba39fc84013c63399d915956e30 -//----------------- -"use strict"; - -function delay(time) { - return new Promise(resolve => { - setTimeout(resolve, time); - }); -} - -test("Promise unhandled rejection performance", async () => { - for (let i = 0; i < 100000; i++) { - await new Promise((resolve, reject) => { - reject("value"); - }).then( - () => {}, - () => {}, - ); - } - - const time0 = Date.now(); - await delay(0); - - const diff = Date.now() - time0; - expect(diff).toBeLessThan(500); -}, 10000); // Increased timeout to 10 seconds to ensure enough time for the test - -//<#END_FILE: test-promise-unhandled-issue-43655.js diff --git a/test/js/node/test/parallel/querystring-maxkeys-non-finite.test.js b/test/js/node/test/parallel/querystring-maxkeys-non-finite.test.js deleted file mode 100644 index 10116e91b3..0000000000 --- a/test/js/node/test/parallel/querystring-maxkeys-non-finite.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-querystring-maxKeys-non-finite.js -//#SHA1: a1b76b45daad6e46e5504d52e5931d9e4a6d745d -//----------------- -"use strict"; - -// This test was originally written to test a regression -// that was introduced by -// https://github.com/nodejs/node/pull/2288#issuecomment-179543894 - -const querystring = require("querystring"); - -// Taken from express-js/body-parser -// https://github.com/expressjs/body-parser/blob/ed25264fb494cf0c8bc992b8257092cd4f694d5e/test/urlencoded.js#L636-L651 -function createManyParams(count) { - let str = ""; - - if (count === 0) { - return str; - } - - str += "0=0"; - - for (let i = 1; i < count; i++) { - const n = i.toString(36); - str += `&${n}=${n}`; - } - - return str; -} - -const count = 10000; -const originalMaxLength = 1000; -const params = createManyParams(count); - -// thealphanerd -// 27def4f introduced a change to parse that would cause Infinity -// to be passed to String.prototype.split as an argument for limit -// In this instance split will always return an empty array -// this test confirms that the output of parse is the expected length -// when passed Infinity as the argument for maxKeys -describe("querystring.parse with non-finite maxKeys", () => { - test("Infinity maxKeys should return the length of input", () => { - const resultInfinity = querystring.parse(params, undefined, undefined, { - maxKeys: Infinity, - }); - expect(Object.keys(resultInfinity)).toHaveLength(count); - }); - - test("NaN maxKeys should return the length of input", () => { - const resultNaN = querystring.parse(params, undefined, undefined, { - maxKeys: NaN, - }); - expect(Object.keys(resultNaN)).toHaveLength(count); - }); - - test('String "Infinity" maxKeys should return the maxLength defined by parse internals', () => { - const resultInfinityString = querystring.parse(params, undefined, undefined, { - maxKeys: "Infinity", - }); - expect(Object.keys(resultInfinityString)).toHaveLength(originalMaxLength); - }); - - test('String "NaN" maxKeys should return the maxLength defined by parse internals', () => { - const resultNaNString = querystring.parse(params, undefined, undefined, { - maxKeys: "NaN", - }); - expect(Object.keys(resultNaNString)).toHaveLength(originalMaxLength); - }); -}); - -//<#END_FILE: test-querystring-maxKeys-non-finite.js diff --git a/test/js/node/test/parallel/querystring-multichar-separator.test.js b/test/js/node/test/parallel/querystring-multichar-separator.test.js deleted file mode 100644 index cba18fc69a..0000000000 --- a/test/js/node/test/parallel/querystring-multichar-separator.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-querystring-multichar-separator.js -//#SHA1: 22b484432502e6f32fc4517ea91060b983c7be25 -//----------------- -"use strict"; - -const qs = require("querystring"); - -function check(actual, expected) { - expect(actual).not.toBeInstanceOf(Object); - expect(Object.keys(actual).sort()).toEqual(Object.keys(expected).sort()); - Object.keys(expected).forEach(function (key) { - expect(actual[key]).toEqual(expected[key]); - }); -} - -test("qs.parse with multi-character separator", () => { - check(qs.parse("foo=>bar&&bar=>baz", "&&", "=>"), { foo: "bar", bar: "baz" }); -}); - -test("qs.stringify with multi-character separator", () => { - check(qs.stringify({ foo: "bar", bar: "baz" }, "&&", "=>"), "foo=>bar&&bar=>baz"); -}); - -test("qs.parse with different multi-character separators", () => { - check(qs.parse("foo==>bar, bar==>baz", ", ", "==>"), { foo: "bar", bar: "baz" }); -}); - -test("qs.stringify with different multi-character separators", () => { - check(qs.stringify({ foo: "bar", bar: "baz" }, ", ", "==>"), "foo==>bar, bar==>baz"); -}); - -//<#END_FILE: test-querystring-multichar-separator.js diff --git a/test/js/node/test/parallel/quic-internal-endpoint-options.test.js b/test/js/node/test/parallel/quic-internal-endpoint-options.test.js deleted file mode 100644 index 9a5694dcfd..0000000000 --- a/test/js/node/test/parallel/quic-internal-endpoint-options.test.js +++ /dev/null @@ -1,192 +0,0 @@ -//#FILE: test-quic-internal-endpoint-options.js -//#SHA1: 089ba4358a2a9ed3c5463e59d205ee7854f26f30 -//----------------- -// Flags: --expose-internals -"use strict"; - -const common = require("../common"); -if (!common.hasQuic) common.skip("missing quic"); - -const { internalBinding } = require("internal/test/binding"); -const quic = internalBinding("quic"); - -quic.setCallbacks({ - onEndpointClose() {}, - onSessionNew() {}, - onSessionClose() {}, - onSessionDatagram() {}, - onSessionDatagramStatus() {}, - onSessionHandshake() {}, - onSessionPathValidation() {}, - onSessionTicket() {}, - onSessionVersionNegotiation() {}, - onStreamCreated() {}, - onStreamBlocked() {}, - onStreamClose() {}, - onStreamReset() {}, - onStreamHeaders() {}, - onStreamTrailers() {}, -}); - -test("Invalid Endpoint constructor arguments", () => { - expect(() => new quic.Endpoint()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - expect(() => new quic.Endpoint("a")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - expect(() => new quic.Endpoint(null)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - expect(() => new quic.Endpoint(false)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -test("Default options work", () => { - expect(() => new quic.Endpoint({})).not.toThrow(); -}); - -const cases = [ - { - key: "retryTokenExpiration", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "tokenExpiration", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxConnectionsPerHost", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxConnectionsTotal", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxStatelessResetsPerHost", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "addressLRUSize", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxRetries", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxPayloadSize", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "unacknowledgedPacketThreshold", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "validateAddress", - valid: [true, false, 0, 1, "a"], - invalid: [], - }, - { - key: "disableStatelessReset", - valid: [true, false, 0, 1, "a"], - invalid: [], - }, - { - key: "ipv6Only", - valid: [true, false, 0, 1, "a"], - invalid: [], - }, - { - key: "cc", - valid: [ - quic.CC_ALGO_RENO, - quic.CC_ALGO_CUBIC, - quic.CC_ALGO_BBR, - quic.CC_ALGO_BBR2, - quic.CC_ALGO_RENO_STR, - quic.CC_ALGO_CUBIC_STR, - quic.CC_ALGO_BBR_STR, - quic.CC_ALGO_BBR2_STR, - ], - invalid: [-1, 4, 1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "udpReceiveBufferSize", - valid: [0, 1, 2, 3, 4, 1000], - invalid: [-1, "a", null, false, true, {}, [], () => {}], - }, - { - key: "udpSendBufferSize", - valid: [0, 1, 2, 3, 4, 1000], - invalid: [-1, "a", null, false, true, {}, [], () => {}], - }, - { - key: "udpTTL", - valid: [0, 1, 2, 3, 4, 255], - invalid: [-1, 256, "a", null, false, true, {}, [], () => {}], - }, - { - key: "resetTokenSecret", - valid: [new Uint8Array(16), new Uint16Array(8), new Uint32Array(4)], - invalid: ["a", null, false, true, {}, [], () => {}, new Uint8Array(15), new Uint8Array(17), new ArrayBuffer(16)], - }, - { - key: "tokenSecret", - valid: [new Uint8Array(16), new Uint16Array(8), new Uint32Array(4)], - invalid: ["a", null, false, true, {}, [], () => {}, new Uint8Array(15), new Uint8Array(17), new ArrayBuffer(16)], - }, - { - // Unknown options are ignored entirely for any value type - key: "ignored", - valid: ["a", null, false, true, {}, [], () => {}], - invalid: [], - }, -]; - -for (const { key, valid, invalid } of cases) { - describe(`Endpoint option: ${key}`, () => { - test.each(valid)("valid value: %p", value => { - const options = { [key]: value }; - expect(() => new quic.Endpoint(options)).not.toThrow(); - }); - - test.each(invalid)("invalid value: %p", value => { - const options = { [key]: value }; - expect(() => new quic.Endpoint(options)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - message: expect.any(String), - }), - ); - }); - }); -} - -//<#END_FILE: test-quic-internal-endpoint-options.js diff --git a/test/js/node/test/parallel/readable-large-hwm.test.js b/test/js/node/test/parallel/readable-large-hwm.test.js deleted file mode 100644 index 2fc8b24615..0000000000 --- a/test/js/node/test/parallel/readable-large-hwm.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-readable-large-hwm.js -//#SHA1: 1f1184c10e91262eb541830677abcd3e759d304e -//----------------- -"use strict"; -const { Readable } = require("stream"); - -// Make sure that readable completes -// even when reading larger buffer. -test("readable completes when reading larger buffer", done => { - const bufferSize = 10 * 1024 * 1024; - let n = 0; - const r = new Readable({ - read() { - // Try to fill readable buffer piece by piece. - r.push(Buffer.alloc(bufferSize / 10)); - - if (n++ > 10) { - r.push(null); - } - }, - }); - - r.on("readable", () => { - while (true) { - const ret = r.read(bufferSize); - if (ret === null) break; - } - }); - - r.on("end", () => { - done(); - }); -}); - -//<#END_FILE: test-readable-large-hwm.js diff --git a/test/js/node/test/parallel/readable-single-end.test.js b/test/js/node/test/parallel/readable-single-end.test.js deleted file mode 100644 index f8d6813997..0000000000 --- a/test/js/node/test/parallel/readable-single-end.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-readable-single-end.js -//#SHA1: eb85fac7020fa4b5bc4a0f17ba287e8ab3c9dd2f -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test ensures that there will not be an additional empty 'readable' -// event when stream has ended (only 1 event signalling about end) - -test("Readable stream emits only one event when ended", () => { - const r = new Readable({ - read: () => {}, - }); - - r.push(null); - - const readableSpy = jest.fn(); - const endSpy = jest.fn(); - - r.on("readable", readableSpy); - r.on("end", endSpy); - - return new Promise(resolve => { - setTimeout(() => { - expect(readableSpy).toHaveBeenCalledTimes(1); - expect(endSpy).toHaveBeenCalledTimes(1); - resolve(); - }, 0); - }); -}); - -//<#END_FILE: test-readable-single-end.js diff --git a/test/js/node/test/parallel/readline-async-iterators-destroy.test.js b/test/js/node/test/parallel/readline-async-iterators-destroy.test.js deleted file mode 100644 index 1680fec8d8..0000000000 --- a/test/js/node/test/parallel/readline-async-iterators-destroy.test.js +++ /dev/null @@ -1,99 +0,0 @@ -//#FILE: test-readline-async-iterators-destroy.js -//#SHA1: c082e44d93a4bc199683c7cad216da7bc3f0dc7b -//----------------- -"use strict"; - -const fs = require("fs"); -const { once } = require("events"); -const readline = require("readline"); -const path = require("path"); -const os = require("os"); - -const tmpdir = os.tmpdir(); - -const filename = path.join(tmpdir, "test.txt"); - -const testContents = [ - "", - "\n", - "line 1", - "line 1\nline 2 南越国是前203年至前111年存在于岭南地区的一个国家\nline 3\ntrailing", - "line 1\nline 2\nline 3 ends with newline\n", -]; - -async function testSimpleDestroy() { - for (const fileContent of testContents) { - fs.writeFileSync(filename, fileContent); - - const readable = fs.createReadStream(filename); - const rli = readline.createInterface({ - input: readable, - crlfDelay: Infinity, - }); - - const iteratedLines = []; - for await (const k of rli) { - iteratedLines.push(k); - break; - } - - const expectedLines = fileContent.split("\n"); - if (expectedLines[expectedLines.length - 1] === "") { - expectedLines.pop(); - } - expectedLines.splice(1); - - expect(iteratedLines).toEqual(expectedLines); - - rli.close(); - readable.destroy(); - - await once(readable, "close"); - } -} - -async function testMutualDestroy() { - for (const fileContent of testContents) { - fs.writeFileSync(filename, fileContent); - - const readable = fs.createReadStream(filename); - const rli = readline.createInterface({ - input: readable, - crlfDelay: Infinity, - }); - - const expectedLines = fileContent.split("\n"); - if (expectedLines[expectedLines.length - 1] === "") { - expectedLines.pop(); - } - expectedLines.splice(2); - - const iteratedLines = []; - for await (const k of rli) { - iteratedLines.push(k); - for await (const l of rli) { - iteratedLines.push(l); - break; - } - expect(iteratedLines).toEqual(expectedLines); - break; - } - - expect(iteratedLines).toEqual(expectedLines); - - rli.close(); - readable.destroy(); - - await once(readable, "close"); - } -} - -test("Simple destroy", async () => { - await testSimpleDestroy(); -}); - -test("Mutual destroy", async () => { - await testMutualDestroy(); -}); - -//<#END_FILE: test-readline-async-iterators-destroy.js diff --git a/test/js/node/test/parallel/readline-csi.test.js b/test/js/node/test/parallel/readline-csi.test.js deleted file mode 100644 index e22cdf3b53..0000000000 --- a/test/js/node/test/parallel/readline-csi.test.js +++ /dev/null @@ -1,211 +0,0 @@ -//#FILE: test-readline-csi.js -//#SHA1: 6c80ba1b15c53086d80064d93c6f4cf56d1056d6 -//----------------- -"use strict"; - -const readline = require("readline"); -const { Writable } = require("stream"); - -// Mock the CSI object -const CSI = { - kClearToLineBeginning: "\x1b[1K", - kClearToLineEnd: "\x1b[0K", - kClearLine: "\x1b[2K", - kClearScreenDown: "\x1b[0J", -}; - -test("CSI constants", () => { - expect(CSI).toBeDefined(); - expect(CSI.kClearToLineBeginning).toBe("\x1b[1K"); - expect(CSI.kClearToLineEnd).toBe("\x1b[0K"); - expect(CSI.kClearLine).toBe("\x1b[2K"); - expect(CSI.kClearScreenDown).toBe("\x1b[0J"); -}); - -class TestWritable extends Writable { - constructor() { - super(); - this.data = ""; - } - _write(chunk, encoding, callback) { - this.data += chunk.toString(); - callback(); - } -} - -let writable; - -beforeEach(() => { - writable = new TestWritable(); -}); - -test("clearScreenDown", () => { - expect(readline.clearScreenDown(writable)).toBe(true); - expect(writable.data).toBe(CSI.kClearScreenDown); - - writable.data = ""; - expect(readline.clearScreenDown(writable, jest.fn())).toBe(true); - - expect(() => { - readline.clearScreenDown(writable, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(readline.clearScreenDown(null, jest.fn())).toBe(true); - expect(readline.clearScreenDown(undefined, jest.fn())).toBe(true); -}); - -test("clearLine", () => { - expect(readline.clearLine(writable, -1)).toBe(true); - expect(writable.data).toBe(CSI.kClearToLineBeginning); - - writable.data = ""; - expect(readline.clearLine(writable, 1)).toBe(true); - expect(writable.data).toBe(CSI.kClearToLineEnd); - - writable.data = ""; - expect(readline.clearLine(writable, 0)).toBe(true); - expect(writable.data).toBe(CSI.kClearLine); - - writable.data = ""; - expect(readline.clearLine(writable, -1, jest.fn())).toBe(true); - expect(writable.data).toBe(CSI.kClearToLineBeginning); - - expect(() => { - readline.clearLine(writable, 0, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(readline.clearLine(null, 0)).toBe(true); - expect(readline.clearLine(undefined, 0)).toBe(true); - expect(readline.clearLine(null, 0, jest.fn())).toBe(true); - expect(readline.clearLine(undefined, 0, jest.fn())).toBe(true); -}); - -test("moveCursor", () => { - const testCases = [ - [0, 0, ""], - [1, 0, "\x1b[1C"], - [-1, 0, "\x1b[1D"], - [0, 1, "\x1b[1B"], - [0, -1, "\x1b[1A"], - [1, 1, "\x1b[1C\x1b[1B"], - [-1, 1, "\x1b[1D\x1b[1B"], - [-1, -1, "\x1b[1D\x1b[1A"], - [1, -1, "\x1b[1C\x1b[1A"], - ]; - - testCases.forEach(([dx, dy, expected]) => { - writable.data = ""; - expect(readline.moveCursor(writable, dx, dy)).toBe(true); - expect(writable.data).toBe(expected); - - writable.data = ""; - expect(readline.moveCursor(writable, dx, dy, jest.fn())).toBe(true); - expect(writable.data).toBe(expected); - }); - - expect(() => { - readline.moveCursor(writable, 1, 1, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(readline.moveCursor(null, 1, 1)).toBe(true); - expect(readline.moveCursor(undefined, 1, 1)).toBe(true); - expect(readline.moveCursor(null, 1, 1, jest.fn())).toBe(true); - expect(readline.moveCursor(undefined, 1, 1, jest.fn())).toBe(true); -}); - -test("cursorTo", () => { - expect(readline.cursorTo(null)).toBe(true); - expect(readline.cursorTo()).toBe(true); - expect(readline.cursorTo(null, 1, 1, jest.fn())).toBe(true); - expect(readline.cursorTo(undefined, 1, 1, jest.fn())).toBe(true); - - expect(readline.cursorTo(writable, "a")).toBe(true); - expect(writable.data).toBe(""); - - writable.data = ""; - expect(readline.cursorTo(writable, "a", "b")).toBe(true); - expect(writable.data).toBe(""); - - writable.data = ""; - expect(() => readline.cursorTo(writable, "a", 1)).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_CURSOR_POS", - message: "Cannot set cursor row without setting its column", - }), - ); - expect(writable.data).toBe(""); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, "a")).toBe(true); - expect(writable.data).toBe("\x1b[2G"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1)).toBe(true); - expect(writable.data).toBe("\x1b[2G"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, 2)).toBe(true); - expect(writable.data).toBe("\x1b[3;2H"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, 2, jest.fn())).toBe(true); - expect(writable.data).toBe("\x1b[3;2H"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, jest.fn())).toBe(true); - expect(writable.data).toBe("\x1b[2G"); - - expect(() => { - readline.cursorTo(writable, 1, 1, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(() => { - readline.cursorTo(writable, NaN); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - }), - ); - - expect(() => { - readline.cursorTo(writable, 1, NaN); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - }), - ); - - expect(() => { - readline.cursorTo(writable, NaN, NaN); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - }), - ); -}); - -//<#END_FILE: test-readline-csi.js diff --git a/test/js/node/test/parallel/readline-emit-keypress-events.test.js b/test/js/node/test/parallel/readline-emit-keypress-events.test.js deleted file mode 100644 index 34eaeaa63b..0000000000 --- a/test/js/node/test/parallel/readline-emit-keypress-events.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-readline-emit-keypress-events.js -//#SHA1: 79b97832d1108222b690320e06d4028f73910125 -//----------------- -"use strict"; -// emitKeypressEvents is thoroughly tested in test-readline-keys.js. -// However, that test calls it implicitly. This is just a quick sanity check -// to verify that it works when called explicitly. - -const readline = require("readline"); -const { PassThrough } = require("stream"); - -const expectedSequence = ["f", "o", "o"]; -const expectedKeys = [ - { sequence: "f", name: "f", ctrl: false, meta: false, shift: false }, - { sequence: "o", name: "o", ctrl: false, meta: false, shift: false }, - { sequence: "o", name: "o", ctrl: false, meta: false, shift: false }, -]; - -test("emitKeypressEvents with stream", () => { - const stream = new PassThrough(); - const sequence = []; - const keys = []; - - readline.emitKeypressEvents(stream); - stream.on("keypress", (s, k) => { - sequence.push(s); - keys.push(k); - }); - stream.write("foo"); - - expect(sequence).toEqual(expectedSequence); - expect(keys).toEqual(expectedKeys); -}); - -test("emitKeypressEvents after attaching listener", () => { - const stream = new PassThrough(); - const sequence = []; - const keys = []; - - stream.on("keypress", (s, k) => { - sequence.push(s); - keys.push(k); - }); - readline.emitKeypressEvents(stream); - stream.write("foo"); - - expect(sequence).toEqual(expectedSequence); - expect(keys).toEqual(expectedKeys); -}); - -test("emitKeypressEvents with listener removal", () => { - const stream = new PassThrough(); - const sequence = []; - const keys = []; - const keypressListener = (s, k) => { - sequence.push(s); - keys.push(k); - }; - - stream.on("keypress", keypressListener); - readline.emitKeypressEvents(stream); - stream.removeListener("keypress", keypressListener); - stream.write("foo"); - - expect(sequence).toEqual([]); - expect(keys).toEqual([]); - - stream.on("keypress", keypressListener); - stream.write("foo"); - - expect(sequence).toEqual(expectedSequence); - expect(keys).toEqual(expectedKeys); -}); - -//<#END_FILE: test-readline-emit-keypress-events.js diff --git a/test/js/node/test/parallel/readline-interface-escapecodetimeout.test.js b/test/js/node/test/parallel/readline-interface-escapecodetimeout.test.js deleted file mode 100644 index c11f399b5a..0000000000 --- a/test/js/node/test/parallel/readline-interface-escapecodetimeout.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-readline-interface-escapecodetimeout.js -//#SHA1: 6d32b42ce02228999a37e3f7017ddd747b346d5c -//----------------- -"use strict"; - -const readline = require("readline"); -const EventEmitter = require("events").EventEmitter; - -// This test ensures that the escapeCodeTimeout option set correctly - -class FakeInput extends EventEmitter { - resume() {} - pause() {} - write() {} - end() {} -} - -test("escapeCodeTimeout option is set correctly", () => { - const fi = new FakeInput(); - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: 50, - }); - expect(rli.escapeCodeTimeout).toBe(50); - rli.close(); -}); - -test.each([null, {}, NaN, "50"])("invalid escapeCodeTimeout input throws TypeError", invalidInput => { - const fi = new FakeInput(); - expect(() => { - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: invalidInput, - }); - rli.close(); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-readline-interface-escapecodetimeout.js diff --git a/test/js/node/test/parallel/readline-position.test.js b/test/js/node/test/parallel/readline-position.test.js deleted file mode 100644 index 967f5ba830..0000000000 --- a/test/js/node/test/parallel/readline-position.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-readline-position.js -//#SHA1: 652d50a766a0728968f155ca650ff2ba9f5c32a8 -//----------------- -"use strict"; -const { PassThrough } = require("stream"); -const readline = require("readline"); - -const ctrlU = { ctrl: true, name: "u" }; - -// Skip test if running in a dumb terminal -const isDumbTerminal = process.env.TERM === "dumb"; -if (isDumbTerminal) { - test.skip("Skipping test in dumb terminal", () => {}); -} else { - describe("readline position", () => { - let input; - let rl; - - beforeEach(() => { - input = new PassThrough(); - rl = readline.createInterface({ - terminal: true, - input: input, - prompt: "", - }); - }); - - afterEach(() => { - rl.close(); - }); - - test.each([ - [1, "a"], - [2, "ab"], - [2, "丁"], - [0, "\u0301"], // COMBINING ACUTE ACCENT - [1, "a\u0301"], // á - [0, "\u20DD"], // COMBINING ENCLOSING CIRCLE - [2, "a\u20DDb"], // a⃝b - [0, "\u200E"], // LEFT-TO-RIGHT MARK - ])('cursor position for "%s" should be %i', (expectedCursor, string) => { - rl.write(string); - expect(rl.getCursorPos().cols).toBe(expectedCursor); - rl.write(null, ctrlU); - }); - }); -} - -//<#END_FILE: test-readline-position.js diff --git a/test/js/node/test/parallel/readline-reopen.test.js b/test/js/node/test/parallel/readline-reopen.test.js deleted file mode 100644 index 490034547d..0000000000 --- a/test/js/node/test/parallel/readline-reopen.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-readline-reopen.js -//#SHA1: 39894fe94eb8e03222c86db2daa35a7b449447eb -//----------------- -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/13557 -// Tests that multiple subsequent readline instances can re-use an input stream. - -const readline = require("readline"); -const { PassThrough } = require("stream"); - -test("multiple readline instances can re-use an input stream", async () => { - const input = new PassThrough(); - const output = new PassThrough(); - - const rl1 = readline.createInterface({ - input, - output, - terminal: true, - }); - - const rl1LinePromise = new Promise(resolve => { - rl1.once("line", line => { - expect(line).toBe("foo"); - resolve(); - }); - }); - - // Write a line plus the first byte of a UTF-8 multibyte character to make sure - // that it doesn't get lost when closing the readline instance. - input.write( - Buffer.concat([ - Buffer.from("foo\n"), - Buffer.from([0xe2]), // Exactly one third of a ☃ snowman. - ]), - ); - - await rl1LinePromise; - rl1.close(); - - const rl2 = readline.createInterface({ - input, - output, - terminal: true, - }); - - const rl2LinePromise = new Promise(resolve => { - rl2.once("line", line => { - expect(line).toBe("☃bar"); - resolve(); - }); - }); - - input.write(Buffer.from([0x98, 0x83])); // The rest of the ☃ snowman. - input.write("bar\n"); - - await rl2LinePromise; - rl2.close(); -}); - -//<#END_FILE: test-readline-reopen.js diff --git a/test/js/node/test/parallel/ref-unref-return.test.js b/test/js/node/test/parallel/ref-unref-return.test.js deleted file mode 100644 index c17af7034a..0000000000 --- a/test/js/node/test/parallel/ref-unref-return.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-ref-unref-return.js -//#SHA1: c7275fa0ca17f1cee96244c48f1044ce4d2e67c1 -//----------------- -"use strict"; - -const net = require("net"); -const dgram = require("dgram"); - -test("ref and unref methods return the same instance", () => { - expect(new net.Server().ref()).toBeInstanceOf(net.Server); - expect(new net.Server().unref()).toBeInstanceOf(net.Server); - expect(new net.Socket().ref()).toBeInstanceOf(net.Socket); - expect(new net.Socket().unref()).toBeInstanceOf(net.Socket); - expect(new dgram.Socket("udp4").ref()).toBeInstanceOf(dgram.Socket); - expect(new dgram.Socket("udp6").unref()).toBeInstanceOf(dgram.Socket); -}); - -//<#END_FILE: test-ref-unref-return.js diff --git a/test/js/node/test/parallel/require-empty-main.test.js b/test/js/node/test/parallel/require-empty-main.test.js deleted file mode 100644 index 93e53d4aed..0000000000 --- a/test/js/node/test/parallel/require-empty-main.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-require-empty-main.js -//#SHA1: 1c03cef0482df2bd119e42f418f54123675b532d -//----------------- -"use strict"; - -const path = require("path"); -const fixtures = require("../common/fixtures"); - -const where = fixtures.path("require-empty-main"); -const expected = path.join(where, "index.js"); - -const testRequireResolve = () => { - expect(require.resolve(where)).toBe(expected); - expect(require(where)).toBe(42); - expect(require.resolve(where)).toBe(expected); -}; - -test('A package.json with an empty "main" property should use index.js if present', testRequireResolve); - -test("require.resolve() should resolve to index.js for the same reason", testRequireResolve); - -test('Any "main" property that doesn\'t resolve to a file should result in index.js being used', testRequireResolve); - -test("Asynchronous test execution", done => { - setImmediate(() => { - testRequireResolve(); - done(); - }); -}); - -//<#END_FILE: test-require-empty-main.js diff --git a/test/js/node/test/parallel/require-extensions-main.test.js b/test/js/node/test/parallel/require-extensions-main.test.js deleted file mode 100644 index 47dee39446..0000000000 --- a/test/js/node/test/parallel/require-extensions-main.test.js +++ /dev/null @@ -1,15 +0,0 @@ -//#FILE: test-require-extensions-main.js -//#SHA1: c3dd50393bbc3eb542e40c67611fc48707ad3cba -//----------------- -"use strict"; - -const path = require("path"); - -test("require extensions main", () => { - const fixturesPath = path.join(__dirname, "..", "fixtures"); - const fixturesRequire = require(path.join(fixturesPath, "require-bin", "bin", "req.js")); - - expect(fixturesRequire).toBe(""); -}); - -//<#END_FILE: test-require-extensions-main.js diff --git a/test/js/node/test/parallel/require-process.test.js b/test/js/node/test/parallel/require-process.test.js deleted file mode 100644 index b6efc34c07..0000000000 --- a/test/js/node/test/parallel/require-process.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-require-process.js -//#SHA1: 699b499b3f906d140de0d550c310085aa0791c95 -//----------------- -"use strict"; - -test('require("process") should return global process reference', () => { - const nativeProcess = require("process"); - expect(nativeProcess).toBe(process); -}); - -//<#END_FILE: test-require-process.js diff --git a/test/js/node/test/parallel/require-unicode.test.js b/test/js/node/test/parallel/require-unicode.test.js deleted file mode 100644 index b4fa1f1f01..0000000000 --- a/test/js/node/test/parallel/require-unicode.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-require-unicode.js -//#SHA1: 3101d8c9e69745baeed9e6f09f32ca9ab31684a8 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); - -const tmpdir = require("../common/tmpdir"); - -test("require with unicode path", () => { - tmpdir.refresh(); - - const dirname = tmpdir.resolve("\u4e2d\u6587\u76ee\u5f55"); - fs.mkdirSync(dirname); - fs.writeFileSync(path.join(dirname, "file.js"), "module.exports = 42;"); - fs.writeFileSync(path.join(dirname, "package.json"), JSON.stringify({ name: "test", main: "file.js" })); - - expect(require(dirname)).toBe(42); - expect(require(path.join(dirname, "file.js"))).toBe(42); -}); - -//<#END_FILE: test-require-unicode.js diff --git a/test/js/node/test/parallel/runner-filter-warning.test.js b/test/js/node/test/parallel/runner-filter-warning.test.js deleted file mode 100644 index 56e4817871..0000000000 --- a/test/js/node/test/parallel/runner-filter-warning.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-runner-filter-warning.js -//#SHA1: c0887965f213c569d83684255054bf9e4bc27c29 -//----------------- -// Flags: --test-only -"use strict"; - -const { defaultMaxListeners } = require("node:events"); - -// Remove the process.on('warning') listener as it's not needed in Jest - -for (let i = 0; i < defaultMaxListeners + 1; ++i) { - test(`test ${i + 1}`, () => { - // Empty test body, just to create the specified number of tests - }); -} - -// Add a test to ensure no warnings are emitted -test("no warnings should be emitted", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - // Run all tests - return new Promise(resolve => { - setTimeout(() => { - expect(warningListener).not.toHaveBeenCalled(); - process.removeListener("warning", warningListener); - resolve(); - }, 100); // Wait a short time to ensure all tests have run - }); -}); - -//<#END_FILE: test-runner-filter-warning.js diff --git a/test/js/node/test/parallel/runner-root-after-with-refed-handles.test.js b/test/js/node/test/parallel/runner-root-after-with-refed-handles.test.js deleted file mode 100644 index 7656ca6ca0..0000000000 --- a/test/js/node/test/parallel/runner-root-after-with-refed-handles.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-runner-root-after-with-refed-handles.js -//#SHA1: cdfe0c601f7139167bfdc5fb5ab39ecf7b403a10 -//----------------- -"use strict"; - -const { createServer } = require("node:http"); - -let server; - -beforeAll(() => { - return new Promise((resolve, reject) => { - server = createServer(); - server.listen(0, err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -test("placeholder test", () => { - // This is a placeholder test to ensure the test suite runs - expect(true).toBe(true); -}); - -//<#END_FILE: test-runner-root-after-with-refed-handles.js diff --git a/test/js/node/test/parallel/safe-get-env.test.js b/test/js/node/test/parallel/safe-get-env.test.js deleted file mode 100644 index 118585eeab..0000000000 --- a/test/js/node/test/parallel/safe-get-env.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-safe-get-env.js -//#SHA1: 4d2553ac6bc24242b872b748a62aca05ca6fbcc8 -//----------------- -"use strict"; - -// This test has been converted to use Jest's API and removed dependencies on internal bindings. -// The original functionality of testing safeGetenv is preserved by mocking process.env. - -describe("safeGetenv", () => { - let originalEnv; - - beforeEach(() => { - originalEnv = { ...process.env }; - }); - - afterEach(() => { - process.env = originalEnv; - }); - - test("should return the same values as process.env", () => { - // Mock some environment variables - process.env = { - TEST_VAR1: "value1", - TEST_VAR2: "value2", - TEST_VAR3: "value3", - }; - - // In a real scenario, we would use the actual safeGetenv function. - // For this test, we'll simulate its behavior by directly accessing process.env - const safeGetenv = key => process.env[key]; - - for (const oneEnv in process.env) { - expect(safeGetenv(oneEnv)).toBe(process.env[oneEnv]); - } - }); - - // Note: The following comment is preserved from the original test file - // FIXME(joyeecheung): this test is not entirely useful. To properly - // test this we could create a mismatch between the effective/real - // group/user id of a Node.js process and see if the environment variables - // are no longer available - but that might be tricky to set up reliably. -}); - -//<#END_FILE: test-safe-get-env.js diff --git a/test/js/node/test/parallel/sigint-infinite-loop.test.js b/test/js/node/test/parallel/sigint-infinite-loop.test.js deleted file mode 100644 index 8c511f99d9..0000000000 --- a/test/js/node/test/parallel/sigint-infinite-loop.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-sigint-infinite-loop.js -//#SHA1: 9c59ace1c99605bce14768e06d62a29e254e6116 -//----------------- -"use strict"; -// This test is to assert that we can SIGINT a script which loops forever. -// Ref(http): -// groups.google.com/group/nodejs-dev/browse_thread/thread/e20f2f8df0296d3f -const { spawn } = require("child_process"); - -test("SIGINT can kill an infinite loop", async () => { - console.log("start"); - - const c = spawn(process.execPath, ["-e", 'while(true) { console.log("hi"); }']); - - let sentKill = false; - - c.stdout.on("data", function (s) { - // Prevent race condition: - // Wait for the first bit of output from the child process - // so that we're sure that it's in the V8 event loop and not - // just in the startup phase of execution. - if (!sentKill) { - c.kill("SIGINT"); - console.log("SIGINT infinite-loop.js"); - sentKill = true; - } - }); - - await new Promise(resolve => { - c.on("exit", code => { - expect(code).not.toBe(0); - console.log("killed infinite-loop.js"); - resolve(); - }); - }); - - expect(sentKill).toBe(true); -}); - -//<#END_FILE: test-sigint-infinite-loop.js diff --git a/test/js/node/test/parallel/signal-safety.test.js b/test/js/node/test/parallel/signal-safety.test.js deleted file mode 100644 index 51f6c19fe8..0000000000 --- a/test/js/node/test/parallel/signal-safety.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-signal-safety.js -//#SHA1: 49090f0605b0ba01c323138ac8e94d423925cbf2 -//----------------- -"use strict"; - -test("Signal `this` safety", () => { - // We cannot use internal bindings in Jest, so we'll mock the Signal class - class Signal { - start() { - // This method should be called with the correct 'this' context - if (!(this instanceof Signal)) { - throw new TypeError("Illegal invocation"); - } - } - } - - const s = new Signal(); - const nots = { start: s.start }; - - expect(() => { - nots.start(9); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-signal-safety.js diff --git a/test/js/node/test/parallel/signal-unregister.test.js b/test/js/node/test/parallel/signal-unregister.test.js deleted file mode 100644 index 0f7b55ce66..0000000000 --- a/test/js/node/test/parallel/signal-unregister.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-signal-unregister.js -//#SHA1: 44f7b12e1cb3d5e2bc94cb244f52d5a6a591d8f4 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); -const path = require("path"); - -const fixturesPath = path.resolve(__dirname, "..", "fixtures"); - -test("Child process exits on SIGINT", () => { - const child = spawn(process.argv[0], [path.join(fixturesPath, "should_exit.js")]); - - return new Promise(resolve => { - child.stdout.once("data", () => { - child.kill("SIGINT"); - }); - - child.on("exit", (exitCode, signalCode) => { - expect(exitCode).toBeNull(); - expect(signalCode).toBe("SIGINT"); - resolve(); - }); - }); -}); - -//<#END_FILE: test-signal-unregister.js diff --git a/test/js/node/test/parallel/stdin-from-file-spawn.test.js b/test/js/node/test/parallel/stdin-from-file-spawn.test.js deleted file mode 100644 index aad7445891..0000000000 --- a/test/js/node/test/parallel/stdin-from-file-spawn.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-stdin-from-file-spawn.js -//#SHA1: 1f8f432985d08b841ebb2c8142b865ae417737a1 -//----------------- -"use strict"; - -const process = require("process"); -const { execSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -let defaultShell; -if (process.platform === "linux" || process.platform === "darwin") { - defaultShell = "/bin/sh"; -} else if (process.platform === "win32") { - defaultShell = "cmd.exe"; -} else { - it.skip("This test exists only on Linux/Win32/OSX", () => {}); -} - -if (defaultShell) { - test("stdin from file spawn", () => { - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - const tmpCmdFile = path.join(tmpDir, "test-stdin-from-file-spawn-cmd"); - const tmpJsFile = path.join(tmpDir, "test-stdin-from-file-spawn.js"); - - fs.writeFileSync(tmpCmdFile, "echo hello"); - fs.writeFileSync( - tmpJsFile, - ` - 'use strict'; - const { spawn } = require('child_process'); - // Reference the object to invoke the getter - process.stdin; - setTimeout(() => { - let ok = false; - const child = spawn(process.env.SHELL || '${defaultShell}', - [], { stdio: ['inherit', 'pipe'] }); - child.stdout.on('data', () => { - ok = true; - }); - child.on('close', () => { - process.exit(ok ? 0 : -1); - }); - }, 100); - `, - ); - - expect(() => { - execSync(`${process.argv[0]} ${tmpJsFile} < ${tmpCmdFile}`); - }).not.toThrow(); - - // Clean up - fs.unlinkSync(tmpCmdFile); - fs.unlinkSync(tmpJsFile); - fs.rmdirSync(tmpDir); - }); -} - -//<#END_FILE: test-stdin-from-file-spawn.js diff --git a/test/js/node/test/parallel/stdin-hang.test.js b/test/js/node/test/parallel/stdin-hang.test.js deleted file mode 100644 index b9f9dd71a9..0000000000 --- a/test/js/node/test/parallel/stdin-hang.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-stdin-hang.js -//#SHA1: f28512893c8d9cd6500247aa87881347638eb616 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// This test *only* verifies that invoking the stdin getter does not -// cause node to hang indefinitely. -// If it does, then the test-runner will nuke it. - -test("process.stdin getter does not cause indefinite hang", () => { - // invoke the getter. - process.stdin; // eslint-disable-line no-unused-expressions - - // If we reach this point, it means the process didn't hang - expect(true).toBe(true); -}); - -test("Console output", () => { - const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); - - console.error("Should exit normally now."); - - expect(consoleErrorSpy).toHaveBeenCalledWith("Should exit normally now."); - - consoleErrorSpy.mockRestore(); -}); - -//<#END_FILE: test-stdin-hang.js diff --git a/test/js/node/test/parallel/stdin-pause-resume-sync.test.js b/test/js/node/test/parallel/stdin-pause-resume-sync.test.js deleted file mode 100644 index ed69ac4afe..0000000000 --- a/test/js/node/test/parallel/stdin-pause-resume-sync.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-stdin-pause-resume-sync.js -//#SHA1: 2256a91336e221a1770b59ebebb4afb406a770dd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("stdin pause and resume", () => { - const consoleSpy = jest.spyOn(console, "error"); - const stdinResumeSpy = jest.spyOn(process.stdin, "resume"); - const stdinPauseSpy = jest.spyOn(process.stdin, "pause"); - - console.error("before opening stdin"); - process.stdin.resume(); - console.error("stdin opened"); - console.error("pausing stdin"); - process.stdin.pause(); - console.error("opening again"); - process.stdin.resume(); - console.error("pausing again"); - process.stdin.pause(); - console.error("should exit now"); - - expect(consoleSpy).toHaveBeenCalledTimes(6); - expect(consoleSpy).toHaveBeenNthCalledWith(1, "before opening stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(2, "stdin opened"); - expect(consoleSpy).toHaveBeenNthCalledWith(3, "pausing stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(4, "opening again"); - expect(consoleSpy).toHaveBeenNthCalledWith(5, "pausing again"); - expect(consoleSpy).toHaveBeenNthCalledWith(6, "should exit now"); - - expect(stdinResumeSpy).toHaveBeenCalledTimes(2); - expect(stdinPauseSpy).toHaveBeenCalledTimes(2); - - consoleSpy.mockRestore(); - stdinResumeSpy.mockRestore(); - stdinPauseSpy.mockRestore(); -}); - -//<#END_FILE: test-stdin-pause-resume-sync.js diff --git a/test/js/node/test/parallel/stdin-pause-resume.test.js b/test/js/node/test/parallel/stdin-pause-resume.test.js deleted file mode 100644 index f172615922..0000000000 --- a/test/js/node/test/parallel/stdin-pause-resume.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-stdin-pause-resume.js -//#SHA1: 941cff7d9e52f178538b4fdd09458bb2fc6a12b7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("stdin pause and resume", async () => { - const consoleSpy = jest.spyOn(console, "error"); - const stdinResumeSpy = jest.spyOn(process.stdin, "resume"); - const stdinPauseSpy = jest.spyOn(process.stdin, "pause"); - - console.error("before opening stdin"); - process.stdin.resume(); - console.error("stdin opened"); - - await new Promise(resolve => setTimeout(resolve, 1)); - - console.error("pausing stdin"); - process.stdin.pause(); - - await new Promise(resolve => setTimeout(resolve, 1)); - - console.error("opening again"); - process.stdin.resume(); - - await new Promise(resolve => setTimeout(resolve, 1)); - - console.error("pausing again"); - process.stdin.pause(); - console.error("should exit now"); - - expect(consoleSpy).toHaveBeenCalledTimes(6); - expect(consoleSpy).toHaveBeenNthCalledWith(1, "before opening stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(2, "stdin opened"); - expect(consoleSpy).toHaveBeenNthCalledWith(3, "pausing stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(4, "opening again"); - expect(consoleSpy).toHaveBeenNthCalledWith(5, "pausing again"); - expect(consoleSpy).toHaveBeenNthCalledWith(6, "should exit now"); - - expect(stdinResumeSpy).toHaveBeenCalledTimes(2); - expect(stdinPauseSpy).toHaveBeenCalledTimes(2); - - consoleSpy.mockRestore(); - stdinResumeSpy.mockRestore(); - stdinPauseSpy.mockRestore(); -}); - -//<#END_FILE: test-stdin-pause-resume.js diff --git a/test/js/node/test/parallel/stdin-pipe-resume.test.js b/test/js/node/test/parallel/stdin-pipe-resume.test.js deleted file mode 100644 index 7a38ca23c3..0000000000 --- a/test/js/node/test/parallel/stdin-pipe-resume.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-stdin-pipe-resume.js -//#SHA1: 6775f16e6a971590e3a5308d4e3678029be47411 -//----------------- -"use strict"; - -// This tests that piping stdin will cause it to resume() as well. - -const { spawn } = require("child_process"); - -if (process.argv[2] === "child") { - process.stdin.pipe(process.stdout); -} else { - test("piping stdin causes it to resume", done => { - const buffers = []; - const child = spawn(process.execPath, [__filename, "child"]); - - child.stdout.on("data", c => { - buffers.push(c); - }); - - child.stdout.on("close", () => { - const b = Buffer.concat(buffers).toString(); - expect(b).toBe("Hello, world\n"); - done(); - }); - - child.stdin.write("Hel"); - child.stdin.write("lo,"); - child.stdin.write(" wo"); - - setTimeout(() => { - child.stdin.write("rld\n"); - child.stdin.end(); - }, 10); - }); -} - -//<#END_FILE: test-stdin-pipe-resume.js diff --git a/test/js/node/test/parallel/stdin-resume-pause.test.js b/test/js/node/test/parallel/stdin-resume-pause.test.js deleted file mode 100644 index b455110d35..0000000000 --- a/test/js/node/test/parallel/stdin-resume-pause.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-stdin-resume-pause.js -//#SHA1: ef96cec1b4e29ec6b96da37fd44e3bbdb50e1393 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("process.stdin can be resumed and paused", () => { - const originalResume = process.stdin.resume; - const originalPause = process.stdin.pause; - - const mockResume = jest.fn(); - const mockPause = jest.fn(); - - process.stdin.resume = mockResume; - process.stdin.pause = mockPause; - - process.stdin.resume(); - process.stdin.pause(); - - expect(mockResume).toHaveBeenCalledTimes(1); - expect(mockPause).toHaveBeenCalledTimes(1); - - // Restore original methods - process.stdin.resume = originalResume; - process.stdin.pause = originalPause; -}); - -//<#END_FILE: test-stdin-resume-pause.js diff --git a/test/js/node/test/parallel/stdin-script-child-option.test.js b/test/js/node/test/parallel/stdin-script-child-option.test.js deleted file mode 100644 index 3dc4f26ce6..0000000000 --- a/test/js/node/test/parallel/stdin-script-child-option.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-stdin-script-child-option.js -//#SHA1: 80d38c88249e1ceb4ef9b029e6910c91dd08ccc5 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); - -test("child process receives option from command line", done => { - const expected = "--option-to-be-seen-on-child"; - const child = spawn(process.execPath, ["-", expected], { stdio: "pipe" }); - - child.stdin.end("console.log(process.argv[2])"); - - let actual = ""; - child.stdout.setEncoding("utf8"); - child.stdout.on("data", chunk => (actual += chunk)); - child.stdout.on("end", () => { - expect(actual.trim()).toBe(expected); - done(); - }); -}); - -//<#END_FILE: test-stdin-script-child-option.js diff --git a/test/js/node/test/parallel/stdio-pipe-access.test.js b/test/js/node/test/parallel/stdio-pipe-access.test.js deleted file mode 100644 index 026d7ae891..0000000000 --- a/test/js/node/test/parallel/stdio-pipe-access.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stdio-pipe-access.js -//#SHA1: a1f6e6c04c96ad54cbbd4270958bcb2934a3ce6b -//----------------- -"use strict"; - -// Test if Node handles accessing process.stdin if it is a redirected -// pipe without deadlocking -const { spawn, spawnSync } = require("child_process"); - -const numTries = 5; -const who = process.argv.length <= 2 ? "runner" : process.argv[2]; - -// Skip test for Workers as they don't have process-like stdio -if (typeof Worker !== "undefined") { - test.skip("Workers don't have process-like stdio", () => {}); -} else { - test("stdio pipe access", () => { - switch (who) { - case "runner": - for (let num = 0; num < numTries; ++num) { - const result = spawnSync(process.argv0, [process.argv[1], "parent"], { stdio: "inherit" }); - expect(result.status).toBe(0); - } - break; - case "parent": { - const middle = spawn(process.argv0, [process.argv[1], "middle"], { stdio: "pipe" }); - middle.stdout.on("data", () => {}); - break; - } - case "middle": - spawn(process.argv0, [process.argv[1], "bottom"], { stdio: [process.stdin, process.stdout, process.stderr] }); - break; - case "bottom": - process.stdin; // eslint-disable-line no-unused-expressions - break; - } - }); -} - -//<#END_FILE: test-stdio-pipe-access.js diff --git a/test/js/node/test/parallel/stdio-pipe-stderr.test.js b/test/js/node/test/parallel/stdio-pipe-stderr.test.js deleted file mode 100644 index eaae7ef20c..0000000000 --- a/test/js/node/test/parallel/stdio-pipe-stderr.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-stdio-pipe-stderr.js -//#SHA1: 5a30748a31ac72c12cd7438b96a8e09c7c8f07f7 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const { spawnSync } = require("child_process"); - -// Test that invoking node with require, and piping stderr to file, -// does not result in exception, -// see: https://github.com/nodejs/node/issues/11257 - -describe("stdio pipe stderr", () => { - const tmpdir = path.join(__dirname, "tmp"); - const fakeModulePath = path.join(tmpdir, "batman.js"); - const stderrOutputPath = path.join(tmpdir, "stderr-output.txt"); - - beforeAll(() => { - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - test("piping stderr to file should not result in exception", done => { - // We need to redirect stderr to a file to produce #11257 - const stream = fs.createWriteStream(stderrOutputPath); - - // The error described in #11257 only happens when we require a - // non-built-in module. - fs.writeFileSync(fakeModulePath, "", "utf8"); - - stream.on("open", () => { - spawnSync(process.execPath, { - input: `require(${JSON.stringify(fakeModulePath)})`, - stdio: ["pipe", "pipe", stream], - }); - - const stderr = fs.readFileSync(stderrOutputPath, "utf8").trim(); - expect(stderr).toBe(""); - - stream.end(); - fs.unlinkSync(stderrOutputPath); - fs.unlinkSync(fakeModulePath); - done(); - }); - }); -}); - -//<#END_FILE: test-stdio-pipe-stderr.js diff --git a/test/js/node/test/parallel/stdout-cannot-be-closed-child-process-pipe.test.js b/test/js/node/test/parallel/stdout-cannot-be-closed-child-process-pipe.test.js deleted file mode 100644 index 1626f01a67..0000000000 --- a/test/js/node/test/parallel/stdout-cannot-be-closed-child-process-pipe.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-stdout-cannot-be-closed-child-process-pipe.js -//#SHA1: 405380c20ca8313c3f58109a4928d90eae9b79b9 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); - -if (process.argv[2] === "child") { - process.stdout.end("foo"); -} else { - test("stdout cannot be closed in child process pipe", done => { - const child = spawn(process.execPath, [__filename, "child"]); - let out = ""; - let err = ""; - - child.stdout.setEncoding("utf8"); - child.stderr.setEncoding("utf8"); - - child.stdout.on("data", c => { - out += c; - }); - child.stderr.on("data", c => { - err += c; - }); - - child.on("close", (code, signal) => { - expect(code).toBe(0); - expect(err).toBe(""); - expect(out).toBe("foo"); - console.log("ok"); - done(); - }); - }); -} - -//<#END_FILE: test-stdout-cannot-be-closed-child-process-pipe.js diff --git a/test/js/node/test/parallel/stdout-stderr-write.test.js b/test/js/node/test/parallel/stdout-stderr-write.test.js deleted file mode 100644 index 3ee9dba103..0000000000 --- a/test/js/node/test/parallel/stdout-stderr-write.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-stdout-stderr-write.js -//#SHA1: 8811b3e776c91c3300ee6eb32b7d1ccb4d3d5d6a -//----------------- -"use strict"; - -// This test checks if process.stderr.write() and process.stdout.write() return true - -test("process.stderr.write() and process.stdout.write() return true", () => { - expect(process.stderr.write("asd")).toBe(true); - expect(process.stdout.write("asd")).toBe(true); -}); - -//<#END_FILE: test-stdout-stderr-write.js diff --git a/test/js/node/test/parallel/stream-add-abort-signal.test.js b/test/js/node/test/parallel/stream-add-abort-signal.test.js deleted file mode 100644 index 1a704c5d5b..0000000000 --- a/test/js/node/test/parallel/stream-add-abort-signal.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream-add-abort-signal.js -//#SHA1: 8caf14dd370aac5a01fad14026a78e1994dd3e4e -//----------------- -"use strict"; - -const { addAbortSignal, Readable } = require("stream"); - -describe("addAbortSignal", () => { - test("throws error for invalid signal", () => { - expect(() => { - addAbortSignal("INVALID_SIGNAL"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); - - test("throws error for invalid stream", () => { - const ac = new AbortController(); - expect(() => { - addAbortSignal(ac.signal, "INVALID_STREAM"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); -}); - -describe("addAbortSignalNoValidate", () => { - test("returns the same readable stream", () => { - const r = new Readable({ - read: () => {}, - }); - - // Since addAbortSignalNoValidate is an internal function, - // we'll skip this test in the Jest version. - // In a real-world scenario, we'd need to mock or implement this function. - expect(true).toBe(true); - }); -}); - -//<#END_FILE: test-stream-add-abort-signal.js diff --git a/test/js/node/test/parallel/stream-base-prototype-accessors-enumerability.test.js b/test/js/node/test/parallel/stream-base-prototype-accessors-enumerability.test.js deleted file mode 100644 index d1776dd952..0000000000 --- a/test/js/node/test/parallel/stream-base-prototype-accessors-enumerability.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-stream-base-prototype-accessors-enumerability.js -//#SHA1: a5423c2b42bae0fbdd1530553de4d40143c010cf -//----------------- -"use strict"; - -// This tests that the prototype accessors added by StreamBase::AddMethods -// are not enumerable. They could be enumerated when inspecting the prototype -// with util.inspect or the inspector protocol. - -// Or anything that calls StreamBase::AddMethods when setting up its prototype -const TTY = process.binding("tty_wrap").TTY; - -test("StreamBase prototype accessors are not enumerable", () => { - const ttyIsEnumerable = Object.prototype.propertyIsEnumerable.bind(TTY); - expect(ttyIsEnumerable("bytesRead")).toBe(false); - expect(ttyIsEnumerable("fd")).toBe(false); - expect(ttyIsEnumerable("_externalStream")).toBe(false); -}); - -//<#END_FILE: test-stream-base-prototype-accessors-enumerability.js diff --git a/test/js/node/test/parallel/stream-big-push.test.js b/test/js/node/test/parallel/stream-big-push.test.js deleted file mode 100644 index 664dc59bea..0000000000 --- a/test/js/node/test/parallel/stream-big-push.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-stream-big-push.js -//#SHA1: 833718bae7463fa469ed5acc9a1c69aa321785b7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); -const str = "asdfasdfasdfasdfasdf"; - -test("stream big push", async () => { - const r = new stream.Readable({ - highWaterMark: 5, - encoding: "utf8", - }); - - let reads = 0; - - function _read() { - if (reads === 0) { - setTimeout(() => { - r.push(str); - }, 1); - reads++; - } else if (reads === 1) { - const ret = r.push(str); - expect(ret).toBe(false); - reads++; - } else { - r.push(null); - } - } - - r._read = jest.fn(_read); - - const endPromise = new Promise(resolve => { - r.on("end", resolve); - }); - - // Push some data in to start. - // We've never gotten any read event at this point. - const ret = r.push(str); - // Should be false. > hwm - expect(ret).toBe(false); - let chunk = r.read(); - expect(chunk).toBe(str); - chunk = r.read(); - expect(chunk).toBeNull(); - - await new Promise(resolve => { - r.once("readable", () => { - // This time, we'll get *all* the remaining data, because - // it's been added synchronously, as the read WOULD take - // us below the hwm, and so it triggered a _read() again, - // which synchronously added more, which we then return. - chunk = r.read(); - expect(chunk).toBe(str + str); - - chunk = r.read(); - expect(chunk).toBeNull(); - resolve(); - }); - }); - - await endPromise; - expect(r._read).toHaveBeenCalledTimes(3); -}); - -//<#END_FILE: test-stream-big-push.js diff --git a/test/js/node/test/parallel/stream-decoder-objectmode.test.js b/test/js/node/test/parallel/stream-decoder-objectmode.test.js deleted file mode 100644 index a78775aa5d..0000000000 --- a/test/js/node/test/parallel/stream-decoder-objectmode.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-stream-decoder-objectmode.js -//#SHA1: 373c0c494e625b8264fae296b46f16a5cd5a9ef8 -//----------------- -"use strict"; - -const stream = require("stream"); - -test("stream.Readable with objectMode and utf16le encoding", () => { - const readable = new stream.Readable({ - read: () => {}, - encoding: "utf16le", - objectMode: true, - }); - - readable.push(Buffer.from("abc", "utf16le")); - readable.push(Buffer.from("def", "utf16le")); - readable.push(null); - - // Without object mode, these would be concatenated into a single chunk. - expect(readable.read()).toBe("abc"); - expect(readable.read()).toBe("def"); - expect(readable.read()).toBeNull(); -}); - -//<#END_FILE: test-stream-decoder-objectmode.js diff --git a/test/js/node/test/parallel/stream-destroy-event-order.test.js b/test/js/node/test/parallel/stream-destroy-event-order.test.js deleted file mode 100644 index 480b80267d..0000000000 --- a/test/js/node/test/parallel/stream-destroy-event-order.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-stream-destroy-event-order.js -//#SHA1: 0d5e12d85e093a1d7c118a2e15cf0c38c1ab96f6 -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Readable stream destroy event order", () => { - const rs = new Readable({ - read() {}, - }); - - let closed = false; - let errored = false; - - rs.on("close", () => { - closed = true; - expect(errored).toBe(true); - }); - - rs.on("error", () => { - errored = true; - expect(closed).toBe(false); - }); - - rs.destroy(new Error("kaboom")); - - return new Promise(resolve => { - rs.on("close", () => { - expect(closed).toBe(true); - expect(errored).toBe(true); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-destroy-event-order.js diff --git a/test/js/node/test/parallel/stream-duplex-props.test.js b/test/js/node/test/parallel/stream-duplex-props.test.js deleted file mode 100644 index 8fbc7f3cff..0000000000 --- a/test/js/node/test/parallel/stream-duplex-props.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-stream-duplex-props.js -//#SHA1: ae1e09a8b6631f457ad8587544a2a245f3c2ef04 -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -test("Duplex stream with same object mode and high water mark for readable and writable", () => { - const d = new Duplex({ - objectMode: true, - highWaterMark: 100, - }); - - expect(d.writableObjectMode).toBe(true); - expect(d.writableHighWaterMark).toBe(100); - expect(d.readableObjectMode).toBe(true); - expect(d.readableHighWaterMark).toBe(100); -}); - -test("Duplex stream with different object mode and high water mark for readable and writable", () => { - const d = new Duplex({ - readableObjectMode: false, - readableHighWaterMark: 10, - writableObjectMode: true, - writableHighWaterMark: 100, - }); - - expect(d.writableObjectMode).toBe(true); - expect(d.writableHighWaterMark).toBe(100); - expect(d.readableObjectMode).toBe(false); - expect(d.readableHighWaterMark).toBe(10); -}); - -//<#END_FILE: test-stream-duplex-props.js diff --git a/test/js/node/test/parallel/stream-duplex-readable-end.test.js b/test/js/node/test/parallel/stream-duplex-readable-end.test.js deleted file mode 100644 index 7ccfe779b6..0000000000 --- a/test/js/node/test/parallel/stream-duplex-readable-end.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-stream-duplex-readable-end.js -//#SHA1: 8cecff6703e081aeb94abe2b37332bc7aef28b1d -//----------------- -"use strict"; - -// https://github.com/nodejs/node/issues/35926 -const stream = require("stream"); - -test("stream duplex readable end", done => { - let loops = 5; - - const src = new stream.Readable({ - highWaterMark: 16 * 1024, - read() { - if (loops--) this.push(Buffer.alloc(20000)); - }, - }); - - const dst = new stream.Transform({ - highWaterMark: 16 * 1024, - transform(chunk, output, fn) { - this.push(null); - fn(); - }, - }); - - src.pipe(dst); - - dst.on("data", () => {}); - dst.on("end", () => { - expect(loops).toBe(3); - expect(src.isPaused()).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-stream-duplex-readable-end.js diff --git a/test/js/node/test/parallel/stream-duplex-readable-writable.test.js b/test/js/node/test/parallel/stream-duplex-readable-writable.test.js deleted file mode 100644 index c37a46d6dd..0000000000 --- a/test/js/node/test/parallel/stream-duplex-readable-writable.test.js +++ /dev/null @@ -1,95 +0,0 @@ -//#FILE: test-stream-duplex-readable-writable.js -//#SHA1: d56d29f5fbb8adc3d61708839985f0ea7ffb9b5c -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -test("Duplex with readable false", () => { - const duplex = new Duplex({ - readable: false, - }); - expect(duplex.readable).toBe(false); - duplex.push("asd"); - - const errorHandler = jest.fn(); - duplex.on("error", errorHandler); - - const dataHandler = jest.fn(); - duplex.on("data", dataHandler); - - const endHandler = jest.fn(); - duplex.on("end", endHandler); - - return new Promise(resolve => { - setImmediate(() => { - expect(errorHandler).toHaveBeenCalledTimes(1); - expect(errorHandler).toHaveBeenCalledWith( - expect.objectContaining({ - code: "ERR_STREAM_PUSH_AFTER_EOF", - message: expect.any(String), - }), - ); - expect(dataHandler).not.toHaveBeenCalled(); - expect(endHandler).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -test("Duplex with writable false", () => { - const writeSpy = jest.fn(); - const duplex = new Duplex({ - writable: false, - write: writeSpy, - }); - expect(duplex.writable).toBe(false); - duplex.write("asd"); - - const errorHandler = jest.fn(); - duplex.on("error", errorHandler); - - const finishHandler = jest.fn(); - duplex.on("finish", finishHandler); - - return new Promise(resolve => { - setImmediate(() => { - expect(errorHandler).toHaveBeenCalledTimes(1); - expect(errorHandler).toHaveBeenCalledWith( - expect.objectContaining({ - code: "ERR_STREAM_WRITE_AFTER_END", - message: expect.any(String), - }), - ); - expect(writeSpy).not.toHaveBeenCalled(); - expect(finishHandler).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -test("Duplex with readable false and async iteration", async () => { - const duplex = new Duplex({ - readable: false, - }); - expect(duplex.readable).toBe(false); - - const dataHandler = jest.fn(); - duplex.on("data", dataHandler); - - const endHandler = jest.fn(); - duplex.on("end", endHandler); - - async function run() { - for await (const chunk of duplex) { - expect(chunk).toBeFalsy(); // This should never be reached - } - } - - await run(); - - expect(dataHandler).not.toHaveBeenCalled(); - expect(endHandler).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-stream-duplex-readable-writable.js diff --git a/test/js/node/test/parallel/stream-duplex-writable-finished.test.js b/test/js/node/test/parallel/stream-duplex-writable-finished.test.js deleted file mode 100644 index 0a2400bb78..0000000000 --- a/test/js/node/test/parallel/stream-duplex-writable-finished.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-stream-duplex-writable-finished.js -//#SHA1: ba8c61c576c3a900076baa134c8a0d6876e84db5 -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -// basic -test("Duplex.prototype has writableFinished", () => { - expect(Object.hasOwn(Duplex.prototype, "writableFinished")).toBe(true); -}); - -// event -test("writableFinished state changes correctly", done => { - const duplex = new Duplex(); - - duplex._write = (chunk, encoding, cb) => { - // The state finished should start in false. - expect(duplex.writableFinished).toBe(false); - cb(); - }; - - duplex.on("finish", () => { - expect(duplex.writableFinished).toBe(true); - }); - - duplex.end("testing finished state", () => { - expect(duplex.writableFinished).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-stream-duplex-writable-finished.js diff --git a/test/js/node/test/parallel/stream-end-of-streams.test.js b/test/js/node/test/parallel/stream-end-of-streams.test.js deleted file mode 100644 index af768bc70e..0000000000 --- a/test/js/node/test/parallel/stream-end-of-streams.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-stream-end-of-streams.js -//#SHA1: 3fa13e31cef06059026b0fcf90c151a8a975752c -//----------------- -"use strict"; - -const { Duplex, finished } = require("stream"); - -test("finished function with invalid stream", () => { - // Passing empty object to mock invalid stream - // should throw error - expect(() => { - finished({}, () => {}); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -test("finished function with valid stream", () => { - const streamObj = new Duplex(); - streamObj.end(); - // Below code should not throw any errors as the - // streamObj is `Stream` - expect(() => { - finished(streamObj, () => {}); - }).not.toThrow(); -}); - -//<#END_FILE: test-stream-end-of-streams.js diff --git a/test/js/node/test/parallel/stream-events-prepend.test.js b/test/js/node/test/parallel/stream-events-prepend.test.js deleted file mode 100644 index 590421f79d..0000000000 --- a/test/js/node/test/parallel/stream-events-prepend.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-stream-events-prepend.js -//#SHA1: db830318b8c5a2e990a75320319c8fcd96fa760a -//----------------- -"use strict"; -const stream = require("stream"); - -class Writable extends stream.Writable { - constructor() { - super(); - this.prependListener = undefined; - } - - _write(chunk, end, cb) { - cb(); - } -} - -class Readable extends stream.Readable { - _read() { - this.push(null); - } -} - -test("pipe event is emitted even when prependListener is undefined", () => { - const w = new Writable(); - const pipeSpy = jest.fn(); - w.on("pipe", pipeSpy); - - const r = new Readable(); - r.pipe(w); - - expect(pipeSpy).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-events-prepend.js diff --git a/test/js/node/test/parallel/stream-once-readable-pipe.test.js b/test/js/node/test/parallel/stream-once-readable-pipe.test.js deleted file mode 100644 index 71b0c32698..0000000000 --- a/test/js/node/test/parallel/stream-once-readable-pipe.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-stream-once-readable-pipe.js -//#SHA1: 4f12e7a8a1c06ba3cef54605469eb67d23306aa3 -//----------------- -"use strict"; - -const { Readable, Writable } = require("stream"); - -// This test ensures that if have 'readable' listener -// on Readable instance it will not disrupt the pipe. - -test("readable listener before pipe", () => { - let receivedData = ""; - const w = new Writable({ - write: (chunk, env, callback) => { - receivedData += chunk; - callback(); - }, - }); - - const data = ["foo", "bar", "baz"]; - const r = new Readable({ - read: () => {}, - }); - - const readableSpy = jest.fn(); - r.once("readable", readableSpy); - - r.pipe(w); - r.push(data[0]); - r.push(data[1]); - r.push(data[2]); - r.push(null); - - return new Promise(resolve => { - w.on("finish", () => { - expect(receivedData).toBe(data.join("")); - expect(readableSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -test("readable listener after pipe", () => { - let receivedData = ""; - const w = new Writable({ - write: (chunk, env, callback) => { - receivedData += chunk; - callback(); - }, - }); - - const data = ["foo", "bar", "baz"]; - const r = new Readable({ - read: () => {}, - }); - - r.pipe(w); - r.push(data[0]); - r.push(data[1]); - r.push(data[2]); - r.push(null); - - const readableSpy = jest.fn(); - r.once("readable", readableSpy); - - return new Promise(resolve => { - w.on("finish", () => { - expect(receivedData).toBe(data.join("")); - expect(readableSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-once-readable-pipe.js diff --git a/test/js/node/test/parallel/stream-passthrough-drain.test.js b/test/js/node/test/parallel/stream-passthrough-drain.test.js deleted file mode 100644 index b77fa6daf3..0000000000 --- a/test/js/node/test/parallel/stream-passthrough-drain.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-stream-passthrough-drain.js -//#SHA1: c17561a8fc9a14d7abc05af3528d0ead32502a57 -//----------------- -"use strict"; -const { PassThrough } = require("stream"); - -test("PassThrough stream emits drain event when buffer is emptied", () => { - const pt = new PassThrough({ highWaterMark: 0 }); - - const drainHandler = jest.fn(); - pt.on("drain", drainHandler); - - expect(pt.write("hello1")).toBe(false); - - pt.read(); - pt.read(); - - // Use process.nextTick to ensure the drain event has a chance to fire - return new Promise(resolve => { - process.nextTick(() => { - expect(drainHandler).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-passthrough-drain.js diff --git a/test/js/node/test/parallel/stream-pipe-needdrain.test.js b/test/js/node/test/parallel/stream-pipe-needdrain.test.js deleted file mode 100644 index 42e4b2e9f3..0000000000 --- a/test/js/node/test/parallel/stream-pipe-needdrain.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stream-pipe-needDrain.js -//#SHA1: 5c524a253a770fcbb95365aea011a38a665c61da -//----------------- -"use strict"; - -const { Readable, Writable } = require("stream"); - -// Pipe should pause temporarily if writable needs drain. -test("Pipe pauses when writable needs drain", done => { - const w = new Writable({ - write(buf, encoding, callback) { - process.nextTick(callback); - }, - highWaterMark: 1, - }); - - while (w.write("asd")); - - expect(w.writableNeedDrain).toBe(true); - - const r = new Readable({ - read() { - this.push("asd"); - this.push(null); - }, - }); - - const pauseSpy = jest.fn(); - r.on("pause", pauseSpy); - - const endSpy = jest.fn().mockImplementation(() => { - expect(pauseSpy).toHaveBeenCalledTimes(2); - done(); - }); - r.on("end", endSpy); - - r.pipe(w); -}); - -//<#END_FILE: test-stream-pipe-needDrain.js diff --git a/test/js/node/test/parallel/stream-preprocess.test.js b/test/js/node/test/parallel/stream-preprocess.test.js deleted file mode 100644 index 8c5dd3cc8a..0000000000 --- a/test/js/node/test/parallel/stream-preprocess.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-stream-preprocess.js -//#SHA1: 4061428f95671f257c4a57a92d77c0dc63a1394a -//----------------- -"use strict"; - -const fs = require("fs"); -const rl = require("readline"); -const fixtures = require("../common/fixtures"); - -const BOM = "\uFEFF"; - -// Get the data using a non-stream way to compare with the streamed data. -const modelData = fixtures.readSync("file-to-read-without-bom.txt", "utf8"); -const modelDataFirstCharacter = modelData[0]; - -// Detect the number of forthcoming 'line' events for mustCall() 'expected' arg. -const lineCount = modelData.match(/\n/g).length; - -test("Ensure both without-bom and with-bom test files are textwise equal", () => { - expect(fixtures.readSync("file-to-read-with-bom.txt", "utf8")).toBe(`${BOM}${modelData}`); -}); - -test("An unjustified BOM stripping with a non-BOM character unshifted to a stream", done => { - const inputWithoutBOM = fs.createReadStream(fixtures.path("file-to-read-without-bom.txt"), "utf8"); - - inputWithoutBOM.once("readable", () => { - const maybeBOM = inputWithoutBOM.read(1); - expect(maybeBOM).toBe(modelDataFirstCharacter); - expect(maybeBOM).not.toBe(BOM); - - inputWithoutBOM.unshift(maybeBOM); - - let streamedData = ""; - rl.createInterface({ - input: inputWithoutBOM, - }) - .on("line", line => { - streamedData += `${line}\n`; - }) - .on("close", () => { - expect(streamedData).toBe(modelData); - done(); - }); - }); -}); - -test("A justified BOM stripping", done => { - const inputWithBOM = fs.createReadStream(fixtures.path("file-to-read-with-bom.txt"), "utf8"); - - inputWithBOM.once("readable", () => { - const maybeBOM = inputWithBOM.read(1); - expect(maybeBOM).toBe(BOM); - - let streamedData = ""; - rl.createInterface({ - input: inputWithBOM, - }) - .on("line", line => { - streamedData += `${line}\n`; - }) - .on("close", () => { - expect(streamedData).toBe(modelData); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-preprocess.js diff --git a/test/js/node/test/parallel/stream-readable-aborted.test.js b/test/js/node/test/parallel/stream-readable-aborted.test.js deleted file mode 100644 index 3d005e5901..0000000000 --- a/test/js/node/test/parallel/stream-readable-aborted.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-stream-readable-aborted.js -//#SHA1: b4d59c7cd8eda084bae2d2ff603dd153aff79f98 -//----------------- -"use strict"; - -const { Readable, Duplex } = require("stream"); - -test("Readable stream aborted state", () => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.destroy(); - expect(readable.readableAborted).toBe(true); -}); - -test("Readable stream aborted state after push(null)", () => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.push(null); - readable.destroy(); - expect(readable.readableAborted).toBe(true); -}); - -test("Readable stream aborted state after push(data)", () => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.push("asd"); - readable.destroy(); - expect(readable.readableAborted).toBe(true); -}); - -test("Readable stream aborted state after end event", done => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.push("asd"); - readable.push(null); - expect(readable.readableAborted).toBe(false); - readable.on("end", () => { - expect(readable.readableAborted).toBe(false); - readable.destroy(); - expect(readable.readableAborted).toBe(false); - queueMicrotask(() => { - expect(readable.readableAborted).toBe(false); - done(); - }); - }); - readable.resume(); -}); - -test("Duplex stream with readable false", () => { - const duplex = new Duplex({ - readable: false, - write() {}, - }); - duplex.destroy(); - expect(duplex.readableAborted).toBe(false); -}); - -//<#END_FILE: test-stream-readable-aborted.js diff --git a/test/js/node/test/parallel/stream-readable-add-chunk-during-data.test.js b/test/js/node/test/parallel/stream-readable-add-chunk-during-data.test.js deleted file mode 100644 index 2a75e47cbf..0000000000 --- a/test/js/node/test/parallel/stream-readable-add-chunk-during-data.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-stream-readable-add-chunk-during-data.js -//#SHA1: f34525f5ea022c837ba1e40e98b1b41da5df50b2 -//----------------- -"use strict"; -const { Readable } = require("stream"); - -// Verify that .push() and .unshift() can be called from 'data' listeners. - -["push", "unshift"].forEach(method => { - test(`Readable ${method} can be called from 'data' listeners`, done => { - const r = new Readable({ read() {} }); - - r.once("data", chunk => { - expect(r.readableLength).toBe(0); - r[method](chunk); - expect(r.readableLength).toBe(chunk.length); - - r.on("data", newChunk => { - expect(newChunk.toString()).toBe("Hello, world"); - done(); - }); - }); - - r.push("Hello, world"); - }); -}); - -//<#END_FILE: test-stream-readable-add-chunk-during-data.js diff --git a/test/js/node/test/parallel/stream-readable-data.test.js b/test/js/node/test/parallel/stream-readable-data.test.js deleted file mode 100644 index b654147c26..0000000000 --- a/test/js/node/test/parallel/stream-readable-data.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-stream-readable-data.js -//#SHA1: c679a0f31d84cd65d9e85ab0617abfd65a053afc -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Readable stream emits data event after removing readable listener", done => { - const readable = new Readable({ - read() {}, - }); - - function read() {} - - readable.setEncoding("utf8"); - readable.on("readable", read); - readable.removeListener("readable", read); - - process.nextTick(() => { - const dataHandler = jest.fn(); - readable.on("data", dataHandler); - readable.push("hello"); - - // Use setImmediate to ensure the data event has time to be emitted - setImmediate(() => { - expect(dataHandler).toHaveBeenCalledTimes(1); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-data.js diff --git a/test/js/node/test/parallel/stream-readable-emittedreadable.test.js b/test/js/node/test/parallel/stream-readable-emittedreadable.test.js deleted file mode 100644 index 744dba984c..0000000000 --- a/test/js/node/test/parallel/stream-readable-emittedreadable.test.js +++ /dev/null @@ -1,111 +0,0 @@ -//#FILE: test-stream-readable-emittedReadable.js -//#SHA1: 1c9463d9bb0e7927d8d18949aae849a04cc034fe -//----------------- -"use strict"; -const { Readable } = require("stream"); - -describe("Readable Stream emittedReadable", () => { - test("emittedReadable state changes correctly", () => { - const readable = new Readable({ - read: () => {}, - }); - - // Initialized to false. - expect(readable._readableState.emittedReadable).toBe(false); - - const expected = [Buffer.from("foobar"), Buffer.from("quo"), null]; - const readableSpy = jest.fn(() => { - // emittedReadable should be true when the readable event is emitted - expect(readable._readableState.emittedReadable).toBe(true); - expect(readable.read()).toEqual(expected.shift()); - // emittedReadable is reset to false during read() - expect(readable._readableState.emittedReadable).toBe(false); - }); - - readable.on("readable", readableSpy); - - // When the first readable listener is just attached, - // emittedReadable should be false - expect(readable._readableState.emittedReadable).toBe(false); - - // These trigger a single 'readable', as things are batched up - process.nextTick(() => { - readable.push("foo"); - }); - process.nextTick(() => { - readable.push("bar"); - }); - - // These triggers two readable events - setImmediate(() => { - readable.push("quo"); - process.nextTick(() => { - readable.push(null); - }); - }); - - return new Promise(resolve => { - setTimeout(() => { - expect(readableSpy).toHaveBeenCalledTimes(3); - resolve(); - }, 100); - }); - }); - - test("emittedReadable with read(0)", () => { - const noRead = new Readable({ - read: () => {}, - }); - - const readableSpy = jest.fn(() => { - // emittedReadable should be true when the readable event is emitted - expect(noRead._readableState.emittedReadable).toBe(true); - noRead.read(0); - // emittedReadable is not reset during read(0) - expect(noRead._readableState.emittedReadable).toBe(true); - }); - - noRead.on("readable", readableSpy); - - noRead.push("foo"); - noRead.push(null); - - return new Promise(resolve => { - setTimeout(() => { - expect(readableSpy).toHaveBeenCalledTimes(1); - resolve(); - }, 100); - }); - }); - - test("emittedReadable in flowing mode", () => { - const flowing = new Readable({ - read: () => {}, - }); - - const dataSpy = jest.fn(() => { - // When in flowing mode, emittedReadable is always false. - expect(flowing._readableState.emittedReadable).toBe(false); - flowing.read(); - expect(flowing._readableState.emittedReadable).toBe(false); - }); - - flowing.on("data", dataSpy); - - flowing.push("foooo"); - flowing.push("bar"); - flowing.push("quo"); - process.nextTick(() => { - flowing.push(null); - }); - - return new Promise(resolve => { - setTimeout(() => { - expect(dataSpy).toHaveBeenCalledTimes(3); - resolve(); - }, 100); - }); - }); -}); - -//<#END_FILE: test-stream-readable-emittedReadable.js diff --git a/test/js/node/test/parallel/stream-readable-end-destroyed.test.js b/test/js/node/test/parallel/stream-readable-end-destroyed.test.js deleted file mode 100644 index c8b98db765..0000000000 --- a/test/js/node/test/parallel/stream-readable-end-destroyed.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-stream-readable-end-destroyed.js -//#SHA1: 20c8bb870db11018d2eaa1c9e4dece071917d03d -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Don't emit 'end' after 'close'", () => { - const r = new Readable(); - - const endListener = jest.fn(); - r.on("end", endListener); - r.resume(); - r.destroy(); - - return new Promise(resolve => { - r.on("close", () => { - r.push(null); - expect(endListener).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-end-destroyed.js diff --git a/test/js/node/test/parallel/stream-readable-ended.test.js b/test/js/node/test/parallel/stream-readable-ended.test.js deleted file mode 100644 index fb6a3c3b64..0000000000 --- a/test/js/node/test/parallel/stream-readable-ended.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-stream-readable-ended.js -//#SHA1: 93aa267630bb32f94783c5bbdeb8e345c0acd94a -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// basic -test("Readable.prototype has readableEnded property", () => { - expect(Object.hasOwn(Readable.prototype, "readableEnded")).toBe(true); -}); - -// event -test("readableEnded state changes correctly", done => { - const readable = new Readable(); - - readable._read = () => { - // The state ended should start in false. - expect(readable.readableEnded).toBe(false); - readable.push("asd"); - expect(readable.readableEnded).toBe(false); - readable.push(null); - expect(readable.readableEnded).toBe(false); - }; - - readable.on("end", () => { - expect(readable.readableEnded).toBe(true); - done(); - }); - - readable.on("data", () => { - expect(readable.readableEnded).toBe(false); - }); -}); - -// Verifies no `error` triggered on multiple .push(null) invocations -test("No error triggered on multiple .push(null) invocations", done => { - const readable = new Readable(); - - readable.on("readable", () => { - readable.read(); - }); - - const errorHandler = jest.fn(); - readable.on("error", errorHandler); - - readable.on("end", () => { - expect(errorHandler).not.toHaveBeenCalled(); - done(); - }); - - readable.push("a"); - readable.push(null); - readable.push(null); -}); - -//<#END_FILE: test-stream-readable-ended.js diff --git a/test/js/node/test/parallel/stream-readable-event.test.js b/test/js/node/test/parallel/stream-readable-event.test.js deleted file mode 100644 index 688f207d1b..0000000000 --- a/test/js/node/test/parallel/stream-readable-event.test.js +++ /dev/null @@ -1,146 +0,0 @@ -//#FILE: test-stream-readable-event.js -//#SHA1: 8a3da958252097730dcd22e82d325d106d5512a5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Readable } = require("stream"); - -test("not reading when the readable is added", done => { - const r = new Readable({ - highWaterMark: 3, - }); - - r._read = jest.fn(); - - // This triggers a 'readable' event, which is lost. - r.push(Buffer.from("blerg")); - - setTimeout(() => { - // We're testing what we think we are - expect(r._readableState.reading).toBe(false); - const readableSpy = jest.fn(); - r.on("readable", readableSpy); - - // Allow time for the 'readable' event to potentially fire - setTimeout(() => { - expect(readableSpy).toHaveBeenCalled(); - expect(r._read).not.toHaveBeenCalled(); - done(); - }, 10); - }, 1); -}); - -test("readable is re-emitted if there's already a length, while it IS reading", done => { - const r = new Readable({ - highWaterMark: 3, - }); - - r._read = jest.fn(); - - // This triggers a 'readable' event, which is lost. - r.push(Buffer.from("bl")); - - setTimeout(() => { - // Assert we're testing what we think we are - expect(r._readableState.reading).toBe(true); - const readableSpy = jest.fn(); - r.on("readable", readableSpy); - - // Allow time for the 'readable' event to potentially fire - setTimeout(() => { - expect(readableSpy).toHaveBeenCalled(); - expect(r._read).toHaveBeenCalled(); - done(); - }, 10); - }, 1); -}); - -test("not reading when the stream has not passed the highWaterMark but has reached EOF", done => { - const r = new Readable({ - highWaterMark: 30, - }); - - r._read = jest.fn(); - - // This triggers a 'readable' event, which is lost. - r.push(Buffer.from("blerg")); - r.push(null); - - setTimeout(() => { - // Assert we're testing what we think we are - expect(r._readableState.reading).toBe(false); - const readableSpy = jest.fn(); - r.on("readable", readableSpy); - - // Allow time for the 'readable' event to potentially fire - setTimeout(() => { - expect(readableSpy).toHaveBeenCalled(); - expect(r._read).not.toHaveBeenCalled(); - done(); - }, 10); - }, 1); -}); - -test("Pushing an empty string in non-objectMode should trigger next `read()`", done => { - const underlyingData = ["", "x", "y", "", "z"]; - const expected = underlyingData.filter(data => data); - const result = []; - - const r = new Readable({ - encoding: "utf8", - }); - r._read = function () { - process.nextTick(() => { - if (!underlyingData.length) { - this.push(null); - } else { - this.push(underlyingData.shift()); - } - }); - }; - - r.on("readable", () => { - const data = r.read(); - if (data !== null) result.push(data); - }); - - r.on("end", () => { - expect(result).toEqual(expected); - done(); - }); -}); - -test("#20923 - removeAllListeners should clear all event listeners", () => { - const r = new Readable(); - r._read = function () { - // Actually doing thing here - }; - r.on("data", function () {}); - - r.removeAllListeners(); - - expect(r.eventNames().length).toBe(0); -}); - -//<#END_FILE: test-stream-readable-event.js diff --git a/test/js/node/test/parallel/stream-readable-hwm-0-async.test.js b/test/js/node/test/parallel/stream-readable-hwm-0-async.test.js deleted file mode 100644 index 0d2388c333..0000000000 --- a/test/js/node/test/parallel/stream-readable-hwm-0-async.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream-readable-hwm-0-async.js -//#SHA1: ddaa3718bf6d6ae9258293494ac3449800169768 -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test ensures that Readable stream will continue to call _read -// for streams with highWaterMark === 0 once the stream returns data -// by calling push() asynchronously. - -test("Readable stream with highWaterMark 0 and async push", async () => { - let count = 5; - const readMock = jest.fn(() => { - process.nextTick(() => { - if (count--) { - r.push("a"); - } else { - r.push(null); - } - }); - }); - - const r = new Readable({ - read: readMock, - highWaterMark: 0, - }); - - const dataHandler = jest.fn(); - const endHandler = jest.fn(); - - r.on("data", dataHandler); - r.on("end", endHandler); - - // Consume the stream - for await (const chunk of r) { - // This loop will iterate 5 times - } - - // Called 6 times: First 5 return data, last one signals end of stream. - expect(readMock).toHaveBeenCalledTimes(6); - expect(dataHandler).toHaveBeenCalledTimes(5); - expect(endHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-readable-hwm-0-async.js diff --git a/test/js/node/test/parallel/stream-readable-hwm-0.test.js b/test/js/node/test/parallel/stream-readable-hwm-0.test.js deleted file mode 100644 index 3d8266581d..0000000000 --- a/test/js/node/test/parallel/stream-readable-hwm-0.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-stream-readable-hwm-0.js -//#SHA1: 986085294672eff1ba0a13f99633edd30c3fb54a -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test ensures that Readable stream will call _read() for streams -// with highWaterMark === 0 upon .read(0) instead of just trying to -// emit 'readable' event. - -test("Readable stream with highWaterMark 0 calls _read()", () => { - const mockRead = jest.fn(); - - const r = new Readable({ - // Must be called only once upon setting 'readable' listener - read: mockRead, - highWaterMark: 0, - }); - - let pushedNull = false; - - // This will trigger read(0) but must only be called after push(null) - // because we haven't pushed any data - r.on( - "readable", - jest.fn(() => { - expect(r.read()).toBeNull(); - expect(pushedNull).toBe(true); - }), - ); - - const endHandler = jest.fn(); - r.on("end", endHandler); - - return new Promise(resolve => { - process.nextTick(() => { - expect(r.read()).toBeNull(); - pushedNull = true; - r.push(null); - - // Use setImmediate to ensure all events have been processed - setImmediate(() => { - expect(mockRead).toHaveBeenCalledTimes(1); - expect(endHandler).toHaveBeenCalledTimes(1); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-stream-readable-hwm-0.js diff --git a/test/js/node/test/parallel/stream-readable-needreadable.test.js b/test/js/node/test/parallel/stream-readable-needreadable.test.js deleted file mode 100644 index 171eeee0df..0000000000 --- a/test/js/node/test/parallel/stream-readable-needreadable.test.js +++ /dev/null @@ -1,131 +0,0 @@ -//#FILE: test-stream-readable-needReadable.js -//#SHA1: 301ca49c86e59196821c0fcd419c71f5ffd4a94d -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -describe("Readable stream needReadable property", () => { - test("Initial state and readable event", () => { - const readable = new Readable({ - read: () => {}, - }); - - // Initialized to false. - expect(readable._readableState.needReadable).toBe(false); - - readable.on("readable", () => { - // When the readable event fires, needReadable is reset. - expect(readable._readableState.needReadable).toBe(false); - readable.read(); - }); - - // If a readable listener is attached, then a readable event is needed. - expect(readable._readableState.needReadable).toBe(true); - - readable.push("foo"); - readable.push(null); - - return new Promise(resolve => { - readable.on("end", () => { - // No need to emit readable anymore when the stream ends. - expect(readable._readableState.needReadable).toBe(false); - resolve(); - }); - }); - }); - - test("Async readable stream", done => { - const asyncReadable = new Readable({ - read: () => {}, - }); - - let readableCallCount = 0; - asyncReadable.on("readable", () => { - if (asyncReadable.read() !== null) { - // After each read(), the buffer is empty. - // If the stream doesn't end now, - // then we need to notify the reader on future changes. - expect(asyncReadable._readableState.needReadable).toBe(true); - } - readableCallCount++; - if (readableCallCount === 2) { - done(); - } - }); - - process.nextTick(() => { - asyncReadable.push("foooo"); - }); - process.nextTick(() => { - asyncReadable.push("bar"); - }); - setImmediate(() => { - asyncReadable.push(null); - expect(asyncReadable._readableState.needReadable).toBe(false); - }); - }); - - test("Flowing mode", done => { - const flowing = new Readable({ - read: () => {}, - }); - - // Notice this must be above the on('data') call. - flowing.push("foooo"); - flowing.push("bar"); - flowing.push("quo"); - process.nextTick(() => { - flowing.push(null); - }); - - let dataCallCount = 0; - // When the buffer already has enough data, and the stream is - // in flowing mode, there is no need for the readable event. - flowing.on("data", data => { - expect(flowing._readableState.needReadable).toBe(false); - dataCallCount++; - if (dataCallCount === 3) { - done(); - } - }); - }); - - test("Slow producer", done => { - const slowProducer = new Readable({ - read: () => {}, - }); - - let readableCallCount = 0; - slowProducer.on("readable", () => { - const chunk = slowProducer.read(8); - const state = slowProducer._readableState; - if (chunk === null) { - // The buffer doesn't have enough data, and the stream is not end, - // we need to notify the reader when data arrives. - expect(state.needReadable).toBe(true); - } else { - expect(state.needReadable).toBe(false); - } - readableCallCount++; - if (readableCallCount === 4) { - done(); - } - }); - - process.nextTick(() => { - slowProducer.push("foo"); - process.nextTick(() => { - slowProducer.push("foo"); - process.nextTick(() => { - slowProducer.push("foo"); - process.nextTick(() => { - slowProducer.push(null); - }); - }); - }); - }); - }); -}); - -//<#END_FILE: test-stream-readable-needReadable.js diff --git a/test/js/node/test/parallel/stream-readable-next-no-null.test.js b/test/js/node/test/parallel/stream-readable-next-no-null.test.js deleted file mode 100644 index c0129802e9..0000000000 --- a/test/js/node/test/parallel/stream-readable-next-no-null.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-stream-readable-next-no-null.js -//#SHA1: b3362d071d6f13ce317db2b897ff1146441d1bea -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -describe("Readable.from with null value", () => { - it("should throw ERR_STREAM_NULL_VALUES error", async () => { - async function* generate() { - yield null; - } - - const stream = Readable.from(generate()); - - const errorPromise = new Promise(resolve => { - stream.on("error", error => { - resolve(error); - }); - }); - - const dataPromise = new Promise(resolve => { - stream.on("data", () => { - resolve("data"); - }); - }); - - const endPromise = new Promise(resolve => { - stream.on("end", () => { - resolve("end"); - }); - }); - - await expect(errorPromise).resolves.toEqual( - expect.objectContaining({ - code: "ERR_STREAM_NULL_VALUES", - name: "TypeError", - message: expect.any(String), - }), - ); - - await expect(Promise.race([dataPromise, endPromise, errorPromise])).resolves.not.toBe("data"); - await expect(Promise.race([dataPromise, endPromise, errorPromise])).resolves.not.toBe("end"); - }); -}); - -//<#END_FILE: test-stream-readable-next-no-null.js diff --git a/test/js/node/test/parallel/stream-readable-readable-then-resume.test.js b/test/js/node/test/parallel/stream-readable-readable-then-resume.test.js deleted file mode 100644 index e542119c61..0000000000 --- a/test/js/node/test/parallel/stream-readable-readable-then-resume.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stream-readable-readable-then-resume.js -//#SHA1: 79790a4cd766421a6891704bf157072eaef2d5b4 -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test verifies that a stream could be resumed after -// removing the readable event in the same tick - -function check(s) { - const readableListener = jest.fn(); - s.on("readable", readableListener); - s.on("end", jest.fn()); - expect(s.removeListener).toBe(s.off); - s.removeListener("readable", readableListener); - s.resume(); - - expect(readableListener).not.toHaveBeenCalled(); -} - -test("Readable stream can be resumed after removing readable event", () => { - const s = new Readable({ - objectMode: true, - highWaterMark: 1, - read() { - if (!this.first) { - this.push("hello"); - this.first = true; - return; - } - - this.push(null); - }, - }); - - check(s); -}); - -//<#END_FILE: test-stream-readable-readable-then-resume.js diff --git a/test/js/node/test/parallel/stream-readable-readable.test.js b/test/js/node/test/parallel/stream-readable-readable.test.js deleted file mode 100644 index cb0b9c2dba..0000000000 --- a/test/js/node/test/parallel/stream-readable-readable.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-stream-readable-readable.js -//#SHA1: f2d897473803968b7ee8efb20a8f1f374987980b -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -describe("Readable.readable", () => { - test("readable property is set correctly", () => { - const r = new Readable({ - read() {}, - }); - expect(r.readable).toBe(true); - r.destroy(); - expect(r.readable).toBe(false); - }); - - test("readable property remains true until end event", () => { - const r = new Readable({ - read() {}, - }); - expect(r.readable).toBe(true); - - const endHandler = jest.fn(); - r.on("end", endHandler); - r.resume(); - r.push(null); - expect(r.readable).toBe(true); - r.off("end", endHandler); - - return new Promise(resolve => { - r.on("end", () => { - expect(r.readable).toBe(false); - resolve(); - }); - }); - }); - - test("readable property becomes false on error", () => { - const r = new Readable({ - read: jest.fn(() => { - process.nextTick(() => { - r.destroy(new Error()); - expect(r.readable).toBe(false); - }); - }), - }); - r.resume(); - - return new Promise(resolve => { - r.on("error", () => { - expect(r.readable).toBe(false); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-stream-readable-readable.js diff --git a/test/js/node/test/parallel/stream-readable-resume-hwm.test.js b/test/js/node/test/parallel/stream-readable-resume-hwm.test.js deleted file mode 100644 index 7e18b7f9a0..0000000000 --- a/test/js/node/test/parallel/stream-readable-resume-hwm.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-stream-readable-resume-hwm.js -//#SHA1: 8149b27327258da89c087856f54e7e7584ddf1e5 -//----------------- -"use strict"; -const { Readable } = require("stream"); - -// readable.resume() should not lead to a ._read() call being scheduled -// when we exceed the high water mark already. - -test("readable.resume() should not call _read() when exceeding highWaterMark", () => { - const mockRead = jest.fn(); - const readable = new Readable({ - read: mockRead, - highWaterMark: 100, - }); - - // Fill up the internal buffer so that we definitely exceed the HWM: - for (let i = 0; i < 10; i++) readable.push("a".repeat(200)); - - // Call resume, and pause after one chunk. - // The .pause() is just so that we don't empty the buffer fully, which would - // be a valid reason to call ._read(). - readable.resume(); - - return new Promise(resolve => { - readable.once("data", () => { - readable.pause(); - expect(mockRead).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-resume-hwm.js diff --git a/test/js/node/test/parallel/stream-readable-resumescheduled.test.js b/test/js/node/test/parallel/stream-readable-resumescheduled.test.js deleted file mode 100644 index 499d82925f..0000000000 --- a/test/js/node/test/parallel/stream-readable-resumescheduled.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-stream-readable-resumeScheduled.js -//#SHA1: 3327b31acfd00e4df0bac4e89d7a764c4de6cb4b -//----------------- -"use strict"; - -const { Readable, Writable } = require("stream"); - -// Testing Readable Stream resumeScheduled state - -describe("Readable Stream resumeScheduled state", () => { - test("pipe() test case", done => { - const r = new Readable({ read() {} }); - const w = new Writable(); - - // resumeScheduled should start = `false`. - expect(r._readableState.resumeScheduled).toBe(false); - - // Calling pipe() should change the state value = true. - r.pipe(w); - expect(r._readableState.resumeScheduled).toBe(true); - - process.nextTick(() => { - expect(r._readableState.resumeScheduled).toBe(false); - done(); - }); - }); - - test("data listener test case", done => { - const r = new Readable({ read() {} }); - - // resumeScheduled should start = `false`. - expect(r._readableState.resumeScheduled).toBe(false); - - r.push(Buffer.from([1, 2, 3])); - - // Adding 'data' listener should change the state value - r.on( - "data", - jest.fn(() => { - expect(r._readableState.resumeScheduled).toBe(false); - }), - ); - expect(r._readableState.resumeScheduled).toBe(true); - - process.nextTick(() => { - expect(r._readableState.resumeScheduled).toBe(false); - done(); - }); - }); - - test("resume() test case", done => { - const r = new Readable({ read() {} }); - - // resumeScheduled should start = `false`. - expect(r._readableState.resumeScheduled).toBe(false); - - // Calling resume() should change the state value. - r.resume(); - expect(r._readableState.resumeScheduled).toBe(true); - - const resumeHandler = jest.fn(() => { - // The state value should be `false` again - expect(r._readableState.resumeScheduled).toBe(false); - }); - - r.on("resume", resumeHandler); - - process.nextTick(() => { - expect(r._readableState.resumeScheduled).toBe(false); - expect(resumeHandler).toHaveBeenCalledTimes(1); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-resumeScheduled.js diff --git a/test/js/node/test/parallel/stream-readable-setencoding-existing-buffers.test.js b/test/js/node/test/parallel/stream-readable-setencoding-existing-buffers.test.js deleted file mode 100644 index f79124ae7f..0000000000 --- a/test/js/node/test/parallel/stream-readable-setencoding-existing-buffers.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-stream-readable-setEncoding-existing-buffers.js -//#SHA1: 1b54f93d0be77b949ce81135243cc9ab3318db5b -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Call .setEncoding() while there are bytes already in the buffer", done => { - const r = new Readable({ read() {} }); - - r.push(Buffer.from("a")); - r.push(Buffer.from("b")); - - r.setEncoding("utf8"); - const chunks = []; - r.on("data", chunk => chunks.push(chunk)); - - process.nextTick(() => { - expect(chunks).toEqual(["ab"]); - done(); - }); -}); - -test("Call .setEncoding() while the buffer contains a complete, but chunked character", done => { - const r = new Readable({ read() {} }); - - r.push(Buffer.from([0xf0])); - r.push(Buffer.from([0x9f])); - r.push(Buffer.from([0x8e])); - r.push(Buffer.from([0x89])); - - r.setEncoding("utf8"); - const chunks = []; - r.on("data", chunk => chunks.push(chunk)); - - process.nextTick(() => { - expect(chunks).toEqual(["🎉"]); - done(); - }); -}); - -test("Call .setEncoding() while the buffer contains an incomplete character, and finish the character later", done => { - const r = new Readable({ read() {} }); - - r.push(Buffer.from([0xf0])); - r.push(Buffer.from([0x9f])); - - r.setEncoding("utf8"); - - r.push(Buffer.from([0x8e])); - r.push(Buffer.from([0x89])); - - const chunks = []; - r.on("data", chunk => chunks.push(chunk)); - - process.nextTick(() => { - expect(chunks).toEqual(["🎉"]); - done(); - }); -}); - -//<#END_FILE: test-stream-readable-setEncoding-existing-buffers.js diff --git a/test/js/node/test/parallel/stream-readable-with-unimplemented-_read.test.js b/test/js/node/test/parallel/stream-readable-with-unimplemented-_read.test.js deleted file mode 100644 index 4160e39091..0000000000 --- a/test/js/node/test/parallel/stream-readable-with-unimplemented-_read.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-stream-readable-with-unimplemented-_read.js -//#SHA1: ba318da76b4c594580f62650bad861933a59c215 -//----------------- -"use strict"; -const { Readable } = require("stream"); - -test("Readable stream with unimplemented _read method", done => { - const readable = new Readable(); - - readable.read(); - readable.on("error", error => { - expect(error).toEqual( - expect.objectContaining({ - code: "ERR_METHOD_NOT_IMPLEMENTED", - name: "Error", - message: expect.any(String), - }), - ); - }); - - readable.on("close", () => { - done(); - }); -}); - -//<#END_FILE: test-stream-readable-with-unimplemented-_read.js diff --git a/test/js/node/test/parallel/stream-set-default-hwm.test.js b/test/js/node/test/parallel/stream-set-default-hwm.test.js deleted file mode 100644 index 6bd5bbee52..0000000000 --- a/test/js/node/test/parallel/stream-set-default-hwm.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-stream-set-default-hwm.js -//#SHA1: bc1189f9270a4b5463d8421ef234fc7baaad667f -//----------------- -"use strict"; - -const { setDefaultHighWaterMark, getDefaultHighWaterMark, Writable, Readable, Transform } = require("stream"); - -test("setDefaultHighWaterMark and getDefaultHighWaterMark for object mode", () => { - expect(getDefaultHighWaterMark(false)).not.toBe(32 * 1000); - setDefaultHighWaterMark(false, 32 * 1000); - expect(getDefaultHighWaterMark(false)).toBe(32 * 1000); -}); - -test("setDefaultHighWaterMark and getDefaultHighWaterMark for non-object mode", () => { - expect(getDefaultHighWaterMark(true)).not.toBe(32); - setDefaultHighWaterMark(true, 32); - expect(getDefaultHighWaterMark(true)).toBe(32); -}); - -test("Writable stream uses new default high water mark", () => { - const w = new Writable({ - write() {}, - }); - expect(w.writableHighWaterMark).toBe(32 * 1000); -}); - -test("Readable stream uses new default high water mark", () => { - const r = new Readable({ - read() {}, - }); - expect(r.readableHighWaterMark).toBe(32 * 1000); -}); - -test("Transform stream uses new default high water mark for both readable and writable", () => { - const t = new Transform({ - transform() {}, - }); - expect(t.writableHighWaterMark).toBe(32 * 1000); - expect(t.readableHighWaterMark).toBe(32 * 1000); -}); - -//<#END_FILE: test-stream-set-default-hwm.js diff --git a/test/js/node/test/parallel/stream-transform-callback-twice.test.js b/test/js/node/test/parallel/stream-transform-callback-twice.test.js deleted file mode 100644 index 3be1a53af3..0000000000 --- a/test/js/node/test/parallel/stream-transform-callback-twice.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-stream-transform-callback-twice.js -//#SHA1: f2ad2048f83461d93a84b8b5696230beb4dba9f2 -//----------------- -"use strict"; - -const { Transform } = require("stream"); - -test("Transform stream callback called twice", done => { - const stream = new Transform({ - transform(chunk, enc, cb) { - cb(); - cb(); - }, - }); - - stream.on("error", error => { - expect(error).toMatchObject({ - name: "Error", - code: "ERR_MULTIPLE_CALLBACK", - message: expect.any(String), - }); - done(); - }); - - stream.write("foo"); -}); - -//<#END_FILE: test-stream-transform-callback-twice.js diff --git a/test/js/node/test/parallel/stream-transform-constructor-set-methods.test.js b/test/js/node/test/parallel/stream-transform-constructor-set-methods.test.js deleted file mode 100644 index ec66e53654..0000000000 --- a/test/js/node/test/parallel/stream-transform-constructor-set-methods.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-stream-transform-constructor-set-methods.js -//#SHA1: a827edab0555cd9f8bd240738812b4d6a48b4e7d -//----------------- -"use strict"; - -const { Transform } = require("stream"); - -test("Transform constructor throws when _transform is not implemented", () => { - const t = new Transform(); - - expect(() => { - t.end(Buffer.from("blerg")); - }).toThrow( - expect.objectContaining({ - name: "Error", - code: "ERR_METHOD_NOT_IMPLEMENTED", - message: expect.any(String), - }), - ); -}); - -test("Transform constructor sets methods correctly", () => { - const _transform = jest.fn((chunk, _, next) => { - next(); - }); - - const _final = jest.fn(next => { - next(); - }); - - const _flush = jest.fn(next => { - next(); - }); - - const t2 = new Transform({ - transform: _transform, - flush: _flush, - final: _final, - }); - - expect(t2._transform).toBe(_transform); - expect(t2._flush).toBe(_flush); - expect(t2._final).toBe(_final); - - t2.end(Buffer.from("blerg")); - t2.resume(); - - expect(_transform).toHaveBeenCalled(); - expect(_final).toHaveBeenCalled(); - expect(_flush).toHaveBeenCalled(); -}); - -//<#END_FILE: test-stream-transform-constructor-set-methods.js diff --git a/test/js/node/test/parallel/stream-transform-flush-data.test.js b/test/js/node/test/parallel/stream-transform-flush-data.test.js deleted file mode 100644 index 6bd8e013c0..0000000000 --- a/test/js/node/test/parallel/stream-transform-flush-data.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-stream-transform-flush-data.js -//#SHA1: 04d0db2bec19ea79b0914db96fd2c996ab8d67bd -//----------------- -"use strict"; - -const { Transform } = require("stream"); - -test("Transform flush should emit expected data", done => { - const expected = "asdf"; - - function _transform(d, e, n) { - n(); - } - - function _flush(n) { - n(null, expected); - } - - const t = new Transform({ - transform: _transform, - flush: _flush, - }); - - t.end(Buffer.from("blerg")); - t.on("data", data => { - expect(data.toString()).toBe(expected); - done(); - }); -}); - -//<#END_FILE: test-stream-transform-flush-data.js diff --git a/test/js/node/test/parallel/stream-unpipe-event.test.js b/test/js/node/test/parallel/stream-unpipe-event.test.js deleted file mode 100644 index 7ad2afc486..0000000000 --- a/test/js/node/test/parallel/stream-unpipe-event.test.js +++ /dev/null @@ -1,140 +0,0 @@ -//#FILE: test-stream-unpipe-event.js -//#SHA1: 17303ffe85d8760f81f6294d9004c98638985bd6 -//----------------- -"use strict"; - -const { Writable, Readable } = require("stream"); - -class NullWriteable extends Writable { - _write(chunk, encoding, callback) { - return callback(); - } -} - -class QuickEndReadable extends Readable { - _read() { - this.push(null); - } -} - -class NeverEndReadable extends Readable { - _read() {} -} - -test("QuickEndReadable pipes and unpipes", done => { - const dest = new NullWriteable(); - const src = new QuickEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -test("NeverEndReadable pipes but does not unpipe", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).not.toHaveBeenCalled(); - expect(src._readableState.pipes.length).toBe(1); - done(); - }); -}); - -test("NeverEndReadable pipes and manually unpipes", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest); - src.unpipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -test("QuickEndReadable pipes and unpipes with end: false", done => { - const dest = new NullWriteable(); - const src = new QuickEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest, { end: false }); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -test("NeverEndReadable pipes but does not unpipe with end: false", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest, { end: false }); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).not.toHaveBeenCalled(); - expect(src._readableState.pipes.length).toBe(1); - done(); - }); -}); - -test("NeverEndReadable pipes and manually unpipes with end: false", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest, { end: false }); - src.unpipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -//<#END_FILE: test-stream-unpipe-event.js diff --git a/test/js/node/test/parallel/stream-unshift-read-race.test.js b/test/js/node/test/parallel/stream-unshift-read-race.test.js deleted file mode 100644 index 9a03dbb0f0..0000000000 --- a/test/js/node/test/parallel/stream-unshift-read-race.test.js +++ /dev/null @@ -1,141 +0,0 @@ -//#FILE: test-stream-unshift-read-race.js -//#SHA1: 3b6a1e1b0ae4b58251211a49cd8171569cfd86ed -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -// This test verifies that: -// 1. unshift() does not cause colliding _read() calls. -// 2. unshift() after the 'end' event is an error, but after the EOF -// signalling null, it is ok, and just creates a new readable chunk. -// 3. push() after the EOF signaling null is an error. -// 4. _read() is not called after pushing the EOF null chunk. - -test("stream unshift read race", done => { - const hwm = 10; - const r = stream.Readable({ highWaterMark: hwm, autoDestroy: false }); - const chunks = 10; - - const data = Buffer.allocUnsafe(chunks * hwm + Math.ceil(hwm / 2)); - for (let i = 0; i < data.length; i++) { - const c = "asdf".charCodeAt(i % 4); - data[i] = c; - } - - let pos = 0; - let pushedNull = false; - r._read = function (n) { - expect(pushedNull).toBe(false); - - // Every third chunk is fast - push(!(chunks % 3)); - - function push(fast) { - expect(pushedNull).toBe(false); - const c = pos >= data.length ? null : data.slice(pos, pos + n); - pushedNull = c === null; - if (fast) { - pos += n; - r.push(c); - if (c === null) pushError(); - } else { - setTimeout(function () { - pos += n; - r.push(c); - if (c === null) pushError(); - }, 1); - } - } - }; - - function pushError() { - r.unshift(Buffer.allocUnsafe(1)); - w.end(); - - expect(() => { - r.push(Buffer.allocUnsafe(1)); - }).toThrow( - expect.objectContaining({ - code: "ERR_STREAM_PUSH_AFTER_EOF", - name: "Error", - message: expect.any(String), - }), - ); - } - - const w = stream.Writable(); - const written = []; - w._write = function (chunk, encoding, cb) { - written.push(chunk.toString()); - cb(); - }; - - r.on("end", () => { - throw new Error("end event should not be emitted"); - }); - - r.on("readable", function () { - let chunk; - while (null !== (chunk = r.read(10))) { - w.write(chunk); - if (chunk.length > 4) r.unshift(Buffer.from("1234")); - } - }); - - w.on("finish", () => { - // Each chunk should start with 1234, and then be asfdasdfasdf... - // The first got pulled out before the first unshift('1234'), so it's - // lacking that piece. - expect(written[0]).toBe("asdfasdfas"); - let asdf = "d"; - console.error(`0: ${written[0]}`); - for (let i = 1; i < written.length; i++) { - console.error(`${i.toString(32)}: ${written[i]}`); - expect(written[i].slice(0, 4)).toBe("1234"); - for (let j = 4; j < written[i].length; j++) { - const c = written[i].charAt(j); - expect(c).toBe(asdf); - switch (asdf) { - case "a": - asdf = "s"; - break; - case "s": - asdf = "d"; - break; - case "d": - asdf = "f"; - break; - case "f": - asdf = "a"; - break; - } - } - } - expect(written).toHaveLength(18); - console.log("ok"); - done(); - }); -}); - -//<#END_FILE: test-stream-unshift-read-race.js diff --git a/test/js/node/test/parallel/stream-wrap.test.js b/test/js/node/test/parallel/stream-wrap.test.js deleted file mode 100644 index ab020ef2b1..0000000000 --- a/test/js/node/test/parallel/stream-wrap.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-stream-wrap.js -//#SHA1: 675a22f043e5a5a38cdd7fff376f269f8d341aab -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -// Remove internal bindings and StreamWrap import as they're not accessible in a normal environment -// We'll mock the necessary functionality instead - -test("StreamWrap shutdown behavior", done => { - const stream = new Duplex({ - read: function () {}, - write: function () {}, - }); - - // Mock StreamWrap - class MockStreamWrap { - constructor(stream) { - this.stream = stream; - this._handle = { - shutdown: jest.fn(req => { - // Simulate async completion with error - process.nextTick(() => { - req.oncomplete(-1); - }); - }), - }; - } - - destroy() { - // Simulate handle closure - } - } - - // Mock ShutdownWrap - class MockShutdownWrap { - oncomplete = null; - } - - function testShutdown(callback) { - const wrap = new MockStreamWrap(stream); - - const req = new MockShutdownWrap(); - req.oncomplete = function (code) { - expect(code).toBeLessThan(0); - callback(); - }; - req.handle = wrap._handle; - - // Close the handle to simulate - wrap.destroy(); - req.handle.shutdown(req); - } - - testShutdown(done); -}); - -//<#END_FILE: test-stream-wrap.js diff --git a/test/js/node/test/parallel/stream-writable-aborted.test.js b/test/js/node/test/parallel/stream-writable-aborted.test.js deleted file mode 100644 index 82c4be4233..0000000000 --- a/test/js/node/test/parallel/stream-writable-aborted.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-stream-writable-aborted.js -//#SHA1: be315bbc27ad16f13bb6b3022e864c8902265391 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -describe("Writable stream aborted property", () => { - test("writableAborted is false initially and true after destroy", () => { - const writable = new Writable({ - write() {}, - }); - expect(writable.writableAborted).toBe(false); - writable.destroy(); - expect(writable.writableAborted).toBe(true); - }); - - test("writableAborted is false initially and true after end and destroy", () => { - const writable = new Writable({ - write() {}, - }); - expect(writable.writableAborted).toBe(false); - writable.end(); - writable.destroy(); - expect(writable.writableAborted).toBe(true); - }); -}); - -//<#END_FILE: test-stream-writable-aborted.js diff --git a/test/js/node/test/parallel/stream-writable-clear-buffer.test.js b/test/js/node/test/parallel/stream-writable-clear-buffer.test.js deleted file mode 100644 index 329e26d512..0000000000 --- a/test/js/node/test/parallel/stream-writable-clear-buffer.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-stream-writable-clear-buffer.js -//#SHA1: 0088292e626fb952c1777f191acee9a6d92d3f4d -//----------------- -"use strict"; - -const Stream = require("stream"); - -class StreamWritable extends Stream.Writable { - constructor() { - super({ objectMode: true }); - } - - // Refs: https://github.com/nodejs/node/issues/6758 - // We need a timer like on the original issue thread. - // Otherwise the code will never reach our test case. - _write(chunk, encoding, cb) { - setImmediate(cb); - } -} - -test("StreamWritable bufferedRequestCount matches actual buffered request count", done => { - const testStream = new StreamWritable(); - testStream.cork(); - - const writeOperations = 5; - let completedWrites = 0; - - for (let i = 1; i <= writeOperations; i++) { - testStream.write(i, () => { - expect(testStream._writableState.bufferedRequestCount).toBe(testStream._writableState.getBuffer().length); - completedWrites++; - - if (completedWrites === writeOperations) { - done(); - } - }); - } - - testStream.end(); -}); - -//<#END_FILE: test-stream-writable-clear-buffer.js diff --git a/test/js/node/test/parallel/stream-writable-constructor-set-methods.test.js b/test/js/node/test/parallel/stream-writable-constructor-set-methods.test.js deleted file mode 100644 index 932e14afb6..0000000000 --- a/test/js/node/test/parallel/stream-writable-constructor-set-methods.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-stream-writable-constructor-set-methods.js -//#SHA1: 5610c9523a02a55898e40f7c72a09973affd133f -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -const bufferBlerg = Buffer.from("blerg"); - -test("Writable without _write method throws", () => { - const w = new Writable(); - - expect(() => { - w.end(bufferBlerg); - }).toThrow( - expect.objectContaining({ - name: "Error", - code: "ERR_METHOD_NOT_IMPLEMENTED", - message: expect.any(String), - }), - ); -}); - -test("Writable with custom write and writev methods", () => { - const _write = jest.fn((chunk, _, next) => { - next(); - }); - - const _writev = jest.fn((chunks, next) => { - expect(chunks.length).toBe(2); - next(); - }); - - const w2 = new Writable({ write: _write, writev: _writev }); - - expect(w2._write).toBe(_write); - expect(w2._writev).toBe(_writev); - - w2.write(bufferBlerg); - - w2.cork(); - w2.write(bufferBlerg); - w2.write(bufferBlerg); - - w2.end(); - - expect(_write).toHaveBeenCalledTimes(1); - expect(_writev).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-writable-constructor-set-methods.js diff --git a/test/js/node/test/parallel/stream-writable-end-multiple.test.js b/test/js/node/test/parallel/stream-writable-end-multiple.test.js deleted file mode 100644 index 71b55c8cc7..0000000000 --- a/test/js/node/test/parallel/stream-writable-end-multiple.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream-writable-end-multiple.js -//#SHA1: db58e265a3eb8bdf7dba1cb959979059ba686f6d -//----------------- -"use strict"; - -const stream = require("stream"); - -test("stream writable end multiple", async () => { - const writable = new stream.Writable(); - writable._write = (chunk, encoding, cb) => { - setTimeout(() => cb(), 10); - }; - - const endCallback1 = jest.fn(); - const endCallback2 = jest.fn(); - const finishCallback = jest.fn(); - const endCallback3 = jest.fn(); - - writable.end("testing ended state", endCallback1); - writable.end(endCallback2); - - writable.on("finish", finishCallback); - - await new Promise(resolve => setTimeout(resolve, 20)); - - expect(endCallback1).toHaveBeenCalledTimes(1); - expect(endCallback2).toHaveBeenCalledTimes(1); - expect(finishCallback).toHaveBeenCalledTimes(1); - - let ticked = false; - writable.end(endCallback3); - ticked = true; - - await new Promise(resolve => setTimeout(resolve, 0)); - - expect(endCallback3).toHaveBeenCalledTimes(1); - expect(endCallback3).toHaveBeenCalledWith( - expect.objectContaining({ - code: "ERR_STREAM_ALREADY_FINISHED", - message: expect.any(String), - }), - ); - expect(ticked).toBe(true); -}); - -//<#END_FILE: test-stream-writable-end-multiple.js diff --git a/test/js/node/test/parallel/stream-writable-ended-state.test.js b/test/js/node/test/parallel/stream-writable-ended-state.test.js deleted file mode 100644 index fb6c3d10c6..0000000000 --- a/test/js/node/test/parallel/stream-writable-ended-state.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-stream-writable-ended-state.js -//#SHA1: e6a35fad059c742def91bd4cab4786faffa26f5b -//----------------- -"use strict"; - -const stream = require("stream"); - -describe("Stream Writable Ended State", () => { - let writable; - - beforeEach(() => { - writable = new stream.Writable(); - - writable._write = (chunk, encoding, cb) => { - expect(writable._writableState.ended).toBe(false); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writableEnded).toBe(false); - cb(); - }; - }); - - test("initial state", () => { - expect(writable._writableState.ended).toBe(false); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writable).toBe(true); - expect(writable.writableEnded).toBe(false); - }); - - test("ended state after end() call", done => { - writable.end("testing ended state", () => { - expect(writable._writableState.ended).toBe(true); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writable).toBe(false); - expect(writable.writableEnded).toBe(true); - done(); - }); - - expect(writable._writableState.ended).toBe(true); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writable).toBe(false); - expect(writable.writableEnded).toBe(true); - }); -}); - -//<#END_FILE: test-stream-writable-ended-state.js diff --git a/test/js/node/test/parallel/stream-writable-final-destroy.test.js b/test/js/node/test/parallel/stream-writable-final-destroy.test.js deleted file mode 100644 index 0d3b853b3b..0000000000 --- a/test/js/node/test/parallel/stream-writable-final-destroy.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stream-writable-final-destroy.js -//#SHA1: 4213d1382f0e5b950211e183a94adc5f3e7a1468 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -test("Writable stream with final and destroy", () => { - const w = new Writable({ - write(chunk, encoding, callback) { - callback(null); - }, - final(callback) { - queueMicrotask(callback); - }, - }); - - w.end(); - w.destroy(); - - const prefinishSpy = jest.fn(); - const finishSpy = jest.fn(); - const closeSpy = jest.fn(); - - w.on("prefinish", prefinishSpy); - w.on("finish", finishSpy); - w.on("close", closeSpy); - - return new Promise(resolve => { - // Use setImmediate to ensure all microtasks have been processed - setImmediate(() => { - expect(prefinishSpy).not.toHaveBeenCalled(); - expect(finishSpy).not.toHaveBeenCalled(); - expect(closeSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-writable-final-destroy.js diff --git a/test/js/node/test/parallel/stream-writable-finished-state.test.js b/test/js/node/test/parallel/stream-writable-finished-state.test.js deleted file mode 100644 index c2d8e8ab49..0000000000 --- a/test/js/node/test/parallel/stream-writable-finished-state.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-stream-writable-finished-state.js -//#SHA1: e9ea6f7cc3e0262bf187b9cf08e9a054c93d7b5f -//----------------- -"use strict"; - -const stream = require("stream"); - -test("Writable stream finished state", done => { - const writable = new stream.Writable(); - - writable._write = (chunk, encoding, cb) => { - // The state finished should start in false. - expect(writable._writableState.finished).toBe(false); - cb(); - }; - - writable.on("finish", () => { - expect(writable._writableState.finished).toBe(true); - }); - - writable.end("testing finished state", () => { - expect(writable._writableState.finished).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-stream-writable-finished-state.js diff --git a/test/js/node/test/parallel/stream-writable-finished.test.js b/test/js/node/test/parallel/stream-writable-finished.test.js deleted file mode 100644 index d7db2acf1d..0000000000 --- a/test/js/node/test/parallel/stream-writable-finished.test.js +++ /dev/null @@ -1,97 +0,0 @@ -//#FILE: test-stream-writable-finished.js -//#SHA1: 20d27885cc10a6787d3e6c6fb877c0aba2310f93 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -// basic -test("Writable.prototype has writableFinished", () => { - expect(Object.hasOwn(Writable.prototype, "writableFinished")).toBe(true); -}); - -// event -test("writableFinished state changes correctly", done => { - const writable = new Writable(); - - writable._write = (chunk, encoding, cb) => { - // The state finished should start in false. - expect(writable.writableFinished).toBe(false); - cb(); - }; - - writable.on("finish", () => { - expect(writable.writableFinished).toBe(true); - done(); - }); - - writable.end("testing finished state", () => { - expect(writable.writableFinished).toBe(true); - }); -}); - -test("Emit finish asynchronously", done => { - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - }); - - w.end(); - w.on("finish", done); -}); - -test("Emit prefinish synchronously", () => { - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - }); - - let sync = true; - w.on("prefinish", () => { - expect(sync).toBe(true); - }); - w.end(); - sync = false; -}); - -test("Emit prefinish synchronously w/ final", () => { - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - final(cb) { - cb(); - }, - }); - - let sync = true; - w.on("prefinish", () => { - expect(sync).toBe(true); - }); - w.end(); - sync = false; -}); - -test("Call _final synchronously", () => { - let sync = true; - const finalMock = jest.fn(cb => { - expect(sync).toBe(true); - cb(); - }); - - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - final: finalMock, - }); - - w.end(); - sync = false; - - expect(finalMock).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-writable-finished.js diff --git a/test/js/node/test/parallel/stream-writable-invalid-chunk.test.js b/test/js/node/test/parallel/stream-writable-invalid-chunk.test.js deleted file mode 100644 index 259e993173..0000000000 --- a/test/js/node/test/parallel/stream-writable-invalid-chunk.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-stream-writable-invalid-chunk.js -//#SHA1: 8febb7872aa1c8bfb7ebcb33db7a5fcd2903b2bd -//----------------- -"use strict"; - -const stream = require("stream"); - -function testWriteType(val, objectMode, code) { - const writable = new stream.Writable({ - objectMode, - write: () => {}, - }); - - const writeOperation = () => writable.write(val); - - if (code) { - expect(writeOperation).toThrow( - expect.objectContaining({ - code, - message: expect.any(String), - }), - ); - } else { - expect(writeOperation).not.toThrow(); - } -} - -describe("Writable stream invalid chunk tests", () => { - test("non-object mode invalid types", () => { - testWriteType([], false, "ERR_INVALID_ARG_TYPE"); - testWriteType({}, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(0, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(true, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(0.0, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(undefined, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(null, false, "ERR_STREAM_NULL_VALUES"); - }); - - test("object mode valid types", () => { - testWriteType([], true); - testWriteType({}, true); - testWriteType(0, true); - testWriteType(true, true); - testWriteType(0.0, true); - testWriteType(undefined, true); - }); - - test("object mode null value", () => { - testWriteType(null, true, "ERR_STREAM_NULL_VALUES"); - }); -}); - -//<#END_FILE: test-stream-writable-invalid-chunk.js diff --git a/test/js/node/test/parallel/stream-writable-needdrain-state.test.js b/test/js/node/test/parallel/stream-writable-needdrain-state.test.js deleted file mode 100644 index 3cac2829fa..0000000000 --- a/test/js/node/test/parallel/stream-writable-needdrain-state.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-stream-writable-needdrain-state.js -//#SHA1: c73d65b940e3ea2fe9c94d9c9d0d4ffe36c47397 -//----------------- -"use strict"; - -const stream = require("stream"); - -test("Transform stream needDrain state", done => { - const transform = new stream.Transform({ - transform: _transform, - highWaterMark: 1, - }); - - function _transform(chunk, encoding, cb) { - process.nextTick(() => { - expect(transform._writableState.needDrain).toBe(true); - cb(); - }); - } - - expect(transform._writableState.needDrain).toBe(false); - - transform.write("asdasd", () => { - expect(transform._writableState.needDrain).toBe(false); - done(); - }); - - expect(transform._writableState.needDrain).toBe(true); -}); - -//<#END_FILE: test-stream-writable-needdrain-state.js diff --git a/test/js/node/test/parallel/stream-writable-null.test.js b/test/js/node/test/parallel/stream-writable-null.test.js deleted file mode 100644 index 6f747df705..0000000000 --- a/test/js/node/test/parallel/stream-writable-null.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-stream-writable-null.js -//#SHA1: 5a080b117b05a98d0b7bf6895b554892c2690ed8 -//----------------- -"use strict"; - -const stream = require("stream"); - -class MyWritable extends stream.Writable { - constructor(options) { - super({ autoDestroy: false, ...options }); - } - _write(chunk, encoding, callback) { - expect(chunk).not.toBe(null); - callback(); - } -} - -test("MyWritable throws on null in object mode", () => { - const m = new MyWritable({ objectMode: true }); - expect(() => { - m.write(null); - }).toThrow( - expect.objectContaining({ - code: "ERR_STREAM_NULL_VALUES", - }), - ); -}); - -test("MyWritable throws on false in non-object mode", () => { - const m = new MyWritable(); - expect(() => { - m.write(false); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); -}); - -test("MyWritable should not throw on false in object mode", done => { - const m = new MyWritable({ objectMode: true }); - m.write(false, err => { - expect(err).toBeFalsy(); - done(); - }); -}); - -test("MyWritable should not throw or emit error on false in object mode", done => { - const m = new MyWritable({ objectMode: true }).on("error", e => { - done(e || new Error("should not get here")); - }); - m.write(false, err => { - expect(err).toBeFalsy(); - done(); - }); -}); - -//<#END_FILE: test-stream-writable-null.js diff --git a/test/js/node/test/parallel/stream-writable-properties.test.js b/test/js/node/test/parallel/stream-writable-properties.test.js deleted file mode 100644 index 86bf40656b..0000000000 --- a/test/js/node/test/parallel/stream-writable-properties.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-stream-writable-properties.js -//#SHA1: 3af7ed348fc81b0a70901cb29735a8778d0a8875 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -describe("Writable stream properties", () => { - test("writableCorked property", () => { - const w = new Writable(); - - expect(w.writableCorked).toBe(0); - - w.uncork(); - expect(w.writableCorked).toBe(0); - - w.cork(); - expect(w.writableCorked).toBe(1); - - w.cork(); - expect(w.writableCorked).toBe(2); - - w.uncork(); - expect(w.writableCorked).toBe(1); - - w.uncork(); - expect(w.writableCorked).toBe(0); - - w.uncork(); - expect(w.writableCorked).toBe(0); - }); -}); - -//<#END_FILE: test-stream-writable-properties.js diff --git a/test/js/node/test/parallel/stream-writable-write-error.test.js b/test/js/node/test/parallel/stream-writable-write-error.test.js deleted file mode 100644 index 6c4a7d0b51..0000000000 --- a/test/js/node/test/parallel/stream-writable-write-error.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-stream-writable-write-error.js -//#SHA1: 16053b21f2a6c80ae69ae55424550e7213f1f868 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -function expectError(w, args, code, sync) { - if (sync) { - if (code) { - expect(() => w.write(...args)).toThrow( - expect.objectContaining({ - code, - message: expect.any(String), - }), - ); - } else { - w.write(...args); - } - } else { - let errorCalled = false; - let ticked = false; - w.write(...args, err => { - expect(ticked).toBe(true); - expect(errorCalled).toBe(false); - expect(err.code).toBe(code); - }); - ticked = true; - w.on("error", err => { - errorCalled = true; - expect(err.code).toBe(code); - }); - } -} - -function runTest(autoDestroy) { - test("write after end", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - w.end(); - expectError(w, ["asd"], "ERR_STREAM_WRITE_AFTER_END"); - }); - - test("write after destroy", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - w.destroy(); - }); - - test("write null values", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - expectError(w, [null], "ERR_STREAM_NULL_VALUES", true); - }); - - test("write invalid arg type", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - expectError(w, [{}], "ERR_INVALID_ARG_TYPE", true); - }); - - test("write with unknown encoding", () => { - const w = new Writable({ - decodeStrings: false, - autoDestroy, - write() {}, - }); - expectError(w, ["asd", "noencoding"], "ERR_UNKNOWN_ENCODING", true); - }); -} - -describe("Writable stream write errors (autoDestroy: false)", () => { - runTest(false); -}); - -describe("Writable stream write errors (autoDestroy: true)", () => { - runTest(true); -}); - -//<#END_FILE: test-stream-writable-write-error.js diff --git a/test/js/node/test/parallel/stream-writablestate-ending.test.js b/test/js/node/test/parallel/stream-writablestate-ending.test.js deleted file mode 100644 index 43063afed8..0000000000 --- a/test/js/node/test/parallel/stream-writablestate-ending.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-stream-writableState-ending.js -//#SHA1: 97f5685bff2d1c4507caed842006d83c7317e0c0 -//----------------- -"use strict"; - -const stream = require("stream"); - -describe("Writable Stream State", () => { - let writable; - - beforeEach(() => { - writable = new stream.Writable(); - }); - - function testStates(ending, finished, ended) { - expect(writable._writableState.ending).toBe(ending); - expect(writable._writableState.finished).toBe(finished); - expect(writable._writableState.ended).toBe(ended); - } - - test("Writable state transitions", done => { - writable._write = (chunk, encoding, cb) => { - // Ending, finished, ended start in false. - testStates(false, false, false); - cb(); - }; - - writable.on("finish", () => { - // Ending, finished, ended = true. - testStates(true, true, true); - done(); - }); - - const result = writable.end("testing function end()", () => { - // Ending, finished, ended = true. - testStates(true, true, true); - }); - - // End returns the writable instance - expect(result).toBe(writable); - - // Ending, ended = true. - // finished = false. - testStates(true, false, true); - }); -}); - -//<#END_FILE: test-stream-writableState-ending.js diff --git a/test/js/node/test/parallel/stream-writablestate-uncorked-bufferedrequestcount.test.js b/test/js/node/test/parallel/stream-writablestate-uncorked-bufferedrequestcount.test.js deleted file mode 100644 index 7a9e5b4db1..0000000000 --- a/test/js/node/test/parallel/stream-writablestate-uncorked-bufferedrequestcount.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-stream-writableState-uncorked-bufferedRequestCount.js -//#SHA1: 39a95157551d47517d4c7aa46d1806b5dbccebcf -//----------------- -"use strict"; - -const stream = require("stream"); - -describe("Writable stream corking and uncorking", () => { - let writable; - - beforeEach(() => { - writable = new stream.Writable(); - - writable._writev = jest.fn((chunks, cb) => { - expect(chunks.length).toBe(2); - cb(); - }); - - writable._write = jest.fn((chunk, encoding, cb) => { - cb(); - }); - }); - - test("corking and uncorking behavior", done => { - // first cork - writable.cork(); - expect(writable._writableState.corked).toBe(1); - expect(writable._writableState.bufferedRequestCount).toBe(0); - - // cork again - writable.cork(); - expect(writable._writableState.corked).toBe(2); - - // The first chunk is buffered - writable.write("first chunk"); - expect(writable._writableState.bufferedRequestCount).toBe(1); - - // First uncork does nothing - writable.uncork(); - expect(writable._writableState.corked).toBe(1); - expect(writable._writableState.bufferedRequestCount).toBe(1); - - process.nextTick(() => { - // The second chunk is buffered, because we uncork at the end of tick - writable.write("second chunk"); - expect(writable._writableState.corked).toBe(1); - expect(writable._writableState.bufferedRequestCount).toBe(2); - - // Second uncork flushes the buffer - writable.uncork(); - expect(writable._writableState.corked).toBe(0); - expect(writable._writableState.bufferedRequestCount).toBe(0); - - // Verify that end() uncorks correctly - writable.cork(); - writable.write("third chunk"); - writable.end(); - - // End causes an uncork() as well - expect(writable._writableState.corked).toBe(0); - expect(writable._writableState.bufferedRequestCount).toBe(0); - - expect(writable._writev).toHaveBeenCalledTimes(1); - expect(writable._write).toHaveBeenCalledTimes(1); - - done(); - }); - }); -}); - -//<#END_FILE: test-stream-writableState-uncorked-bufferedRequestCount.js diff --git a/test/js/node/test/parallel/stream-write-destroy.test.js b/test/js/node/test/parallel/stream-write-destroy.test.js deleted file mode 100644 index 4fd760ef04..0000000000 --- a/test/js/node/test/parallel/stream-write-destroy.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-stream-write-destroy.js -//#SHA1: d39354900702b56f19b37407f2e8459ca063fbd6 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -// Test interaction between calling .destroy() on a writable and pending -// writes. - -describe("Stream write and destroy interaction", () => { - for (const withPendingData of [false, true]) { - for (const useEnd of [false, true]) { - test(`withPendingData: ${withPendingData}, useEnd: ${useEnd}`, () => { - const callbacks = []; - - const w = new Writable({ - write(data, enc, cb) { - callbacks.push(cb); - }, - // Effectively disable the HWM to observe 'drain' events more easily. - highWaterMark: 1, - }); - - let chunksWritten = 0; - let drains = 0; - w.on("drain", () => drains++); - - function onWrite(err) { - if (err) { - expect(w.destroyed).toBe(true); - expect(err.code).toBe("ERR_STREAM_DESTROYED"); - } else { - chunksWritten++; - } - } - - w.write("abc", onWrite); - expect(chunksWritten).toBe(0); - expect(drains).toBe(0); - callbacks.shift()(); - expect(chunksWritten).toBe(1); - expect(drains).toBe(1); - - if (withPendingData) { - // Test 2 cases: There either is or is not data still in the write queue. - // (The second write will never actually get executed either way.) - w.write("def", onWrite); - } - if (useEnd) { - // Again, test 2 cases: Either we indicate that we want to end the - // writable or not. - w.end("ghi", onWrite); - } else { - w.write("ghi", onWrite); - } - - expect(chunksWritten).toBe(1); - w.destroy(); - expect(chunksWritten).toBe(1); - callbacks.shift()(); - expect(chunksWritten).toBe(useEnd && !withPendingData ? 1 : 2); - expect(callbacks.length).toBe(0); - expect(drains).toBe(1); - }); - } - } -}); - -//<#END_FILE: test-stream-write-destroy.js diff --git a/test/js/node/test/parallel/stream-write-drain.test.js b/test/js/node/test/parallel/stream-write-drain.test.js deleted file mode 100644 index d0c89e4302..0000000000 --- a/test/js/node/test/parallel/stream-write-drain.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-stream-write-drain.js -//#SHA1: 893708699284e105a409388953fae28a836370b2 -//----------------- -"use strict"; -const { Writable } = require("stream"); - -// Don't emit 'drain' if ended - -test("Writable stream should not emit 'drain' if ended", done => { - const w = new Writable({ - write(data, enc, cb) { - process.nextTick(cb); - }, - highWaterMark: 1, - }); - - const drainSpy = jest.fn(); - w.on("drain", drainSpy); - - w.write("asd"); - w.end(); - - // Use process.nextTick to ensure that any potential 'drain' event would have been emitted - process.nextTick(() => { - expect(drainSpy).not.toHaveBeenCalled(); - done(); - }); -}); - -//<#END_FILE: test-stream-write-drain.js diff --git a/test/js/node/test/parallel/stream2-decode-partial.test.js b/test/js/node/test/parallel/stream2-decode-partial.test.js deleted file mode 100644 index 47cbf45918..0000000000 --- a/test/js/node/test/parallel/stream2-decode-partial.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-stream2-decode-partial.js -//#SHA1: bc4bec1c0be7857c86b9cd75dbb76b939d9619ab -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -let buf = ""; -const euro = Buffer.from([0xe2, 0x82, 0xac]); -const cent = Buffer.from([0xc2, 0xa2]); -const source = Buffer.concat([euro, cent]); - -test("Readable stream decodes partial UTF-8 characters correctly", done => { - const readable = Readable({ encoding: "utf8" }); - readable.push(source.slice(0, 2)); - readable.push(source.slice(2, 4)); - readable.push(source.slice(4, 6)); - readable.push(null); - - readable.on("data", function (data) { - buf += data; - }); - - readable.on("end", function () { - expect(buf).toBe("€¢"); - done(); - }); -}); - -//<#END_FILE: test-stream2-decode-partial.js diff --git a/test/js/node/test/parallel/stream2-objects.test.js b/test/js/node/test/parallel/stream2-objects.test.js deleted file mode 100644 index 847802f2db..0000000000 --- a/test/js/node/test/parallel/stream2-objects.test.js +++ /dev/null @@ -1,298 +0,0 @@ -//#FILE: test-stream2-objects.js -//#SHA1: e9aa308270bcb656e33df7deb63c8ce8739c0f35 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Readable, Writable } = require("stream"); - -function toArray(callback) { - const stream = new Writable({ objectMode: true }); - const list = []; - stream.write = function (chunk) { - list.push(chunk); - }; - - stream.end = function () { - callback(list); - }; - - return stream; -} - -function fromArray(list) { - const r = new Readable({ objectMode: true }); - r._read = jest.fn(); - list.forEach(function (chunk) { - r.push(chunk); - }); - r.push(null); - - return r; -} - -test("Verify that objects can be read from the stream", () => { - const r = fromArray([{ one: "1" }, { two: "2" }]); - - const v1 = r.read(); - const v2 = r.read(); - const v3 = r.read(); - - expect(v1).toEqual({ one: "1" }); - expect(v2).toEqual({ two: "2" }); - expect(v3).toBeNull(); -}); - -test("Verify that objects can be piped into the stream", done => { - const r = fromArray([{ one: "1" }, { two: "2" }]); - - r.pipe( - toArray(list => { - expect(list).toEqual([{ one: "1" }, { two: "2" }]); - done(); - }), - ); -}); - -test("Verify that read(n) is ignored", () => { - const r = fromArray([{ one: "1" }, { two: "2" }]); - const value = r.read(2); - - expect(value).toEqual({ one: "1" }); -}); - -test("Verify that objects can be synchronously read", done => { - const r = new Readable({ objectMode: true }); - const list = [{ one: "1" }, { two: "2" }]; - r._read = function (n) { - const item = list.shift(); - r.push(item || null); - }; - - r.pipe( - toArray(list => { - expect(list).toEqual([{ one: "1" }, { two: "2" }]); - done(); - }), - ); -}); - -test("Verify that objects can be asynchronously read", done => { - const r = new Readable({ objectMode: true }); - const list = [{ one: "1" }, { two: "2" }]; - r._read = function (n) { - const item = list.shift(); - process.nextTick(function () { - r.push(item || null); - }); - }; - - r.pipe( - toArray(list => { - expect(list).toEqual([{ one: "1" }, { two: "2" }]); - done(); - }), - ); -}); - -test("Verify that strings can be read as objects", done => { - const r = new Readable({ - objectMode: true, - }); - r._read = jest.fn(); - const list = ["one", "two", "three"]; - list.forEach(function (str) { - r.push(str); - }); - r.push(null); - - r.pipe( - toArray(array => { - expect(array).toEqual(list); - done(); - }), - ); -}); - -test("Verify read(0) behavior for object streams", done => { - const r = new Readable({ - objectMode: true, - }); - r._read = jest.fn(); - - r.push("foobar"); - r.push(null); - - r.pipe( - toArray(array => { - expect(array).toEqual(["foobar"]); - done(); - }), - ); -}); - -test("Verify the behavior of pushing falsey values", done => { - const r = new Readable({ - objectMode: true, - }); - r._read = jest.fn(); - - r.push(false); - r.push(0); - r.push(""); - r.push(null); - - r.pipe( - toArray(array => { - expect(array).toEqual([false, 0, ""]); - done(); - }), - ); -}); - -test("Verify high watermark _read() behavior", () => { - const r = new Readable({ - highWaterMark: 6, - objectMode: true, - }); - let calls = 0; - const list = ["1", "2", "3", "4", "5", "6", "7", "8"]; - - r._read = function (n) { - calls++; - }; - - list.forEach(function (c) { - r.push(c); - }); - - const v = r.read(); - - expect(calls).toBe(0); - expect(v).toBe("1"); - - const v2 = r.read(); - expect(v2).toBe("2"); - - const v3 = r.read(); - expect(v3).toBe("3"); - - expect(calls).toBe(1); -}); - -test("Verify high watermark push behavior", () => { - const r = new Readable({ - highWaterMark: 6, - objectMode: true, - }); - r._read = jest.fn(); - for (let i = 0; i < 6; i++) { - const bool = r.push(i); - expect(bool).toBe(i !== 5); - } -}); - -test("Verify that objects can be written to stream", done => { - const w = new Writable({ objectMode: true }); - - w._write = function (chunk, encoding, cb) { - expect(chunk).toEqual({ foo: "bar" }); - cb(); - }; - - w.on("finish", done); - w.write({ foo: "bar" }); - w.end(); -}); - -test("Verify that multiple objects can be written to stream", done => { - const w = new Writable({ objectMode: true }); - const list = []; - - w._write = function (chunk, encoding, cb) { - list.push(chunk); - cb(); - }; - - w.on("finish", () => { - expect(list).toEqual([0, 1, 2, 3, 4]); - done(); - }); - - w.write(0); - w.write(1); - w.write(2); - w.write(3); - w.write(4); - w.end(); -}); - -test("Verify that strings can be written as objects", done => { - const w = new Writable({ - objectMode: true, - }); - const list = []; - - w._write = function (chunk, encoding, cb) { - list.push(chunk); - process.nextTick(cb); - }; - - w.on("finish", () => { - expect(list).toEqual(["0", "1", "2", "3", "4"]); - done(); - }); - - w.write("0"); - w.write("1"); - w.write("2"); - w.write("3"); - w.write("4"); - w.end(); -}); - -test("Verify that stream buffers finish until callback is called", done => { - const w = new Writable({ - objectMode: true, - }); - let called = false; - - w._write = function (chunk, encoding, cb) { - expect(chunk).toBe("foo"); - - process.nextTick(function () { - called = true; - cb(); - }); - }; - - w.on("finish", () => { - expect(called).toBe(true); - done(); - }); - - w.write("foo"); - w.end(); -}); - -//<#END_FILE: test-stream2-objects.js diff --git a/test/js/node/test/parallel/stream2-push.test.js b/test/js/node/test/parallel/stream2-push.test.js deleted file mode 100644 index d623f82a98..0000000000 --- a/test/js/node/test/parallel/stream2-push.test.js +++ /dev/null @@ -1,139 +0,0 @@ -//#FILE: test-stream2-push.js -//#SHA1: 9b6aded0c40321f8d2c15dceab98b26033f23adf -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { Readable, Writable } = require("stream"); -const EE = require("events").EventEmitter; - -test("Stream2 push behavior", async () => { - const stream = new Readable({ - highWaterMark: 16, - encoding: "utf8", - }); - - const source = new EE(); - - stream._read = function () { - console.error("stream._read"); - readStart(); - }; - - let ended = false; - stream.on("end", function () { - ended = true; - }); - - source.on("data", function (chunk) { - const ret = stream.push(chunk); - console.error("data", stream.readableLength); - if (!ret) readStop(); - }); - - source.on("end", function () { - stream.push(null); - }); - - let reading = false; - - function readStart() { - console.error("readStart"); - reading = true; - } - - function readStop() { - console.error("readStop"); - reading = false; - process.nextTick(function () { - const r = stream.read(); - if (r !== null) writer.write(r); - }); - } - - const writer = new Writable({ - decodeStrings: false, - }); - - const written = []; - - const expectWritten = [ - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - ]; - - writer._write = function (chunk, encoding, cb) { - console.error(`WRITE ${chunk}`); - written.push(chunk); - process.nextTick(cb); - }; - - const finishPromise = new Promise(resolve => { - writer.on("finish", () => { - console.error("finish"); - expect(written).toEqual(expectWritten); - console.log("ok"); - resolve(); - }); - }); - - // Now emit some chunks. - const chunk = "asdfg"; - - let set = 0; - readStart(); - - function data() { - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(false); - if (set++ < 5) return new Promise(resolve => setTimeout(() => resolve(data()), 10)); - else return end(); - } - - function end() { - source.emit("end"); - expect(reading).toBe(false); - writer.end(stream.read()); - return new Promise(resolve => - setImmediate(() => { - expect(ended).toBe(true); - resolve(); - }), - ); - } - - await data(); - await finishPromise; -}); - -//<#END_FILE: test-stream2-push.js diff --git a/test/js/node/test/parallel/stream2-readable-wrap-destroy.test.js b/test/js/node/test/parallel/stream2-readable-wrap-destroy.test.js deleted file mode 100644 index b001ee2a9b..0000000000 --- a/test/js/node/test/parallel/stream2-readable-wrap-destroy.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-stream2-readable-wrap-destroy.js -//#SHA1: 632a198f6b4fc882942984df461383047f6b78a6 -//----------------- -"use strict"; - -const { Readable } = require("stream"); -const EventEmitter = require("events"); - -test('Readable.wrap should call destroy on "destroy" event', () => { - const oldStream = new EventEmitter(); - oldStream.pause = jest.fn(); - oldStream.resume = jest.fn(); - - const destroyMock = jest.fn(); - - const readable = new Readable({ - autoDestroy: false, - destroy: destroyMock, - }); - - readable.wrap(oldStream); - oldStream.emit("destroy"); - - expect(destroyMock).toHaveBeenCalledTimes(1); -}); - -test('Readable.wrap should call destroy on "close" event', () => { - const oldStream = new EventEmitter(); - oldStream.pause = jest.fn(); - oldStream.resume = jest.fn(); - - const destroyMock = jest.fn(); - - const readable = new Readable({ - autoDestroy: false, - destroy: destroyMock, - }); - - readable.wrap(oldStream); - oldStream.emit("close"); - - expect(destroyMock).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream2-readable-wrap-destroy.js diff --git a/test/js/node/test/parallel/stream3-cork-end.test.js b/test/js/node/test/parallel/stream3-cork-end.test.js deleted file mode 100644 index 974e12ce1a..0000000000 --- a/test/js/node/test/parallel/stream3-cork-end.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-stream3-cork-end.js -//#SHA1: 1ac6a2589bee41bc1e9e08ef308bcae3cd999106 -//----------------- -"use strict"; - -const stream = require("stream"); -const Writable = stream.Writable; - -// Test the buffering behavior of Writable streams. -// -// The call to cork() triggers storing chunks which are flushed -// on calling end() and the stream subsequently ended. -// -// node version target: 0.12 - -test("Writable stream buffering behavior with cork() and end()", done => { - const expectedChunks = ["please", "buffer", "me", "kindly"]; - const inputChunks = expectedChunks.slice(0); - let seenChunks = []; - let seenEnd = false; - - const w = new Writable(); - // Let's arrange to store the chunks. - w._write = function (chunk, encoding, cb) { - // Stream end event is not seen before the last write. - expect(seenEnd).toBe(false); - // Default encoding given none was specified. - expect(encoding).toBe("buffer"); - - seenChunks.push(chunk); - cb(); - }; - // Let's record the stream end event. - w.on("finish", () => { - seenEnd = true; - }); - - function writeChunks(remainingChunks, callback) { - const writeChunk = remainingChunks.shift(); - let writeState; - - if (writeChunk) { - setImmediate(() => { - writeState = w.write(writeChunk); - // We were not told to stop writing. - expect(writeState).toBe(true); - - writeChunks(remainingChunks, callback); - }); - } else { - callback(); - } - } - - // Do an initial write. - w.write("stuff"); - // The write was immediate. - expect(seenChunks.length).toBe(1); - // Reset the seen chunks. - seenChunks = []; - - // Trigger stream buffering. - w.cork(); - - // Write the bufferedChunks. - writeChunks(inputChunks, () => { - // Should not have seen anything yet. - expect(seenChunks.length).toBe(0); - - // Trigger flush and ending the stream. - w.end(); - - // Stream should not ended in current tick. - expect(seenEnd).toBe(false); - - // Buffered bytes should be seen in current tick. - expect(seenChunks.length).toBe(4); - - // Did the chunks match. - for (let i = 0, l = expectedChunks.length; i < l; i++) { - const seen = seenChunks[i]; - // There was a chunk. - expect(seen).toBeTruthy(); - - const expected = Buffer.from(expectedChunks[i]); - // It was what we expected. - expect(seen.equals(expected)).toBe(true); - } - - setImmediate(() => { - // Stream should have ended in next tick. - expect(seenEnd).toBe(true); - done(); - }); - }); -}); - -//<#END_FILE: test-stream3-cork-end.js diff --git a/test/js/node/test/parallel/stream3-cork-uncork.test.js b/test/js/node/test/parallel/stream3-cork-uncork.test.js deleted file mode 100644 index 85aa626dec..0000000000 --- a/test/js/node/test/parallel/stream3-cork-uncork.test.js +++ /dev/null @@ -1,104 +0,0 @@ -//#FILE: test-stream3-cork-uncork.js -//#SHA1: d1cc0d9e9be4ae657ab2db8e02589ac485268c63 -//----------------- -"use strict"; - -const stream = require("stream"); -const Writable = stream.Writable; - -// Test the buffering behavior of Writable streams. -// -// The call to cork() triggers storing chunks which are flushed -// on calling uncork() in the same tick. -// -// node version target: 0.12 - -describe("Writable stream cork and uncork", () => { - const expectedChunks = ["please", "buffer", "me", "kindly"]; - let inputChunks; - let seenChunks; - let seenEnd; - let w; - - beforeEach(() => { - inputChunks = expectedChunks.slice(0); - seenChunks = []; - seenEnd = false; - - w = new Writable(); - // Let's arrange to store the chunks. - w._write = function (chunk, encoding, cb) { - // Default encoding given none was specified. - expect(encoding).toBe("buffer"); - - seenChunks.push(chunk); - cb(); - }; - // Let's record the stream end event. - w.on("finish", () => { - seenEnd = true; - }); - }); - - function writeChunks(remainingChunks) { - return new Promise(resolve => { - function write() { - const writeChunk = remainingChunks.shift(); - if (writeChunk) { - setImmediate(() => { - const writeState = w.write(writeChunk); - // We were not told to stop writing. - expect(writeState).toBe(true); - write(); - }); - } else { - resolve(); - } - } - write(); - }); - } - - test("initial write is immediate", () => { - w.write("stuff"); - // The write was immediate. - expect(seenChunks.length).toBe(1); - }); - - test("cork buffers writes and uncork flushes", async () => { - // Reset the chunks seen so far. - seenChunks = []; - - // Trigger stream buffering. - w.cork(); - - // Write the bufferedChunks. - await writeChunks(inputChunks); - - // Should not have seen anything yet. - expect(seenChunks.length).toBe(0); - - // Trigger writing out the buffer. - w.uncork(); - - // Buffered bytes should be seen in current tick. - expect(seenChunks.length).toBe(4); - - // Did the chunks match. - for (let i = 0, l = expectedChunks.length; i < l; i++) { - const seen = seenChunks[i]; - // There was a chunk. - expect(seen).toBeTruthy(); - - const expected = Buffer.from(expectedChunks[i]); - // It was what we expected. - expect(seen.equals(expected)).toBe(true); - } - - await new Promise(resolve => setImmediate(resolve)); - // The stream should not have been ended. - expect(seenEnd).toBe(false); - }); -}); - -//<#END_FILE: test-stream3-cork-uncork.js diff --git a/test/js/node/test/parallel/stream3-pause-then-read.test.js b/test/js/node/test/parallel/stream3-pause-then-read.test.js deleted file mode 100644 index f7df831524..0000000000 --- a/test/js/node/test/parallel/stream3-pause-then-read.test.js +++ /dev/null @@ -1,187 +0,0 @@ -//#FILE: test-stream3-pause-then-read.js -//#SHA1: bd44dc04c63140e4b65c0755eec67a55eaf48158 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const stream = require("stream"); -const Readable = stream.Readable; -const Writable = stream.Writable; - -const totalChunks = 100; -const chunkSize = 99; -const expectTotalData = totalChunks * chunkSize; -let expectEndingData = expectTotalData; - -let r, totalPushed; - -beforeEach(() => { - r = new Readable({ highWaterMark: 1000 }); - let chunks = totalChunks; - r._read = function (n) { - console.log("_read called", chunks); - if (!(chunks % 2)) setImmediate(push); - else if (!(chunks % 3)) process.nextTick(push); - else push(); - }; - - totalPushed = 0; - function push() { - const chunk = chunks-- > 0 ? Buffer.alloc(chunkSize, "x") : null; - if (chunk) { - totalPushed += chunk.length; - } - console.log("chunks", chunks); - r.push(chunk); - } -}); - -test("stream3 pause then read", async () => { - await read100(); - await new Promise(resolve => setImmediate(resolve)); - await pipeLittle(); - await read1234(); - await resumePause(); - await pipe(); -}); - -// First we read 100 bytes. -async function read100() { - await readn(100); -} - -async function readn(n) { - console.error(`read ${n}`); - expectEndingData -= n; - return new Promise(resolve => { - function read() { - const c = r.read(n); - console.error("c", c); - if (!c) r.once("readable", read); - else { - expect(c.length).toBe(n); - expect(r.readableFlowing).toBeFalsy(); - resolve(); - } - } - read(); - }); -} - -// Then we listen to some data events. -function onData() { - return new Promise(resolve => { - expectEndingData -= 100; - console.error("onData"); - let seen = 0; - r.on("data", function od(c) { - seen += c.length; - if (seen >= 100) { - // Seen enough - r.removeListener("data", od); - r.pause(); - if (seen > 100) { - // Oh no, seen too much! - // Put the extra back. - const diff = seen - 100; - r.unshift(c.slice(c.length - diff)); - console.error("seen too much", seen, diff); - } - resolve(); - } - }); - }); -} - -// Just pipe 200 bytes, then unshift the extra and unpipe. -async function pipeLittle() { - expectEndingData -= 200; - console.error("pipe a little"); - const w = new Writable(); - let written = 0; - await new Promise(resolve => { - w.on("finish", () => { - expect(written).toBe(200); - resolve(); - }); - w._write = function (chunk, encoding, cb) { - written += chunk.length; - if (written >= 200) { - r.unpipe(w); - w.end(); - cb(); - if (written > 200) { - const diff = written - 200; - written -= diff; - r.unshift(chunk.slice(chunk.length - diff)); - } - } else { - setImmediate(cb); - } - }; - r.pipe(w); - }); -} - -// Now read 1234 more bytes. -async function read1234() { - await readn(1234); -} - -function resumePause() { - console.error("resumePause"); - // Don't read anything, just resume and re-pause a whole bunch. - r.resume(); - r.pause(); - r.resume(); - r.pause(); - r.resume(); - r.pause(); - r.resume(); - r.pause(); - r.resume(); - r.pause(); - return new Promise(resolve => setImmediate(resolve)); -} - -function pipe() { - console.error("pipe the rest"); - const w = new Writable(); - let written = 0; - w._write = function (chunk, encoding, cb) { - written += chunk.length; - cb(); - }; - return new Promise(resolve => { - w.on("finish", () => { - console.error("written", written, totalPushed); - expect(written).toBe(expectEndingData); - expect(totalPushed).toBe(expectTotalData); - console.log("ok"); - resolve(); - }); - r.pipe(w); - }); -} - -//<#END_FILE: test-stream3-pause-then-read.js diff --git a/test/js/node/test/parallel/stream3-pipeline-async-iterator.test.js b/test/js/node/test/parallel/stream3-pipeline-async-iterator.test.js deleted file mode 100644 index d84b763fcc..0000000000 --- a/test/js/node/test/parallel/stream3-pipeline-async-iterator.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-stream3-pipeline-async-iterator.js -//#SHA1: db2d5b4cb6c502fdccdfa1ed9384d6baa70b1e0b -//----------------- -/* eslint-disable node-core/require-common-first, require-yield */ -"use strict"; -const { pipeline } = require("node:stream/promises"); - -test("async iterators can act as readable and writable streams", async () => { - // Ensure that async iterators can act as readable and writable streams - async function* myCustomReadable() { - yield "Hello"; - yield "World"; - } - - const messages = []; - async function* myCustomWritable(stream) { - for await (const chunk of stream) { - messages.push(chunk); - } - } - - await pipeline(myCustomReadable, myCustomWritable); - - expect(messages).toEqual(["Hello", "World"]); -}); - -//<#END_FILE: test-stream3-pipeline-async-iterator.js diff --git a/test/js/node/test/parallel/string-decoder-end.test.js b/test/js/node/test/parallel/string-decoder-end.test.js deleted file mode 100644 index fc12a567f7..0000000000 --- a/test/js/node/test/parallel/string-decoder-end.test.js +++ /dev/null @@ -1,124 +0,0 @@ -//#FILE: test-string-decoder-end.js -//#SHA1: 9b9cd65cf41dc419c54b8c47317aef7fcb251c5c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Verify that the string decoder works getting 1 byte at a time, -// the whole buffer at once, and that both match the .toString(enc) -// result of the entire buffer. - -const assert = require("assert"); -const SD = require("string_decoder").StringDecoder; -const encodings = ["base64", "base64url", "hex", "utf8", "utf16le", "ucs2"]; - -const bufs = ["☃💩", "asdf"].map(b => Buffer.from(b)); - -// Also test just arbitrary bytes from 0-15. -for (let i = 1; i <= 16; i++) { - const bytes = "." - .repeat(i - 1) - .split(".") - .map((_, j) => j + 0x78); - bufs.push(Buffer.from(bytes)); -} - -encodings.forEach(testEncoding); - -function testEncoding(encoding) { - bufs.forEach(buf => { - testBuf(encoding, buf); - }); -} - -function testBuf(encoding, buf) { - test(`StringDecoder ${encoding} - ${buf.toString()}`, () => { - // Write one byte at a time. - let s = new SD(encoding); - let res1 = ""; - for (let i = 0; i < buf.length; i++) { - res1 += s.write(buf.slice(i, i + 1)); - } - res1 += s.end(); - - // Write the whole buffer at once. - let res2 = ""; - s = new SD(encoding); - res2 += s.write(buf); - res2 += s.end(); - - // .toString() on the buffer - const res3 = buf.toString(encoding); - - // One byte at a time should match toString - expect(res1).toBe(res3); - // All bytes at once should match toString - expect(res2).toBe(res3); - }); -} - -function testEnd(encoding, incomplete, next, expected) { - test(`StringDecoder ${encoding} end - ${incomplete.toString("hex")} + ${next.toString("hex")}`, () => { - let res = ""; - const s = new SD(encoding); - res += s.write(incomplete); - res += s.end(); - res += s.write(next); - res += s.end(); - - expect(res).toBe(expected); - }); -} - -testEnd("utf8", Buffer.of(0xe2), Buffer.of(0x61), "\uFFFDa"); -testEnd("utf8", Buffer.of(0xe2), Buffer.of(0x82), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2), Buffer.of(0xe2), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2, 0x82), Buffer.of(0x61), "\uFFFDa"); -testEnd("utf8", Buffer.of(0xe2, 0x82), Buffer.of(0xac), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2, 0x82), Buffer.of(0xe2), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2, 0x82, 0xac), Buffer.of(0x61), "€a"); - -testEnd("utf16le", Buffer.of(0x3d), Buffer.of(0x61, 0x00), "a"); -testEnd("utf16le", Buffer.of(0x3d), Buffer.of(0xd8, 0x4d, 0xdc), "\u4DD8"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8), Buffer.of(), "\uD83D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8), Buffer.of(0x61, 0x00), "\uD83Da"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8), Buffer.of(0x4d, 0xdc), "\uD83D\uDC4D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d), Buffer.of(), "\uD83D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d), Buffer.of(0x61, 0x00), "\uD83Da"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d), Buffer.of(0xdc), "\uD83D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d, 0xdc), Buffer.of(0x61, 0x00), "👍a"); - -testEnd("base64", Buffer.of(0x61), Buffer.of(), "YQ=="); -testEnd("base64", Buffer.of(0x61), Buffer.of(0x61), "YQ==YQ=="); -testEnd("base64", Buffer.of(0x61, 0x61), Buffer.of(), "YWE="); -testEnd("base64", Buffer.of(0x61, 0x61), Buffer.of(0x61), "YWE=YQ=="); -testEnd("base64", Buffer.of(0x61, 0x61, 0x61), Buffer.of(), "YWFh"); -testEnd("base64", Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), "YWFhYQ=="); - -testEnd("base64url", Buffer.of(0x61), Buffer.of(), "YQ"); -testEnd("base64url", Buffer.of(0x61), Buffer.of(0x61), "YQYQ"); -testEnd("base64url", Buffer.of(0x61, 0x61), Buffer.of(), "YWE"); -testEnd("base64url", Buffer.of(0x61, 0x61), Buffer.of(0x61), "YWEYQ"); -testEnd("base64url", Buffer.of(0x61, 0x61, 0x61), Buffer.of(), "YWFh"); -testEnd("base64url", Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), "YWFhYQ"); - -//<#END_FILE: test-string-decoder-end.js diff --git a/test/js/node/test/parallel/tcp-wrap-connect.test.js b/test/js/node/test/parallel/tcp-wrap-connect.test.js deleted file mode 100644 index 819fd28e18..0000000000 --- a/test/js/node/test/parallel/tcp-wrap-connect.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-tcp-wrap-connect.js -//#SHA1: cc302b52d997beac187400587ce2dffc0978a7da -//----------------- -"use strict"; - -const net = require("net"); - -let connectCount = 0; -let endCount = 0; -let shutdownCount = 0; - -function makeConnection(server) { - return new Promise((resolve, reject) => { - const client = new net.Socket(); - - client.connect(server.address().port, "127.0.0.1", () => { - expect(client.readable).toBe(true); - expect(client.writable).toBe(true); - - client.end(() => { - shutdownCount++; - client.destroy(); - resolve(); - }); - }); - - client.on("error", reject); - }); -} - -test("TCP connection and shutdown", async () => { - const server = net.createServer(socket => { - connectCount++; - socket.resume(); - socket.on("end", () => { - endCount++; - socket.destroy(); - server.close(); - }); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - await makeConnection(server); - - await new Promise(resolve => server.on("close", resolve)); - - expect(shutdownCount).toBe(1); - expect(connectCount).toBe(1); - expect(endCount).toBe(1); -}); - -//<#END_FILE: test-tcp-wrap-connect.js diff --git a/test/js/node/test/parallel/test-arm-math-illegal-instruction.js b/test/js/node/test/parallel/test-arm-math-illegal-instruction.js new file mode 100644 index 0000000000..4bf881d1b3 --- /dev/null +++ b/test/js/node/test/parallel/test-arm-math-illegal-instruction.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); + +// This test ensures Math functions don't fail with an "illegal instruction" +// error on ARM devices (primarily on the Raspberry Pi 1) +// See https://github.com/nodejs/node/issues/1376 +// and https://code.google.com/p/v8/issues/detail?id=4019 + +// Iterate over all Math functions +Object.getOwnPropertyNames(Math).forEach((functionName) => { + if (!/[A-Z]/.test(functionName)) { + // The function names don't have capital letters. + Math[functionName](-0.5); + } +}); diff --git a/test/js/node/test/parallel/test-assert-builtins-not-read-from-filesystem.js b/test/js/node/test/parallel/test-assert-builtins-not-read-from-filesystem.js new file mode 100644 index 0000000000..7a713a2ea4 --- /dev/null +++ b/test/js/node/test/parallel/test-assert-builtins-not-read-from-filesystem.js @@ -0,0 +1,48 @@ +'use strict'; + +// Do not read filesystem when creating AssertionError messages for code in +// builtin modules. + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); +const e = new EventEmitter(); +e.on('hello', assert); + +if (process.argv[2] !== 'child') { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const { spawnSync } = require('child_process'); + + let threw = false; + try { + e.emit('hello', false); + } catch (err) { + const frames = err.stack.split('\n'); + const [, filename, line, column] = frames[1].match(/\((.+):(\d+):(\d+)\)/); + // Spawn a child process to avoid the error having been cached in the assert + // module's `errorCache` Map. + + const { output, status, error } = + spawnSync(process.execPath, + [process.argv[1], 'child', filename, line, column], + { cwd: tmpdir.path, env: process.env }); + assert.ifError(error); + assert.strictEqual(status, 0, `Exit code: ${status}\n${output}`); + threw = true; + } + assert.ok(threw); +} else { + const { writeFileSync } = require('fs'); + const [, , , filename, line, column] = process.argv; + const data = `${'\n'.repeat(line - 1)}${' '.repeat(column - 1)}` + + 'ok(failed(badly));'; + + writeFileSync(filename, data); + assert.throws( + () => e.emit('hello', false), + { + message: 'false == true' + } + ); +} diff --git a/test/js/node/test/parallel/test-assert-strict-exists.js b/test/js/node/test/parallel/test-assert-strict-exists.js new file mode 100644 index 0000000000..50cd8a49a7 --- /dev/null +++ b/test/js/node/test/parallel/test-assert-strict-exists.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('assert/strict'), assert.strict); diff --git a/test/js/node/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js b/test/js/node/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js new file mode 100644 index 0000000000..bc4ac86e7f --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js @@ -0,0 +1,20 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// This test verifies that the async ID stack can grow indefinitely. + +function recurse(n) { + const a = new async_hooks.AsyncResource('foobar'); + a.runInAsyncScope(() => { + assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId()); + assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId()); + if (n >= 0) + recurse(n - 1); + assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId()); + assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId()); + }); +} + +recurse(1000); diff --git a/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-caught-exception.js b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-caught-exception.js new file mode 100644 index 0000000000..e38cefd20e --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-caught-exception.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); +const { AsyncResource } = require('async_hooks'); + +try { + new AsyncResource('foo').runInAsyncScope(() => { throw new Error('bar'); }); +} catch { + // Continue regardless of error. +} +// Should abort (fail the case) if async id is not matching. diff --git a/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js new file mode 100644 index 0000000000..a5016da9d5 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js @@ -0,0 +1,17 @@ +'use strict'; + +// Test that passing thisArg to runInAsyncScope() works. + +const common = require('../common'); +const assert = require('assert'); +const { AsyncResource } = require('async_hooks'); + +const thisArg = {}; + +const res = new AsyncResource('fhqwhgads'); + +function callback() { + assert.strictEqual(this, thisArg); +} + +res.runInAsyncScope(common.mustCall(callback), thisArg); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-1.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-1.js new file mode 100644 index 0000000000..eb16645913 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-1.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +const w = new Worker(` +const { createHook } = require('async_hooks'); + +setImmediate(async () => { + createHook({ init() {} }).enable(); + await 0; + process.exit(); +}); +`, { eval: true }); + +w.on('exit', common.mustCall()); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-2.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-2.js new file mode 100644 index 0000000000..049264d3e8 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-2.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Like test-async-hooks-worker-promise.js but with the `await` and `createHook` +// lines switched, because that resulted in different assertion failures +// (one a Node.js assertion and one a V8 DCHECK) and it seems prudent to +// cover both of those failures. + +const w = new Worker(` +const { createHook } = require('async_hooks'); + +setImmediate(async () => { + await 0; + createHook({ init() {} }).enable(); + process.exit(); +}); +`, { eval: true }); + +w.postMessage({}); +w.on('exit', common.mustCall()); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-3.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-3.js new file mode 100644 index 0000000000..40c7d85835 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-3.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Like test-async-hooks-worker-promise.js but with an additional statement +// after the `process.exit()` call, that shouldn’t really make a difference +// but apparently does. + +const w = new Worker(` +const { createHook } = require('async_hooks'); + +setImmediate(async () => { + createHook({ init() {} }).enable(); + await 0; + process.exit(); + process._rawDebug('THIS SHOULD NEVER BE REACHED'); +}); +`, { eval: true }); + +w.on('exit', common.mustCall()); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js new file mode 100644 index 0000000000..c522091006 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Like test-async-hooks-worker-promise.js but doing a trivial counter increase +// after process.exit(). This should not make a difference, but apparently it +// does. This is *also* different from test-async-hooks-worker-promise-3.js, +// in that the statement is an ArrayBuffer access rather than a full method, +// which *also* makes a difference even though it shouldn’t. + +const workerData = new Int32Array(new SharedArrayBuffer(4)); +const w = new Worker(` +const { createHook } = require('async_hooks'); +const { workerData } = require('worker_threads'); + +setImmediate(async () => { + createHook({ init() {} }).enable(); + await 0; + process.exit(); + workerData[0]++; +}); +`, { eval: true, workerData }); + +w.on('exit', common.mustCall(() => assert.strictEqual(workerData[0], 0))); diff --git a/test/js/node/test/parallel/test-async-local-storage-contexts.js b/test/js/node/test/parallel/test-async-local-storage-contexts.js new file mode 100644 index 0000000000..9a63271337 --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-contexts.js @@ -0,0 +1,35 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const { AsyncLocalStorage } = require('async_hooks'); + +// Regression test for https://github.com/nodejs/node/issues/38781 + +const context = vm.createContext({ + AsyncLocalStorage, + assert +}); + +vm.runInContext(` + const storage = new AsyncLocalStorage() + async function test() { + return storage.run({ test: 'vm' }, async () => { + assert.strictEqual(storage.getStore().test, 'vm'); + await 42; + assert.strictEqual(storage.getStore().test, 'vm'); + }); + } + test() +`, context); + +const storage = new AsyncLocalStorage(); +async function test() { + return storage.run({ test: 'main context' }, async () => { + assert.strictEqual(storage.getStore().test, 'main context'); + await 42; + assert.strictEqual(storage.getStore().test, 'main context'); + }); +} +test(); diff --git a/test/js/node/test/parallel/test-async-local-storage-deep-stack.js b/test/js/node/test/parallel/test-async-local-storage-deep-stack.js new file mode 100644 index 0000000000..b5e1048d94 --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-deep-stack.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); + +// Regression test for: https://github.com/nodejs/node/issues/34556 + +const als = new AsyncLocalStorage(); + +const done = common.mustCall(); + +function run(count) { + if (count !== 0) return als.run({}, run, --count); + done(); +} +run(1000); diff --git a/test/js/node/test/parallel/test-async-local-storage-http-multiclients.js b/test/js/node/test/parallel/test-async-local-storage-http-multiclients.js new file mode 100644 index 0000000000..1903d5825d --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-http-multiclients.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { AsyncLocalStorage } = require('async_hooks'); +const http = require('http'); +const cls = new AsyncLocalStorage(); +const NUM_CLIENTS = 10; + +// Run multiple clients that receive data from a server +// in multiple chunks, in a single non-closure function. +// Use the AsyncLocalStorage (ALS) APIs to maintain the context +// and data download. Make sure that individual clients +// receive their respective data, with no conflicts. + +// Set up a server that sends large buffers of data, filled +// with cardinal numbers, increasing per request +let index = 0; +const server = http.createServer((q, r) => { + // Send a large chunk as response, otherwise the data + // may be sent in a single chunk, and the callback in the + // client may be called only once, defeating the purpose of test + r.end((index++ % 10).toString().repeat(1024 * 1024)); +}); + +const countdown = new Countdown(NUM_CLIENTS, () => { + server.close(); +}); + +server.listen(0, common.mustCall(() => { + for (let i = 0; i < NUM_CLIENTS; i++) { + cls.run(new Map(), common.mustCall(() => { + const options = { port: server.address().port }; + const req = http.get(options, common.mustCall((res) => { + const store = cls.getStore(); + store.set('data', ''); + + // Make ondata and onend non-closure + // functions and fully dependent on ALS + res.setEncoding('utf8'); + res.on('data', ondata); + res.on('end', common.mustCall(onend)); + })); + req.end(); + })); + } +})); + +// Accumulate the current data chunk with the store data +function ondata(d) { + const store = cls.getStore(); + assert.notStrictEqual(store, undefined); + let chunk = store.get('data'); + chunk += d; + store.set('data', chunk); +} + +// Retrieve the store data, and test for homogeneity +function onend() { + const store = cls.getStore(); + assert.notStrictEqual(store, undefined); + const data = store.get('data'); + assert.strictEqual(data, data[0].repeat(data.length)); + countdown.dec(); +} diff --git a/test/js/node/test/parallel/test-async-local-storage-snapshot.js b/test/js/node/test/parallel/test-async-local-storage-snapshot.js new file mode 100644 index 0000000000..63e47ba3ce --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-snapshot.js @@ -0,0 +1,16 @@ +'use strict'; + +const common = require('../common'); +const { strictEqual } = require('assert'); +const { AsyncLocalStorage } = require('async_hooks'); + +const asyncLocalStorage = new AsyncLocalStorage(); +const runInAsyncScope = + asyncLocalStorage.run(123, common.mustCall(() => AsyncLocalStorage.snapshot())); +const result = + asyncLocalStorage.run(321, common.mustCall(() => { + return runInAsyncScope(() => { + return asyncLocalStorage.getStore(); + }); + })); +strictEqual(result, 123); diff --git a/test/js/node/test/parallel/test-atomics-wake.js b/test/js/node/test/parallel/test-atomics-wake.js new file mode 100644 index 0000000000..0f38700176 --- /dev/null +++ b/test/js/node/test/parallel/test-atomics-wake.js @@ -0,0 +1,7 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// https://github.com/nodejs/node/issues/21219 +assert.strictEqual(Atomics.wake, undefined); diff --git a/test/js/node/test/parallel/test-bad-unicode.js b/test/js/node/test/parallel/test-bad-unicode.js new file mode 100644 index 0000000000..b4fccc0644 --- /dev/null +++ b/test/js/node/test/parallel/test-bad-unicode.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +let exception = null; + +try { + eval('"\\uc/ef"'); +} catch (e) { + exception = e; +} + +assert(exception instanceof SyntaxError); diff --git a/test/js/node/test/parallel/test-beforeexit-event-exit.js b/test/js/node/test/parallel/test-beforeexit-event-exit.js new file mode 100644 index 0000000000..4210ad04b6 --- /dev/null +++ b/test/js/node/test/parallel/test-beforeexit-event-exit.js @@ -0,0 +1,27 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { mustNotCall } = require('../common'); + +process.on('beforeExit', mustNotCall('exit should not allow this to occur')); + +process.exit(); diff --git a/test/js/node/test/parallel/test-blob-createobjecturl.js b/test/js/node/test/parallel/test-blob-createobjecturl.js new file mode 100644 index 0000000000..614b8ae4a6 --- /dev/null +++ b/test/js/node/test/parallel/test-blob-createobjecturl.js @@ -0,0 +1,54 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); + +// Because registering a Blob URL requires generating a random +// UUID, it can only be done if crypto support is enabled. +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { + URL, +} = require('url'); + +const { + Blob, + resolveObjectURL, +} = require('buffer'); + +const assert = require('assert'); + +(async () => { + const blob = new Blob(['hello']); + const id = URL.createObjectURL(blob); + assert.strictEqual(typeof id, 'string'); + const otherBlob = resolveObjectURL(id); + assert.ok(otherBlob instanceof Blob); + assert.strictEqual(otherBlob.constructor, Blob); + assert.strictEqual(otherBlob.size, 5); + assert.strictEqual( + Buffer.from(await otherBlob.arrayBuffer()).toString(), + 'hello'); + URL.revokeObjectURL(id); + + // should do nothing + URL.revokeObjectURL(id); + + assert.strictEqual(resolveObjectURL(id), undefined); + + // Leaving a Blob registered should not cause an assert + // when Node.js exists + URL.createObjectURL(new Blob()); + +})().then(common.mustCall()); + +['not a url', undefined, 1, 'blob:nodedata:1:wrong', {}].forEach((i) => { + assert.strictEqual(resolveObjectURL(i), undefined); +}); + +[undefined, 1, '', false, {}].forEach((i) => { + assert.throws(() => URL.createObjectURL(i), { + code: 'ERR_INVALID_ARG_TYPE', + }); +}); diff --git a/test/js/node/test/parallel/test-btoa-atob.js b/test/js/node/test/parallel/test-btoa-atob.js new file mode 100644 index 0000000000..abf05adeef --- /dev/null +++ b/test/js/node/test/parallel/test-btoa-atob.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); + +const { strictEqual, throws } = require('assert'); +const buffer = require('buffer'); + +// Exported on the global object +strictEqual(globalThis.atob, buffer.atob); +strictEqual(globalThis.btoa, buffer.btoa); + +// Throws type error on no argument passed +throws(() => buffer.atob(), /TypeError/); +throws(() => buffer.btoa(), /TypeError/); + +strictEqual(atob(' '), ''); +strictEqual(atob(' Y\fW\tJ\njZ A=\r= '), 'abcd'); + +strictEqual(atob(null), '\x9Eée'); +strictEqual(atob(NaN), '5£'); +strictEqual(atob(Infinity), '"wâ\x9E+r'); +strictEqual(atob(true), '¶»\x9E'); +strictEqual(atob(1234), '×mø'); +strictEqual(atob([]), ''); +strictEqual(atob({ toString: () => '' }), ''); +strictEqual(atob({ [Symbol.toPrimitive]: () => '' }), ''); + +throws(() => atob(Symbol()), /TypeError/); +[ + undefined, false, () => {}, {}, [1], + 0, 1, 0n, 1n, -Infinity, + 'a', 'a\n\n\n', '\ra\r\r', ' a ', '\t\t\ta', 'a\f\f\f', '\ta\r \n\f', +].forEach((value) => + // See #2 - https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob + throws(() => atob(value), { + constructor: DOMException, + name: 'InvalidCharacterError', + code: 5, + })); diff --git a/test/js/node/test/parallel/microtask-queue-run.test.js b/test/js/node/test/parallel/test-buffer-ascii.js similarity index 56% rename from test/js/node/test/parallel/microtask-queue-run.test.js rename to test/js/node/test/parallel/test-buffer-ascii.js index 8e25e318d2..afedb7252c 100644 --- a/test/js/node/test/parallel/microtask-queue-run.test.js +++ b/test/js/node/test/parallel/test-buffer-ascii.js @@ -1,6 +1,3 @@ -//#FILE: test-microtask-queue-run.js -//#SHA1: caf14b2c15bbc84816eb2ce6b9bdb073c009b447 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,46 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); -function enqueueMicrotask(fn) { - Promise.resolve().then(fn); +// ASCII conversion in node.js simply masks off the high bits, +// it doesn't do transliteration. +assert.strictEqual(Buffer.from('hérité').toString('ascii'), 'hC)ritC)'); + +// 71 characters, 78 bytes. The ’ character is a triple-byte sequence. +const input = 'C’est, graphiquement, la réunion d’un accent aigu ' + + 'et d’un accent grave.'; + +const expected = 'Cb\u0000\u0019est, graphiquement, la rC)union ' + + 'db\u0000\u0019un accent aigu et db\u0000\u0019un ' + + 'accent grave.'; + +const buf = Buffer.from(input); + +for (let i = 0; i < expected.length; ++i) { + assert.strictEqual(buf.slice(i).toString('ascii'), expected.slice(i)); + + // Skip remainder of multi-byte sequence. + if (input.charCodeAt(i) > 65535) ++i; + if (input.charCodeAt(i) > 127) ++i; } - -test("microtask queue runs correctly", async () => { - let done = 0; - - // No nextTick, microtask - await new Promise(resolve => { - setTimeout(() => { - enqueueMicrotask(() => { - done++; - resolve(); - }); - }, 0); - }); - - // No nextTick, microtask with nextTick - await new Promise(resolve => { - setTimeout(() => { - let called = false; - - enqueueMicrotask(() => { - process.nextTick(() => { - called = true; - }); - }); - - setTimeout(() => { - if (called) { - done++; - } - resolve(); - }, 0); - }, 0); - }); - - expect(done).toBe(2); -}); - -//<#END_FILE: test-microtask-queue-run.js diff --git a/test/js/node/test/parallel/test-buffer-compare-offset.js b/test/js/node/test/parallel/test-buffer-compare-offset.js new file mode 100644 index 0000000000..9f6f733547 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-compare-offset.js @@ -0,0 +1,94 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); +const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]); + +assert.strictEqual(a.compare(b), -1); + +// Equivalent to a.compare(b). +assert.strictEqual(a.compare(b, 0), -1); +assert.throws(() => a.compare(b, '0'), { code: 'ERR_INVALID_ARG_TYPE' }); +assert.strictEqual(a.compare(b, undefined), -1); + +// Equivalent to a.compare(b). +assert.strictEqual(a.compare(b, 0, undefined, 0), -1); + +// Zero-length target, return 1 +assert.strictEqual(a.compare(b, 0, 0, 0), 1); +assert.throws( + () => a.compare(b, 0, '0', '0'), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Equivalent to Buffer.compare(a, b.slice(6, 10)) +assert.strictEqual(a.compare(b, 6, 10), 1); + +// Zero-length source, return -1 +assert.strictEqual(a.compare(b, 6, 10, 0, 0), -1); + +// Zero-length source and target, return 0 +assert.strictEqual(a.compare(b, 0, 0, 0, 0), 0); +assert.strictEqual(a.compare(b, 1, 1, 2, 2), 0); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 5)) +assert.strictEqual(a.compare(b, 0, 5, 4), 1); + +// Equivalent to Buffer.compare(a.slice(1), b.slice(5)) +assert.strictEqual(a.compare(b, 5, undefined, 1), 1); + +// Equivalent to Buffer.compare(a.slice(2), b.slice(2, 4)) +assert.strictEqual(a.compare(b, 2, 4, 2), -1); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 7)) +assert.strictEqual(a.compare(b, 0, 7, 4), -1); + +// Equivalent to Buffer.compare(a.slice(4, 6), b.slice(0, 7)); +assert.strictEqual(a.compare(b, 0, 7, 4, 6), -1); + +// Null is ambiguous. +assert.throws( + () => a.compare(b, 0, null), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Values do not get coerced. +assert.throws( + () => a.compare(b, 0, { valueOf: () => 5 }), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Infinity should not be coerced. +assert.throws( + () => a.compare(b, Infinity, -Infinity), + { code: 'ERR_OUT_OF_RANGE' } +); + +// Zero length target because default for targetEnd <= targetSource +assert.strictEqual(a.compare(b, 0xff), 1); + +assert.throws( + () => a.compare(b, '0xff'), + { code: 'ERR_INVALID_ARG_TYPE' } +); +assert.throws( + () => a.compare(b, 0, '0xff'), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +const oor = { code: 'ERR_OUT_OF_RANGE' }; + +assert.throws(() => a.compare(b, 0, 100, 0), oor); +assert.throws(() => a.compare(b, 0, 1, 0, 100), oor); +assert.throws(() => a.compare(b, -1), oor); +assert.throws(() => a.compare(b, 0, Infinity), oor); +assert.throws(() => a.compare(b, 0, 1, -1), oor); +assert.throws(() => a.compare(b, -Infinity, Infinity), oor); +assert.throws(() => a.compare(), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "target" argument must be an instance of ' + + 'Buffer or Uint8Array. Received undefined' +}); diff --git a/test/js/node/test/parallel/test-buffer-failed-alloc-typed-arrays.js b/test/js/node/test/parallel/test-buffer-failed-alloc-typed-arrays.js new file mode 100644 index 0000000000..699475ad0a --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-failed-alloc-typed-arrays.js @@ -0,0 +1,33 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const SlowBuffer = require('buffer').SlowBuffer; + +// Test failed or zero-sized Buffer allocations not affecting typed arrays. +// This test exists because of a regression that occurred. Because Buffer +// instances are allocated with the same underlying allocator as TypedArrays, +// but Buffer's can optional be non-zero filled, there was a regression that +// occurred when a Buffer allocated failed, the internal flag specifying +// whether or not to zero-fill was not being reset, causing TypedArrays to +// allocate incorrectly. +const zeroArray = new Uint32Array(10).fill(0); +const sizes = [1e20, 0, 0.1, -1, 'a', undefined, null, NaN]; +const allocators = [ + Buffer, + SlowBuffer, + Buffer.alloc, + Buffer.allocUnsafe, + Buffer.allocUnsafeSlow, +]; +for (const allocator of allocators) { + for (const size of sizes) { + try { + // Some of these allocations are known to fail. If they do, + // Uint32Array should still produce a zeroed out result. + allocator(size); + } catch { + assert.deepStrictEqual(zeroArray, new Uint32Array(10)); + } + } +} diff --git a/test/js/node/test/parallel/test-buffer-fakes.js b/test/js/node/test/parallel/test-buffer-fakes.js new file mode 100644 index 0000000000..da78fe0895 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-fakes.js @@ -0,0 +1,54 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +function FakeBuffer() { } +Object.setPrototypeOf(FakeBuffer, Buffer); +Object.setPrototypeOf(FakeBuffer.prototype, Buffer.prototype); + +const fb = new FakeBuffer(); + +assert.throws(function() { + Buffer.from(fb); +}, TypeError); + +assert.throws(function() { + +Buffer.prototype; // eslint-disable-line no-unused-expressions +}, TypeError); + +assert.throws(function() { + Buffer.compare(fb, Buffer.alloc(0)); +}, TypeError); + +assert.throws(function() { + fb.write('foo'); +}, TypeError); + +assert.throws(function() { + Buffer.concat([fb, fb]); +}, TypeError); + +assert.throws(function() { + fb.toString(); +}, TypeError); + +assert.throws(function() { + fb.equals(Buffer.alloc(0)); +}, TypeError); + +assert.throws(function() { + fb.indexOf(5); +}, TypeError); + +assert.throws(function() { + fb.readFloatLE(0); +}, TypeError); + +assert.throws(function() { + fb.writeFloatLE(0); +}, TypeError); + +assert.throws(function() { + fb.fill(0); +}, TypeError); diff --git a/test/js/node/test/parallel/test-buffer-inheritance.js b/test/js/node/test/parallel/test-buffer-inheritance.js new file mode 100644 index 0000000000..4794f56717 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-inheritance.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + + +function T(n) { + const ui8 = new Uint8Array(n); + Object.setPrototypeOf(ui8, T.prototype); + return ui8; +} +Object.setPrototypeOf(T.prototype, Buffer.prototype); +Object.setPrototypeOf(T, Buffer); + +T.prototype.sum = function sum() { + let cntr = 0; + for (let i = 0; i < this.length; i++) + cntr += this[i]; + return cntr; +}; + + +const vals = [new T(4), T(4)]; + +vals.forEach(function(t) { + assert.strictEqual(t.constructor, T); + assert.strictEqual(Object.getPrototypeOf(t), T.prototype); + assert.strictEqual(Object.getPrototypeOf(Object.getPrototypeOf(t)), + Buffer.prototype); + + t.fill(5); + let cntr = 0; + for (let i = 0; i < t.length; i++) + cntr += t[i]; + assert.strictEqual(cntr, t.length * 5); + + // Check this does not throw + t.toString(); +}); diff --git a/test/js/node/test/parallel/test-buffer-isascii.js b/test/js/node/test/parallel/test-buffer-isascii.js new file mode 100644 index 0000000000..b9468ca133 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-isascii.js @@ -0,0 +1,42 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { isAscii, Buffer } = require('buffer'); +const { TextEncoder } = require('util'); + +const encoder = new TextEncoder(); + +assert.strictEqual(isAscii(encoder.encode('hello')), true); +assert.strictEqual(isAscii(encoder.encode('ğ')), false); +assert.strictEqual(isAscii(Buffer.from([])), true); + +[ + undefined, + '', 'hello', + false, true, + 0, 1, + 0n, 1n, + Symbol(), + () => {}, + {}, [], null, +].forEach((input) => { + assert.throws( + () => { isAscii(input); }, + { + code: 'ERR_INVALID_ARG_TYPE', + }, + ); +}); + +{ + // Test with detached array buffers + const arrayBuffer = new ArrayBuffer(1024); + structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); + assert.throws( + () => { isAscii(arrayBuffer); }, + { + code: 'ERR_INVALID_STATE' + } + ); +} diff --git a/test/js/node/test/parallel/test-buffer-isencoding.js b/test/js/node/test/parallel/test-buffer-isencoding.js new file mode 100644 index 0000000000..f9150055cc --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-isencoding.js @@ -0,0 +1,38 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +[ + 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'base64url', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), true); +}); + +[ + 'utf9', + 'utf-7', + 'Unicode-FTW', + 'new gnu gun', + false, + NaN, + {}, + Infinity, + [], + 1, + 0, + -1, +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), false); +}); diff --git a/test/js/node/test/parallel/test-buffer-isutf8.js b/test/js/node/test/parallel/test-buffer-isutf8.js new file mode 100644 index 0000000000..204db3e6a5 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-isutf8.js @@ -0,0 +1,86 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { isUtf8, Buffer } = require('buffer'); +const { TextEncoder } = require('util'); + +const encoder = new TextEncoder(); + +assert.strictEqual(isUtf8(encoder.encode('hello')), true); +assert.strictEqual(isUtf8(encoder.encode('ğ')), true); +assert.strictEqual(isUtf8(Buffer.from([])), true); + +// Taken from test/fixtures/wpt/encoding/textdecoder-fatal.any.js +[ + [0xFF], // 'invalid code' + [0xC0], // 'ends early' + [0xE0], // 'ends early 2' + [0xC0, 0x00], // 'invalid trail' + [0xC0, 0xC0], // 'invalid trail 2' + [0xE0, 0x00], // 'invalid trail 3' + [0xE0, 0xC0], // 'invalid trail 4' + [0xE0, 0x80, 0x00], // 'invalid trail 5' + [0xE0, 0x80, 0xC0], // 'invalid trail 6' + [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], // '> 0x10FFFF' + [0xFE, 0x80, 0x80, 0x80, 0x80, 0x80], // 'obsolete lead byte' + + // Overlong encodings + [0xC0, 0x80], // 'overlong U+0000 - 2 bytes' + [0xE0, 0x80, 0x80], // 'overlong U+0000 - 3 bytes' + [0xF0, 0x80, 0x80, 0x80], // 'overlong U+0000 - 4 bytes' + [0xF8, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 5 bytes' + [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 6 bytes' + + [0xC1, 0xBF], // 'overlong U+007F - 2 bytes' + [0xE0, 0x81, 0xBF], // 'overlong U+007F - 3 bytes' + [0xF0, 0x80, 0x81, 0xBF], // 'overlong U+007F - 4 bytes' + [0xF8, 0x80, 0x80, 0x81, 0xBF], // 'overlong U+007F - 5 bytes' + [0xFC, 0x80, 0x80, 0x80, 0x81, 0xBF], // 'overlong U+007F - 6 bytes' + + [0xE0, 0x9F, 0xBF], // 'overlong U+07FF - 3 bytes' + [0xF0, 0x80, 0x9F, 0xBF], // 'overlong U+07FF - 4 bytes' + [0xF8, 0x80, 0x80, 0x9F, 0xBF], // 'overlong U+07FF - 5 bytes' + [0xFC, 0x80, 0x80, 0x80, 0x9F, 0xBF], // 'overlong U+07FF - 6 bytes' + + [0xF0, 0x8F, 0xBF, 0xBF], // 'overlong U+FFFF - 4 bytes' + [0xF8, 0x80, 0x8F, 0xBF, 0xBF], // 'overlong U+FFFF - 5 bytes' + [0xFC, 0x80, 0x80, 0x8F, 0xBF, 0xBF], // 'overlong U+FFFF - 6 bytes' + + [0xF8, 0x84, 0x8F, 0xBF, 0xBF], // 'overlong U+10FFFF - 5 bytes' + [0xFC, 0x80, 0x84, 0x8F, 0xBF, 0xBF], // 'overlong U+10FFFF - 6 bytes' + + // UTF-16 surrogates encoded as code points in UTF-8 + [0xED, 0xA0, 0x80], // 'lead surrogate' + [0xED, 0xB0, 0x80], // 'trail surrogate' + [0xED, 0xA0, 0x80, 0xED, 0xB0, 0x80], // 'surrogate pair' +].forEach((input) => { + assert.strictEqual(isUtf8(Buffer.from(input)), false); +}); + +[ + null, + undefined, + 'hello', + true, + false, +].forEach((input) => { + assert.throws( + () => { isUtf8(input); }, + { + code: 'ERR_INVALID_ARG_TYPE', + }, + ); +}); + +{ + // Test with detached array buffers + const arrayBuffer = new ArrayBuffer(1024); + structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); + assert.throws( + () => { isUtf8(arrayBuffer); }, + { + code: 'ERR_INVALID_STATE' + } + ); +} diff --git a/test/js/node/test/parallel/test-buffer-iterator.js b/test/js/node/test/parallel/test-buffer-iterator.js new file mode 100644 index 0000000000..6cf64712c0 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-iterator.js @@ -0,0 +1,62 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const buffer = Buffer.from([1, 2, 3, 4, 5]); +let arr; +let b; + +// Buffers should be iterable + +arr = []; + +for (b of buffer) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// Buffer iterators should be iterable + +arr = []; + +for (b of buffer[Symbol.iterator]()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#values() should return iterator for values + +arr = []; + +for (b of buffer.values()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#keys() should return iterator for keys + +arr = []; + +for (b of buffer.keys()) + arr.push(b); + +assert.deepStrictEqual(arr, [0, 1, 2, 3, 4]); + + +// buffer#entries() should return iterator for entries + +arr = []; + +for (b of buffer.entries()) + arr.push(b); + +assert.deepStrictEqual(arr, [ + [0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], +]); diff --git a/test/js/node/test/parallel/test-buffer-no-negative-allocation.js b/test/js/node/test/parallel/test-buffer-no-negative-allocation.js new file mode 100644 index 0000000000..055e2d5dc6 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-no-negative-allocation.js @@ -0,0 +1,37 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { SlowBuffer } = require('buffer'); + +const msg = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}; + +// Test that negative Buffer length inputs throw errors. + +assert.throws(() => Buffer(-Buffer.poolSize), msg); +assert.throws(() => Buffer(-100), msg); +assert.throws(() => Buffer(-1), msg); +assert.throws(() => Buffer(NaN), msg); + +assert.throws(() => Buffer.alloc(-Buffer.poolSize), msg); +assert.throws(() => Buffer.alloc(-100), msg); +assert.throws(() => Buffer.alloc(-1), msg); +assert.throws(() => Buffer.alloc(NaN), msg); + +assert.throws(() => Buffer.allocUnsafe(-Buffer.poolSize), msg); +assert.throws(() => Buffer.allocUnsafe(-100), msg); +assert.throws(() => Buffer.allocUnsafe(-1), msg); +assert.throws(() => Buffer.allocUnsafe(NaN), msg); + +assert.throws(() => Buffer.allocUnsafeSlow(-Buffer.poolSize), msg); +assert.throws(() => Buffer.allocUnsafeSlow(-100), msg); +assert.throws(() => Buffer.allocUnsafeSlow(-1), msg); +assert.throws(() => Buffer.allocUnsafeSlow(NaN), msg); + +assert.throws(() => SlowBuffer(-Buffer.poolSize), msg); +assert.throws(() => SlowBuffer(-100), msg); +assert.throws(() => SlowBuffer(-1), msg); +assert.throws(() => SlowBuffer(NaN), msg); diff --git a/test/js/node/test/parallel/test-buffer-nopendingdep-map.js b/test/js/node/test/parallel/test-buffer-nopendingdep-map.js new file mode 100644 index 0000000000..c85d184fbc --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-nopendingdep-map.js @@ -0,0 +1,13 @@ +// Flags: --no-warnings --pending-deprecation +'use strict'; + +const common = require('../common'); + +process.on('warning', common.mustNotCall('A warning should not be emitted')); + +// With the --pending-deprecation flag, the deprecation warning for +// new Buffer() should not be emitted when Uint8Array methods are called. + +Buffer.from('abc').map((i) => i); +Buffer.from('abc').filter((i) => i); +Buffer.from('abc').slice(1, 2); diff --git a/test/js/node/test/parallel/test-buffer-of-no-deprecation.js b/test/js/node/test/parallel/test-buffer-of-no-deprecation.js new file mode 100644 index 0000000000..d0ac75b4a3 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-of-no-deprecation.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); + +process.on('warning', common.mustNotCall()); + +Buffer.of(0, 1); diff --git a/test/js/node/test/parallel/test-buffer-over-max-length.js b/test/js/node/test/parallel/test-buffer-over-max-length.js new file mode 100644 index 0000000000..f29d6b62d4 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-over-max-length.js @@ -0,0 +1,19 @@ +'use strict'; +require('../common'); + +const assert = require('assert'); + +const buffer = require('buffer'); +const SlowBuffer = buffer.SlowBuffer; + +const kMaxLength = buffer.kMaxLength; +const bufferMaxSizeMsg = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}; + +assert.throws(() => Buffer(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.alloc(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafe(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafeSlow(kMaxLength + 1), bufferMaxSizeMsg); diff --git a/test/js/node/test/parallel/test-buffer-parent-property.js b/test/js/node/test/parallel/test-buffer-parent-property.js new file mode 100644 index 0000000000..24cdaade43 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-parent-property.js @@ -0,0 +1,21 @@ +'use strict'; + +// Fix for https://github.com/nodejs/node/issues/8266 +// +// Zero length Buffer objects should expose the `buffer` property of the +// TypedArrays, via the `parent` property. +require('../common'); +const assert = require('assert'); + +// If the length of the buffer object is zero +assert((new Buffer(0)).parent instanceof ArrayBuffer); + +// If the length of the buffer object is equal to the underlying ArrayBuffer +assert((new Buffer(Buffer.poolSize)).parent instanceof ArrayBuffer); + +// Same as the previous test, but with user created buffer +const arrayBuffer = new ArrayBuffer(0); +assert.strictEqual(new Buffer(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(new Buffer(arrayBuffer).buffer, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).buffer, arrayBuffer); diff --git a/test/js/node/test/parallel/test-buffer-safe-unsafe.js b/test/js/node/test/parallel/test-buffer-safe-unsafe.js new file mode 100644 index 0000000000..9f8b6b7410 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-safe-unsafe.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const safe = Buffer.alloc(10); + +function isZeroFilled(buf) { + for (let n = 0; n < buf.length; n++) + if (buf[n] !== 0) return false; + return true; +} + +assert(isZeroFilled(safe)); + +// Test that unsafe allocations doesn't affect subsequent safe allocations +Buffer.allocUnsafe(10); +assert(isZeroFilled(new Float64Array(10))); + +new Buffer(10); +assert(isZeroFilled(new Float64Array(10))); + +Buffer.allocUnsafe(10); +assert(isZeroFilled(Buffer.alloc(10))); diff --git a/test/js/node/test/parallel/test-buffer-set-inspect-max-bytes.js b/test/js/node/test/parallel/test-buffer-set-inspect-max-bytes.js new file mode 100644 index 0000000000..975c828111 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-set-inspect-max-bytes.js @@ -0,0 +1,34 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const buffer = require('buffer'); + +const rangeErrorObjs = [NaN, -1]; +const typeErrorObj = 'and even this'; + +for (const obj of rangeErrorObjs) { + assert.throws( + () => buffer.INSPECT_MAX_BYTES = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); + + assert.throws( + () => buffer.INSPECT_MAX_BYTES = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); +} + +assert.throws( + () => buffer.INSPECT_MAX_BYTES = typeErrorObj, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); diff --git a/test/js/node/test/parallel/test-buffer-slice.js b/test/js/node/test/parallel/test-buffer-slice.js new file mode 100644 index 0000000000..52720bb87b --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-slice.js @@ -0,0 +1,129 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(Buffer.from('hello', 'utf8').slice(0, 0).length, 0); +assert.strictEqual(Buffer('hello', 'utf8').slice(0, 0).length, 0); + +const buf = Buffer.from('0123456789', 'utf8'); +const expectedSameBufs = [ + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, -10), Buffer.from('', 'utf8')], + [buf.slice(), Buffer.from('0123456789', 'utf8')], + [buf.slice(0), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, 0), Buffer.from('', 'utf8')], + [buf.slice(undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice('foobar'), Buffer.from('0123456789', 'utf8')], + [buf.slice(undefined, undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice(2), Buffer.from('23456789', 'utf8')], + [buf.slice(5), Buffer.from('56789', 'utf8')], + [buf.slice(10), Buffer.from('', 'utf8')], + [buf.slice(5, 8), Buffer.from('567', 'utf8')], + [buf.slice(8, -1), Buffer.from('8', 'utf8')], + [buf.slice(-10), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, -9), Buffer.from('0', 'utf8')], + [buf.slice(0, -10), Buffer.from('', 'utf8')], + [buf.slice(0, -1), Buffer.from('012345678', 'utf8')], + [buf.slice(2, -2), Buffer.from('234567', 'utf8')], + [buf.slice(0, 65536), Buffer.from('0123456789', 'utf8')], + [buf.slice(65536, 0), Buffer.from('', 'utf8')], + [buf.slice(-5, -8), Buffer.from('', 'utf8')], + [buf.slice(-5, -3), Buffer.from('56', 'utf8')], + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice('0', '1'), Buffer.from('0', 'utf8')], + [buf.slice('-5', '10'), Buffer.from('56789', 'utf8')], + [buf.slice('-10', '10'), Buffer.from('0123456789', 'utf8')], + [buf.slice('-10', '-5'), Buffer.from('01234', 'utf8')], + [buf.slice('-10', '-0'), Buffer.from('', 'utf8')], + [buf.slice('111'), Buffer.from('', 'utf8')], + [buf.slice('0', '-111'), Buffer.from('', 'utf8')], +]; + +for (let i = 0, s = buf.toString(); i < buf.length; ++i) { + expectedSameBufs.push( + [buf.slice(i), Buffer.from(s.slice(i))], + [buf.slice(0, i), Buffer.from(s.slice(0, i))], + [buf.slice(-i), Buffer.from(s.slice(-i))], + [buf.slice(0, -i), Buffer.from(s.slice(0, -i))] + ); +} + +for (const [buf1, buf2] of expectedSameBufs) { + assert.strictEqual(Buffer.compare(buf1, buf2), 0); +} + +const utf16Buf = Buffer.from('0123456789', 'utf16le'); +assert.deepStrictEqual(utf16Buf.slice(0, 6), Buffer.from('012', 'utf16le')); +// Try to slice a zero length Buffer. +// See https://github.com/joyent/node/issues/5881 +assert.strictEqual(Buffer.alloc(0).slice(0, 1).length, 0); + +{ + // Single argument slice + assert.strictEqual(Buffer.from('abcde', 'utf8').slice(1).toString('utf8'), + 'bcde'); +} + +// slice(0,0).length === 0 +assert.strictEqual(Buffer.from('hello', 'utf8').slice(0, 0).length, 0); + +{ + // Regression tests for https://github.com/nodejs/node/issues/9096 + const buf = Buffer.from('abcd', 'utf8'); + assert.strictEqual(buf.slice(buf.length / 3).toString('utf8'), 'bcd'); + assert.strictEqual( + buf.slice(buf.length / 3, buf.length).toString(), + 'bcd' + ); +} + +{ + const buf = Buffer.from('abcdefg', 'utf8'); + assert.strictEqual(buf.slice(-(-1 >>> 0) - 1).toString('utf8'), + buf.toString('utf8')); +} + +{ + const buf = Buffer.from('abc', 'utf8'); + assert.strictEqual(buf.slice(-0.5).toString('utf8'), buf.toString('utf8')); +} + +{ + const buf = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, + ]); + const chunk1 = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + ]); + const chunk2 = Buffer.from([ + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, + ]); + const middle = buf.length / 2; + + assert.deepStrictEqual(buf.slice(0, middle), chunk1); + assert.deepStrictEqual(buf.slice(middle), chunk2); +} diff --git a/test/js/node/test/parallel/test-buffer-slow.js b/test/js/node/test/parallel/test-buffer-slow.js new file mode 100644 index 0000000000..07138d5db0 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-slow.js @@ -0,0 +1,53 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const buffer = require('buffer'); +const SlowBuffer = buffer.SlowBuffer; + +const ones = [1, 1, 1, 1]; + +// Should create a Buffer +let sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// underlying ArrayBuffer should have the same length +assert.strictEqual(sb.buffer.byteLength, 4); + +// Should work without new +sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// Should work with edge cases +assert.strictEqual(SlowBuffer(0).length, 0); + +// Should throw with invalid length type +const bufferInvalidTypeMsg = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "size" argument must be of type number/, +}; +assert.throws(() => SlowBuffer(), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer({}), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer('6'), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer(true), bufferInvalidTypeMsg); + +// Should throw with invalid length value +const bufferMaxSizeMsg = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}; +assert.throws(() => SlowBuffer(NaN), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(Infinity), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(-1), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(buffer.kMaxLength + 1), bufferMaxSizeMsg); diff --git a/test/js/node/test/parallel/test-buffer-swap.js b/test/js/node/test/parallel/test-buffer-swap.js new file mode 100644 index 0000000000..82b550790e --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-swap.js @@ -0,0 +1,152 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test buffers small enough to use the JS implementation +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + assert.strictEqual(buf, buf.swap16()); + assert.deepStrictEqual(buf, Buffer.from([0x02, 0x01, 0x04, 0x03, 0x06, 0x05, + 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, + 0x0e, 0x0d, 0x10, 0x0f])); + buf.swap16(); // restore + + assert.strictEqual(buf, buf.swap32()); + assert.deepStrictEqual(buf, Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, + 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, + 0x10, 0x0f, 0x0e, 0x0d])); + buf.swap32(); // restore + + assert.strictEqual(buf, buf.swap64()); + assert.deepStrictEqual(buf, Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, + 0x0c, 0x0b, 0x0a, 0x09])); +} + +// Operates in-place +{ + const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); + buf.slice(1, 5).swap32(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); + buf.slice(1, 5).swap16(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); + + // Length assertions + const re16 = /Buffer size must be a multiple of 16-bits/; + const re32 = /Buffer size must be a multiple of 32-bits/; + const re64 = /Buffer size must be a multiple of 64-bits/; + + assert.throws(() => Buffer.from(buf).swap16(), re16); + assert.throws(() => Buffer.alloc(1025).swap16(), re16); + assert.throws(() => Buffer.from(buf).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap32(), re32); + assert.throws(() => Buffer.alloc(1025).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap64(), re64); + assert.throws(() => Buffer.alloc(1025).swap64(), re64); +} + +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + buf.slice(2, 18).swap64(); + + assert.deepStrictEqual(buf, Buffer.from([0x01, 0x02, 0x0a, 0x09, 0x08, 0x07, + 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10])); +} + +// Force use of native code (Buffer size above threshold limit for js impl) +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBufData = new Uint32Array(256).fill(0x03040102); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap16(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer); + const otherBufData = new Uint32Array(256).fill(0x01020304); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap32(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + otherBufData[otherBufData.length - i - 1] = i % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap64(); + assert.deepStrictEqual(buf, otherBuf); +} + +// Test native code with buffers that are not memory-aligned +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 2); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 2; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 2; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 0|1 0|1... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 1|0 1|0... + + buf.slice(1, buf.length - 1).swap16(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 4); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 4; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 4; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 0|1 2 3... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 3 2 1|0 3 2... + + buf.slice(1, buf.length - 3).swap32(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 4 5 6 7 0|1 2 3 4... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 7 6 5 4 3 2 1|0 7 6 5... + + buf.slice(1, buf.length - 7).swap64(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} diff --git a/test/js/node/test/parallel/test-buffer-tojson.js b/test/js/node/test/parallel/test-buffer-tojson.js new file mode 100644 index 0000000000..d9a4a85e81 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tojson.js @@ -0,0 +1,35 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + assert.strictEqual(JSON.stringify(Buffer.alloc(0)), + '{"type":"Buffer","data":[]}'); + assert.strictEqual(JSON.stringify(Buffer.from([1, 2, 3, 4])), + '{"type":"Buffer","data":[1,2,3,4]}'); +} + +// issue GH-7849 +{ + const buf = Buffer.from('test'); + const json = JSON.stringify(buf); + const obj = JSON.parse(json); + const copy = Buffer.from(obj); + + assert.deepStrictEqual(buf, copy); +} + +// GH-5110 +{ + const buffer = Buffer.from('test'); + const string = JSON.stringify(buffer); + + assert.strictEqual(string, '{"type":"Buffer","data":[116,101,115,116]}'); + + function receiver(key, value) { + return value && value.type === 'Buffer' ? Buffer.from(value.data) : value; + } + + assert.deepStrictEqual(buffer, JSON.parse(string, receiver)); +} diff --git a/test/js/node/test/parallel/test-buffer-tostring-range.js b/test/js/node/test/parallel/test-buffer-tostring-range.js new file mode 100644 index 0000000000..f4adf64c8d --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tostring-range.js @@ -0,0 +1,100 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const rangeBuffer = Buffer.from('abc'); + +// If start >= buffer's length, empty string will be returned +assert.strictEqual(rangeBuffer.toString('ascii', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', +Infinity), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 3.14, 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 'Infinity', 3), ''); + +// If end <= 0, empty string will be returned +assert.strictEqual(rangeBuffer.toString('ascii', 1, 0), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -1.2), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -100), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -Infinity), ''); + +// If start < 0, start will be taken as zero +assert.strictEqual(rangeBuffer.toString('ascii', -1, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', -1.99, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', -Infinity, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); + +// If start is an invalid integer, start will be taken as zero +assert.strictEqual(rangeBuffer.toString('ascii', 'node.js', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', {}, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', [], 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', NaN, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', null, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', undefined, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', false, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '', 3), 'abc'); + +// But, if start is an integer when coerced, then it will be coerced and used. +assert.strictEqual(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '1', 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '3', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', Number(3), 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', '3.14', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', '1.99', 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 1.99, 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', true, 3), 'bc'); + +// If end > buffer's length, end will be taken as buffer's length +assert.strictEqual(rangeBuffer.toString('ascii', 0, 5), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 6.99), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, Infinity), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '5'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '6.99'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 'Infinity'), 'abc'); + +// If end is an invalid integer, end will be taken as buffer's length +assert.strictEqual(rangeBuffer.toString('ascii', 0, 'node.js'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, {}), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, NaN), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, undefined), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, null), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, []), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, false), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, ''), ''); + +// But, if end is an integer when coerced, then it will be coerced and used. +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-1'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '1'), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-Infinity'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '3'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, Number(3)), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '3.14'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '1.99'), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-1.99'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 1.99), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, true), 'a'); + +// Try toString() with an object as an encoding +assert.strictEqual(rangeBuffer.toString({ toString: function() { + return 'ascii'; +} }), 'abc'); + +// Try toString() with 0 and null as the encoding +assert.throws(() => { + rangeBuffer.toString(0, 1, 2); +}, { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: 0' +}); +assert.throws(() => { + rangeBuffer.toString(null, 1, 2); +}, { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: null' +}); diff --git a/test/js/node/test/parallel/test-buffer-tostring-rangeerror.js b/test/js/node/test/parallel/test-buffer-tostring-rangeerror.js new file mode 100644 index 0000000000..0ebea759b5 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tostring-rangeerror.js @@ -0,0 +1,25 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js throws an Error when trying to convert a +// large buffer into a string. +// Regression test for https://github.com/nodejs/node/issues/649. + +const assert = require('assert'); +const { + SlowBuffer, + constants: { + MAX_STRING_LENGTH, + }, +} = require('buffer'); + +const len = MAX_STRING_LENGTH + 1; +const message = { + code: 'ERR_STRING_TOO_LONG', + name: 'Error', +}; +assert.throws(() => Buffer(len).toString('utf8'), message); +assert.throws(() => SlowBuffer(len).toString('utf8'), message); +assert.throws(() => Buffer.alloc(len).toString('utf8'), message); +assert.throws(() => Buffer.allocUnsafe(len).toString('utf8'), message); +assert.throws(() => Buffer.allocUnsafeSlow(len).toString('utf8'), message); diff --git a/test/js/node/test/parallel/test-buffer-tostring.js b/test/js/node/test/parallel/test-buffer-tostring.js new file mode 100644 index 0000000000..4219649fa3 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tostring.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// utf8, ucs2, ascii, latin1, utf16le +const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', + 'binary', 'utf16le', 'utf-16le']; + +encodings + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.from('foo', encoding).toString(encoding), 'foo'); + }); + +// base64 +['base64', 'BASE64'].forEach((encoding) => { + assert.strictEqual(Buffer.from('Zm9v', encoding).toString(encoding), 'Zm9v'); +}); + +// hex +['hex', 'HEX'].forEach((encoding) => { + assert.strictEqual(Buffer.from('666f6f', encoding).toString(encoding), + '666f6f'); +}); + +// Invalid encodings +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + const error = common.expectsError({ + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: `Unknown encoding: ${encoding}` + }); + assert.ok(!Buffer.isEncoding(encoding)); + assert.throws(() => Buffer.from('foo').toString(encoding), error); +} diff --git a/test/js/node/test/parallel/test-buffer-zero-fill-reset.js b/test/js/node/test/parallel/test-buffer-zero-fill-reset.js new file mode 100644 index 0000000000..334ee1b618 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-zero-fill-reset.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + + +function testUint8Array(ui) { + const length = ui.length; + for (let i = 0; i < length; i++) + if (ui[i] !== 0) return false; + return true; +} + + +for (let i = 0; i < 100; i++) { + Buffer.alloc(0); + const ui = new Uint8Array(65); + assert.ok(testUint8Array(ui), `Uint8Array is not zero-filled: ${ui}`); +} diff --git a/test/js/node/test/parallel/test-buffer-zero-fill.js b/test/js/node/test/parallel/test-buffer-zero-fill.js new file mode 100644 index 0000000000..7a9f0c1250 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-zero-fill.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Tests deprecated Buffer API on purpose +const buf1 = Buffer(100); +const buf2 = new Buffer(100); + +for (let n = 0; n < buf1.length; n++) + assert.strictEqual(buf1[n], 0); + +for (let n = 0; n < buf2.length; n++) + assert.strictEqual(buf2[n], 0); diff --git a/test/js/node/test/parallel/test-child-process-can-write-to-stdout.js b/test/js/node/test/parallel/test-child-process-can-write-to-stdout.js new file mode 100644 index 0000000000..069e07fb16 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-can-write-to-stdout.js @@ -0,0 +1,22 @@ +'use strict'; +// Tests that a spawned child process can write to stdout without throwing. +// See https://github.com/nodejs/node-v0.x-archive/issues/1899. + +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.argv[0], [ + fixtures.path('GH-1899-output.js'), +]); +let output = ''; + +child.stdout.on('data', function(data) { + output += data; +}); + +child.on('exit', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(output, 'hello, world!\n'); +}); diff --git a/test/js/node/test/parallel/tls-client-default-ciphers.test.js b/test/js/node/test/parallel/test-child-process-default-options.js similarity index 62% rename from test/js/node/test/parallel/tls-client-default-ciphers.test.js rename to test/js/node/test/parallel/test-child-process-default-options.js index d337071777..39f90deaeb 100644 --- a/test/js/node/test/parallel/tls-client-default-ciphers.test.js +++ b/test/js/node/test/parallel/test-child-process-default-options.js @@ -1,6 +1,3 @@ -//#FILE: test-tls-client-default-ciphers.js -//#SHA1: 8a9af503503ffc8b8a7c27089fd7cf417e22ec16 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,31 +19,33 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); -const tls = require("tls"); +const spawn = require('child_process').spawn; +const debug = require('util').debuglog('test'); -if (typeof Bun !== "undefined") { - test = it; -} +process.env.HELLO = 'WORLD'; -if (!("crypto" in process.versions)) { - test.skip("missing crypto", () => {}); +let child; +if (isWindows) { + child = spawn('cmd.exe', ['/c', 'set'], {}); } else { - test("tls.connect uses default ciphers", () => { - let ciphers = ""; - - class Done extends Error {} - - tls.createSecureContext = function (options) { - ciphers = options.ciphers; - throw new Done(); - }; - - expect(() => tls.connect()).toThrow(Done); - - expect(ciphers).toBe(tls.DEFAULT_CIPHERS); - }); + child = spawn('/usr/bin/env', [], {}); } -//<#END_FILE: test-tls-client-default-ciphers.js +let response = ''; + +child.stdout.setEncoding('utf8'); + +child.stdout.on('data', function(chunk) { + debug(`stdout: ${chunk}`); + response += chunk; +}); + +process.on('exit', function() { + assert.ok(response.includes('HELLO=WORLD'), + 'spawn did not use process.env as default ' + + `(process.env.HELLO = ${process.env.HELLO})`); +}); diff --git a/test/js/node/test/parallel/test-child-process-destroy.js b/test/js/node/test/parallel/test-child-process-destroy.js new file mode 100644 index 0000000000..50763bb031 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-destroy.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const cat = spawn(common.isWindows ? 'cmd' : 'cat'); + +cat.stdout.on('end', common.mustCall()); +cat.stderr.on('data', common.mustNotCall()); +cat.stderr.on('end', common.mustCall()); + +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); + +assert.strictEqual(cat.signalCode, null); +assert.strictEqual(cat.killed, false); +cat[Symbol.dispose](); +assert.strictEqual(cat.killed, true); diff --git a/test/js/node/test/parallel/test-child-process-double-pipe.js b/test/js/node/test/parallel/test-child-process-double-pipe.js new file mode 100644 index 0000000000..7a432d3892 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-double-pipe.js @@ -0,0 +1,122 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + isWindows, + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const os = require('os'); +const spawn = require('child_process').spawn; +const debug = require('util').debuglog('test'); + +// We're trying to reproduce: +// $ echo "hello\nnode\nand\nworld" | grep o | sed s/o/a/ + +let grep, sed, echo; + +if (isWindows) { + grep = spawn('grep', ['--binary', 'o']); + sed = spawn('sed', ['--binary', 's/o/O/']); + echo = spawn('cmd.exe', + ['/c', 'echo', 'hello&&', 'echo', + 'node&&', 'echo', 'and&&', 'echo', 'world']); +} else { + grep = spawn('grep', ['o']); + sed = spawn('sed', ['s/o/O/']); + echo = spawn('echo', ['hello\nnode\nand\nworld\n']); +} + +// If the spawn function leaks file descriptors to subprocesses, grep and sed +// hang. +// This happens when calling pipe(2) and then forgetting to set the +// FD_CLOEXEC flag on the resulting file descriptors. +// +// This test checks child processes exit, meaning they don't hang like +// explained above. + + +// pipe echo | grep +echo.stdout.on('data', mustCallAtLeast((data) => { + debug(`grep stdin write ${data.length}`); + if (!grep.stdin.write(data)) { + echo.stdout.pause(); + } +})); + +// TODO(@jasnell): This does not appear to ever be +// emitted. It's not clear if it is necessary. +grep.stdin.on('drain', (data) => { + echo.stdout.resume(); +}); + +// Propagate end from echo to grep +echo.stdout.on('end', mustCall((code) => { + grep.stdin.end(); +})); + +echo.on('exit', mustCall(() => { + debug('echo exit'); +})); + +grep.on('exit', mustCall(() => { + debug('grep exit'); +})); + +sed.on('exit', mustCall(() => { + debug('sed exit'); +})); + + +// pipe grep | sed +grep.stdout.on('data', mustCallAtLeast((data) => { + debug(`grep stdout ${data.length}`); + if (!sed.stdin.write(data)) { + grep.stdout.pause(); + } +})); + +// TODO(@jasnell): This does not appear to ever be +// emitted. It's not clear if it is necessary. +sed.stdin.on('drain', (data) => { + grep.stdout.resume(); +}); + +// Propagate end from grep to sed +grep.stdout.on('end', mustCall((code) => { + debug('grep stdout end'); + sed.stdin.end(); +})); + + +let result = ''; + +// print sed's output +sed.stdout.on('data', mustCallAtLeast((data) => { + result += data.toString('utf8', 0, data.length); + debug(data); +})); + +sed.stdout.on('end', mustCall((code) => { + assert.strictEqual(result, `hellO${os.EOL}nOde${os.EOL}wOrld${os.EOL}`); +})); diff --git a/test/js/node/test/parallel/test-child-process-env.js b/test/js/node/test/parallel/test-child-process-env.js new file mode 100644 index 0000000000..f9815ff015 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-env.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + isWindows, + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const os = require('os'); +const debug = require('util').debuglog('test'); + +const spawn = require('child_process').spawn; + +const env = { + ...process.env, + 'HELLO': 'WORLD', + 'UNDEFINED': undefined, + 'NULL': null, + 'EMPTY': '', + 'duplicate': 'lowercase', + 'DUPLICATE': 'uppercase', +}; +Object.setPrototypeOf(env, { + 'FOO': 'BAR' +}); + +let child; +if (isWindows) { + child = spawn('cmd.exe', ['/c', 'set'], { env }); +} else { + child = spawn('/usr/bin/env', [], { env }); +} + + +let response = ''; + +child.stdout.setEncoding('utf8'); + +child.stdout.on('data', mustCallAtLeast((chunk) => { + debug(`stdout: ${chunk}`); + response += chunk; +})); + +child.stdout.on('end', mustCall(() => { + assert.ok(response.includes('HELLO=WORLD')); + assert.ok(response.includes('FOO=BAR')); + assert.ok(!response.includes('UNDEFINED=undefined')); + assert.ok(response.includes('NULL=null')); + assert.ok(response.includes(`EMPTY=${os.EOL}`)); + if (isWindows) { + assert.ok(response.includes('DUPLICATE=uppercase')); + assert.ok(!response.includes('duplicate=lowercase')); + } else { + assert.ok(response.includes('DUPLICATE=uppercase')); + assert.ok(response.includes('duplicate=lowercase')); + } +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-any-shells-windows.js b/test/js/node/test/parallel/test-child-process-exec-any-shells-windows.js new file mode 100644 index 0000000000..5c34bc7730 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-any-shells-windows.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +// This test is only relevant on Windows. +if (!common.isWindows) + common.skip('Windows specific test.'); + +// This test ensures that child_process.exec can work with any shells. + +tmpdir.refresh(); +const tmpPath = `${tmpdir.path}\\path with spaces`; +fs.mkdirSync(tmpPath); + +const test = (shell) => { + cp.exec('echo foo bar', { shell: shell }, + common.mustSucceed((stdout, stderror) => { + assert.ok(!stderror); + assert.ok(stdout.includes('foo') && stdout.includes('bar')); + })); +}; +const testCopy = (shellName, shellPath) => { + // Symlink the executable to a path with spaces, to ensure there are no issues + // related to quoting of argv0 + const copyPath = `${tmpPath}\\${shellName}`; + fs.symlinkSync(shellPath, copyPath); + test(copyPath); +}; + +const system32 = `${process.env.SystemRoot}\\System32`; + +// Test CMD +test(true); +test('cmd'); +testCopy('cmd.exe', `${system32}\\cmd.exe`); +test('cmd.exe'); +test('CMD'); + +// Test PowerShell +test('powershell'); +testCopy('powershell.exe', + `${system32}\\WindowsPowerShell\\v1.0\\powershell.exe`); +fs.writeFile(`${tmpPath}\\test file`, 'Test', common.mustSucceed(() => { + cp.exec(`Get-ChildItem "${tmpPath}" | Select-Object -Property Name`, + { shell: 'PowerShell' }, + common.mustSucceed((stdout, stderror) => { + assert.ok(!stderror); + assert.ok(stdout.includes( + 'test file')); + })); +})); + +// Test Bash (from WSL and Git), if available +cp.exec('where bash', common.mustCall((error, stdout) => { + if (error) { + return; + } + const lines = stdout.trim().split(/[\r\n]+/g); + for (let i = 0; i < lines.length; ++i) { + const bashPath = lines[i].trim(); + test(bashPath); + testCopy(`bash_${i}.exe`, bashPath); + } +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-cwd.js b/test/js/node/test/parallel/test-child-process-exec-cwd.js new file mode 100644 index 0000000000..49e56ef551 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-cwd.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +let pwdcommand, dir; + +if (common.isWindows) { + pwdcommand = 'echo %cd%'; + dir = 'c:\\windows'; +} else { + pwdcommand = 'pwd'; + dir = '/dev'; +} + +exec(pwdcommand, { cwd: dir }, common.mustSucceed((stdout, stderr) => { + assert(stdout.toLowerCase().startsWith(dir)); +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-encoding.js b/test/js/node/test/parallel/test-child-process-exec-encoding.js new file mode 100644 index 0000000000..0c3178e3f2 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-encoding.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +const stdoutData = 'foo'; +const stderrData = 'bar'; + +if (process.argv[2] === 'child') { + // The following console calls are part of the test. + console.log(stdoutData); + console.error(stderrData); +} else { + const assert = require('assert'); + const cp = require('child_process'); + const expectedStdout = `${stdoutData}\n`; + const expectedStderr = `${stderrData}\n`; + function run(options, callback) { + const cmd = `"${process.execPath}" "${__filename}" child`; + + cp.exec(cmd, options, common.mustSucceed((stdout, stderr) => { + callback(stdout, stderr); + })); + } + + // Test default encoding, which should be utf8. + run({}, (stdout, stderr) => { + assert.strictEqual(typeof stdout, 'string'); + assert.strictEqual(typeof stderr, 'string'); + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + }); + + // Test explicit utf8 encoding. + run({ encoding: 'utf8' }, (stdout, stderr) => { + assert.strictEqual(typeof stdout, 'string'); + assert.strictEqual(typeof stderr, 'string'); + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + }); + + // Test cases that result in buffer encodings. + [undefined, null, 'buffer', 'invalid'].forEach((encoding) => { + run({ encoding }, (stdout, stderr) => { + assert(stdout instanceof Buffer); + assert(stdout instanceof Buffer); + assert.strictEqual(stdout.toString(), expectedStdout); + assert.strictEqual(stderr.toString(), expectedStderr); + }); + }); +} diff --git a/test/js/node/test/parallel/test-child-process-exec-env.js b/test/js/node/test/parallel/test-child-process-exec-env.js new file mode 100644 index 0000000000..f515643619 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-env.js @@ -0,0 +1,64 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; +const debug = require('util').debuglog('test'); + +let success_count = 0; +let error_count = 0; +let response = ''; +let child; + +function after(err, stdout, stderr) { + if (err) { + error_count++; + debug(`error!: ${err.code}`); + debug(`stdout: ${JSON.stringify(stdout)}`); + debug(`stderr: ${JSON.stringify(stderr)}`); + assert.strictEqual(err.killed, false); + } else { + success_count++; + assert.notStrictEqual(stdout, ''); + } +} + +if (!isWindows) { + child = exec('/usr/bin/env', { env: { 'HELLO': 'WORLD' } }, after); +} else { + child = exec('set', + { env: { ...process.env, 'HELLO': 'WORLD' } }, + after); +} + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(chunk) { + response += chunk; +}); + +process.on('exit', function() { + debug('response: ', response); + assert.strictEqual(success_count, 1); + assert.strictEqual(error_count, 0); + assert.ok(response.includes('HELLO=WORLD')); +}); diff --git a/test/js/node/test/parallel/test-child-process-exec-std-encoding.js b/test/js/node/test/parallel/test-child-process-exec-std-encoding.js new file mode 100644 index 0000000000..0818731672 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-std-encoding.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const stdoutData = 'foo'; +const stderrData = 'bar'; +const expectedStdout = `${stdoutData}\n`; +const expectedStderr = `${stderrData}\n`; + +if (process.argv[2] === 'child') { + // The following console calls are part of the test. + console.log(stdoutData); + console.error(stderrData); +} else { + const cmd = `"${process.execPath}" "${__filename}" child`; + const child = cp.exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + })); + child.stdout.setEncoding('utf-8'); + child.stderr.setEncoding('utf-8'); +} diff --git a/test/js/node/test/parallel/test-child-process-exec-stdout-stderr-data-string.js b/test/js/node/test/parallel/test-child-process-exec-stdout-stderr-data-string.js new file mode 100644 index 0000000000..1fbdfbf8e4 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-stdout-stderr-data-string.js @@ -0,0 +1,13 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/7342 +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +const command = common.isWindows ? 'dir' : 'ls'; + +exec(command).stdout.on('data', common.mustCallAtLeast()); + +exec('fhqwhgads').stderr.on('data', common.mustCallAtLeast((data) => { + assert.strictEqual(typeof data, 'string'); +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-timeout-expire.js b/test/js/node/test/parallel/test-child-process-exec-timeout-expire.js new file mode 100644 index 0000000000..6b62d131cb --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-timeout-expire.js @@ -0,0 +1,50 @@ +'use strict'; + +// Test exec() with a timeout that expires. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime, + kExpiringChildRunTime, + kExpiringParentTimer +} = require('../common/child_process'); + +if (process.argv[2] === 'child') { + logAfterTime(kExpiringChildRunTime); + return; +} + +const cmd = `"${process.execPath}" "${__filename}" child`; + +cp.exec(cmd, { + timeout: kExpiringParentTimer, +}, common.mustCall((err, stdout, stderr) => { + console.log('[stdout]', stdout.trim()); + console.log('[stderr]', stderr.trim()); + + let sigterm = 'SIGTERM'; + assert.strictEqual(err.killed, true); + // TODO OpenBSD returns a null signal and 143 for code + if (common.isOpenBSD) { + assert.strictEqual(err.code, 143); + sigterm = null; + } else { + assert.strictEqual(err.code, null); + } + // At least starting with Darwin Kernel Version 16.4.0, sending a SIGTERM to a + // process that is still starting up kills it with SIGKILL instead of SIGTERM. + // See: https://github.com/libuv/libuv/issues/1226 + if (common.isMacOS) + assert.ok(err.signal === 'SIGTERM' || err.signal === 'SIGKILL'); + else + assert.strictEqual(err.signal, sigterm); + assert.strictEqual(err.cmd, cmd); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); +})); + +cleanupStaleProcess(__filename); diff --git a/test/js/node/test/parallel/test-child-process-exec-timeout-kill.js b/test/js/node/test/parallel/test-child-process-exec-timeout-kill.js new file mode 100644 index 0000000000..845fd1eaec --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-timeout-kill.js @@ -0,0 +1,39 @@ +'use strict'; + +// Test exec() with both a timeout and a killSignal. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime, + kExpiringChildRunTime, + kExpiringParentTimer, +} = require('../common/child_process'); + +if (process.argv[2] === 'child') { + logAfterTime(kExpiringChildRunTime); + return; +} + +const cmd = `"${process.execPath}" "${__filename}" child`; + +// Test with a different kill signal. +cp.exec(cmd, { + timeout: kExpiringParentTimer, + killSignal: 'SIGKILL' +}, common.mustCall((err, stdout, stderr) => { + console.log('[stdout]', stdout.trim()); + console.log('[stderr]', stderr.trim()); + + assert.strictEqual(err.killed, true); + assert.strictEqual(err.code, null); + assert.strictEqual(err.signal, 'SIGKILL'); + assert.strictEqual(err.cmd, cmd); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); +})); + +cleanupStaleProcess(__filename); diff --git a/test/js/node/test/parallel/test-child-process-exec-timeout-not-expired.js b/test/js/node/test/parallel/test-child-process-exec-timeout-not-expired.js new file mode 100644 index 0000000000..fb0af5fa8f --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-timeout-not-expired.js @@ -0,0 +1,34 @@ +'use strict'; + +// Test exec() when a timeout is set, but not expired. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime +} = require('../common/child_process'); + +const kTimeoutNotSupposedToExpire = 2 ** 30; +const childRunTime = common.platformTimeout(100); + +// The time spent in the child should be smaller than the timeout below. +assert(childRunTime < kTimeoutNotSupposedToExpire); + +if (process.argv[2] === 'child') { + logAfterTime(childRunTime); + return; +} + +const cmd = `"${process.execPath}" "${__filename}" child`; + +cp.exec(cmd, { + timeout: kTimeoutNotSupposedToExpire +}, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'child stdout'); + assert.strictEqual(stderr.trim(), 'child stderr'); +})); + +cleanupStaleProcess(__filename); diff --git a/test/js/node/test/parallel/test-child-process-execFile-promisified-abortController.js b/test/js/node/test/parallel/test-child-process-execFile-promisified-abortController.js new file mode 100644 index 0000000000..38c177eb33 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-execFile-promisified-abortController.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { promisify } = require('util'); +const execFile = require('child_process').execFile; +const fixtures = require('../common/fixtures'); + +const echoFixture = fixtures.path('echo.js'); +const promisified = promisify(execFile); +const invalidArgTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; + +{ + // Verify that the signal option works properly + const ac = new AbortController(); + const signal = ac.signal; + const promise = promisified(process.execPath, [echoFixture, 0], { signal }); + + ac.abort(); + + assert.rejects( + promise, + { name: 'AbortError' } + ).then(common.mustCall()); +} + +{ + // Verify that the signal option works properly when already aborted + const signal = AbortSignal.abort(); + + assert.rejects( + promisified(process.execPath, [echoFixture, 0], { signal }), + { name: 'AbortError' } + ).then(common.mustCall()); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + const signal = {}; + assert.throws(() => { + promisified(process.execPath, [echoFixture, 0], { signal }); + }, invalidArgTypeError); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + const signal = 'world!'; + assert.throws(() => { + promisified(process.execPath, [echoFixture, 0], { signal }); + }, invalidArgTypeError); +} diff --git a/test/js/node/test/parallel/test-child-process-flush-stdio.js b/test/js/node/test/parallel/test-child-process-flush-stdio.js new file mode 100644 index 0000000000..7b9a2a049c --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-flush-stdio.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const cp = require('child_process'); +const assert = require('assert'); + +// Windows' `echo` command is a built-in shell command and not an external +// executable like on *nix +const opts = { shell: common.isWindows }; + +const p = cp.spawn('echo', [], opts); + +p.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + spawnWithReadable(); +})); + +p.stdout.read(); + +const spawnWithReadable = () => { + const buffer = []; + const p = cp.spawn('echo', ['123'], opts); + p.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(Buffer.concat(buffer).toString().trim(), '123'); + })); + p.stdout.on('readable', () => { + let buf; + while ((buf = p.stdout.read()) !== null) + buffer.push(buf); + }); +}; diff --git a/test/js/node/test/parallel/test-child-process-fork-abort-signal.js b/test/js/node/test/parallel/test-child-process-fork-abort-signal.js new file mode 100644 index 0000000000..b963306fb1 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-abort-signal.js @@ -0,0 +1,105 @@ +'use strict'; + +const { mustCall, mustNotCall } = require('../common'); +const { strictEqual } = require('assert'); +const fixtures = require('../common/fixtures'); +const { fork } = require('child_process'); + +{ + // Test aborting a forked child_process after calling fork + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); + process.nextTick(() => ac.abort()); +} + +{ + // Test aborting with custom error + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + strictEqual(err.cause.name, 'Error'); + strictEqual(err.cause.message, 'boom'); + })); + process.nextTick(() => ac.abort(new Error('boom'))); +} + +{ + // Test passing an already aborted signal to a forked child_process + const signal = AbortSignal.abort(); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); +} + +{ + // Test passing an aborted signal with custom error to a forked child_process + const signal = AbortSignal.abort(new Error('boom')); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + strictEqual(err.cause.name, 'Error'); + strictEqual(err.cause.message, 'boom'); + })); +} + +{ + // Test passing a different kill signal + const signal = AbortSignal.abort(); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal, + killSignal: 'SIGKILL', + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGKILL'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); +} + +{ + // Test aborting a cp before close but after exit + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall(() => { + ac.abort(); + })); + cp.on('error', mustNotCall()); + + setTimeout(() => cp.kill(), 1); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-and-spawn.js b/test/js/node/test/parallel/test-child-process-fork-and-spawn.js new file mode 100644 index 0000000000..88e634ba8b --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-and-spawn.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { fork, spawn } = require('child_process'); + +// Fork, then spawn. The spawned process should not hang. +switch (process.argv[2] || '') { + case '': + fork(__filename, ['fork']).on('exit', common.mustCall(checkExit)); + break; + case 'fork': + spawn(process.execPath, [__filename, 'spawn']) + .on('exit', common.mustCall(checkExit)); + break; + case 'spawn': + break; + default: + assert.fail(); +} + +function checkExit(statusCode) { + assert.strictEqual(statusCode, 0); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-args.js b/test/js/node/test/parallel/test-child-process-fork-args.js new file mode 100644 index 0000000000..2ed31fa4e9 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-args.js @@ -0,0 +1,105 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { fork } = require('child_process'); + +// This test check the arguments of `fork` method +// Refs: https://github.com/nodejs/node/issues/20749 +const expectedEnv = { foo: 'bar' }; + +// Ensure that first argument `modulePath` must be provided +// and be of type string +{ + const invalidModulePath = [ + 0, + true, + undefined, + null, + [], + {}, + () => {}, + Symbol('t'), + ]; + invalidModulePath.forEach((modulePath) => { + assert.throws(() => fork(modulePath), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "modulePath" argument must be of type string/ + }); + }); + + const cp = fork(fixtures.path('child-process-echo-options.js')); + cp.on( + 'exit', + common.mustCall((code) => { + assert.strictEqual(code, 0); + }) + ); +} + +// Ensure that the second argument of `fork` +// and `fork` should parse options +// correctly if args is undefined or null +{ + const invalidSecondArgs = [ + 0, + true, + () => {}, + Symbol('t'), + ]; + invalidSecondArgs.forEach((arg) => { + assert.throws( + () => { + fork(fixtures.path('child-process-echo-options.js'), arg); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); + + const argsLists = [undefined, null, []]; + + argsLists.forEach((args) => { + const cp = fork(fixtures.path('child-process-echo-options.js'), args, { + env: { ...process.env, ...expectedEnv } + }); + + cp.on( + 'message', + common.mustCall(({ env }) => { + assert.strictEqual(env.foo, expectedEnv.foo); + }) + ); + + cp.on( + 'exit', + common.mustCall((code) => { + assert.strictEqual(code, 0); + }) + ); + }); +} + +// Ensure that the third argument should be type of object if provided +{ + const invalidThirdArgs = [ + 0, + true, + () => {}, + Symbol('t'), + ]; + invalidThirdArgs.forEach((arg) => { + assert.throws( + () => { + fork(fixtures.path('child-process-echo-options.js'), [], arg); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-close.js b/test/js/node/test/parallel/test-child-process-fork-close.js new file mode 100644 index 0000000000..bfaabd3d2f --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-close.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fork = require('child_process').fork; +const fixtures = require('../common/fixtures'); + +const cp = fork(fixtures.path('child-process-message-and-exit.js')); + +let gotMessage = false; +let gotExit = false; +let gotClose = false; + +cp.on('message', common.mustCall(function(message) { + assert(!gotMessage); + assert(!gotClose); + assert.strictEqual(message, 'hello'); + gotMessage = true; +})); + +cp.on('exit', common.mustCall(function() { + assert(!gotExit); + assert(!gotClose); + gotExit = true; +})); + +cp.on('close', common.mustCall(function() { + assert(gotMessage); + assert(gotExit); + assert(!gotClose); + gotClose = true; +})); diff --git a/test/js/node/test/parallel/test-child-process-fork-detached.js b/test/js/node/test/parallel/test-child-process-fork-detached.js new file mode 100644 index 0000000000..87c1517328 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-detached.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fork = require('child_process').fork; +const fixtures = require('../common/fixtures'); + +const nonPersistentNode = fork( + fixtures.path('parent-process-nonpersistent-fork.js'), + [], + { silent: true }); + +let childId = -1; + +nonPersistentNode.stdout.on('data', (data) => { + childId = parseInt(data, 10); + nonPersistentNode.kill(); +}); + +process.on('exit', () => { + assert.notStrictEqual(childId, -1); + // Killing the child process should not throw an error + process.kill(childId); +}); diff --git a/test/js/node/test/parallel/test-child-process-fork-exec-path.js b/test/js/node/test/parallel/test-child-process-fork-exec-path.js new file mode 100644 index 0000000000..3417a102c6 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-exec-path.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Test that `fork()` respects the `execPath` option. + +const tmpdir = require('../common/tmpdir'); +const { addLibraryPath } = require('../common/shared-lib-util'); +const assert = require('assert'); +const fs = require('fs'); +const { fork } = require('child_process'); + +const msg = { test: 'this' }; +const nodePath = process.execPath; +const copyPath = tmpdir.resolve('node-copy.exe'); + +addLibraryPath(process.env); + +// Child +if (process.env.FORK) { + assert.strictEqual(process.execPath, copyPath); + assert.ok(process.send); + process.send(msg); + return process.exit(); +} + +// Parent +tmpdir.refresh(); +assert.strictEqual(fs.existsSync(copyPath), false); +fs.copyFileSync(nodePath, copyPath, fs.constants.COPYFILE_FICLONE); +fs.chmodSync(copyPath, '0755'); + +const envCopy = { ...process.env, FORK: 'true' }; +const child = fork(__filename, { execPath: copyPath, env: envCopy }); +child.on('message', common.mustCall(function(recv) { + assert.deepStrictEqual(recv, msg); +})); +child.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-fork-no-shell.js b/test/js/node/test/parallel/test-child-process-fork-no-shell.js new file mode 100644 index 0000000000..81ceab61fd --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-no-shell.js @@ -0,0 +1,20 @@ +'use strict'; +// This test verifies that the shell option is not supported by fork(). +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const expected = common.isWindows ? '%foo%' : '$foo'; + +if (process.argv[2] === undefined) { + const child = cp.fork(__filename, [expected], { + shell: true, + env: { ...process.env, foo: 'bar' } + }); + + child.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); +} else { + assert.strictEqual(process.argv[2], expected); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-url.mjs b/test/js/node/test/parallel/test-child-process-fork-url.mjs new file mode 100644 index 0000000000..9261b87563 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-url.mjs @@ -0,0 +1,11 @@ +import { mustCall } from '../common/index.mjs'; +import { fork } from 'child_process'; + +if (process.argv[2] === 'child') { + process.disconnect(); +} else { + const child = fork(new URL(import.meta.url), ['child']); + + child.on('disconnect', mustCall()); + child.once('exit', mustCall()); +} diff --git a/test/js/node/test/parallel/test-child-process-fork3.js b/test/js/node/test/parallel/test-child-process-fork3.js new file mode 100644 index 0000000000..735a441950 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork3.js @@ -0,0 +1,27 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const child_process = require('child_process'); +const fixtures = require('../common/fixtures'); + +child_process.fork(fixtures.path('empty.js')); // should not hang diff --git a/test/js/node/test/parallel/next-tick-ordering.test.js b/test/js/node/test/parallel/test-child-process-ipc.js similarity index 55% rename from test/js/node/test/parallel/next-tick-ordering.test.js rename to test/js/node/test/parallel/test-child-process-ipc.js index 3790be15ab..d776f9594b 100644 --- a/test/js/node/test/parallel/next-tick-ordering.test.js +++ b/test/js/node/test/parallel/test-child-process-ipc.js @@ -1,6 +1,3 @@ -//#FILE: test-next-tick-ordering.js -//#SHA1: 224023554e0ccd7644d70c5acef5bf3a094409ac -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,38 +19,45 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; -test("next tick ordering", async () => { - const N = 30; - const done = []; +const { + mustCall, + mustNotCall, +} = require('../common'); +const assert = require('assert'); +const debug = require('util').debuglog('test'); - function get_printer(timeout) { - return function () { - console.log(`Running from setTimeout ${timeout}`); - done.push(timeout); - }; +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('echo.js'); + +const child = spawn(process.argv[0], [sub]); + +child.stderr.on('data', mustNotCall()); + +child.stdout.setEncoding('utf8'); + +const messages = [ + 'hello world\r\n', + 'echo me\r\n', +]; + +child.stdout.on('data', mustCall((data) => { + debug(`child said: ${JSON.stringify(data)}`); + const test = messages.shift(); + debug(`testing for '${test}'`); + assert.strictEqual(data, test); + if (messages.length) { + debug(`writing '${messages[0]}'`); + child.stdin.write(messages[0]); + } else { + assert.strictEqual(messages.length, 0); + child.stdin.end(); } +}, messages.length)); - process.nextTick(function () { - console.log("Running from nextTick"); - done.push("nextTick"); - }); - - for (let i = 0; i < N; i += 1) { - setTimeout(get_printer(i), i); - } - - console.log("Running from main."); - - // Wait for all timeouts to complete - await new Promise(resolve => setTimeout(resolve, N + 100)); - - expect(done[0]).toBe("nextTick"); - // Disabling this test. I don't think we can ensure the order - // for (let i = 0; i < N; i += 1) { - // expect(done[i + 1]).toBe(i); - // } -}); - -//<#END_FILE: test-next-tick-ordering.js +child.stdout.on('end', mustCall((data) => { + debug('child end'); +})); diff --git a/test/js/node/test/parallel/test-child-process-kill.js b/test/js/node/test/parallel/test-child-process-kill.js new file mode 100644 index 0000000000..1025c69ba1 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-kill.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const cat = spawn(common.isWindows ? 'cmd' : 'cat'); + +cat.stdout.on('end', common.mustCall()); +cat.stderr.on('data', common.mustNotCall()); +cat.stderr.on('end', common.mustCall()); + +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); + +assert.strictEqual(cat.signalCode, null); +assert.strictEqual(cat.killed, false); +cat.kill(); +assert.strictEqual(cat.killed, true); diff --git a/test/js/node/test/parallel/test-child-process-no-deprecation.js b/test/js/node/test/parallel/test-child-process-no-deprecation.js new file mode 100644 index 0000000000..d12e5b882f --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-no-deprecation.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +process.noDeprecation = true; + +if (process.argv[2] === 'child') { + process.emitWarning('Something else is deprecated.', 'DeprecationWarning'); +} else { + // parent process + const spawn = require('child_process').spawn; + + // spawn self as child + const child = spawn(process.execPath, [process.argv[1], 'child']); + + child.stderr.on('data', common.mustNotCall()); +} diff --git a/test/js/node/test/parallel/test-child-process-prototype-tampering.mjs b/test/js/node/test/parallel/test-child-process-prototype-tampering.mjs new file mode 100644 index 0000000000..d94c4bdbc6 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-prototype-tampering.mjs @@ -0,0 +1,91 @@ +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { EOL } from 'node:os'; +import { strictEqual, notStrictEqual, throws } from 'node:assert'; +import cp from 'node:child_process'; + +// TODO(LiviaMedeiros): test on different platforms +if (!common.isLinux) + common.skip(); + +const expectedCWD = process.cwd(); +const expectedUID = process.getuid(); + +for (const tamperedCwd of ['', '/tmp', '/not/existing/malicious/path', 42n]) { + Object.prototype.cwd = tamperedCwd; + + cp.exec('pwd', common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedCWD}${EOL}`); + })); + strictEqual(`${cp.execSync('pwd')}`, `${expectedCWD}${EOL}`); + cp.execFile('pwd', common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedCWD}${EOL}`); + })); + strictEqual(`${cp.execFileSync('pwd')}`, `${expectedCWD}${EOL}`); + cp.spawn('pwd').stdout.on('data', common.mustCall((out) => { + strictEqual(`${out}`, `${expectedCWD}${EOL}`); + })); + strictEqual(`${cp.spawnSync('pwd').stdout}`, `${expectedCWD}${EOL}`); + + delete Object.prototype.cwd; +} + +for (const tamperedUID of [0, 1, 999, 1000, 0n, 'gwak']) { + Object.prototype.uid = tamperedUID; + + cp.exec('id -u', common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedUID}${EOL}`); + })); + strictEqual(`${cp.execSync('id -u')}`, `${expectedUID}${EOL}`); + cp.execFile('id', ['-u'], common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedUID}${EOL}`); + })); + strictEqual(`${cp.execFileSync('id', ['-u'])}`, `${expectedUID}${EOL}`); + cp.spawn('id', ['-u']).stdout.on('data', common.mustCall((out) => { + strictEqual(`${out}`, `${expectedUID}${EOL}`); + })); + strictEqual(`${cp.spawnSync('id', ['-u']).stdout}`, `${expectedUID}${EOL}`); + + delete Object.prototype.uid; +} + +{ + Object.prototype.execPath = '/not/existing/malicious/path'; + + // Does not throw ENOENT + cp.fork(fixtures.path('empty.js')); + + delete Object.prototype.execPath; +} + +for (const shellCommandArgument of ['-L && echo "tampered"']) { + Object.prototype.shell = true; + const cmd = 'pwd'; + let cmdExitCode = ''; + + const program = cp.spawn(cmd, [shellCommandArgument], { cwd: expectedCWD }); + program.stderr.on('data', common.mustCall()); + program.stdout.on('data', common.mustNotCall()); + + program.on('exit', common.mustCall((code) => { + notStrictEqual(code, 0); + })); + + cp.execFile(cmd, [shellCommandArgument], { cwd: expectedCWD }, + common.mustCall((err) => { + notStrictEqual(err.code, 0); + }) + ); + + throws(() => { + cp.execFileSync(cmd, [shellCommandArgument], { cwd: expectedCWD }); + }, (e) => { + notStrictEqual(e.status, 0); + return true; + }); + + cmdExitCode = cp.spawnSync(cmd, [shellCommandArgument], { cwd: expectedCWD }).status; + notStrictEqual(cmdExitCode, 0); + + delete Object.prototype.shell; +} diff --git a/test/js/node/test/parallel/test-child-process-send-type-error.js b/test/js/node/test/parallel/test-child-process-send-type-error.js new file mode 100644 index 0000000000..65c620dd29 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-send-type-error.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const cp = require('child_process'); + +function fail(proc, args) { + assert.throws(() => { + proc.send.apply(proc, args); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} + +let target = process; + +if (process.argv[2] !== 'child') { + target = cp.fork(__filename, ['child']); + target.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); +} + +fail(target, ['msg', null, null]); +fail(target, ['msg', null, '']); +fail(target, ['msg', null, 'foo']); +fail(target, ['msg', null, 0]); +fail(target, ['msg', null, NaN]); +fail(target, ['msg', null, 1]); +fail(target, ['msg', null, null, common.mustNotCall()]); diff --git a/test/js/node/test/parallel/test-child-process-set-blocking.js b/test/js/node/test/parallel/test-child-process-set-blocking.js new file mode 100644 index 0000000000..6dcfa93141 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-set-blocking.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const ch = require('child_process'); + +const SIZE = 100000; +const python = process.env.PYTHON || (common.isWindows ? 'python' : 'python3'); + +const cp = ch.spawn(python, ['-c', `print(${SIZE} * "C")`], { + stdio: 'inherit' +}); + +cp.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-silent.js b/test/js/node/test/parallel/test-child-process-silent.js new file mode 100644 index 0000000000..892c4527c9 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-silent.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const childProcess = require('child_process'); + +// Child pipe test +if (process.argv[2] === 'pipe') { + process.stdout.write('stdout message'); + process.stderr.write('stderr message'); + +} else if (process.argv[2] === 'ipc') { + // Child IPC test + process.send('message from child'); + process.on('message', function() { + process.send('got message from primary'); + }); + +} else if (process.argv[2] === 'primary') { + // Primary | start child pipe test + + const child = childProcess.fork(process.argv[1], ['pipe'], { silent: true }); + + // Allow child process to self terminate + child.disconnect(); + + child.on('exit', function() { + process.exit(0); + }); + +} else { + // Testcase | start primary && child IPC test + + // testing: is stderr and stdout piped to primary + const args = [process.argv[1], 'primary']; + const primary = childProcess.spawn(process.execPath, args); + + // Got any stderr or std data + let stdoutData = false; + primary.stdout.on('data', function() { + stdoutData = true; + }); + let stderrData = false; + primary.stderr.on('data', function() { + stderrData = true; + }); + + // testing: do message system work when using silent + const child = childProcess.fork(process.argv[1], ['ipc'], { silent: true }); + + // Manual pipe so we will get errors + child.stderr.pipe(process.stderr, { end: false }); + child.stdout.pipe(process.stdout, { end: false }); + + let childSending = false; + let childReceiving = false; + child.on('message', function(message) { + if (childSending === false) { + childSending = (message === 'message from child'); + } + + if (childReceiving === false) { + childReceiving = (message === 'got message from primary'); + } + + if (childReceiving === true) { + child.kill(); + } + }); + child.send('message to child'); + + // Check all values + process.on('exit', function() { + // clean up + child.kill(); + primary.kill(); + + // Check std(out|err) pipes + assert.ok(!stdoutData); + assert.ok(!stderrData); + + // Check message system + assert.ok(childSending); + assert.ok(childReceiving); + }); +} diff --git a/test/js/node/test/parallel/test-child-process-spawn-args.js b/test/js/node/test/parallel/test-child-process-spawn-args.js new file mode 100644 index 0000000000..ec56f409fa --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawn-args.js @@ -0,0 +1,55 @@ +'use strict'; + +// This test confirms that `undefined`, `null`, and `[]` +// can be used as a placeholder for the second argument (`args`) of `spawn()`. +// Previously, there was a bug where using `undefined` for the second argument +// caused the third argument (`options`) to be ignored. +// See https://github.com/nodejs/node/issues/24912. + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const assert = require('assert'); +const { spawn } = require('child_process'); + +tmpdir.refresh(); + +const command = common.isWindows ? 'cd' : 'pwd'; +const options = { cwd: tmpdir.path }; + +if (common.isWindows) { + // This test is not the case for Windows based systems + // unless the `shell` options equals to `true` + + options.shell = true; +} + +const testCases = [ + undefined, + null, + [], +]; + +const expectedResult = tmpdir.path.trim().toLowerCase(); + +(async () => { + const results = await Promise.all( + testCases.map((testCase) => { + return new Promise((resolve) => { + const subprocess = spawn(command, testCase, options); + + let accumulatedData = Buffer.alloc(0); + + subprocess.stdout.on('data', common.mustCall((data) => { + accumulatedData = Buffer.concat([accumulatedData, data]); + })); + + subprocess.stdout.on('end', () => { + resolve(accumulatedData.toString().trim().toLowerCase()); + }); + }); + }) + ); + + assert.deepStrictEqual([...new Set(results)], [expectedResult]); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-child-process-spawn-controller.js b/test/js/node/test/parallel/test-child-process-spawn-controller.js new file mode 100644 index 0000000000..20facb09b3 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawn-controller.js @@ -0,0 +1,183 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const aliveScript = fixtures.path('child-process-stay-alive-forever.js'); +{ + // Verify that passing an AbortSignal works + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + + controller.abort(); +} + +{ + // Verify that passing an AbortSignal with custom abort error works + const controller = new AbortController(); + const { signal } = controller; + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause.name, 'Error'); + assert.strictEqual(e.cause.message, 'boom'); + })); + + controller.abort(new Error('boom')); +} + +{ + const controller = new AbortController(); + const { signal } = controller; + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause, 'boom'); + })); + + controller.abort('boom'); +} + +{ + // Verify that passing an already-aborted signal works. + const signal = AbortSignal.abort(); + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); +} + +{ + // Verify that passing an already-aborted signal with custom abort error + // works. + const signal = AbortSignal.abort(new Error('boom')); + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause.name, 'Error'); + assert.strictEqual(e.cause.message, 'boom'); + })); +} + +{ + const signal = AbortSignal.abort('boom'); + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause, 'boom'); + })); +} + +{ + // Verify that waiting a bit and closing works + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + + setTimeout(() => controller.abort(), 1); +} + +{ + // Test passing a different killSignal + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + killSignal: 'SIGKILL', + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGKILL'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + + setTimeout(() => controller.abort(), 1); +} + +{ + // Test aborting a cp before close but after exit + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall(() => { + controller.abort(); + })); + + cp.on('error', common.mustNotCall()); + + setTimeout(() => cp.kill(), 1); +} diff --git a/test/js/node/test/parallel/test-child-process-spawn-event.js b/test/js/node/test/parallel/test-child-process-spawn-event.js new file mode 100644 index 0000000000..c025d86286 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawn-event.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const spawn = require('child_process').spawn; +const assert = require('assert'); + +const subprocess = spawn('echo', ['ok']); + +let didSpawn = false; +subprocess.on('spawn', function() { + didSpawn = true; +}); +function mustCallAfterSpawn() { + return common.mustCall(function() { + assert.ok(didSpawn); + }); +} + +subprocess.on('error', common.mustNotCall()); +subprocess.on('spawn', common.mustCall()); +subprocess.stdout.on('data', mustCallAfterSpawn()); +subprocess.stdout.on('end', mustCallAfterSpawn()); +subprocess.stdout.on('close', mustCallAfterSpawn()); +subprocess.stderr.on('data', common.mustNotCall()); +subprocess.stderr.on('end', mustCallAfterSpawn()); +subprocess.stderr.on('close', mustCallAfterSpawn()); +subprocess.on('exit', mustCallAfterSpawn()); +subprocess.on('close', mustCallAfterSpawn()); diff --git a/test/js/node/test/parallel/test-child-process-spawnsync-args.js b/test/js/node/test/parallel/test-child-process-spawnsync-args.js new file mode 100644 index 0000000000..4e65b22f6e --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawnsync-args.js @@ -0,0 +1,48 @@ +'use strict'; + +// This test confirms that `undefined`, `null`, and `[]` can be used +// as a placeholder for the second argument (`args`) of `spawnSync()`. +// Previously, there was a bug where using `undefined` for the second argument +// caused the third argument (`options`) to be ignored. +// See https://github.com/nodejs/node/issues/24912. + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const command = common.isWindows ? 'cd' : 'pwd'; +const options = { cwd: tmpdir.path }; + +tmpdir.refresh(); + +if (common.isWindows) { + // This test is not the case for Windows based systems + // unless the `shell` options equals to `true` + + options.shell = true; +} + +const testCases = [ + undefined, + null, + [], +]; + +const expectedResult = tmpdir.path.trim().toLowerCase(); + +const results = testCases.map((testCase) => { + const { stdout, stderr, error } = spawnSync( + command, + testCase, + options + ); + + assert.ifError(error); + assert.deepStrictEqual(stderr, Buffer.alloc(0)); + + return stdout.toString().trim().toLowerCase(); +}); + +assert.deepStrictEqual([...new Set(results)], [expectedResult]); diff --git a/test/js/node/test/parallel/test-child-process-spawnsync-env.js b/test/js/node/test/parallel/test-child-process-spawnsync-env.js new file mode 100644 index 0000000000..c8e11b5067 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawnsync-env.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (process.argv[2] === 'child') { + console.log(process.env.foo); +} else { + const expected = 'bar'; + const child = cp.spawnSync(process.execPath, [__filename, 'child'], { + env: Object.assign(process.env, { foo: expected }) + }); + + assert.strictEqual(child.stdout.toString().trim(), expected); +} diff --git a/test/js/node/test/parallel/test-child-process-spawnsync-input.js b/test/js/node/test/parallel/test-child-process-spawnsync-input.js new file mode 100644 index 0000000000..62ae476ae1 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawnsync-input.js @@ -0,0 +1,125 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); + +const spawnSync = require('child_process').spawnSync; + +const msgOut = 'this is stdout'; +const msgErr = 'this is stderr'; + +// This is actually not os.EOL? +const msgOutBuf = Buffer.from(`${msgOut}\n`); +const msgErrBuf = Buffer.from(`${msgErr}\n`); + +const args = [ + '-e', + `console.log("${msgOut}"); console.error("${msgErr}");`, +]; + +let ret; + + +function checkSpawnSyncRet(ret) { + assert.strictEqual(ret.status, 0); + assert.strictEqual(ret.error, undefined); +} + +function verifyBufOutput(ret) { + checkSpawnSyncRet(ret); + assert.deepStrictEqual(ret.stdout, msgOutBuf); + assert.deepStrictEqual(ret.stderr, msgErrBuf); +} + +if (process.argv.includes('spawnchild')) { + switch (process.argv[3]) { + case '1': + ret = spawnSync(process.execPath, args, { stdio: 'inherit' }); + checkSpawnSyncRet(ret); + break; + case '2': + ret = spawnSync(process.execPath, args, { + stdio: ['inherit', 'inherit', 'inherit'] + }); + checkSpawnSyncRet(ret); + break; + } + process.exit(0); + return; +} + +verifyBufOutput(spawnSync(process.execPath, [__filename, 'spawnchild', 1])); +verifyBufOutput(spawnSync(process.execPath, [__filename, 'spawnchild', 2])); + +let options = { + input: 1234 +}; + +assert.throws( + () => spawnSync('cat', [], options), + { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); + +options = { + input: 'hello world' +}; + +ret = spawnSync('cat', [], options); + +checkSpawnSyncRet(ret); +assert.strictEqual(ret.stdout.toString('utf8'), options.input); +assert.strictEqual(ret.stderr.toString('utf8'), ''); + +options = { + input: Buffer.from('hello world') +}; + +ret = spawnSync('cat', [], options); + +checkSpawnSyncRet(ret); +assert.deepStrictEqual(ret.stdout, options.input); +assert.deepStrictEqual(ret.stderr, Buffer.from('')); + +// common.getArrayBufferViews expects a buffer +// with length an multiple of 8 +const msgBuf = Buffer.from('hello world'.repeat(8)); +for (const arrayBufferView of common.getArrayBufferViews(msgBuf)) { + options = { + input: arrayBufferView + }; + + ret = spawnSync('cat', [], options); + + checkSpawnSyncRet(ret); + + assert.deepStrictEqual(ret.stdout, msgBuf); + assert.deepStrictEqual(ret.stderr, Buffer.from('')); +} + +verifyBufOutput(spawnSync(process.execPath, args)); + +ret = spawnSync(process.execPath, args, { encoding: 'utf8' }); + +checkSpawnSyncRet(ret); +assert.strictEqual(ret.stdout, `${msgOut}\n`); +assert.strictEqual(ret.stderr, `${msgErr}\n`); diff --git a/test/js/node/test/parallel/test-child-process-stdin-ipc.js b/test/js/node/test/parallel/test-child-process-stdin-ipc.js new file mode 100644 index 0000000000..945960b99b --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdin-ipc.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'child') { + // Just reference stdin, it should start it + process.stdin; // eslint-disable-line no-unused-expressions + return; +} + +const proc = spawn(process.execPath, [__filename, 'child'], { + stdio: ['ipc', 'inherit', 'inherit'] +}); + +proc.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-stdio-big-write-end.js b/test/js/node/test/parallel/test-child-process-stdio-big-write-end.js new file mode 100644 index 0000000000..85e6a8b321 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdio-big-write-end.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const debug = require('util').debuglog('test'); + +let bufsize = 0; + +switch (process.argv[2]) { + case undefined: + return parent(); + case 'child': + return child(); + default: + throw new Error('invalid'); +} + +function parent() { + const spawn = require('child_process').spawn; + const child = spawn(process.execPath, [__filename, 'child']); + let sent = 0; + + let n = ''; + child.stdout.setEncoding('ascii'); + child.stdout.on('data', mustCallAtLeast((c) => { + n += c; + })); + child.stdout.on('end', mustCall(() => { + assert.strictEqual(+n, sent); + debug('ok'); + })); + + // Write until the buffer fills up. + let buf; + do { + bufsize += 1024; + buf = Buffer.alloc(bufsize, '.'); + sent += bufsize; + } while (child.stdin.write(buf)); + + // Then write a bunch more times. + for (let i = 0; i < 100; i++) { + const buf = Buffer.alloc(bufsize, '.'); + sent += bufsize; + child.stdin.write(buf); + } + + // Now end, before it's all flushed. + child.stdin.end(); + + // now we wait... +} + +function child() { + let received = 0; + process.stdin.on('data', mustCallAtLeast((c) => { + received += c.length; + })); + process.stdin.on('end', mustCall(() => { + // This console.log is part of the test. + console.log(received); + })); +} diff --git a/test/js/node/test/parallel/vm-create-context-accessors.test.js b/test/js/node/test/parallel/test-child-process-stdio-inherit.js similarity index 60% rename from test/js/node/test/parallel/vm-create-context-accessors.test.js rename to test/js/node/test/parallel/test-child-process-stdio-inherit.js index 6f55c5f5af..034a077016 100644 --- a/test/js/node/test/parallel/vm-create-context-accessors.test.js +++ b/test/js/node/test/parallel/test-child-process-stdio-inherit.js @@ -1,6 +1,3 @@ -//#FILE: test-vm-create-context-accessors.js -//#SHA1: af7f3fb956333bd0669014a9e3c8f3f308efc68e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,33 +19,38 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const vm = require("vm"); +'use strict'; +require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; -test("vm.createContext with accessors", () => { - let ctx = {}; +if (process.argv[2] === 'parent') + parent(); +else + grandparent(); - Object.defineProperty(ctx, "getter", { - get: function () { - return "ok"; - }, +function grandparent() { + const child = spawn(process.execPath, [__filename, 'parent']); + child.stderr.pipe(process.stderr); + let output = ''; + const input = 'asdfasdf'; + + child.stdout.on('data', function(chunk) { + output += chunk; }); + child.stdout.setEncoding('utf8'); - let val; - Object.defineProperty(ctx, "setter", { - set: function (_val) { - val = _val; - }, - get: function () { - return `ok=${val}`; - }, + child.stdin.end(input); + + child.on('close', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + // 'cat' on windows adds a \r\n at the end. + assert.strictEqual(output.trim(), input.trim()); }); +} - ctx = vm.createContext(ctx); - - const result = vm.runInContext('setter = "test";[getter,setter]', ctx); - expect(result[0]).toBe("ok"); - expect(result[1]).toBe("ok=test"); -}); - -//<#END_FILE: test-vm-create-context-accessors.js +function parent() { + // Should not immediately exit. + spawn('cat', [], { stdio: 'inherit' }); +} diff --git a/test/js/node/test/parallel/child-process-stdio-overlapped.test.js b/test/js/node/test/parallel/test-child-process-stdio-overlapped.js similarity index 56% rename from test/js/node/test/parallel/child-process-stdio-overlapped.test.js rename to test/js/node/test/parallel/test-child-process-stdio-overlapped.js index e37232fce3..5c48e7ee10 100644 --- a/test/js/node/test/parallel/child-process-stdio-overlapped.test.js +++ b/test/js/node/test/parallel/test-child-process-stdio-overlapped.js @@ -1,6 +1,3 @@ -//#FILE: test-child-process-stdio-overlapped.js -//#SHA1: 20ead82f2ea74983af7bead33605fe8f33fccf6d -//----------------- // Test for "overlapped" stdio option. This test uses the "overlapped-checker" // helper program which basically a specialized echo program. // @@ -26,66 +23,57 @@ // - Extra assertion: Every time the test program writes a string to its stdout, // it will write the number of bytes written to stderr. // - If overlapped I/O is not setup correctly, this test is going to hang. -"use strict"; -const path = require("path"); -const child_process = require("child_process"); -const fs = require("fs"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const child_process = require('child_process'); -const exeExtension = process.platform === "win32" ? ".exe" : ""; -const exe = "overlapped-checker" + exeExtension; +const exeExtension = process.platform === 'win32' ? '.exe' : ''; +const exe = 'overlapped-checker' + exeExtension; const exePath = path.join(path.dirname(process.execPath), exe); -test("overlapped stdio option", async () => { - if (!fs.existsSync(exePath)) { - console.log(exe + " binary is not available"); +if (!require('fs').existsSync(exePath)) { + common.skip(exe + ' binary is not available'); +} + +const child = child_process.spawn(exePath, [], { + stdio: ['overlapped', 'pipe', 'pipe'] +}); + +child.stdin.setEncoding('utf8'); +child.stdout.setEncoding('utf8'); +child.stderr.setEncoding('utf8'); + +function writeNext(n) { + child.stdin.write((n + 50).toString()); +} + +child.stdout.on('data', (s) => { + const n = Number(s); + if (n >= 200) { + child.stdin.write('exit'); return; } + writeNext(n); +}); - const child = child_process.spawn(exePath, [], { - stdio: ["overlapped", "pipe", "pipe"], - }); - - child.stdin.setEncoding("utf8"); - child.stdout.setEncoding("utf8"); - child.stderr.setEncoding("utf8"); - - function writeNext(n) { - child.stdin.write((n + 50).toString()); - } - - child.stdout.on("data", s => { - const n = Number(s); - if (n >= 200) { - child.stdin.write("exit"); - return; - } - writeNext(n); - }); - - let stderr = ""; - child.stderr.on("data", s => { - stderr += s; - }); - - await new Promise(resolve => { - child.stderr.on("end", resolve); - }); +let stderr = ''; +child.stderr.on('data', (s) => { + stderr += s; +}); +child.stderr.on('end', common.mustCall(() => { // This is the sequence of numbers sent to us: // - 0 (1 byte written) // - 50 (2 bytes written) // - 100 (3 bytes written) // - 150 (3 bytes written) // - 200 (3 bytes written) - expect(stderr).toBe("12333"); + assert.strictEqual(stderr, '12333'); +})); - const exitPromise = new Promise(resolve => { - child.on("exit", resolve); - }); - - const status = await exitPromise; +child.on('exit', common.mustCall((status) => { // The test program will return the number of writes as status code. - expect(status).toBe(0); -}); - -//<#END_FILE: test-child-process-stdio-overlapped.js + assert.strictEqual(status, 0); +})); diff --git a/test/js/node/test/parallel/tls-client-abort.test.js b/test/js/node/test/parallel/test-child-process-stdout-flush-exit.js similarity index 54% rename from test/js/node/test/parallel/tls-client-abort.test.js rename to test/js/node/test/parallel/test-child-process-stdout-flush-exit.js index 4679c1f0e4..3c5f00d9bb 100644 --- a/test/js/node/test/parallel/tls-client-abort.test.js +++ b/test/js/node/test/parallel/test-child-process-stdout-flush-exit.js @@ -1,6 +1,3 @@ -//#FILE: test-tls-client-abort.js -//#SHA1: e4f4d09f8de79ff5f4bdefcdaf1bbebb49f3cc16 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,36 +19,38 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const assert = require('assert'); -const tls = require("tls"); -const fs = require("fs"); -const path = require("path"); - -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch { - return false; +// If child process output to console and exit +// The console.log statements here are part of the test. +if (process.argv[2] === 'child') { + console.log('hello'); + for (let i = 0; i < 200; i++) { + console.log('filler'); } -})(); - -if (!hasCrypto) { - test.skip("missing crypto", () => {}); + console.log('goodbye'); + process.exit(0); } else { - test("TLS client abort", () => { - const cert = fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "rsa_cert.crt")); - const key = fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "rsa_private.pem")); + // parent process + const spawn = require('child_process').spawn; - const onConnect = jest.fn(); - const conn = tls.connect({ cert, key, port: 0 }, onConnect); + // spawn self as child + const child = spawn(process.argv[0], [process.argv[1], 'child']); - conn.on("error", () => {}); // Expecting an error, but not testing its content - conn.destroy(); + let stdout = ''; - expect(onConnect).not.toHaveBeenCalled(); - }); + child.stderr.on('data', common.mustNotCall()); + + // Check if we receive both 'hello' at start and 'goodbye' at end + child.stdout.setEncoding('utf8'); + child.stdout.on('data', common.mustCallAtLeast((data) => { + stdout += data; + })); + + child.on('close', common.mustCall(() => { + assert.strictEqual(stdout.slice(0, 6), 'hello\n'); + assert.strictEqual(stdout.slice(stdout.length - 8), 'goodbye\n'); + })); } - -//<#END_FILE: test-tls-client-abort.js diff --git a/test/js/node/test/parallel/test-child-process-stdout-flush.js b/test/js/node/test/parallel/test-child-process-stdout-flush.js new file mode 100644 index 0000000000..bc549fb6f3 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdout-flush.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('print-chars.js'); + +const n = 500000; + +const child = spawn(process.argv[0], [sub, n]); + +let count = 0; + +child.stderr.setEncoding('utf8'); +child.stderr.on('data', common.mustNotCall()); + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + count += data.length; +}); + +child.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(n, count); +})); diff --git a/test/js/node/test/parallel/test-cli-eval-event.js b/test/js/node/test/parallel/test-cli-eval-event.js new file mode 100644 index 0000000000..df356e50d3 --- /dev/null +++ b/test/js/node/test/parallel/test-cli-eval-event.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, ['-e', ` + const server = require('net').createServer().listen(0); + server.once('listening', server.close); +`]); + +child.once('exit', common.mustCall(function(exitCode, signalCode) { + assert.strictEqual(exitCode, 0); + assert.strictEqual(signalCode, null); +})); diff --git a/test/js/node/test/parallel/test-client-request-destroy.js b/test/js/node/test/parallel/test-client-request-destroy.js new file mode 100644 index 0000000000..2f3efcf812 --- /dev/null +++ b/test/js/node/test/parallel/test-client-request-destroy.js @@ -0,0 +1,13 @@ +'use strict'; + +// Test that http.ClientRequest,prototype.destroy() returns `this`. +require('../common'); + +const assert = require('assert'); +const http = require('http'); +const clientRequest = new http.ClientRequest({ createConnection: () => {} }); + +assert.strictEqual(clientRequest.destroyed, false); +assert.strictEqual(clientRequest.destroy(), clientRequest); +assert.strictEqual(clientRequest.destroyed, true); +assert.strictEqual(clientRequest.destroy(), clientRequest); diff --git a/test/js/node/test/parallel/test-cluster-advanced-serialization.js b/test/js/node/test/parallel/test-cluster-advanced-serialization.js new file mode 100644 index 0000000000..ffca3a8f97 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-advanced-serialization.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + cluster.settings.serialization = 'advanced'; + const worker = cluster.fork(); + const circular = {}; + circular.circular = circular; + + worker.on('online', common.mustCall(() => { + worker.send(circular); + + worker.on('message', common.mustCall((msg) => { + assert.deepStrictEqual(msg, circular); + worker.kill(); + })); + })); +} else { + process.on('message', (msg) => process.send(msg)); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-bind-privileged-port.js b/test/js/node/test/parallel/test-cluster-bind-privileged-port.js similarity index 56% rename from test/js/node/cluster/upstream/parallel/test-cluster-bind-privileged-port.js rename to test/js/node/test/parallel/test-cluster-bind-privileged-port.js index f3a788984b..3ac36543a2 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-bind-privileged-port.js +++ b/test/js/node/test/parallel/test-cluster-bind-privileged-port.js @@ -19,19 +19,18 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -if (common.isLinux) return; // TODO: bun -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); -const { readFileSync } = require("fs"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); +const { readFileSync } = require('fs'); if (common.isLinux) { try { - const unprivilegedPortStart = parseInt(readFileSync("/proc/sys/net/ipv4/ip_unprivileged_port_start")); + const unprivilegedPortStart = parseInt(readFileSync('/proc/sys/net/ipv4/ip_unprivileged_port_start')); if (unprivilegedPortStart <= 42) { - common.skip("Port 42 is unprivileged"); + common.skip('Port 42 is unprivileged'); } } catch { // Do nothing, feature doesn't exist, minimum is 1024 so 42 is usable. @@ -39,30 +38,28 @@ if (common.isLinux) { } } -// Skip on OS X Mojave. https://github.com/nodejs/node/issues/21679 -if (common.isOSX) common.skip("macOS may allow ordinary processes to use any port"); +// Skip on macOS Mojave. https://github.com/nodejs/node/issues/21679 +if (common.isMacOS) + common.skip('macOS may allow ordinary processes to use any port'); -if (common.isIBMi) common.skip("IBMi may allow ordinary processes to use any port"); +if (common.isIBMi) + common.skip('IBMi may allow ordinary processes to use any port'); -if (common.isWindows) common.skip("not reliable on Windows."); +if (common.isWindows) + common.skip('not reliable on Windows.'); -if (process.getuid() === 0) common.skip("Test is not supposed to be run as root."); +if (process.getuid() === 0) + common.skip('Test is not supposed to be run as root.'); if (cluster.isPrimary) { - cluster.fork().on( - "exit", - common.mustCall(exitCode => { - assert.strictEqual(exitCode, 0); - }), - ); + cluster.fork().on('exit', common.mustCall((exitCode) => { + assert.strictEqual(exitCode, 0); + })); } else { const s = net.createServer(common.mustNotCall()); - s.listen(42, common.mustNotCall("listen should have failed")); - s.on( - "error", - common.mustCall(err => { - assert.strictEqual(err.code, "EACCES"); - process.disconnect(); - }), - ); + s.listen(42, common.mustNotCall('listen should have failed')); + s.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'EACCES'); + process.disconnect(); + })); } diff --git a/test/js/node/test/parallel/test-cluster-call-and-destroy.js b/test/js/node/test/parallel/test-cluster-call-and-destroy.js new file mode 100644 index 0000000000..2ff20cf03a --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-call-and-destroy.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + worker.on('disconnect', common.mustCall(() => { + assert.strictEqual(worker.isConnected(), false); + worker.destroy(); + })); +} else { + assert.strictEqual(cluster.worker.isConnected(), true); + cluster.worker.disconnect(); +} diff --git a/test/js/node/test/parallel/test-cluster-child-index-dgram.js b/test/js/node/test/parallel/test-cluster-child-index-dgram.js new file mode 100644 index 0000000000..0df7bc175b --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-child-index-dgram.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on Windows.'); + +const cluster = require('cluster'); +const dgram = require('dgram'); + +// Test an edge case when using `cluster` and `dgram.Socket.bind()` +// the port of `0`. +const kPort = 0; + +function child() { + const kTime = 2; + const countdown = new Countdown(kTime * 2, () => { + process.exit(0); + }); + for (let i = 0; i < kTime; i += 1) { + const socket = new dgram.Socket('udp4'); + socket.bind(kPort, common.mustCall(() => { + // `process.nextTick()` or `socket2.close()` would throw + // ERR_SOCKET_DGRAM_NOT_RUNNING + process.nextTick(() => { + socket.close(countdown.dec()); + const socket2 = new dgram.Socket('udp4'); + socket2.bind(kPort, common.mustCall(() => { + process.nextTick(() => { + socket2.close(countdown.dec()); + }); + })); + }); + })); + } +} + +if (cluster.isMaster) + cluster.fork(__filename); +else + child(); diff --git a/test/js/node/test/parallel/test-cluster-child-index-net.js b/test/js/node/test/parallel/test-cluster-child-index-net.js new file mode 100644 index 0000000000..d8c3166d1b --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-child-index-net.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); +const cluster = require('cluster'); +const net = require('net'); + +// Test an edge case when using `cluster` and `net.Server.listen()` to +// the port of `0`. +const kPort = 0; + +function child() { + const kTime = 2; + const countdown = new Countdown(kTime * 2, () => { + process.exit(0); + }); + for (let i = 0; i < kTime; i += 1) { + const server = net.createServer(); + server.listen(kPort, common.mustCall(() => { + server.close(countdown.dec()); + const server2 = net.createServer(); + server2.listen(kPort, common.mustCall(() => { + server2.close(countdown.dec()); + })); + })); + } +} + +if (cluster.isMaster) + cluster.fork(__filename); +else + child(); diff --git a/test/js/node/test/parallel/test-cluster-concurrent-disconnect.js b/test/js/node/test/parallel/test-cluster-concurrent-disconnect.js new file mode 100644 index 0000000000..b754fa221a --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-concurrent-disconnect.js @@ -0,0 +1,52 @@ +'use strict'; + +// Ref: https://github.com/nodejs/node/issues/32106 + +const common = require('../common'); + +const assert = require('assert'); +const cluster = require('cluster'); +const os = require('os'); + +if (cluster.isPrimary) { + const workers = []; + const numCPUs = os.availableParallelism(); + let waitOnline = numCPUs; + for (let i = 0; i < numCPUs; i++) { + const worker = cluster.fork(); + workers[i] = worker; + worker.once('online', common.mustCall(() => { + if (--waitOnline === 0) + for (const worker of workers) + if (worker.isConnected()) + worker.send(i % 2 ? 'disconnect' : 'destroy'); + })); + + // These errors can occur due to the nature of the test, we might be trying + // to send messages when the worker is disconnecting. + worker.on('error', (err) => { + assert.strictEqual(err.syscall, 'write'); + if (common.isMacOS) { + assert(['EPIPE', 'ENOTCONN'].includes(err.code), err); + } else { + assert(['EPIPE', 'ECONNRESET'].includes(err.code), err); + } + }); + + worker.once('disconnect', common.mustCall(() => { + for (const worker of workers) + if (worker.isConnected()) + worker.send('disconnect'); + })); + + worker.once('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + } +} else { + process.on('message', (msg) => { + if (cluster.worker.isConnected()) + cluster.worker[msg](); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-cwd.js b/test/js/node/test/parallel/test-cluster-cwd.js new file mode 100644 index 0000000000..c5d47e7301 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-cwd.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const tmpdir = require('../common/tmpdir'); + +if (cluster.isPrimary) { + tmpdir.refresh(); + + assert.strictEqual(cluster.settings.cwd, undefined); + cluster.fork().on('message', common.mustCall((msg) => { + assert.strictEqual(msg, process.cwd()); + })); + + cluster.setupPrimary({ cwd: tmpdir.path }); + assert.strictEqual(cluster.settings.cwd, tmpdir.path); + cluster.fork().on('message', common.mustCall((msg) => { + assert.strictEqual(msg, tmpdir.path); + })); +} else { + process.send(process.cwd()); + process.disconnect(); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-before-exit.js b/test/js/node/test/parallel/test-cluster-disconnect-before-exit.js similarity index 87% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-before-exit.js rename to test/js/node/test/parallel/test-cluster-disconnect-before-exit.js index bb5dda7aa7..f95f1384d5 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-before-exit.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-before-exit.js @@ -19,12 +19,12 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); if (cluster.isPrimary) { - const worker = cluster.fork().on("online", common.mustCall(disconnect)); + const worker = cluster.fork().on('online', common.mustCall(disconnect)); function disconnect() { worker.disconnect(); @@ -32,6 +32,6 @@ if (cluster.isPrimary) { // Disconnect is supposed to disconnect all workers, but not workers that // are already disconnected, since calling disconnect() on an already // disconnected worker would error. - worker.on("disconnect", common.mustCall(cluster.disconnect)); + worker.on('disconnect', common.mustCall(cluster.disconnect)); } } diff --git a/test/js/node/test/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js b/test/js/node/test/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js new file mode 100644 index 0000000000..f1a8dea0a9 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); + +// Test should fail in Node.js 5.4.1 and pass in later versions. + +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + cluster.on('exit', (worker, code) => { + assert.strictEqual(code, 0, `worker exited with code: ${code}, expected 0`); + }); + + return cluster.fork(); +} + +let eventFired = false; + +cluster.worker.disconnect(); + +process.nextTick(common.mustCall(() => { + assert.ok(!eventFired, 'disconnect event should wait for ack'); +})); + +cluster.worker.on('disconnect', common.mustCall(() => { + eventFired = true; +})); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-idle-worker.js b/test/js/node/test/parallel/test-cluster-disconnect-idle-worker.js similarity index 84% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-idle-worker.js rename to test/js/node/test/parallel/test-cluster-disconnect-idle-worker.js index f20bacdede..566f631312 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-idle-worker.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-idle-worker.js @@ -19,18 +19,16 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const fork = cluster.fork; if (cluster.isPrimary) { fork(); // It is intentionally called `fork` instead of fork(); // `cluster.fork` to test that `this` is not used - cluster.disconnect( - common.mustCall(() => { - assert.deepStrictEqual(Object.keys(cluster.workers), []); - }), - ); + cluster.disconnect(common.mustCall(() => { + assert.deepStrictEqual(Object.keys(cluster.workers), []); + })); } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-leak.js b/test/js/node/test/parallel/test-cluster-disconnect-leak.js similarity index 63% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-leak.js rename to test/js/node/test/parallel/test-cluster-disconnect-leak.js index d1b4812004..e2a417e5cd 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-leak.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-leak.js @@ -1,10 +1,10 @@ -"use strict"; +'use strict'; // Test fails in Node v5.4.0 and passes in v5.4.1 and newer. -const common = require("../common"); -const net = require("net"); -const cluster = require("cluster"); +const common = require('../common'); +const net = require('net'); +const cluster = require('cluster'); cluster.schedulingPolicy = cluster.SCHED_NONE; @@ -12,11 +12,11 @@ if (cluster.isPrimary) { const worker = cluster.fork(); // This is the important part of the test: Confirm that `disconnect` fires. - worker.on("disconnect", common.mustCall()); + worker.on('disconnect', common.mustCall()); // These are just some extra stuff we're checking for good measure... - worker.on("exit", common.mustCall()); - cluster.on("exit", common.mustCall()); + worker.on('exit', common.mustCall()); + cluster.on('exit', common.mustCall()); cluster.disconnect(); return; diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-with-no-workers.js b/test/js/node/test/parallel/test-cluster-disconnect-with-no-workers.js similarity index 88% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-with-no-workers.js rename to test/js/node/test/parallel/test-cluster-disconnect-with-no-workers.js index 865dd25f3f..a34e04973c 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-with-no-workers.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-with-no-workers.js @@ -19,18 +19,18 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); let disconnected; -process.on("exit", function () { +process.on('exit', function() { assert(disconnected); }); -cluster.disconnect(function () { +cluster.disconnect(function() { disconnected = true; }); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-fork-env.js b/test/js/node/test/parallel/test-cluster-fork-env.js similarity index 72% rename from test/js/node/cluster/upstream/parallel/test-cluster-fork-env.js rename to test/js/node/test/parallel/test-cluster-fork-env.js index 8bf9ef15d7..90b456b196 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-fork-env.js +++ b/test/js/node/test/parallel/test-cluster-fork-env.js @@ -19,48 +19,52 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); +'use strict'; +require('../common'); // This test checks that arguments provided to cluster.fork() will create // new environment variables and override existing environment variables // in the created worker process. -const assert = require("assert"); -const cluster = require("cluster"); +const assert = require('assert'); +const cluster = require('cluster'); if (cluster.isWorker) { const result = cluster.worker.send({ prop: process.env.cluster_test_prop, - overwrite: process.env.cluster_test_overwrite, + overwrite: process.env.cluster_test_overwrite }); assert.strictEqual(result, true); } else if (cluster.isPrimary) { + const checks = { using: false, - overwrite: false, + overwrite: false }; // To check that the cluster extend on the process.env we will overwrite a // property - process.env.cluster_test_overwrite = "old"; + process.env.cluster_test_overwrite = 'old'; // Fork worker const worker = cluster.fork({ - "cluster_test_prop": "custom", - "cluster_test_overwrite": "new", + 'cluster_test_prop': 'custom', + 'cluster_test_overwrite': 'new' }); // Checks worker env - worker.on("message", function (data) { - checks.using = data.prop === "custom"; - checks.overwrite = data.overwrite === "new"; + worker.on('message', function(data) { + checks.using = (data.prop === 'custom'); + checks.overwrite = (data.overwrite === 'new'); process.exit(0); }); - process.once("exit", function () { - assert.ok(checks.using, "The worker did not receive the correct env."); - assert.ok(checks.overwrite, "The custom environment did not overwrite the existing environment."); + process.once('exit', function() { + assert.ok(checks.using, 'The worker did not receive the correct env.'); + assert.ok( + checks.overwrite, + 'The custom environment did not overwrite the existing environment.'); }); + } diff --git a/test/js/node/test/parallel/test-cluster-fork-windowsHide.js b/test/js/node/test/parallel/test-cluster-fork-windowsHide.js new file mode 100644 index 0000000000..2b90713ceb --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-fork-windowsHide.js @@ -0,0 +1,74 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const cluster = require('cluster'); + +if (!process.argv[2]) { + // It seems Windows only allocate new console window for + // attaching processes spawned by detached processes. i.e. + // - If process D is spawned by process C with `detached: true`, + // and process W is spawned by process D with `detached: false`, + // W will get a new black console window popped up. + // - If D is spawned by C with `detached: false` or W is spawned + // by D with `detached: true`, no console window will pop up for W. + // + // So, we have to spawn a detached process first to run the actual test. + const primary = child_process.spawn( + process.argv[0], + [process.argv[1], '--cluster'], + { detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] }); + + const messageHandlers = { + workerOnline: common.mustCall(), + mainWindowHandle: common.mustCall((msg) => { + assert.match(msg.value, /0\s*/); + }), + workerExit: common.mustCall((msg) => { + assert.strictEqual(msg.code, 0); + assert.strictEqual(msg.signal, null); + }) + }; + + primary.on('message', (msg) => { + const handler = messageHandlers[msg.type]; + assert.ok(handler); + handler(msg); + }); + + primary.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + +} else if (cluster.isPrimary) { + cluster.setupPrimary({ + silent: true, + windowsHide: true + }); + + const worker = cluster.fork(); + worker.on('exit', (code, signal) => { + process.send({ type: 'workerExit', code: code, signal: signal }); + }); + + worker.on('online', (msg) => { + process.send({ type: 'workerOnline' }); + + let output = '0'; + if (process.platform === 'win32') { + output = child_process.execSync( + 'powershell -NoProfile -c ' + + `"(Get-Process -Id ${worker.process.pid}).MainWindowHandle"`, + { windowsHide: true, encoding: 'utf8' }); + } + + process.send({ type: 'mainWindowHandle', value: output }); + worker.send('shutdown'); + }); + +} else { + cluster.worker.on('message', (msg) => { + cluster.worker.disconnect(); + }); +} diff --git a/test/js/node/test/parallel/cluster-disconnect-idle-worker.test.js b/test/js/node/test/parallel/test-cluster-http-pipe.js similarity index 54% rename from test/js/node/test/parallel/cluster-disconnect-idle-worker.test.js rename to test/js/node/test/parallel/test-cluster-http-pipe.js index 217060fd12..bdd8fe8c4f 100644 --- a/test/js/node/test/parallel/cluster-disconnect-idle-worker.test.js +++ b/test/js/node/test/parallel/test-cluster-http-pipe.js @@ -1,6 +1,3 @@ -//#FILE: test-cluster-disconnect-idle-worker.js -//#SHA1: f559db612db77271be32bab2d7cb9a4e38f28670 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,28 +19,41 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const cluster = require("cluster"); -const fork = cluster.fork; +'use strict'; -if (cluster.isPrimary) { - test("cluster disconnect idle worker", async () => { - fork(); // It is intentionally called `fork` instead of - fork(); // `cluster.fork` to test that `this` is not used - - const disconnectCallback = jest.fn(() => { - expect(Object.keys(cluster.workers)).toEqual([]); - }); - - await new Promise(resolve => { - cluster.disconnect(() => { - disconnectCallback(); - resolve(); - }); - }); - - expect(disconnectCallback).toHaveBeenCalledTimes(1); - }); +const common = require('../common'); +if (common.isWindows) { + common.skip( + 'It is not possible to send pipe handles over the IPC pipe on Windows'); } -//<#END_FILE: test-cluster-disconnect-idle-worker.js +const assert = require('assert'); +const cluster = require('cluster'); +const http = require('http'); + +if (cluster.isPrimary) { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const worker = cluster.fork(); + worker.on('message', common.mustCall((msg) => { + assert.strictEqual(msg, 'DONE'); + })); + worker.on('exit', common.mustCall()); + return; +} + +http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.connection.remoteAddress, undefined); + assert.strictEqual(req.connection.localAddress, undefined); + + res.writeHead(200); + res.end('OK'); +})).listen(common.PIPE, common.mustCall(() => { + http.get({ socketPath: common.PIPE, path: '/' }, common.mustCall((res) => { + res.resume(); + res.on('end', common.mustSucceed(() => { + process.send('DONE'); + process.exit(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-cluster-invalid-message.js b/test/js/node/test/parallel/test-cluster-invalid-message.js new file mode 100644 index 0000000000..a42f5284db --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-invalid-message.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + + worker.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + + worker.on('online', () => { + worker.send({ + cmd: 'NODE_CLUSTER', + ack: -1 + }, () => { + worker.disconnect(); + }); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-ipc-throw.js b/test/js/node/test/parallel/test-cluster-ipc-throw.js new file mode 100644 index 0000000000..c9640a23fc --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-ipc-throw.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const cluster = require('cluster'); +const assert = require('assert'); + +cluster.schedulingPolicy = cluster.SCHED_RR; + +const server = http.createServer(); + +if (cluster.isPrimary) { + server.listen({ port: 0 }, common.mustCall(() => { + const worker = cluster.fork({ PORT: server.address().port }); + worker.on('exit', common.mustCall(() => { + server.close(); + })); + })); +} else { + assert(process.env.PORT); + process.on('uncaughtException', common.mustCall()); + server.listen(process.env.PORT); + server.on('error', common.mustCall((e) => { + cluster.worker.disconnect(); + throw e; + })); +} diff --git a/test/js/node/test/parallel/test-cluster-kill-disconnect.js b/test/js/node/test/parallel/test-cluster-kill-disconnect.js new file mode 100644 index 0000000000..3e1f2f0841 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-kill-disconnect.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +// Check that cluster works perfectly for both `kill` and `disconnect` cases. +// Also take into account that the `disconnect` event may be received after the +// `exit` event. +// https://github.com/nodejs/node/issues/3238 + +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + function forkWorker(action) { + const worker = cluster.fork({ action }); + worker.on('disconnect', common.mustCall(() => { + assert.strictEqual(worker.exitedAfterDisconnect, true); + })); + + worker.on('exit', common.mustCall(() => { + assert.strictEqual(worker.exitedAfterDisconnect, true); + })); + } + + forkWorker('disconnect'); + forkWorker('kill'); +} else { + cluster.worker[process.env.action](); +} diff --git a/test/js/node/test/parallel/test-cluster-kill-infinite-loop.js b/test/js/node/test/parallel/test-cluster-kill-infinite-loop.js new file mode 100644 index 0000000000..57781b6972 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-kill-infinite-loop.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + + worker.on('online', common.mustCall(() => { + // Use worker.process.kill() instead of worker.kill() because the latter + // waits for a graceful disconnect, which will never happen. + worker.process.kill(); + })); + + worker.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + })); +} else { + while (true); +} diff --git a/test/js/node/test/parallel/test-cluster-listening-port.js b/test/js/node/test/parallel/test-cluster-listening-port.js new file mode 100644 index 0000000000..c09134e1de --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-listening-port.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + cluster.fork(); + cluster.on('listening', common.mustCall(function(worker, address) { + const port = address.port; + // Ensure that the port is not 0 or null + assert(port); + // Ensure that the port is numerical + assert.strictEqual(typeof port, 'number'); + worker.kill(); + })); +} else { + net.createServer(common.mustNotCall()).listen(0); +} diff --git a/test/js/node/test/parallel/test-cluster-net-listen.js b/test/js/node/test/parallel/test-cluster-net-listen.js new file mode 100644 index 0000000000..9fa975aaaf --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-net-listen.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + // Ensure that the worker exits peacefully + cluster.fork().on('exit', common.mustCall(function(statusCode) { + assert.strictEqual(statusCode, 0); + })); +} else { + // listen() without port should not trigger a libuv assert + net.createServer(common.mustNotCall()).listen(process.exit); +} diff --git a/test/js/node/test/parallel/test-cluster-primary-error.js b/test/js/node/test/parallel/test-cluster-primary-error.js new file mode 100644 index 0000000000..f48682da4e --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-primary-error.js @@ -0,0 +1,104 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +const totalWorkers = 2; + +// Cluster setup +if (cluster.isWorker) { + const http = require('http'); + http.Server(() => {}).listen(0, '127.0.0.1'); +} else if (process.argv[2] === 'cluster') { + // Send PID to testcase process + let forkNum = 0; + cluster.on('fork', common.mustCall(function forkEvent(worker) { + // Send PID + process.send({ + cmd: 'worker', + workerPID: worker.process.pid + }); + + // Stop listening when done + if (++forkNum === totalWorkers) { + cluster.removeListener('fork', forkEvent); + } + }, totalWorkers)); + + // Throw accidental error when all workers are listening + let listeningNum = 0; + cluster.on('listening', common.mustCall(function listeningEvent() { + // When all workers are listening + if (++listeningNum === totalWorkers) { + // Stop listening + cluster.removeListener('listening', listeningEvent); + + // Throw accidental error + process.nextTick(() => { + throw new Error('accidental error'); + }); + } + }, totalWorkers)); + + // Startup a basic cluster + cluster.fork(); + cluster.fork(); +} else { + // This is the testcase + + const fork = require('child_process').fork; + + // List all workers + const workers = []; + + // Spawn a cluster process + const primary = fork(process.argv[1], ['cluster'], { silent: true }); + + // Handle messages from the cluster + primary.on('message', common.mustCall((data) => { + // Add worker pid to list and progress tracker + if (data.cmd === 'worker') { + workers.push(data.workerPID); + } + }, totalWorkers)); + + // When cluster is dead + primary.on('exit', common.mustCall((code) => { + // Check that the cluster died accidentally (non-zero exit code) + assert.strictEqual(code, 1); + + // XXX(addaleax): The fact that this uses raw PIDs makes the test inherently + // flaky – another process might end up being started right after the + // workers finished and receive the same PID. + const pollWorkers = () => { + // When primary is dead all workers should be dead too + if (workers.some((pid) => common.isAlive(pid))) { + setTimeout(pollWorkers, 50); + } + }; + + // Loop indefinitely until worker exit + pollWorkers(); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-primary-kill.js b/test/js/node/test/parallel/test-cluster-primary-kill.js similarity index 56% rename from test/js/node/cluster/upstream/parallel/test-cluster-primary-kill.js rename to test/js/node/test/parallel/test-cluster-primary-kill.js index 1a3a26f34d..08c7809603 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-primary-kill.js +++ b/test/js/node/test/parallel/test-cluster-primary-kill.js @@ -19,67 +19,71 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); if (cluster.isWorker) { + // Keep the worker alive - const http = require("http"); - http.Server().listen(0, "127.0.0.1"); -} else if (process.argv[2] === "cluster") { + const http = require('http'); + http.Server().listen(0, '127.0.0.1'); + +} else if (process.argv[2] === 'cluster') { + const worker = cluster.fork(); // send PID info to testcase process process.send({ - pid: worker.process.pid, + pid: worker.process.pid }); // Terminate the cluster process - worker.once( - "listening", - common.mustCall(() => { - setTimeout(() => { - process.exit(0); - }, 1000); - }), - ); + worker.once('listening', common.mustCall(() => { + setTimeout(() => { + process.exit(0); + }, 1000); + })); + } else { + // This is the testcase - const fork = require("child_process").fork; + const fork = require('child_process').fork; // Spawn a cluster process - const primary = fork(process.argv[1], ["cluster"]); + const primary = fork(process.argv[1], ['cluster']); // get pid info let pid = null; - primary.once("message", data => { + primary.once('message', (data) => { pid = data.pid; }); // When primary is dead let alive = true; - primary.on( - "exit", - common.mustCall(code => { - // Make sure that the primary died on purpose - assert.strictEqual(code, 0); + primary.on('exit', common.mustCall((code) => { - // Check worker process status - const pollWorker = () => { - alive = common.isAlive(pid); - if (alive) { - setTimeout(pollWorker, 50); - } - }; - // Loop indefinitely until worker exit. - pollWorker(); - }), - ); + // Make sure that the primary died on purpose + assert.strictEqual(code, 0); - process.once("exit", () => { - assert.strictEqual(typeof pid, "number", `got ${pid} instead of a worker pid`); - assert.strictEqual(alive, false, `worker was alive after primary died (alive = ${alive})`); + // Check worker process status + const pollWorker = () => { + alive = common.isAlive(pid); + if (alive) { + setTimeout(pollWorker, 50); + } + }; + // Loop indefinitely until worker exit. + pollWorker(); + })); + + process.once('exit', () => { + assert.strictEqual(typeof pid, 'number', + `got ${pid} instead of a worker pid`); + assert.strictEqual(alive, false, + `worker was alive after primary died (alive = ${alive})` + ); }); + } diff --git a/test/js/node/test/parallel/test-cluster-process-disconnect.js b/test/js/node/test/parallel/test-cluster-process-disconnect.js new file mode 100644 index 0000000000..378c4ef24d --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-process-disconnect.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + worker.on('exit', common.mustCall((code, signal) => { + assert.strictEqual( + code, + 0, + `Worker did not exit normally with code: ${code}` + ); + assert.strictEqual( + signal, + null, + `Worker did not exit normally with signal: ${signal}` + ); + })); +} else { + const net = require('net'); + const server = net.createServer(); + server.listen(0, common.mustCall(() => { + process.disconnect(); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-rr-domain-listen.js b/test/js/node/test/parallel/test-cluster-rr-domain-listen.js similarity index 85% rename from test/js/node/cluster/upstream/parallel/test-cluster-rr-domain-listen.js rename to test/js/node/test/parallel/test-cluster-rr-domain-listen.js index c48ed0c55c..6043535d3b 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-rr-domain-listen.js +++ b/test/js/node/test/parallel/test-cluster-rr-domain-listen.js @@ -19,10 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const cluster = require("cluster"); -const domain = require("domain"); +'use strict'; +require('../common'); +const cluster = require('cluster'); +const domain = require('domain'); // RR is the default for v0.11.9+ so the following line is redundant: // cluster.schedulingPolicy = cluster.SCHED_RR; @@ -31,16 +31,18 @@ if (cluster.isWorker) { const d = domain.create(); d.run(() => {}); - const http = require("http"); - http.Server(() => {}).listen(0, "127.0.0.1"); + const http = require('http'); + http.Server(() => {}).listen(0, '127.0.0.1'); + } else if (cluster.isPrimary) { + // Kill worker when listening - cluster.on("listening", function () { + cluster.on('listening', function() { worker.kill(); }); // Kill process when worker is killed - cluster.on("exit", function () { + cluster.on('exit', function() { process.exit(0); }); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-rr-handle-keep-loop-alive.js b/test/js/node/test/parallel/test-cluster-rr-handle-keep-loop-alive.js similarity index 53% rename from test/js/node/cluster/upstream/parallel/test-cluster-rr-handle-keep-loop-alive.js rename to test/js/node/test/parallel/test-cluster-rr-handle-keep-loop-alive.js index 8bb183af33..0b18408a19 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-rr-handle-keep-loop-alive.js +++ b/test/js/node/test/parallel/test-cluster-rr-handle-keep-loop-alive.js @@ -1,16 +1,16 @@ -"use strict"; +'use strict'; -const common = require("../common"); -const cluster = require("cluster"); -const net = require("net"); -const assert = require("assert"); +const common = require('../common'); +const cluster = require('cluster'); +const net = require('net'); +const assert = require('assert'); cluster.schedulingPolicy = cluster.SCHED_RR; if (cluster.isPrimary) { let exited = false; const worker = cluster.fork(); - worker.on("exit", () => { + worker.on('exit', () => { exited = true; }); setTimeout(() => { @@ -19,8 +19,5 @@ if (cluster.isPrimary) { }, 3000); } else { const server = net.createServer(common.mustNotCall()); - server.listen( - 0, - common.mustCall(() => process.channel.unref()), - ); + server.listen(0, common.mustCall(() => process.channel.unref())); } diff --git a/test/js/node/test/parallel/test-cluster-rr-ref.js b/test/js/node/test/parallel/test-cluster-rr-ref.js new file mode 100644 index 0000000000..92bb673ccd --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-rr-ref.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + cluster.fork().on('message', function(msg) { + if (msg === 'done') this.kill(); + }); +} else { + const server = net.createServer(common.mustNotCall()); + server.listen(0, function() { + server.unref(); + server.ref(); + server.close(function() { + process.send('done'); + }); + }); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-send-deadlock.js b/test/js/node/test/parallel/test-cluster-send-deadlock.js similarity index 62% rename from test/js/node/cluster/upstream/parallel/test-cluster-send-deadlock.js rename to test/js/node/test/parallel/test-cluster-send-deadlock.js index c5838a666c..8ddc40c252 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-send-deadlock.js +++ b/test/js/node/test/parallel/test-cluster-send-deadlock.js @@ -19,60 +19,55 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // Testing mutual send of handles: from primary to worker, and from worker to // primary. -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); if (cluster.isPrimary) { const worker = cluster.fork(); - worker.on("exit", (code, signal) => { + worker.on('exit', (code, signal) => { assert.strictEqual(code, 0, `Worker exited with an error code: ${code}`); assert(!signal, `Worker exited by a signal: ${signal}`); server.close(); }); - const server = net.createServer(socket => { - worker.send("handle", socket); + const server = net.createServer((socket) => { + worker.send('handle', socket); }); server.listen(0, () => { - worker.send({ message: "listen", port: server.address().port }); + worker.send({ message: 'listen', port: server.address().port }); }); } else { - process.on("message", (msg, handle) => { - if (msg.message && msg.message === "listen") { + process.on('message', (msg, handle) => { + if (msg.message && msg.message === 'listen') { assert(msg.port); - const client1 = net.connect( - { - host: "localhost", - port: msg.port, - }, - () => { - const client2 = net.connect( - { - host: "localhost", - port: msg.port, - }, - () => { - client1.on("close", onclose); - client2.on("close", onclose); - client1.end(); - client2.end(); - }, - ); - }, - ); + const client1 = net.connect({ + host: 'localhost', + port: msg.port + }, () => { + const client2 = net.connect({ + host: 'localhost', + port: msg.port + }, () => { + client1.on('close', onclose); + client2.on('close', onclose); + client1.end(); + client2.end(); + }); + }); let waiting = 2; const onclose = () => { - if (--waiting === 0) cluster.worker.disconnect(); + if (--waiting === 0) + cluster.worker.disconnect(); }; } else { - process.send("reply", handle); + process.send('reply', handle); } }); } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-argv.js b/test/js/node/test/parallel/test-cluster-setup-primary-argv.js similarity index 67% rename from test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-argv.js rename to test/js/node/test/parallel/test-cluster-setup-primary-argv.js index 8908aa7372..4c465bb72a 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-argv.js +++ b/test/js/node/test/parallel/test-cluster-setup-primary-argv.js @@ -19,23 +19,21 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); -setTimeout(common.mustNotCall("setup not emitted"), 1000).unref(); +setTimeout(common.mustNotCall('setup not emitted'), 1000).unref(); -cluster.on( - "setup", - common.mustCall(function () { - const clusterArgs = cluster.settings.args; - const realArgs = process.argv; - assert.strictEqual(clusterArgs[clusterArgs.length - 1], realArgs[realArgs.length - 1]); - }), -); +cluster.on('setup', common.mustCall(function() { + const clusterArgs = cluster.settings.args; + const realArgs = process.argv; + assert.strictEqual(clusterArgs[clusterArgs.length - 1], + realArgs[realArgs.length - 1]); +})); -assert.notStrictEqual(process.argv[process.argv.length - 1], "OMG,OMG"); -process.argv.push("OMG,OMG"); -process.argv.push("OMG,OMG"); +assert.notStrictEqual(process.argv[process.argv.length - 1], 'OMG,OMG'); +process.argv.push('OMG,OMG'); +process.argv.push('OMG,OMG'); cluster.setupPrimary(); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-cumulative.js b/test/js/node/test/parallel/test-cluster-setup-primary-cumulative.js similarity index 63% rename from test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-cumulative.js rename to test/js/node/test/parallel/test-cluster-setup-primary-cumulative.js index f9b43121fb..cf62291e9d 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-cumulative.js +++ b/test/js/node/test/parallel/test-cluster-setup-primary-cumulative.js @@ -19,10 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); assert(cluster.isPrimary); @@ -36,27 +36,27 @@ assert.deepStrictEqual(cluster.settings, { execArgv: process.execArgv, silent: false, }); -console.log("ok sets defaults"); +console.log('ok sets defaults'); -cluster.setupPrimary({ exec: "overridden" }); -assert.strictEqual(cluster.settings.exec, "overridden"); -console.log("ok overrides defaults"); +cluster.setupPrimary({ exec: 'overridden' }); +assert.strictEqual(cluster.settings.exec, 'overridden'); +console.log('ok overrides defaults'); -cluster.setupPrimary({ args: ["foo", "bar"] }); -assert.strictEqual(cluster.settings.exec, "overridden"); -assert.deepStrictEqual(cluster.settings.args, ["foo", "bar"]); +cluster.setupPrimary({ args: ['foo', 'bar'] }); +assert.strictEqual(cluster.settings.exec, 'overridden'); +assert.deepStrictEqual(cluster.settings.args, ['foo', 'bar']); -cluster.setupPrimary({ execArgv: ["baz", "bang"] }); -assert.strictEqual(cluster.settings.exec, "overridden"); -assert.deepStrictEqual(cluster.settings.args, ["foo", "bar"]); -assert.deepStrictEqual(cluster.settings.execArgv, ["baz", "bang"]); -console.log("ok preserves unchanged settings on repeated calls"); +cluster.setupPrimary({ execArgv: ['baz', 'bang'] }); +assert.strictEqual(cluster.settings.exec, 'overridden'); +assert.deepStrictEqual(cluster.settings.args, ['foo', 'bar']); +assert.deepStrictEqual(cluster.settings.execArgv, ['baz', 'bang']); +console.log('ok preserves unchanged settings on repeated calls'); cluster.setupPrimary(); assert.deepStrictEqual(cluster.settings, { - args: ["foo", "bar"], - exec: "overridden", - execArgv: ["baz", "bang"], + args: ['foo', 'bar'], + exec: 'overridden', + execArgv: ['baz', 'bang'], silent: false, }); -console.log("ok preserves current settings"); +console.log('ok preserves current settings'); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-emit.js b/test/js/node/test/parallel/test-cluster-setup-primary-emit.js similarity index 68% rename from test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-emit.js rename to test/js/node/test/parallel/test-cluster-setup-primary-emit.js index 305ebfced2..08414d5b21 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-emit.js +++ b/test/js/node/test/parallel/test-cluster-setup-primary-emit.js @@ -19,37 +19,29 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); assert(cluster.isPrimary); function emitAndCatch(next) { - cluster.once( - "setup", - common.mustCall(function (settings) { - assert.strictEqual(settings.exec, "new-exec"); - setImmediate(next); - }), - ); - cluster.setupPrimary({ exec: "new-exec" }); + cluster.once('setup', common.mustCall(function(settings) { + assert.strictEqual(settings.exec, 'new-exec'); + setImmediate(next); + })); + cluster.setupPrimary({ exec: 'new-exec' }); } function emitAndCatch2(next) { - cluster.once( - "setup", - common.mustCall(function (settings) { - assert("exec" in settings); - setImmediate(next); - }), - ); + cluster.once('setup', common.mustCall(function(settings) { + assert('exec' in settings); + setImmediate(next); + })); cluster.setupPrimary(); } -emitAndCatch( - common.mustCall(function () { - emitAndCatch2(common.mustCall()); - }), -); +emitAndCatch(common.mustCall(function() { + emitAndCatch2(common.mustCall()); +})); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-multiple.js b/test/js/node/test/parallel/test-cluster-setup-primary-multiple.js similarity index 83% rename from test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-multiple.js rename to test/js/node/test/parallel/test-cluster-setup-primary-multiple.js index 381642cf58..0fd8c0943c 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-multiple.js +++ b/test/js/node/test/parallel/test-cluster-setup-primary-multiple.js @@ -19,11 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const debug = require("util").debuglog("test"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const debug = require('util').debuglog('test'); assert(cluster.isPrimary); @@ -31,19 +31,23 @@ assert(cluster.isPrimary); // makes that unnecessary. This is to make the test less fragile if the // implementation ever changes such that cluster.settings is mutated instead of // replaced. -const cheapClone = obj => JSON.parse(JSON.stringify(obj)); +const cheapClone = (obj) => JSON.parse(JSON.stringify(obj)); const configs = []; // Capture changes -cluster.on("setup", () => { +cluster.on('setup', () => { debug(`"setup" emitted ${JSON.stringify(cluster.settings)}`); configs.push(cheapClone(cluster.settings)); }); -const execs = ["node-next", "node-next-2", "node-next-3"]; +const execs = [ + 'node-next', + 'node-next-2', + 'node-next-3', +]; -process.on("exit", () => { +process.on('exit', () => { // Tests that "setup" is emitted for every call to setupPrimary assert.strictEqual(configs.length, execs.length); @@ -61,9 +65,6 @@ execs.forEach((v, i) => { // Cluster emits 'setup' asynchronously, so we must stay alive long // enough for that to happen -setTimeout( - () => { - debug("cluster setup complete"); - }, - (execs.length + 1) * 100, -); +setTimeout(() => { + debug('cluster setup complete'); +}, (execs.length + 1) * 100); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary.js b/test/js/node/test/parallel/test-cluster-setup-primary.js similarity index 60% rename from test/js/node/cluster/upstream/parallel/test-cluster-setup-primary.js rename to test/js/node/test/parallel/test-cluster-setup-primary.js index ccb103cf08..efba017fd7 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary.js +++ b/test/js/node/test/parallel/test-cluster-setup-primary.js @@ -19,19 +19,22 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); if (cluster.isWorker) { + // Just keep the worker alive process.send(process.argv[2]); + } else if (cluster.isPrimary) { + const checks = { args: false, setupEvent: false, - settingsObject: false, + settingsObject: false }; const totalWorkers = 2; @@ -39,55 +42,52 @@ if (cluster.isWorker) { // Setup primary cluster.setupPrimary({ - args: ["custom argument"], - silent: true, + args: ['custom argument'], + silent: true }); - cluster.once("setup", function () { + cluster.once('setup', function() { checks.setupEvent = true; settings = cluster.settings; - if ( - settings && - settings.args && - settings.args[0] === "custom argument" && - settings.silent === true && - settings.exec === process.argv[1] - ) { + if (settings && + settings.args && settings.args[0] === 'custom argument' && + settings.silent === true && + settings.exec === process.argv[1]) { checks.settingsObject = true; } }); let correctInput = 0; - cluster.on( - "online", - common.mustCall(function listener(worker) { - worker.once("message", function (data) { - correctInput += data === "custom argument" ? 1 : 0; - if (correctInput === totalWorkers) { - checks.args = true; - } - worker.kill(); - }); - }, totalWorkers), - ); + cluster.on('online', common.mustCall(function listener(worker) { + + worker.once('message', function(data) { + correctInput += (data === 'custom argument' ? 1 : 0); + if (correctInput === totalWorkers) { + checks.args = true; + } + worker.kill(); + }); + + }, totalWorkers)); // Start all workers cluster.fork(); cluster.fork(); // Check all values - process.once("exit", function () { - const argsMsg = - "Arguments was not send for one or more worker. " + - `${correctInput} workers receive argument, ` + - `but ${totalWorkers} were expected.`; + process.once('exit', function() { + const argsMsg = 'Arguments was not send for one or more worker. ' + + `${correctInput} workers receive argument, ` + + `but ${totalWorkers} were expected.`; assert.ok(checks.args, argsMsg); - assert.ok(checks.setupEvent, "The setup event was never emitted"); + assert.ok(checks.setupEvent, 'The setup event was never emitted'); - const settingObjectMsg = "The settingsObject do not have correct " + `properties : ${JSON.stringify(settings)}`; + const settingObjectMsg = 'The settingsObject do not have correct ' + + `properties : ${JSON.stringify(settings)}`; assert.ok(checks.settingsObject, settingObjectMsg); }); + } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-shared-handle-bind-privileged-port.js b/test/js/node/test/parallel/test-cluster-shared-handle-bind-privileged-port.js similarity index 59% rename from test/js/node/cluster/upstream/parallel/test-cluster-shared-handle-bind-privileged-port.js rename to test/js/node/test/parallel/test-cluster-shared-handle-bind-privileged-port.js index e69c79d697..8bdde0a332 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-shared-handle-bind-privileged-port.js +++ b/test/js/node/test/parallel/test-cluster-shared-handle-bind-privileged-port.js @@ -19,40 +19,37 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -if (common.isLinux) return; // TODO: bun +'use strict'; +const common = require('../common'); -// Skip on OS X Mojave. https://github.com/nodejs/node/issues/21679 -if (common.isOSX) common.skip("macOS may allow ordinary processes to use any port"); +// Skip on macOS Mojave. https://github.com/nodejs/node/issues/21679 +if (common.isMacOS) + common.skip('macOS may allow ordinary processes to use any port'); -if (common.isIBMi) common.skip("IBMi may allow ordinary processes to use any port"); +if (common.isIBMi) + common.skip('IBMi may allow ordinary processes to use any port'); -if (common.isWindows) common.skip("not reliable on Windows"); +if (common.isWindows) + common.skip('not reliable on Windows'); -if (process.getuid() === 0) common.skip("as this test should not be run as `root`"); +if (process.getuid() === 0) + common.skip('as this test should not be run as `root`'); -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); if (cluster.isPrimary) { // Primary opens and binds the socket and shares it with the worker. cluster.schedulingPolicy = cluster.SCHED_NONE; - cluster.fork().on( - "exit", - common.mustCall(function (exitCode) { - assert.strictEqual(exitCode, 0); - }), - ); + cluster.fork().on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + })); } else { const s = net.createServer(common.mustNotCall()); - s.listen(42, common.mustNotCall("listen should have failed")); - s.on( - "error", - common.mustCall(function (err) { - assert.strictEqual(err.code, "EACCES"); - process.disconnect(); - }), - ); + s.listen(42, common.mustNotCall('listen should have failed')); + s.on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'EACCES'); + process.disconnect(); + })); } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-uncaught-exception.js b/test/js/node/test/parallel/test-cluster-uncaught-exception.js similarity index 71% rename from test/js/node/cluster/upstream/parallel/test-cluster-uncaught-exception.js rename to test/js/node/test/parallel/test-cluster-uncaught-exception.js index ee1dee617e..80d1ec6118 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-uncaught-exception.js +++ b/test/js/node/test/parallel/test-cluster-uncaught-exception.js @@ -19,38 +19,31 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // Installing a custom uncaughtException handler should override the default // one that the cluster module installs. // https://github.com/joyent/node/issues/2556 -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const fork = require("child_process").fork; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const fork = require('child_process').fork; const MAGIC_EXIT_CODE = 42; -const isTestRunner = process.argv[2] !== "child"; +const isTestRunner = process.argv[2] !== 'child'; if (isTestRunner) { - const primary = fork(__filename, ["child"]); - primary.on( - "exit", - common.mustCall(code => { - assert.strictEqual(code, MAGIC_EXIT_CODE); - }), - ); + const primary = fork(__filename, ['child']); + primary.on('exit', common.mustCall((code) => { + assert.strictEqual(code, MAGIC_EXIT_CODE); + })); } else if (cluster.isPrimary) { - process.on( - "uncaughtException", - common.mustCall(() => { - process.nextTick(() => process.exit(MAGIC_EXIT_CODE)); - }), - ); + process.on('uncaughtException', common.mustCall(() => { + process.nextTick(() => process.exit(MAGIC_EXIT_CODE)); + })); cluster.fork(); - throw new Error("kill primary"); -} else { - // worker + throw new Error('kill primary'); +} else { // worker process.exit(); } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-constructor.js b/test/js/node/test/parallel/test-cluster-worker-constructor.js similarity index 88% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-constructor.js rename to test/js/node/test/parallel/test-cluster-worker-constructor.js index 904eb2e2ab..c116e622e5 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-constructor.js +++ b/test/js/node/test/parallel/test-cluster-worker-constructor.js @@ -19,28 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // test-cluster-worker-constructor.js // validates correct behavior of the cluster.Worker constructor -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); let worker; worker = new cluster.Worker(); assert.strictEqual(worker.exitedAfterDisconnect, undefined); -assert.strictEqual(worker.state, "none"); +assert.strictEqual(worker.state, 'none'); assert.strictEqual(worker.id, 0); assert.strictEqual(worker.process, undefined); worker = new cluster.Worker({ id: 3, - state: "online", - process: process, + state: 'online', + process: process }); assert.strictEqual(worker.exitedAfterDisconnect, undefined); -assert.strictEqual(worker.state, "online"); +assert.strictEqual(worker.state, 'online'); assert.strictEqual(worker.id, 3); assert.strictEqual(worker.process, process); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-death.js b/test/js/node/test/parallel/test-cluster-worker-death.js similarity index 74% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-death.js rename to test/js/node/test/parallel/test-cluster-worker-death.js index bab5c8df8a..700cae7c52 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-death.js +++ b/test/js/node/test/parallel/test-cluster-worker-death.js @@ -19,26 +19,20 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); if (!cluster.isPrimary) { process.exit(42); } else { const worker = cluster.fork(); - worker.on( - "exit", - common.mustCall(function (exitCode, signalCode) { - assert.strictEqual(exitCode, 42); - assert.strictEqual(signalCode, null); - }), - ); - cluster.on( - "exit", - common.mustCall(function (worker_) { - assert.strictEqual(worker_, worker); - }), - ); + worker.on('exit', common.mustCall(function(exitCode, signalCode) { + assert.strictEqual(exitCode, 42); + assert.strictEqual(signalCode, null); + })); + cluster.on('exit', common.mustCall(function(worker_) { + assert.strictEqual(worker_, worker); + })); } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-destroy.js b/test/js/node/test/parallel/test-cluster-worker-destroy.js similarity index 85% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-destroy.js rename to test/js/node/test/parallel/test-cluster-worker-destroy.js index ebffb4fb04..91eee51a5a 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-destroy.js +++ b/test/js/node/test/parallel/test-cluster-worker-destroy.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // The goal of this test is to cover the Workers' implementation of // Worker.prototype.destroy. Worker.prototype.destroy is called within @@ -27,22 +27,22 @@ // primary, and another time when it's not connected to it, so that we cover // both code paths. -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); let worker1, worker2; if (cluster.isPrimary) { worker1 = cluster.fork(); worker2 = cluster.fork(); - [worker1, worker2].forEach(function (worker) { - worker.on("disconnect", common.mustCall()); - worker.on("exit", common.mustCall()); + [worker1, worker2].forEach(function(worker) { + worker.on('disconnect', common.mustCall()); + worker.on('exit', common.mustCall()); }); } else if (cluster.worker.id === 1) { // Call destroy when worker is disconnected - cluster.worker.process.on("disconnect", function () { + cluster.worker.process.on('disconnect', function() { cluster.worker.destroy(); }); diff --git a/test/js/node/test/parallel/test-cluster-worker-disconnect-on-error.js b/test/js/node/test/parallel/test-cluster-worker-disconnect-on-error.js new file mode 100644 index 0000000000..122d31a62e --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-disconnect-on-error.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const cluster = require('cluster'); +const assert = require('assert'); + +cluster.schedulingPolicy = cluster.SCHED_NONE; + +const server = http.createServer(); +if (cluster.isPrimary) { + let worker; + + server.listen(0, common.mustSucceed(() => { + assert(worker); + + worker.send({ port: server.address().port }); + })); + + worker = cluster.fork(); + worker.on('exit', common.mustCall(() => { + server.close(); + })); +} else { + process.on('message', common.mustCall((msg) => { + assert(msg.port); + + server.listen(msg.port); + server.on('error', common.mustCall((e) => { + cluster.worker.disconnect(); + })); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect.js b/test/js/node/test/parallel/test-cluster-worker-disconnect.js similarity index 51% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect.js rename to test/js/node/test/parallel/test-cluster-worker-disconnect.js index 35cae334d9..b28c0fbd8f 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect.js +++ b/test/js/node/test/parallel/test-cluster-worker-disconnect.js @@ -19,27 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); if (cluster.isWorker) { - const http = require("http"); - http.Server(() => {}).listen(0, "127.0.0.1"); + const http = require('http'); + http.Server(() => { + + }).listen(0, '127.0.0.1'); + + cluster.worker.on('disconnect', common.mustCall(() => { + process.exit(42); + })); - cluster.worker.on( - "disconnect", - common.mustCall(() => { - process.exit(42); - }), - ); } else if (cluster.isPrimary) { + const checks = { cluster: { emitDisconnect: false, emitExit: false, - callback: false, + callback: false }, worker: { emitDisconnect: false, @@ -47,72 +48,59 @@ if (cluster.isWorker) { emitExit: false, state: false, voluntaryMode: false, - died: false, - }, + died: false + } }; // start worker const worker = cluster.fork(); // Disconnect worker when it is ready - worker.once( - "listening", - common.mustCall(() => { - const w = worker.disconnect(); - assert.strictEqual(worker, w, `${worker.id} did not return a reference`); - }), - ); + worker.once('listening', common.mustCall(() => { + const w = worker.disconnect(); + assert.strictEqual(worker, w, `${worker.id} did not return a reference`); + })); // Check cluster events - cluster.once( - "disconnect", - common.mustCall(() => { - checks.cluster.emitDisconnect = true; - }), - ); - cluster.once( - "exit", - common.mustCall(() => { - checks.cluster.emitExit = true; - }), - ); + cluster.once('disconnect', common.mustCall(() => { + checks.cluster.emitDisconnect = true; + })); + cluster.once('exit', common.mustCall(() => { + checks.cluster.emitExit = true; + })); // Check worker events and properties - worker.once( - "disconnect", - common.mustCall(() => { - checks.worker.emitDisconnect = true; - checks.worker.voluntaryMode = worker.exitedAfterDisconnect; - checks.worker.state = worker.state; - }), - ); + worker.once('disconnect', common.mustCall(() => { + checks.worker.emitDisconnect = true; + checks.worker.voluntaryMode = worker.exitedAfterDisconnect; + checks.worker.state = worker.state; + })); // Check that the worker died - worker.once( - "exit", - common.mustCall(code => { - checks.worker.emitExit = true; - checks.worker.died = !common.isAlive(worker.process.pid); - checks.worker.emitDisconnectInsideWorker = code === 42; - }), - ); + worker.once('exit', common.mustCall((code) => { + checks.worker.emitExit = true; + checks.worker.died = !common.isAlive(worker.process.pid); + checks.worker.emitDisconnectInsideWorker = code === 42; + })); + + process.once('exit', () => { - process.once("exit", () => { const w = checks.worker; const c = checks.cluster; // events - assert.ok(w.emitDisconnect, "Disconnect event did not emit"); - assert.ok(w.emitDisconnectInsideWorker, "Disconnect event did not emit inside worker"); - assert.ok(c.emitDisconnect, "Disconnect event did not emit"); - assert.ok(w.emitExit, "Exit event did not emit"); - assert.ok(c.emitExit, "Exit event did not emit"); + assert.ok(w.emitDisconnect, 'Disconnect event did not emit'); + assert.ok(w.emitDisconnectInsideWorker, + 'Disconnect event did not emit inside worker'); + assert.ok(c.emitDisconnect, 'Disconnect event did not emit'); + assert.ok(w.emitExit, 'Exit event did not emit'); + assert.ok(c.emitExit, 'Exit event did not emit'); // flags - assert.strictEqual(w.state, "disconnected"); + assert.strictEqual(w.state, 'disconnected'); assert.strictEqual(w.voluntaryMode, true); // is process alive - assert.ok(w.died, "The worker did not die"); + assert.ok(w.died, 'The worker did not die'); }); } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-events.js b/test/js/node/test/parallel/test-cluster-worker-events.js similarity index 82% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-events.js rename to test/js/node/test/parallel/test-cluster-worker-events.js index aaf355a581..6c044ace8d 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-events.js +++ b/test/js/node/test/parallel/test-cluster-worker-events.js @@ -19,22 +19,23 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const OK = 2; if (cluster.isPrimary) { + const worker = cluster.fork(); - worker.on("exit", code => { + worker.on('exit', (code) => { assert.strictEqual(code, OK); process.exit(0); }); - const result = worker.send("SOME MESSAGE"); + const result = worker.send('SOME MESSAGE'); assert.strictEqual(result, true); return; @@ -50,28 +51,28 @@ let sawWorker; const messages = []; -const check = m => { +const check = (m) => { messages.push(m); if (messages.length < 2) return; assert.deepStrictEqual(messages[0], messages[1]); - cluster.worker.once("error", e => { - assert.strictEqual(e, "HI"); + cluster.worker.once('error', (e) => { + assert.strictEqual(e, 'HI'); process.exit(OK); }); - process.emit("error", "HI"); + process.emit('error', 'HI'); }; -process.on("message", m => { +process.on('message', (m) => { assert(!sawProcess); sawProcess = true; check(m); }); -cluster.worker.on("message", m => { +cluster.worker.on('message', (m) => { assert(!sawWorker); sawWorker = true; check(m); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-exit.js b/test/js/node/test/parallel/test-cluster-worker-exit.js similarity index 54% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-exit.js rename to test/js/node/test/parallel/test-cluster-worker-exit.js index e6e61ca604..09e2a83701 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-exit.js +++ b/test/js/node/test/parallel/test-cluster-worker-exit.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // test-cluster-worker-exit.js // verifies that, when a child process exits (by calling `process.exit(code)`) // - the primary receives the proper events in the proper order, no duplicates @@ -27,95 +27,85 @@ // - the worker.exitedAfterDisconnect flag, and worker.state are correct // - the worker process actually goes away -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const EXIT_CODE = 42; if (cluster.isWorker) { - const http = require("http"); - const server = http.Server(() => {}); + const http = require('http'); + const server = http.Server(() => { }); + + server.once('listening', common.mustCall(() => { + process.exit(EXIT_CODE); + })); + server.listen(0, '127.0.0.1'); - server.once( - "listening", - common.mustCall(() => { - process.exit(EXIT_CODE); - }), - ); - server.listen(0, "127.0.0.1"); } else if (cluster.isPrimary) { + const expected_results = { cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"], cluster_emitExit: [1, "the cluster did not emit 'exit'"], - cluster_exitCode: [EXIT_CODE, "the cluster exited w/ incorrect exitCode"], - cluster_signalCode: [null, "the cluster exited w/ incorrect signalCode"], + cluster_exitCode: [EXIT_CODE, 'the cluster exited w/ incorrect exitCode'], + cluster_signalCode: [null, 'the cluster exited w/ incorrect signalCode'], worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], worker_emitExit: [1, "the worker did not emit 'exit'"], - worker_state: ["disconnected", "the worker state is incorrect"], - worker_exitedAfterDisconnect: [false, "the .exitedAfterDisconnect flag is incorrect"], - worker_died: [true, "the worker is still running"], - worker_exitCode: [EXIT_CODE, "the worker exited w/ incorrect exitCode"], - worker_signalCode: [null, "the worker exited w/ incorrect signalCode"], + worker_state: ['disconnected', 'the worker state is incorrect'], + worker_exitedAfterDisconnect: [ + false, 'the .exitedAfterDisconnect flag is incorrect', + ], + worker_died: [true, 'the worker is still running'], + worker_exitCode: [EXIT_CODE, 'the worker exited w/ incorrect exitCode'], + worker_signalCode: [null, 'the worker exited w/ incorrect signalCode'] }; const results = { cluster_emitDisconnect: 0, cluster_emitExit: 0, worker_emitDisconnect: 0, - worker_emitExit: 0, + worker_emitExit: 0 }; + // start worker const worker = cluster.fork(); // Check cluster events - cluster.on( - "disconnect", - common.mustCall(() => { - results.cluster_emitDisconnect += 1; - }), - ); - cluster.on( - "exit", - common.mustCall(worker => { - results.cluster_exitCode = worker.process.exitCode; - results.cluster_signalCode = worker.process.signalCode; - results.cluster_emitExit += 1; - }), - ); + cluster.on('disconnect', common.mustCall(() => { + results.cluster_emitDisconnect += 1; + })); + cluster.on('exit', common.mustCall((worker) => { + results.cluster_exitCode = worker.process.exitCode; + results.cluster_signalCode = worker.process.signalCode; + results.cluster_emitExit += 1; + })); // Check worker events and properties - worker.on( - "disconnect", - common.mustCall(() => { - results.worker_emitDisconnect += 1; - results.worker_exitedAfterDisconnect = worker.exitedAfterDisconnect; - results.worker_state = worker.state; - if (results.worker_emitExit > 0) { - process.nextTick(() => finish_test()); - } - }), - ); + worker.on('disconnect', common.mustCall(() => { + results.worker_emitDisconnect += 1; + results.worker_exitedAfterDisconnect = worker.exitedAfterDisconnect; + results.worker_state = worker.state; + if (results.worker_emitExit > 0) { + process.nextTick(() => finish_test()); + } + })); // Check that the worker died - worker.once( - "exit", - common.mustCall((exitCode, signalCode) => { - results.worker_exitCode = exitCode; - results.worker_signalCode = signalCode; - results.worker_emitExit += 1; - results.worker_died = !common.isAlive(worker.process.pid); - if (results.worker_emitDisconnect > 0) { - process.nextTick(() => finish_test()); - } - }), - ); + worker.once('exit', common.mustCall((exitCode, signalCode) => { + results.worker_exitCode = exitCode; + results.worker_signalCode = signalCode; + results.worker_emitExit += 1; + results.worker_died = !common.isAlive(worker.process.pid); + if (results.worker_emitDisconnect > 0) { + process.nextTick(() => finish_test()); + } + })); const finish_test = () => { try { checkResults(expected_results, results); } catch (exc) { - if (exc.name !== "AssertionError") { + if (exc.name !== 'AssertionError') { console.trace(exc); } @@ -134,9 +124,7 @@ function checkResults(expected_results, results) { const expected = expected_results[k]; assert.strictEqual( - actual, - expected && expected.length ? expected[0] : expected, - `${expected[1] || ""} [expected: ${expected[0]} / actual: ${actual}]`, - ); + actual, expected && expected.length ? expected[0] : expected, + `${expected[1] || ''} [expected: ${expected[0]} / actual: ${actual}]`); } } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-forced-exit.js b/test/js/node/test/parallel/test-cluster-worker-forced-exit.js similarity index 76% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-forced-exit.js rename to test/js/node/test/parallel/test-cluster-worker-forced-exit.js index 901868973d..6d2bf4f537 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-forced-exit.js +++ b/test/js/node/test/parallel/test-cluster-worker-forced-exit.js @@ -19,10 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const SENTINEL = 42; @@ -37,7 +37,7 @@ const SENTINEL = 42; // 3 disconnect worker with child_process's disconnect, confirm // no sentinel value if (cluster.isWorker) { - process.on("disconnect", msg => { + process.on('disconnect', (msg) => { setTimeout(() => process.exit(SENTINEL), 10); }); return; @@ -49,27 +49,15 @@ checkForced(); function checkUnforced() { const worker = cluster.fork(); worker - .on( - "online", - common.mustCall(() => worker.disconnect()), - ) - .on( - "exit", - common.mustCall(status => { - assert.strictEqual(status, SENTINEL); - }), - ); + .on('online', common.mustCall(() => worker.disconnect())) + .on('exit', common.mustCall((status) => { + assert.strictEqual(status, SENTINEL); + })); } function checkForced() { const worker = cluster.fork(); worker - .on( - "online", - common.mustCall(() => worker.process.disconnect()), - ) - .on( - "exit", - common.mustCall(status => assert.strictEqual(status, 0)), - ); + .on('online', common.mustCall(() => worker.process.disconnect())) + .on('exit', common.mustCall((status) => assert.strictEqual(status, 0))); } diff --git a/test/js/node/test/parallel/test-cluster-worker-handle-close.js b/test/js/node/test/parallel/test-cluster-worker-handle-close.js new file mode 100644 index 0000000000..47a80ef1cd --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-handle-close.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + cluster.schedulingPolicy = cluster.SCHED_RR; + cluster.fork(); +} else { + const server = net.createServer(common.mustNotCall()); + server.listen(0, common.mustCall(() => { + net.connect(server.address().port); + })); + process.prependListener('internalMessage', common.mustCallAtLeast((message, handle) => { + if (message.act !== 'newconn') { + return; + } + // Make the worker drops the connection, see `rr` and `onconnection` in child.js + server.close(); + const close = handle.close; + handle.close = common.mustCall(() => { + close.call(handle, common.mustCall(() => { + process.exit(); + })); + }); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-init.js b/test/js/node/test/parallel/test-cluster-worker-init.js similarity index 78% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-init.js rename to test/js/node/test/parallel/test-cluster-worker-init.js index f2dafdf66d..2acc55c148 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-init.js +++ b/test/js/node/test/parallel/test-cluster-worker-init.js @@ -19,34 +19,31 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // test-cluster-worker-init.js // verifies that, when a child process is forked, the cluster.worker // object can receive messages as expected -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const msg = "foo"; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const msg = 'foo'; if (cluster.isPrimary) { const worker = cluster.fork(); - worker.on( - "message", - common.mustCall(message => { - assert.strictEqual(message, true); - const w = worker.disconnect(); - assert.strictEqual(worker, w); - }), - ); + worker.on('message', common.mustCall((message) => { + assert.strictEqual(message, true); + const w = worker.disconnect(); + assert.strictEqual(worker, w); + })); - worker.on("online", () => { + worker.on('online', () => { worker.send(msg); }); } else { // https://github.com/nodejs/node-v0.x-archive/issues/7998 - cluster.worker.on("message", message => { + cluster.worker.on('message', (message) => { process.send(message === msg); }); } diff --git a/test/js/node/test/parallel/test-cluster-worker-isdead.js b/test/js/node/test/parallel/test-cluster-worker-isdead.js new file mode 100644 index 0000000000..6f2aa3c52e --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-isdead.js @@ -0,0 +1,32 @@ +'use strict'; +require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + let workerDead = worker.isDead(); + assert.ok(!workerDead, + `isDead() returned ${workerDead}. isDead() should return ` + + 'false right after the worker has been created.'); + + worker.on('exit', function() { + workerDead = worker.isDead(); + assert.ok(workerDead, + `isDead() returned ${workerDead}. After an event has been ` + + 'emitted, isDead should return true'); + }); + + worker.on('message', function(msg) { + if (msg === 'readyToDie') { + worker.kill(); + } + }); + +} else if (cluster.isWorker) { + const workerDead = cluster.worker.isDead(); + assert.ok(!workerDead, + `isDead() returned ${workerDead}. isDead() should return ` + + 'false when called from within a worker'); + process.send('readyToDie'); +} diff --git a/test/js/node/test/parallel/test-cluster-worker-kill-signal.js b/test/js/node/test/parallel/test-cluster-worker-kill-signal.js new file mode 100644 index 0000000000..53e3739eba --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-kill-signal.js @@ -0,0 +1,49 @@ +'use strict'; +// test-cluster-worker-kill-signal.js +// verifies that when we're killing a worker using Worker.prototype.kill +// and the worker's process was killed with the given signal (SIGKILL) + + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + // Make the worker run something + const http = require('http'); + const server = http.Server(() => { }); + + server.once('listening', common.mustCall()); + server.listen(0, '127.0.0.1'); + +} else if (cluster.isMaster) { + const KILL_SIGNAL = 'SIGKILL'; + + // Start worker + const worker = cluster.fork(); + + // When the worker is up and running, kill it + worker.once('listening', common.mustCall(() => { + worker.kill(KILL_SIGNAL); + })); + + // Check worker events and properties + worker.on('disconnect', common.mustCall(() => { + assert.strictEqual(worker.exitedAfterDisconnect, false); + assert.strictEqual(worker.state, 'disconnected'); + }, 1)); + + // Check that the worker died + worker.once('exit', common.mustCall((exitCode, signalCode) => { + const isWorkerProcessStillAlive = common.isAlive(worker.process.pid); + const numOfRunningWorkers = Object.keys(cluster.workers).length; + + assert.strictEqual(exitCode, null); + assert.strictEqual(signalCode, KILL_SIGNAL); + assert.strictEqual(isWorkerProcessStillAlive, false); + assert.strictEqual(numOfRunningWorkers, 0); + }, 1)); + + // Check if the cluster was killed as well + cluster.on('exit', common.mustCall(1)); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill.js b/test/js/node/test/parallel/test-cluster-worker-kill.js similarity index 55% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-kill.js rename to test/js/node/test/parallel/test-cluster-worker-kill.js index 1ba588b874..7307a93e1b 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill.js +++ b/test/js/node/test/parallel/test-cluster-worker-kill.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // test-cluster-worker-kill.js // verifies that, when a child process is killed (we use SIGKILL) // - the primary receives the proper events in the proper order, no duplicates @@ -27,85 +27,78 @@ // - the worker.exitedAfterDisconnect flag, and worker.state are correct // - the worker process actually goes away -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); if (cluster.isWorker) { - const http = require("http"); - const server = http.Server(() => {}); - server.once("listening", common.mustCall()); - server.listen(0, "127.0.0.1"); + const http = require('http'); + const server = http.Server(() => { }); + + server.once('listening', common.mustCall()); + server.listen(0, '127.0.0.1'); + } else if (cluster.isPrimary) { - const KILL_SIGNAL = "SIGKILL"; + + const KILL_SIGNAL = 'SIGKILL'; const expected_results = { cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"], cluster_emitExit: [1, "the cluster did not emit 'exit'"], - cluster_exitCode: [null, "the cluster exited w/ incorrect exitCode"], - cluster_signalCode: [KILL_SIGNAL, "the cluster exited w/ incorrect signalCode"], + cluster_exitCode: [null, 'the cluster exited w/ incorrect exitCode'], + cluster_signalCode: [KILL_SIGNAL, + 'the cluster exited w/ incorrect signalCode'], worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], worker_emitExit: [1, "the worker did not emit 'exit'"], - worker_state: ["disconnected", "the worker state is incorrect"], - worker_exitedAfter: [false, "the .exitedAfterDisconnect flag is incorrect"], - worker_died: [true, "the worker is still running"], - worker_exitCode: [null, "the worker exited w/ incorrect exitCode"], - worker_signalCode: [KILL_SIGNAL, "the worker exited w/ incorrect signalCode"], + worker_state: ['disconnected', 'the worker state is incorrect'], + worker_exitedAfter: [false, 'the .exitedAfterDisconnect flag is incorrect'], + worker_died: [true, 'the worker is still running'], + worker_exitCode: [null, 'the worker exited w/ incorrect exitCode'], + worker_signalCode: [KILL_SIGNAL, + 'the worker exited w/ incorrect signalCode'] }; const results = { cluster_emitDisconnect: 0, cluster_emitExit: 0, worker_emitDisconnect: 0, - worker_emitExit: 0, + worker_emitExit: 0 }; + // start worker const worker = cluster.fork(); + // When the worker is up and running, kill it - worker.once( - "listening", - common.mustCall(() => { - worker.process.kill(KILL_SIGNAL); - }), - ); + worker.once('listening', common.mustCall(() => { + worker.process.kill(KILL_SIGNAL); + })); + // Check cluster events - cluster.on( - "disconnect", - common.mustCall(() => { - results.cluster_emitDisconnect += 1; - }), - ); - cluster.on( - "exit", - common.mustCall(worker => { - results.cluster_exitCode = worker.process.exitCode; - results.cluster_signalCode = worker.process.signalCode; - results.cluster_emitExit += 1; - }), - ); + cluster.on('disconnect', common.mustCall(() => { + results.cluster_emitDisconnect += 1; + })); + cluster.on('exit', common.mustCall((worker) => { + results.cluster_exitCode = worker.process.exitCode; + results.cluster_signalCode = worker.process.signalCode; + results.cluster_emitExit += 1; + })); // Check worker events and properties - worker.on( - "disconnect", - common.mustCall(() => { - results.worker_emitDisconnect += 1; - results.worker_exitedAfter = worker.exitedAfterDisconnect; - results.worker_state = worker.state; - }), - ); + worker.on('disconnect', common.mustCall(() => { + results.worker_emitDisconnect += 1; + results.worker_exitedAfter = worker.exitedAfterDisconnect; + results.worker_state = worker.state; + })); // Check that the worker died - worker.once( - "exit", - common.mustCall((exitCode, signalCode) => { - results.worker_exitCode = exitCode; - results.worker_signalCode = signalCode; - results.worker_emitExit += 1; - results.worker_died = !common.isAlive(worker.process.pid); - }), - ); + worker.once('exit', common.mustCall((exitCode, signalCode) => { + results.worker_exitCode = exitCode; + results.worker_signalCode = signalCode; + results.worker_emitExit += 1; + results.worker_died = !common.isAlive(worker.process.pid); + })); - process.on("exit", () => { + process.on('exit', () => { checkResults(expected_results, results); }); } @@ -118,9 +111,7 @@ function checkResults(expected_results, results) { const expected = expected_results[k]; assert.strictEqual( - actual, - expected && expected.length ? expected[0] : expected, - `${expected[1] || ""} [expected: ${expected[0]} / actual: ${actual}]`, - ); + actual, expected && expected.length ? expected[0] : expected, + `${expected[1] || ''} [expected: ${expected[0]} / actual: ${actual}]`); } } diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-no-exit.js b/test/js/node/test/parallel/test-cluster-worker-no-exit.js similarity index 64% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-no-exit.js rename to test/js/node/test/parallel/test-cluster-worker-no-exit.js index 8dcfc45f2c..e4694a4a3a 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-no-exit.js +++ b/test/js/node/test/parallel/test-cluster-worker-no-exit.js @@ -19,14 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -if (common.isOSX) return; // TODO: bun -if (common.isLinux) return; // TODO: bun -if (common.isWindows) return; // TODO: bun -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); let destroyed; let success; @@ -44,35 +41,33 @@ let server; // 4 destroy connection // 5 confirm it does exit if (cluster.isPrimary) { - server = net - .createServer(function (conn) { - server.close(); - worker.disconnect(); - worker - .once("disconnect", function () { - setTimeout(function () { - conn.destroy(); - destroyed = true; - }, 1000); - }) - .once("exit", function () { - // Worker should not exit while it has a connection - assert(destroyed, "worker exited before connection destroyed"); - success = true; - }); - }) - .listen(0, function () { - const port = this.address().port; + server = net.createServer(function(conn) { + server.close(); + worker.disconnect(); + worker.once('disconnect', function() { + setTimeout(function() { + conn.destroy(); + destroyed = true; + }, 1000); + }).once('exit', function() { + // Worker should not exit while it has a connection + assert(destroyed, 'worker exited before connection destroyed'); + success = true; + }); - worker = cluster.fork().on("online", function () { + }).listen(0, function() { + const port = this.address().port; + + worker = cluster.fork() + .on('online', function() { this.send({ port }); }); - }); - process.on("exit", function () { + }); + process.on('exit', function() { assert(success); }); } else { - process.on("message", function (msg) { + process.on('message', function(msg) { // We shouldn't exit, not while a network connection exists net.connect(msg.port); }); diff --git a/test/js/node/test/parallel/test-cluster-worker-wait-server-close.js b/test/js/node/test/parallel/test-cluster-worker-wait-server-close.js new file mode 100644 index 0000000000..71a8cacb52 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-wait-server-close.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +let serverClosed = false; + +if (cluster.isWorker) { + const server = net.createServer(function(socket) { + // Wait for any data, then close connection + socket.write('.'); + socket.on('data', () => {}); + }).listen(0, common.localhostIPv4); + + server.once('close', function() { + serverClosed = true; + }); + + // Although not typical, the worker process can exit before the disconnect + // event fires. Use this to keep the process open until the event has fired. + const keepOpen = setInterval(() => {}, 9999); + + // Check worker events and properties + process.once('disconnect', function() { + // Disconnect should occur after socket close + assert(serverClosed); + clearInterval(keepOpen); + }); +} else if (cluster.isPrimary) { + // start worker + const worker = cluster.fork(); + + // Disconnect worker when it is ready + worker.once('listening', function(address) { + const socket = net.createConnection(address.port, common.localhostIPv4); + + socket.on('connect', function() { + socket.on('data', function() { + console.log('got data from client'); + // Socket definitely connected to worker if we got data + worker.disconnect(); + socket.end(); + }); + }); + }); +} diff --git a/test/js/node/test/parallel/test-common-countdown.js b/test/js/node/test/parallel/test-common-countdown.js new file mode 100644 index 0000000000..d3c0daf599 --- /dev/null +++ b/test/js/node/test/parallel/test-common-countdown.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); +const fixtures = require('../common/fixtures'); +const { execFile } = require('child_process'); + +let done = ''; +const countdown = new Countdown(2, () => done = true); +assert.strictEqual(countdown.remaining, 2); +countdown.dec(); +assert.strictEqual(countdown.remaining, 1); +countdown.dec(); +assert.strictEqual(countdown.remaining, 0); +assert.strictEqual(done, true); + +const failFixtures = [ + [ + fixtures.path('failcounter.js'), + 'Mismatched function calls. Expected exactly 1, actual 0.', + ], +]; + +for (const p of failFixtures) { + const [file, expected] = p; + execFile(process.argv[0], [file], common.mustCall((ex, stdout, stderr) => { + assert.ok(ex); + assert.strictEqual(stderr, ''); + const firstLine = stdout.split('\n').shift(); + assert.strictEqual(firstLine, expected); + })); +} diff --git a/test/js/node/test/parallel/test-common-must-not-call.js b/test/js/node/test/parallel/test-common-must-not-call.js new file mode 100644 index 0000000000..b3c94a2390 --- /dev/null +++ b/test/js/node/test/parallel/test-common-must-not-call.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const util = require('util'); + +const message = 'message'; +const testFunction1 = common.mustNotCall(message); + +const testFunction2 = common.mustNotCall(message); + +const createValidate = (line, args = []) => common.mustCall((e) => { + const prefix = `${message} at `; + assert.ok(e.message.startsWith(prefix)); + if (process.platform === 'win32') { + e.message = e.message.substring(2); // remove 'C:' + } + const msg = e.message.substring(prefix.length); + const firstColon = msg.indexOf(':'); + const fileName = msg.substring(0, firstColon); + const rest = msg.substring(firstColon + 1); + assert.strictEqual(path.basename(fileName), 'test-common-must-not-call.js'); + const argsInfo = args.length > 0 ? + `\ncalled with arguments: ${args.map(util.inspect).join(', ')}` : ''; + assert.strictEqual(rest, line + argsInfo); +}); + +const validate1 = createValidate('9'); +try { + testFunction1(); +} catch (e) { + validate1(e); +} + +const validate2 = createValidate('11', ['hello', 42]); +try { + testFunction2('hello', 42); +} catch (e) { + validate2(e); +} + +assert.throws( + () => new Proxy({ prop: Symbol() }, { get: common.mustNotCall() }).prop, + { code: 'ERR_ASSERTION' } +); + +{ + const { inspect } = util; + delete util.inspect; + assert.throws( + () => common.mustNotCall()(null), + { code: 'ERR_ASSERTION' } + ); + util.inspect = inspect; +} diff --git a/test/js/node/test/parallel/test-console-assign-undefined.js b/test/js/node/test/parallel/test-console-assign-undefined.js new file mode 100644 index 0000000000..1021307b3c --- /dev/null +++ b/test/js/node/test/parallel/test-console-assign-undefined.js @@ -0,0 +1,28 @@ +'use strict'; + +// Patch global.console before importing modules that may modify the console +// object. + +const tmp = global.console; +global.console = 42; + +require('../common'); +const assert = require('assert'); + +// Originally the console had a getter. Test twice to verify it had no side +// effect. +assert.strictEqual(global.console, 42); +assert.strictEqual(global.console, 42); + +assert.throws( + () => console.log('foo'), + { name: 'TypeError' } +); + +global.console = 1; +assert.strictEqual(global.console, 1); +assert.strictEqual(console, 1); + +// Reset the console +global.console = tmp; +console.log('foo'); diff --git a/test/js/node/test/parallel/test-console-async-write-error.js b/test/js/node/test/parallel/test-console-async-write-error.js new file mode 100644 index 0000000000..be76c89832 --- /dev/null +++ b/test/js/node/test/parallel/test-console-async-write-error.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + process.nextTick(callback, new Error('foobar')); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. +} diff --git a/test/js/node/test/parallel/test-console-issue-43095.js b/test/js/node/test/parallel/test-console-issue-43095.js new file mode 100644 index 0000000000..647f4af2df --- /dev/null +++ b/test/js/node/test/parallel/test-console-issue-43095.js @@ -0,0 +1,12 @@ +'use strict'; + +require('../common'); +const { inspect } = require('node:util'); + +const r = Proxy.revocable({}, {}); +r.revoke(); + +console.dir(r); +console.dir(r.proxy); +console.log(r.proxy); +console.log(inspect(r.proxy, { showProxy: true })); diff --git a/test/js/node/test/parallel/test-console-log-stdio-broken-dest.js b/test/js/node/test/parallel/test-console-log-stdio-broken-dest.js new file mode 100644 index 0000000000..bdd1b79427 --- /dev/null +++ b/test/js/node/test/parallel/test-console-log-stdio-broken-dest.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); +const { Console } = require('console'); +const { EventEmitter } = require('events'); + +const stream = new Writable({ + write(chunk, enc, cb) { + cb(); + }, + writev(chunks, cb) { + setTimeout(cb, 10, new Error('kaboom')); + } +}); +const myConsole = new Console(stream, stream); + +process.on('warning', common.mustNotCall()); + +stream.cork(); +for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { + myConsole.log('a message'); +} +stream.uncork(); diff --git a/test/js/node/test/parallel/test-console-log-throw-primitive.js b/test/js/node/test/parallel/test-console-log-throw-primitive.js new file mode 100644 index 0000000000..a1a9ca2572 --- /dev/null +++ b/test/js/node/test/parallel/test-console-log-throw-primitive.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const { Writable } = require('stream'); +const { Console } = require('console'); + +const stream = new Writable({ + write() { + throw null; // eslint-disable-line no-throw-literal + } +}); + +const console = new Console({ stdout: stream }); + +console.log('test'); // Should not throw diff --git a/test/js/node/test/parallel/test-console-methods.js b/test/js/node/test/parallel/test-console-methods.js new file mode 100644 index 0000000000..d338cc1f80 --- /dev/null +++ b/test/js/node/test/parallel/test-console-methods.js @@ -0,0 +1,63 @@ +'use strict'; +require('../common'); + +// This test ensures that console methods cannot be invoked as constructors and +// that their name is always correct. + +const assert = require('assert'); + +const { Console } = console; +const newInstance = new Console(process.stdout); +const err = TypeError; + +const methods = [ + 'log', + 'warn', + 'dir', + 'time', + 'timeEnd', + 'timeLog', + 'trace', + 'assert', + 'clear', + 'count', + 'countReset', + 'group', + 'groupEnd', + 'table', + 'debug', + 'info', + 'dirxml', + 'error', + 'groupCollapsed', +]; + +const alternateNames = { + debug: 'log', + info: 'log', + dirxml: 'log', + error: 'warn', + groupCollapsed: 'group' +}; + +function assertEqualName(method) { + try { + assert.strictEqual(console[method].name, method); + } catch { + assert.strictEqual(console[method].name, alternateNames[method]); + } + try { + assert.strictEqual(newInstance[method].name, method); + } catch { + assert.strictEqual(newInstance[method].name, alternateNames[method]); + } +} + +for (const method of methods) { + assertEqualName(method); + + assert.throws(() => new console[method](), err); + assert.throws(() => new newInstance[method](), err); + assert.throws(() => Reflect.construct({}, [], console[method]), err); + assert.throws(() => Reflect.construct({}, [], newInstance[method]), err); +} diff --git a/test/js/node/test/parallel/test-console-not-call-toString.js b/test/js/node/test/parallel/test-console-not-call-toString.js new file mode 100644 index 0000000000..0f6f2624c5 --- /dev/null +++ b/test/js/node/test/parallel/test-console-not-call-toString.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +function func() {} +let toStringCalled = false; +func.toString = function() { + toStringCalled = true; +}; + +require('util').inspect(func); + +assert.ok(!toStringCalled); diff --git a/test/js/node/test/parallel/test-console-self-assign.js b/test/js/node/test/parallel/test-console-self-assign.js new file mode 100644 index 0000000000..53c54ab9a3 --- /dev/null +++ b/test/js/node/test/parallel/test-console-self-assign.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); + +// Assigning to itself should not throw. +global.console = global.console; // eslint-disable-line no-self-assign diff --git a/test/js/node/test/parallel/test-console-sync-write-error.js b/test/js/node/test/parallel/test-console-sync-write-error.js new file mode 100644 index 0000000000..bf916ff5b8 --- /dev/null +++ b/test/js/node/test/parallel/test-console-sync-write-error.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + callback(new Error('foobar')); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } + + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + throw new Error('foobar'); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } + + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + setImmediate(() => callback(new Error('foobar'))); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } +} diff --git a/test/js/node/test/parallel/test-coverage-with-inspector-disabled.js b/test/js/node/test/parallel/test-coverage-with-inspector-disabled.js new file mode 100644 index 0000000000..f2ba070859 --- /dev/null +++ b/test/js/node/test/parallel/test-coverage-with-inspector-disabled.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +if (process.features.inspector) { + common.skip('V8 inspector is enabled'); +} + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const env = { ...process.env, NODE_V8_COVERAGE: '/foo/bar' }; +const childPath = fixtures.path('v8-coverage/subprocess'); +const { status, stderr } = spawnSync( + process.execPath, + [childPath], + { env } +); + +const warningMessage = 'The inspector is disabled, ' + + 'coverage could not be collected'; + +assert.strictEqual(status, 0); +assert.strictEqual( + stderr.toString().includes(`Warning: ${warningMessage}`), + true +); diff --git a/test/js/node/test/parallel/test-crypto-dh-odd-key.js b/test/js/node/test/parallel/test-crypto-dh-odd-key.js new file mode 100644 index 0000000000..69a1eb56c8 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-dh-odd-key.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +function test() { + const odd = Buffer.alloc(39, 'A'); + + const c = crypto.createDiffieHellman(common.hasOpenSSL3 ? 1024 : 32); + c.setPrivateKey(odd); + c.generateKeys(); +} + +// FIPS requires a length of at least 1024 +if (!common.hasFipsCrypto) { + test(); +} else { + assert.throws(function() { test(); }, /key size too small/); +} diff --git a/test/js/node/test/parallel/test-crypto-dh-shared.js b/test/js/node/test/parallel/test-crypto-dh-shared.js new file mode 100644 index 0000000000..515405034d --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-dh-shared.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const alice = crypto.createDiffieHellmanGroup('modp5'); +const bob = crypto.createDiffieHellmanGroup('modp5'); +alice.generateKeys(); +bob.generateKeys(); +const aSecret = alice.computeSecret(bob.getPublicKey()).toString('hex'); +const bSecret = bob.computeSecret(alice.getPublicKey()).toString('hex'); +assert.strictEqual(aSecret, bSecret); diff --git a/test/js/node/test/parallel/test-crypto-domain.js b/test/js/node/test/parallel/test-crypto-domain.js new file mode 100644 index 0000000000..62e2be4c0f --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-domain.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const domain = require('domain'); + +const test = (fn) => { + const ex = new Error('BAM'); + const d = domain.create(); + d.on('error', common.mustCall(function(err) { + assert.strictEqual(err, ex); + })); + const cb = common.mustCall(function() { + throw ex; + }); + d.run(cb); +}; + +test(function(cb) { + crypto.pbkdf2('password', 'salt', 1, 8, cb); +}); + +test(function(cb) { + crypto.randomBytes(32, cb); +}); diff --git a/test/js/node/test/parallel/crypto-from-binary.test.js b/test/js/node/test/parallel/test-crypto-from-binary.js similarity index 56% rename from test/js/node/test/parallel/crypto-from-binary.test.js rename to test/js/node/test/parallel/test-crypto-from-binary.js index 3c015ec6b6..3b3c6a81ef 100644 --- a/test/js/node/test/parallel/crypto-from-binary.test.js +++ b/test/js/node/test/parallel/test-crypto-from-binary.js @@ -1,6 +1,3 @@ -//#FILE: test-crypto-from-binary.js -//#SHA1: 2f3b186e9b549c6910a58dea98e6bdeb7c540afa -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,45 +19,47 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // This is the same as test/simple/test-crypto, but from before the shift // to use buffers by default. -const crypto = require("crypto"); -const EXTERN_APEX = 0xfbee9; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const EXTERN_APEX = 0xFBEE9; // Manually controlled string for checking binary output -let ucs2_control = "a\u0000"; +let ucs2_control = 'a\u0000'; // Grow the strings to proper length while (ucs2_control.length <= EXTERN_APEX) { ucs2_control = ucs2_control.repeat(2); } + // Check resultant buffer and output string -const b = Buffer.from(ucs2_control + ucs2_control, "ucs2"); +const b = Buffer.from(ucs2_control + ucs2_control, 'ucs2'); -describe("Crypto from binary", () => { - beforeAll(() => { - if (!crypto) { - return test.skip("missing crypto"); - } - }); +// +// Test updating from birant data +// +{ + const datum1 = b.slice(700000); + const hash1_converted = crypto.createHash('sha1') + .update(datum1.toString('base64'), 'base64') + .digest('hex'); + const hash1_direct = crypto.createHash('sha1').update(datum1).digest('hex'); + assert.strictEqual(hash1_direct, hash1_converted); - test("Update from binary data - small slice", () => { - const datum1 = b.slice(700000); - const hash1_converted = crypto.createHash("sha1").update(datum1.toString("base64"), "base64").digest("hex"); - const hash1_direct = crypto.createHash("sha1").update(datum1).digest("hex"); - expect(hash1_direct).toBe(hash1_converted); - }); - - test("Update from binary data - full buffer", () => { - const datum2 = b; - const hash2_converted = crypto.createHash("sha1").update(datum2.toString("base64"), "base64").digest("hex"); - const hash2_direct = crypto.createHash("sha1").update(datum2).digest("hex"); - expect(hash2_direct).toBe(hash2_converted); - }); -}); - -//<#END_FILE: test-crypto-from-binary.js + const datum2 = b; + const hash2_converted = crypto.createHash('sha1') + .update(datum2.toString('base64'), 'base64') + .digest('hex'); + const hash2_direct = crypto.createHash('sha1').update(datum2).digest('hex'); + assert.strictEqual(hash2_direct, hash2_converted); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js b/test/js/node/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js new file mode 100644 index 0000000000..449d1a97f9 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); + +// Test async elliptic curve key generation with 'jwk' encoding and RSA. +{ + generateKeyPair('rsa', { + modulusLength: 1024, + publicKeyEncoding: { + format: 'jwk' + }, + privateKeyEncoding: { + format: 'jwk' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(publicKey.kty, 'RSA'); + assert.strictEqual(publicKey.kty, privateKey.kty); + assert.strictEqual(typeof publicKey.n, 'string'); + assert.strictEqual(publicKey.n, privateKey.n); + assert.strictEqual(typeof publicKey.e, 'string'); + assert.strictEqual(publicKey.e, privateKey.e); + assert.strictEqual(typeof privateKey.d, 'string'); + assert.strictEqual(typeof privateKey.p, 'string'); + assert.strictEqual(typeof privateKey.q, 'string'); + assert.strictEqual(typeof privateKey.dp, 'string'); + assert.strictEqual(typeof privateKey.dq, 'string'); + assert.strictEqual(typeof privateKey.qi, 'string'); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-encrypted-private-key-der.js b/test/js/node/test/parallel/test-crypto-keygen-async-encrypted-private-key-der.js new file mode 100644 index 0000000000..3203dfe16e --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-encrypted-private-key-der.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + assertApproximateSize, + testEncryptDecrypt, + testSignVerify, +} = require('../common/crypto'); + +// Test async RSA key generation with an encrypted private key, but encoded as DER. +{ + generateKeyPair('rsa', { + publicExponent: 0x10001, + modulusLength: 512, + publicKeyEncoding: { + type: 'pkcs1', + format: 'der' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'der' + } + }, common.mustSucceed((publicKeyDER, privateKeyDER) => { + assert(Buffer.isBuffer(publicKeyDER)); + assertApproximateSize(publicKeyDER, 74); + + assert(Buffer.isBuffer(privateKeyDER)); + + const publicKey = { + key: publicKeyDER, + type: 'pkcs1', + format: 'der', + }; + const privateKey = { + key: privateKeyDER, + format: 'der', + type: 'pkcs8', + passphrase: 'secret' + }; + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve.js b/test/js/node/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve.js new file mode 100644 index 0000000000..46223f08d7 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + testSignVerify, + spkiExp, + sec1Exp, +} = require('../common/crypto'); + +// Test async explicit elliptic curve key generation, e.g. for ECDSA, +// with a SEC1 private key with paramEncoding explicit. +{ + generateKeyPair('ec', { + namedCurve: 'prime256v1', + paramEncoding: 'explicit', + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'sec1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, spkiExp); + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, sec1Exp); + + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-named-elliptic-curve.js b/test/js/node/test/parallel/test-crypto-keygen-async-named-elliptic-curve.js new file mode 100644 index 0000000000..a1dfdbce1f --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-named-elliptic-curve.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + testSignVerify, + spkiExp, + sec1Exp, +} = require('../common/crypto'); + +// Test async named elliptic curve key generation, e.g. for ECDSA, +// with a SEC1 private key. +{ + generateKeyPair('ec', { + namedCurve: 'prime256v1', + paramEncoding: 'named', + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'sec1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, spkiExp); + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, sec1Exp); + + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-eddsa.js b/test/js/node/test/parallel/test-crypto-keygen-eddsa.js new file mode 100644 index 0000000000..5a097c2524 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-eddsa.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); + +// Test EdDSA key generation. +{ + if (!/^1\.1\.0/.test(process.versions.openssl)) { + ['ed25519', 'ed448', 'x25519', 'x448'].forEach((keyType) => { + generateKeyPair(keyType, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, keyType); + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {}); + + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, keyType); + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {}); + })); + }); + } +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-empty-passphrase-no-error.js b/test/js/node/test/parallel/test-crypto-keygen-empty-passphrase-no-error.js new file mode 100644 index 0000000000..6c7938f99e --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-empty-passphrase-no-error.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); + +// Passing an empty passphrase string should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE even on OpenSSL 3. +// Regression test for https://github.com/nodejs/node/issues/41428. +generateKeyPair('rsa', { + modulusLength: 1024, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: 'aes-256-cbc', + passphrase: '' + } +}, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.strictEqual(typeof privateKey, 'string'); +})); diff --git a/test/js/node/test/parallel/test-crypto-keygen-key-object-without-encoding.js b/test/js/node/test/parallel/test-crypto-keygen-key-object-without-encoding.js new file mode 100644 index 0000000000..abcd282871 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-key-object-without-encoding.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + testEncryptDecrypt, + testSignVerify, +} = require('../common/crypto'); + +// Tests key objects are returned when key encodings are not specified. +{ + // If no publicKeyEncoding is specified, a key object should be returned. + generateKeyPair('rsa', { + modulusLength: 1024, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); + + // The private key should still be a string. + assert.strictEqual(typeof privateKey, 'string'); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); + + // If no privateKeyEncoding is specified, a key object should be returned. + generateKeyPair('rsa', { + modulusLength: 1024, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + // The public key should still be a string. + assert.strictEqual(typeof publicKey, 'string'); + + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-key-objects.js b/test/js/node/test/parallel/test-crypto-keygen-key-objects.js new file mode 100644 index 0000000000..a0f1bdf2bc --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-key-objects.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPairSync, +} = require('crypto'); + +// Test sync key generation with key objects. +{ + const { publicKey, privateKey } = generateKeyPairSync('rsa', { + modulusLength: 512 + }); + + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 65537n + }); + + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 65537n + }); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-missing-oid.js b/test/js/node/test/parallel/test-crypto-keygen-missing-oid.js new file mode 100644 index 0000000000..f7fefe1384 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-missing-oid.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, + generateKeyPairSync, + getCurves, +} = require('crypto'); + +// This test creates EC key pairs on curves without associated OIDs. +// Specifying a key encoding should not crash. +{ + if (process.versions.openssl >= '1.1.1i') { + for (const namedCurve of ['Oakley-EC2N-3', 'Oakley-EC2N-4']) { + if (!getCurves().includes(namedCurve)) + continue; + + const expectedErrorCode = + common.hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID'; + const params = { + namedCurve, + publicKeyEncoding: { + format: 'der', + type: 'spki' + } + }; + + assert.throws(() => { + generateKeyPairSync('ec', params); + }, { + code: expectedErrorCode + }); + + generateKeyPair('ec', params, common.mustCall((err) => { + assert.strictEqual(err.code, expectedErrorCode); + })); + } + } +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-non-standard-public-exponent.js b/test/js/node/test/parallel/test-crypto-keygen-non-standard-public-exponent.js new file mode 100644 index 0000000000..f54a9e8a6d --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-non-standard-public-exponent.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPairSync, +} = require('crypto'); + +// Test sync key generation with key objects with a non-standard +// publicExponent +{ + const { publicKey, privateKey } = generateKeyPairSync('rsa', { + publicExponent: 3, + modulusLength: 512 + }); + + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 3n + }); + + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 3n + }); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-promisify.js b/test/js/node/test/parallel/test-crypto-keygen-promisify.js new file mode 100644 index 0000000000..cd6ca7d6e3 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-promisify.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + assertApproximateSize, + testEncryptDecrypt, + testSignVerify, + pkcs1PubExp, + pkcs1PrivExp, +} = require('../common/crypto'); +const { promisify } = require('util'); + +// Test the util.promisified API with async RSA key generation. +{ + promisify(generateKeyPair)('rsa', { + publicExponent: 0x10001, + modulusLength: 512, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } + }).then(common.mustCall((keys) => { + const { publicKey, privateKey } = keys; + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, pkcs1PubExp); + assertApproximateSize(publicKey, 180); + + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, pkcs1PrivExp); + assertApproximateSize(privateKey, 512); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-sync.js b/test/js/node/test/parallel/test-crypto-keygen-sync.js new file mode 100644 index 0000000000..a100379e21 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-sync.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPairSync, +} = require('crypto'); +const { + assertApproximateSize, + testEncryptDecrypt, + testSignVerify, + pkcs1PubExp, + pkcs8Exp, +} = require('../common/crypto'); + +// To make the test faster, we will only test sync key generation once and +// with a relatively small key. +{ + const ret = generateKeyPairSync('rsa', { + publicExponent: 3, + modulusLength: 512, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem' + } + }); + + assert.strictEqual(Object.keys(ret).length, 2); + const { publicKey, privateKey } = ret; + + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, pkcs1PubExp); + assertApproximateSize(publicKey, 162); + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, pkcs8Exp); + assertApproximateSize(privateKey, 512); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); +} diff --git a/test/js/node/test/parallel/test-crypto-lazy-transform-writable.js b/test/js/node/test/parallel/test-crypto-lazy-transform-writable.js new file mode 100644 index 0000000000..000c6693c8 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-lazy-transform-writable.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const Stream = require('stream'); + +const hasher1 = crypto.createHash('sha256'); +const hasher2 = crypto.createHash('sha256'); + +// Calculate the expected result. +hasher1.write(Buffer.from('hello world')); +hasher1.end(); + +const expected = hasher1.read().toString('hex'); + +class OldStream extends Stream { + constructor() { + super(); + this.readable = true; + } +} + +const stream = new OldStream(); + +stream.pipe(hasher2).on('finish', common.mustCall(function() { + const hash = hasher2.read().toString('hex'); + assert.strictEqual(hash, expected); +})); + +stream.emit('data', Buffer.from('hello')); +stream.emit('data', Buffer.from(' world')); +stream.emit('end'); diff --git a/test/js/node/test/parallel/test-crypto-no-algorithm.js b/test/js/node/test/parallel/test-crypto-no-algorithm.js new file mode 100644 index 0000000000..37db38cf61 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-no-algorithm.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.hasOpenSSL3) + common.skip('this test requires OpenSSL 3.x'); + +const assert = require('node:assert/strict'); +const crypto = require('node:crypto'); + +if (common.isMainThread) { + // TODO(richardlau): Decide if `crypto.setFips` should error if the + // provider named "fips" is not available. + crypto.setFips(1); + crypto.randomBytes(20, common.mustCall((err) => { + // crypto.randomBytes should either succeed or fail but not hang. + if (err) { + assert.match(err.message, /digital envelope routines::unsupported/); + const expected = /random number generator::unable to fetch drbg/; + assert(err.opensslErrorStack.some((msg) => expected.test(msg)), + `did not find ${expected} in ${err.opensslErrorStack}`); + } + })); +} + +{ + // Startup test. Should not hang. + const { path } = require('../common/fixtures'); + const { spawnSync } = require('node:child_process'); + const baseConf = path('openssl3-conf', 'base_only.cnf'); + const cp = spawnSync(process.execPath, + [ `--openssl-config=${baseConf}`, '-p', '"hello"' ], + { encoding: 'utf8' }); + assert(common.nodeProcessAborted(cp.status, cp.signal), + `process did not abort, code:${cp.status} signal:${cp.signal}`); +} diff --git a/test/js/node/test/parallel/test-crypto-op-during-process-exit.js b/test/js/node/test/parallel/test-crypto-op-during-process-exit.js new file mode 100644 index 0000000000..a9a70c39e0 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-op-during-process-exit.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const { generateKeyPair } = require('crypto'); + +if (common.isWindows) { + // Remove this conditional once the libuv change is in Node.js. + common.skip('crashing due to https://github.com/libuv/libuv/pull/2983'); +} + +// Regression test for a race condition: process.exit() might lead to OpenSSL +// cleaning up state from the exit() call via calling its destructor, but +// running OpenSSL operations on another thread might lead to them attempting +// to initialize OpenSSL, leading to a crash. +// This test crashed consistently on x64 Linux on Node v14.9.0. + +generateKeyPair('rsa', { + modulusLength: 2048, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } +}, (err/* , publicKey, privateKey */) => { + assert.ifError(err); +}); + +setTimeout(() => process.exit(), common.platformTimeout(10)); diff --git a/test/js/node/test/parallel/test-crypto-padding-aes256.js b/test/js/node/test/parallel/test-crypto-padding-aes256.js new file mode 100644 index 0000000000..14d853bdfd --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-padding-aes256.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const iv = Buffer.from('00000000000000000000000000000000', 'hex'); +const key = Buffer.from('0123456789abcdef0123456789abcdef' + + '0123456789abcdef0123456789abcdef', 'hex'); + +function encrypt(val, pad) { + const c = crypto.createCipheriv('aes256', key, iv); + c.setAutoPadding(pad); + return c.update(val, 'utf8', 'latin1') + c.final('latin1'); +} + +function decrypt(val, pad) { + const c = crypto.createDecipheriv('aes256', key, iv); + c.setAutoPadding(pad); + return c.update(val, 'latin1', 'utf8') + c.final('utf8'); +} + +// echo 0123456789abcdef0123456789abcdef \ +// | openssl enc -e -aes256 -nopad -K -iv \ +// | openssl enc -d -aes256 -nopad -K -iv +let plaintext = '0123456789abcdef0123456789abcdef'; // Multiple of block size +let encrypted = encrypt(plaintext, false); +let decrypted = decrypt(encrypted, false); +assert.strictEqual(decrypted, plaintext); + +// echo 0123456789abcdef0123456789abcde \ +// | openssl enc -e -aes256 -K -iv \ +// | openssl enc -d -aes256 -K -iv +plaintext = '0123456789abcdef0123456789abcde'; // not a multiple +encrypted = encrypt(plaintext, true); +decrypted = decrypt(encrypted, true); +assert.strictEqual(decrypted, plaintext); diff --git a/test/js/node/test/parallel/test-crypto-publicDecrypt-fails-first-time.js b/test/js/node/test/parallel/test-crypto-publicDecrypt-fails-first-time.js new file mode 100644 index 0000000000..a60b87dbf6 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-publicDecrypt-fails-first-time.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); + +// Test for https://github.com/nodejs/node/issues/40814 + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.hasOpenSSL3) + common.skip('only openssl3'); // https://github.com/nodejs/node/pull/42793#issuecomment-1107491901 + +const assert = require('assert'); +const crypto = require('crypto'); + +const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: 'aes-128-ecb', + passphrase: 'abcdef' + } +}); +assert.notStrictEqual(privateKey.toString(), ''); + +const msg = 'The quick brown fox jumps over the lazy dog'; + +const encryptedString = crypto.privateEncrypt({ + key: privateKey, + passphrase: 'abcdef' +}, Buffer.from(msg)).toString('base64'); +const decryptedString = crypto.publicDecrypt(publicKey, Buffer.from(encryptedString, 'base64')).toString(); +console.log(`Encrypted: ${encryptedString}`); +console.log(`Decrypted: ${decryptedString}`); + +assert.notStrictEqual(encryptedString, ''); +assert.strictEqual(decryptedString, msg); diff --git a/test/js/node/test/parallel/test-crypto-randomfillsync-regression.js b/test/js/node/test/parallel/test-crypto-randomfillsync-regression.js new file mode 100644 index 0000000000..7a37864258 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-randomfillsync-regression.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { randomFillSync } = require('crypto'); +const { notStrictEqual } = require('assert'); + +const ab = new ArrayBuffer(20); +const buf = Buffer.from(ab, 10); + +const before = buf.toString('hex'); + +randomFillSync(buf); + +const after = buf.toString('hex'); + +notStrictEqual(before, after); diff --git a/test/js/node/test/parallel/test-crypto-subtle-zero-length.js b/test/js/node/test/parallel/test-crypto-subtle-zero-length.js new file mode 100644 index 0000000000..f5a84727b0 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-subtle-zero-length.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +(async () => { + const k = await subtle.importKey( + 'raw', + new Uint8Array(32), + { name: 'AES-GCM' }, + false, + [ 'encrypt', 'decrypt' ]); + assert(k instanceof CryptoKey); + + const e = await subtle.encrypt({ + name: 'AES-GCM', + iv: new Uint8Array(12), + }, k, new Uint8Array(0)); + assert(e instanceof ArrayBuffer); + assert.deepStrictEqual( + Buffer.from(e), + Buffer.from([ + 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, + 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b ])); + + const v = await subtle.decrypt({ + name: 'AES-GCM', + iv: new Uint8Array(12), + }, k, e); + assert(v instanceof ArrayBuffer); + assert.strictEqual(v.byteLength, 0); +})().then(common.mustCall()).catch((e) => { + assert.ifError(e); +}); diff --git a/test/js/node/test/parallel/test-crypto-update-encoding.js b/test/js/node/test/parallel/test-crypto-update-encoding.js new file mode 100644 index 0000000000..e1e6d029aa --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-update-encoding.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const crypto = require('crypto'); + +const zeros = Buffer.alloc; +const key = zeros(16); +const iv = zeros(16); + +const cipher = () => crypto.createCipheriv('aes-128-cbc', key, iv); +const decipher = () => crypto.createDecipheriv('aes-128-cbc', key, iv); +const hash = () => crypto.createSign('sha256'); +const hmac = () => crypto.createHmac('sha256', key); +const sign = () => crypto.createSign('sha256'); +const verify = () => crypto.createVerify('sha256'); + +for (const f of [cipher, decipher, hash, hmac, sign, verify]) + for (const n of [15, 16]) + f().update(zeros(n), 'hex'); // Should ignore inputEncoding. diff --git a/test/js/node/test/parallel/test-datetime-change-notify.js b/test/js/node/test/parallel/test-datetime-change-notify.js new file mode 100644 index 0000000000..0184351190 --- /dev/null +++ b/test/js/node/test/parallel/test-datetime-change-notify.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const { isMainThread } = require('worker_threads'); + +if (!common.hasIntl) + common.skip('Intl not present.'); + +if (!isMainThread) + common.skip('Test not support running within a worker'); + +const assert = require('assert'); + +const cases = [ + { + timeZone: 'Etc/UTC', + expected: /Coordinated Universal Time/, + }, + { + timeZone: 'America/New_York', + expected: /Eastern (?:Standard|Daylight) Time/, + }, + { + timeZone: 'America/Los_Angeles', + expected: /Pacific (?:Standard|Daylight) Time/, + }, + { + timeZone: 'Europe/Dublin', + expected: /Irish Standard Time|Greenwich Mean Time/, + }, +]; + +for (const { timeZone, expected } of cases) { + process.env.TZ = timeZone; + const date = new Date().toLocaleString('en-US', { timeZoneName: 'long' }); + assert.match(date, expected); +} diff --git a/test/js/node/test/parallel/test-debugger-backtrace.js b/test/js/node/test/parallel/test-debugger-backtrace.js new file mode 100644 index 0000000000..f66cc11d70 --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-backtrace.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +const assert = require('assert'); +const path = require('path'); + +// Display and navigate backtrace. +{ + const scriptFullPath = fixtures.path('debugger', 'backtrace.js'); + const script = path.relative(process.cwd(), scriptFullPath); + const cli = startCLI(['--port=0', script]); + + async function runTest() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.stepCommand('c'); + await cli.command('bt'); + assert.ok(cli.output.includes(`#0 topFn ${script}:7:2`)); + await cli.command('backtrace'); + assert.ok(cli.output.includes(`#0 topFn ${script}:7:2`)); + } finally { + await cli.quit(); + } + } + + runTest(); +} diff --git a/test/js/node/test/parallel/test-debugger-exec.js b/test/js/node/test/parallel/test-debugger-exec.js new file mode 100644 index 0000000000..51bc749734 --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-exec.js @@ -0,0 +1,68 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +const assert = require('assert'); + +const cli = startCLI(['--port=0', fixtures.path('debugger/alive.js')]); + +async function waitInitialBreak() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.command('exec [typeof heartbeat, typeof process.exit]'); + assert.match(cli.output, /\[ 'function', 'function' \]/, 'works w/o paren'); + + await cli.command('p [typeof heartbeat, typeof process.exit]'); + assert.match( + cli.output, + /\[ 'function', 'function' \]/, + 'works w/o paren, short' + ); + + await cli.command('repl'); + assert.match( + cli.output, + /Press Ctrl\+C to leave debug repl\n+> /, + 'shows hint for how to leave repl' + ); + assert.doesNotMatch(cli.output, /debug>/, 'changes the repl style'); + + await cli.command('[typeof heartbeat, typeof process.exit]'); + await cli.waitFor(/function/); + await cli.waitForPrompt(); + assert.match( + cli.output, + /\[ 'function', 'function' \]/, + 'can evaluate in the repl' + ); + assert.match(cli.output, /> $/); + + await cli.ctrlC(); + await cli.waitFor(/debug> $/); + await cli.command('exec("[typeof heartbeat, typeof process.exit]")'); + assert.match(cli.output, /\[ 'function', 'function' \]/, 'works w/ paren'); + await cli.command('p("[typeof heartbeat, typeof process.exit]")'); + assert.match( + cli.output, + /\[ 'function', 'function' \]/, + 'works w/ paren, short' + ); + + await cli.command('cont'); + await cli.command('exec [typeof heartbeat, typeof process.exit]'); + assert.match( + cli.output, + /\[ 'undefined', 'function' \]/, + 'non-paused exec can see global but not module-scope values' + ); + } finally { + await cli.quit(); + } +} + +waitInitialBreak(); diff --git a/test/js/node/test/parallel/test-debugger-invalid-json.mjs b/test/js/node/test/parallel/test-debugger-invalid-json.mjs new file mode 100644 index 0000000000..e4754a465f --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-invalid-json.mjs @@ -0,0 +1,46 @@ +import { skipIfInspectorDisabled, mustCall } from '../common/index.mjs'; + +skipIfInspectorDisabled(); + +import startCLI from '../common/debugger.js'; + +import assert from 'assert'; +import http from 'http'; + +const host = '127.0.0.1'; + +{ + const server = http.createServer((req, res) => { + res.statusCode = 400; + res.end('Bad Request'); + }); + + server.listen(0, mustCall(async () => { + const port = server.address().port; + const cli = startCLI([`${host}:${port}`]); + try { + const code = await cli.quit(); + assert.strictEqual(code, 1); + } finally { + server.close(); + } + })); +} + +{ + const server = http.createServer((req, res) => { + res.statusCode = 200; + res.end('some data that is invalid json'); + }); + + server.listen(0, host, mustCall(async () => { + const port = server.address().port; + const cli = startCLI([`${host}:${port}`]); + try { + const code = await cli.quit(); + assert.strictEqual(code, 1); + } finally { + server.close(); + } + })); +} diff --git a/test/js/node/test/parallel/test-debugger-low-level.js b/test/js/node/test/parallel/test-debugger-low-level.js new file mode 100644 index 0000000000..31f67849f5 --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-low-level.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); +const assert = require('assert'); + +// Debugger agent direct access. +{ + const cli = startCLI(['--port=0', fixtures.path('debugger/three-lines.js')]); + const scriptPattern = /^\* (\d+): \S+debugger(?:\/|\\)three-lines\.js/m; + + async function testDebuggerLowLevel() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.command('scripts'); + const [, scriptId] = cli.output.match(scriptPattern); + await cli.command( + `Debugger.getScriptSource({ scriptId: '${scriptId}' })` + ); + assert.match( + cli.output, + /scriptSource:[ \n]*'(?:\(function \(|let x = 1)/); + assert.match( + cli.output, + /let x = 1;/); + } finally { + await cli.quit(); + } + } + testDebuggerLowLevel(); +} diff --git a/test/js/node/test/parallel/test-debugger-pid.js b/test/js/node/test/parallel/test-debugger-pid.js new file mode 100644 index 0000000000..157939c05c --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-pid.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); + +if (common.isWindows) + common.skip('unsupported function on windows'); + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +let buffer = ''; + +// Connect to debug agent +const interfacer = spawn(process.execPath, ['inspect', '-p', '655555']); + +interfacer.stdout.setEncoding('utf-8'); +interfacer.stderr.setEncoding('utf-8'); +const onData = (data) => { + data = (buffer + data).split('\n'); + buffer = data.pop(); + data.forEach((line) => interfacer.emit('line', line)); +}; +interfacer.stdout.on('data', onData); +interfacer.stderr.on('data', onData); + +interfacer.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'Target process: 655555 doesn\'t exist.'); +})); diff --git a/test/js/node/test/parallel/test-debugger-preserve-breaks.js b/test/js/node/test/parallel/test-debugger-preserve-breaks.js new file mode 100644 index 0000000000..00168c570d --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-preserve-breaks.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +const assert = require('assert'); +const path = require('path'); + +const scriptFullPath = fixtures.path('debugger', 'three-lines.js'); +const script = path.relative(process.cwd(), scriptFullPath); + +// Run after quit. +const runTest = async () => { + const cli = startCLI(['--port=0', script]); + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.command('breakpoints'); + assert.match(cli.output, /No breakpoints yet/); + await cli.command('sb(2)'); + await cli.command('sb(3)'); + await cli.command('breakpoints'); + assert.ok(cli.output.includes(`#0 ${script}:2`)); + assert.ok(cli.output.includes(`#1 ${script}:3`)); + await cli.stepCommand('c'); // hit line 2 + await cli.stepCommand('c'); // hit line 3 + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 3 }); + await cli.command('restart'); + await cli.waitForInitialBreak(); + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 1 }); + await cli.stepCommand('c'); + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 2 }); + await cli.stepCommand('c'); + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 3 }); + await cli.command('breakpoints'); + const msg = `SCRIPT: ${script}, OUTPUT: ${cli.output}`; + assert.ok(cli.output.includes(`#0 ${script}:2`), msg); + assert.ok(cli.output.includes(`#1 ${script}:3`), msg); + } finally { + await cli.quit(); + } +}; + +runTest(); diff --git a/test/js/node/test/parallel/test-debugger-repeat-last.js b/test/js/node/test/parallel/test-debugger-repeat-last.js new file mode 100644 index 0000000000..9a9b8eecdc --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-repeat-last.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +const path = require('../common/fixtures').path; +const spawn = require('child_process').spawn; +const assert = require('assert'); +const fixture = path('debugger-repeat-last.js'); + +const args = [ + 'inspect', + '--port=0', + fixture, +]; + +const proc = spawn(process.execPath, args, { stdio: 'pipe' }); +proc.stdout.setEncoding('utf8'); + +let stdout = ''; + +let sentCommand = false; +let sentExit = false; + +proc.stdout.on('data', (data) => { + stdout += data; + + // Send 'n' as the first step. + if (!sentCommand && stdout.includes('> 1 ')) { + setImmediate(() => { proc.stdin.write('n\n'); }); + return sentCommand = true; + } + // Send empty (repeat last command) until we reach line 5. + if (sentCommand && !stdout.includes('> 5')) { + setImmediate(() => { proc.stdin.write('\n'); }); + return true; + } + if (!sentExit && stdout.includes('> 5')) { + setTimeout(() => { proc.stdin.write('\n\n\n.exit\n\n\n'); }, 1); + return sentExit = true; + } +}); + +process.on('exit', (exitCode) => { + assert.strictEqual(exitCode, 0); + console.log(stdout); +}); diff --git a/test/js/node/test/parallel/test-debugger-restart-message.js b/test/js/node/test/parallel/test-debugger-restart-message.js new file mode 100644 index 0000000000..e4001b47ee --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-restart-message.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); + +const RESTARTS = 10; + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +// Using `restart` should result in only one "Connect/For help" message. +{ + const script = fixtures.path('debugger', 'three-lines.js'); + const cli = startCLI(['--port=0', script]); + + const listeningRegExp = /Debugger listening on/g; + + async function onWaitForInitialBreak() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + assert.strictEqual(cli.output.match(listeningRegExp).length, 1); + + for (let i = 0; i < RESTARTS; i++) { + await cli.stepCommand('restart'); + assert.strictEqual(cli.output.match(listeningRegExp).length, 1); + } + } finally { + await cli.quit(); + } + } + + onWaitForInitialBreak(); +} diff --git a/test/js/node/test/parallel/test-delayed-require.js b/test/js/node/test/parallel/test-delayed-require.js new file mode 100644 index 0000000000..355d503d26 --- /dev/null +++ b/test/js/node/test/parallel/test-delayed-require.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +setTimeout(common.mustCall(function() { + const a = require(fixtures.path('a')); + assert.strictEqual('A' in a, true); + assert.strictEqual(a.A(), 'A'); + assert.strictEqual(a.D(), 'D'); +}), 50); diff --git a/test/js/node/test/parallel/test-dgram-abort-closed.js b/test/js/node/test/parallel/test-dgram-abort-closed.js new file mode 100644 index 0000000000..6346b5feae --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-abort-closed.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +const dgram = require('dgram'); + +const controller = new AbortController(); +const socket = dgram.createSocket({ type: 'udp4', signal: controller.signal }); + +socket.close(); + +controller.abort(); diff --git a/test/js/node/test/parallel/test-dgram-bind-default-address.js b/test/js/node/test/parallel/test-dgram-bind-default-address.js new file mode 100644 index 0000000000..130d614c58 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-bind-default-address.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +// Skip test in FreeBSD jails since 0.0.0.0 will resolve to default interface +if (common.inFreeBSDJail) + common.skip('In a FreeBSD jail'); + +const assert = require('assert'); +const dgram = require('dgram'); + +dgram.createSocket('udp4').bind(0, common.mustCall(function() { + assert.strictEqual(typeof this.address().port, 'number'); + assert.ok(isFinite(this.address().port)); + assert.ok(this.address().port > 0); + assert.strictEqual(this.address().address, '0.0.0.0'); + this.close(); +})); + +if (!common.hasIPv6) { + common.printSkipMessage('udp6 part of test, because no IPv6 support'); + return; +} + +dgram.createSocket('udp6').bind(0, common.mustCall(function() { + assert.strictEqual(typeof this.address().port, 'number'); + assert.ok(isFinite(this.address().port)); + assert.ok(this.address().port > 0); + let address = this.address().address; + if (address === '::ffff:0.0.0.0') + address = '::'; + assert.strictEqual(address, '::'); + this.close(); +})); diff --git a/test/js/node/test/parallel/test-dgram-bind.js b/test/js/node/test/parallel/test-dgram-bind.js new file mode 100644 index 0000000000..8ae1213c7f --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-bind.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); + +socket.on('listening', common.mustCall(() => { + assert.throws(() => { + socket.bind(); + }, { + code: 'ERR_SOCKET_ALREADY_BOUND', + name: 'Error', + message: /^Socket is already bound$/ + }); + + socket.close(); +})); + +const result = socket.bind(); // Should not throw. + +assert.strictEqual(result, socket); // Should have returned itself. diff --git a/test/js/node/test/parallel/test-dgram-bytes-length.js b/test/js/node/test/parallel/test-dgram-bytes-length.js new file mode 100644 index 0000000000..abf8209b11 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-bytes-length.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const message = Buffer.from('Some bytes'); +const client = dgram.createSocket('udp4'); +client.send( + message, + 0, + message.length, + 41234, + 'localhost', + function(err, bytes) { + assert.strictEqual(bytes, message.length); + client.close(); + } +); diff --git a/test/js/node/test/parallel/test-dgram-close-in-listening.js b/test/js/node/test/parallel/test-dgram-close-in-listening.js new file mode 100644 index 0000000000..ae3ab71d7e --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-close-in-listening.js @@ -0,0 +1,26 @@ +'use strict'; +// Ensure that if a dgram socket is closed before the sendQueue is drained +// will not crash + +const common = require('../common'); +const dgram = require('dgram'); + +const buf = Buffer.alloc(1024, 42); + +const socket = dgram.createSocket('udp4'); + +socket.on('listening', function() { + socket.close(); +}); + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + // Adds a listener to 'listening' to send the data when + // the socket is available + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + portGetter.close(); + })); diff --git a/test/js/node/test/parallel/test-dgram-close-is-not-callback.js b/test/js/node/test/parallel/test-dgram-close-is-not-callback.js new file mode 100644 index 0000000000..bfb0bcd2bb --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-close-is-not-callback.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +const buf = Buffer.alloc(1024, 42); + +const socket = dgram.createSocket('udp4'); + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + // If close callback is not function, ignore the argument. + socket.close('bad argument'); + portGetter.close(); + + socket.on('close', common.mustCall()); + })); diff --git a/test/js/node/test/parallel/test-dgram-close-signal.js b/test/js/node/test/parallel/test-dgram-close-signal.js new file mode 100644 index 0000000000..26de38b504 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-close-signal.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +{ + // Test bad signal. + assert.throws( + () => dgram.createSocket({ type: 'udp4', signal: {} }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +{ + // Test close. + const controller = new AbortController(); + const { signal } = controller; + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); + controller.abort(); +} + +{ + // Test close with pre-aborted signal. + const signal = AbortSignal.abort(); + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-dgram-cluster-close-during-bind.js b/test/js/node/test/parallel/test-dgram-cluster-close-during-bind.js new file mode 100644 index 0000000000..065ff094f1 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-cluster-close-during-bind.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on windows.'); + +const assert = require('assert'); +const cluster = require('cluster'); +const dgram = require('dgram'); + +if (cluster.isPrimary) { + cluster.fork(); +} else { + // When the socket attempts to bind, it requests a handle from the cluster. + // Close the socket before returning the handle from the cluster. + const socket = dgram.createSocket('udp4'); + const _getServer = cluster._getServer; + + cluster._getServer = common.mustCall(function(self, options, callback) { + socket.close(common.mustCall(() => { + _getServer.call(this, self, options, common.mustCall((err, handle) => { + assert.strictEqual(err, 0); + + // When the socket determines that it was already closed, it will + // close the handle. Use handle.close() to terminate the test. + const close = handle.close; + + handle.close = common.mustCall(function() { + setImmediate(() => cluster.worker.disconnect()); + return close.call(this); + }); + + callback(err, handle); + })); + })); + }); + + socket.bind(common.mustNotCall('Socket should not bind.')); +} diff --git a/test/js/node/test/parallel/test-dgram-cluster-close-in-listening.js b/test/js/node/test/parallel/test-dgram-cluster-close-in-listening.js new file mode 100644 index 0000000000..8cce540271 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-cluster-close-in-listening.js @@ -0,0 +1,29 @@ +'use strict'; +// Ensure that closing dgram sockets in 'listening' callbacks of cluster workers +// won't throw errors. + +const common = require('../common'); +const dgram = require('dgram'); +const cluster = require('cluster'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on windows.'); + +if (cluster.isPrimary) { + for (let i = 0; i < 3; i += 1) { + cluster.fork(); + } +} else { + const socket = dgram.createSocket('udp4'); + + socket.on('error', common.mustNotCall()); + + socket.on('listening', common.mustCall(() => { + socket.close(); + })); + + socket.on('close', common.mustCall(() => { + cluster.worker.disconnect(); + })); + + socket.bind(0); +} diff --git a/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer-length.js b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer-length.js new file mode 100644 index 0000000000..723b2738e2 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer-length.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); +const offset = 20; +const len = buf.length - offset; + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, common.mustCall(() => { + client.connect(client.address().port, common.mustCall(() => { + client.send(buf, offset, len, messageSent); + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer.js b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer.js new file mode 100644 index 0000000000..d6ef1f510c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, common.mustCall(() => { + client.connect(client.address().port, common.mustCall(() => { + client.send(buf, onMessage); + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-callback-multi-buffer.js b/test/js/node/test/parallel/test-dgram-connect-send-callback-multi-buffer.js new file mode 100644 index 0000000000..defc73697c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-callback-multi-buffer.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustCall((err, bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', common.mustCall(() => { + const port = client.address().port; + client.connect(port, common.mustCall(() => { + client.send([buf1, buf2], messageSent); + })); +})); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-default-host.js b/test/js/node/test/parallel/test-dgram-connect-send-default-host.js new file mode 100644 index 0000000000..cd22cd2b64 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-default-host.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); +const server = dgram.createSocket('udp4'); + +const toSend = [Buffer.alloc(256, 'x'), + Buffer.alloc(256, 'y'), + Buffer.alloc(256, 'z'), + 'hello']; + +const received = []; + +server.on('listening', common.mustCall(() => { + const port = server.address().port; + client.connect(port, (err) => { + assert.ifError(err); + client.send(toSend[0], 0, toSend[0].length); + client.send(toSend[1]); + client.send([toSend[2]]); + client.send(toSend[3], 0, toSend[3].length); + + client.send(new Uint8Array(toSend[0]), 0, toSend[0].length); + client.send(new Uint8Array(toSend[1])); + client.send([new Uint8Array(toSend[2])]); + client.send(new Uint8Array(Buffer.from(toSend[3])), + 0, toSend[3].length); + }); +})); + +server.on('message', common.mustCall((buf, info) => { + received.push(buf.toString()); + + if (received.length === toSend.length * 2) { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const expected = toSend.concat(toSend).map(String).sort(); + assert.deepStrictEqual(received, expected); + client.close(); + server.close(); + } +}, toSend.length * 2)); + +server.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-empty-array.js b/test/js/node/test/parallel/test-dgram-connect-send-empty-array.js new file mode 100644 index 0000000000..7727e43041 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-empty-array.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.alloc(0); + assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); + client.close(); +})); + +client.on('listening', common.mustCall(() => { + client.connect(client.address().port, + common.localhostIPv4, + common.mustCall(() => client.send([]))); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-empty-buffer.js b/test/js/node/test/parallel/test-dgram-connect-send-empty-buffer.js new file mode 100644 index 0000000000..bce4c82e5c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-empty-buffer.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + const port = this.address().port; + client.connect(port, common.mustCall(() => { + const buf = Buffer.alloc(0); + client.send(buf, 0, 0, common.mustSucceed()); + })); + + client.on('message', common.mustCall((buffer) => { + assert.strictEqual(buffer.length, 0); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-empty-packet.js b/test/js/node/test/parallel/test-dgram-connect-send-empty-packet.js new file mode 100644 index 0000000000..577a9cbefd --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-empty-packet.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + client.connect(client.address().port, common.mustCall(() => { + client.on('message', common.mustCall(callback)); + const buf = Buffer.alloc(1); + + const interval = setInterval(function() { + client.send(buf, 0, 0, common.mustCall(callback)); + }, 10); + + function callback(firstArg) { + // If client.send() callback, firstArg should be null. + // If client.on('message') listener, firstArg should be a 0-length buffer. + if (firstArg instanceof Buffer) { + assert.strictEqual(firstArg.length, 0); + clearInterval(interval); + client.close(); + } + } + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-multi-buffer-copy.js b/test/js/node/test/parallel/test-dgram-connect-send-multi-buffer-copy.js new file mode 100644 index 0000000000..0859a0cf86 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-multi-buffer-copy.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const onMessage = common.mustCall(common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +})); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', common.mustCall(function() { + const toSend = [buf1, buf2]; + client.connect(client.address().port, common.mustCall(() => { + client.send(toSend, onMessage); + })); +})); + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-multi-string-array.js b/test/js/node/test/parallel/test-dgram-connect-send-multi-string-array.js new file mode 100644 index 0000000000..e69aa82d47 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-multi-string-array.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const socket = dgram.createSocket('udp4'); +const data = ['foo', 'bar', 'baz']; + +socket.on('message', common.mustCall((msg, rinfo) => { + socket.close(); + assert.deepStrictEqual(msg.toString(), data.join('')); +})); + +socket.bind(0, () => { + socket.connect(socket.address().port, common.mustCall(() => { + socket.send(data); + })); +}); diff --git a/test/js/node/test/parallel/test-dgram-implicit-bind.js b/test/js/node/test/parallel/test-dgram-implicit-bind.js new file mode 100644 index 0000000000..b5aa2781ce --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-implicit-bind.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +const source = dgram.createSocket('udp4'); +const target = dgram.createSocket('udp4'); +let messages = 0; + +target.on('message', common.mustCall(function(buf) { + if (buf.toString() === 'abc') ++messages; + if (buf.toString() === 'def') ++messages; + if (messages === 2) { + source.close(); + target.close(); + } +}, 2)); + +target.on('listening', common.mustCall(function() { + // Second .send() call should not throw a bind error. + const port = this.address().port; + source.send(Buffer.from('abc'), 0, 3, port, '127.0.0.1'); + source.send(Buffer.from('def'), 0, 3, port, '127.0.0.1'); +})); + +target.bind(0); diff --git a/test/js/node/test/parallel/utf8-scripts.test.js b/test/js/node/test/parallel/test-dgram-listen-after-bind.js similarity index 70% rename from test/js/node/test/parallel/utf8-scripts.test.js rename to test/js/node/test/parallel/test-dgram-listen-after-bind.js index e4c5a4bec4..a580a2386b 100644 --- a/test/js/node/test/parallel/utf8-scripts.test.js +++ b/test/js/node/test/parallel/test-dgram-listen-after-bind.js @@ -1,6 +1,3 @@ -//#FILE: test-utf8-scripts.js -//#SHA1: 103e32f817ebd35d617fba00e947304390c431d5 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,19 +19,27 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); -// üäö +const socket = dgram.createSocket('udp4'); -test("UTF-8 scripts", () => { - const consoleSpy = jest.spyOn(console, "log"); +socket.bind(); - console.log("Σὲ γνωρίζω ἀπὸ τὴν κόψη"); +let fired = false; +const timer = setTimeout(() => { + socket.close(); +}, 100); - expect(consoleSpy).toHaveBeenCalledWith("Σὲ γνωρίζω ἀπὸ τὴν κόψη"); - consoleSpy.mockRestore(); +socket.on('listening', common.mustCall(() => { + clearTimeout(timer); + fired = true; + socket.close(); +})); - expect("Hellö Wörld").toMatch(/Hellö Wörld/); -}); - -//<#END_FILE: test-utf8-scripts.js +socket.on('close', common.mustCall(() => { + assert(fired, 'listening should fire after bind'); +})); diff --git a/test/js/node/test/parallel/dgram-oob-buffer.test.js b/test/js/node/test/parallel/test-dgram-oob-buffer.js similarity index 53% rename from test/js/node/test/parallel/dgram-oob-buffer.test.js rename to test/js/node/test/parallel/test-dgram-oob-buffer.js index 4d1eae8968..1e71815927 100644 --- a/test/js/node/test/parallel/dgram-oob-buffer.test.js +++ b/test/js/node/test/parallel/test-dgram-oob-buffer.js @@ -1,6 +1,3 @@ -//#FILE: test-dgram-oob-buffer.js -//#SHA1: a851da9a2178e92ce8315294d7cebf6eb78eb4bd -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,42 +19,27 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // Some operating systems report errors when an UDP message is sent to an // unreachable host. This error can be reported by sendto() and even by // recvfrom(). Node should not propagate this error to the user. -const dgram = require("dgram"); +const common = require('../common'); +const dgram = require('dgram'); -test("UDP message sent to unreachable host should not propagate error", async () => { - const socket = dgram.createSocket("udp4"); - const buf = Buffer.from([1, 2, 3, 4]); +const socket = dgram.createSocket('udp4'); +const buf = Buffer.from([1, 2, 3, 4]); +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + const { address, port } = portGetter.address(); + portGetter.close(common.mustCall(() => { + socket.send(buf, 0, 0, port, address, common.mustNotCall()); + socket.send(buf, 0, 4, port, address, common.mustNotCall()); + socket.send(buf, 1, 3, port, address, common.mustNotCall()); + socket.send(buf, 3, 1, port, address, common.mustNotCall()); + // Since length of zero means nothing, don't error despite OOB. + socket.send(buf, 4, 0, port, address, common.mustNotCall()); - const portGetter = dgram.createSocket("udp4"); - - await new Promise(resolve => { - portGetter.bind(0, "localhost", () => { - const { address, port } = portGetter.address(); - - portGetter.close(() => { - const sendCallback = jest.fn(); - - socket.send(buf, 0, 0, port, address, sendCallback); - socket.send(buf, 0, 4, port, address, sendCallback); - socket.send(buf, 1, 3, port, address, sendCallback); - socket.send(buf, 3, 1, port, address, sendCallback); - // Since length of zero means nothing, don't error despite OOB. - socket.send(buf, 4, 0, port, address, sendCallback); - - socket.close(); - - // We expect the sendCallback to not be called - expect(sendCallback).not.toHaveBeenCalled(); - - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-dgram-oob-buffer.js + socket.close(); + })); + })); diff --git a/test/js/node/test/parallel/test-dgram-ref.js b/test/js/node/test/parallel/test-dgram-ref.js new file mode 100644 index 0000000000..0c5474b118 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-ref.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +// Should not hang, see https://github.com/nodejs/node-v0.x-archive/issues/1282 +dgram.createSocket('udp4'); +dgram.createSocket('udp6'); + +{ + // Test the case of ref()'ing a socket with no handle. + const s = dgram.createSocket('udp4'); + + s.close(common.mustCall(() => s.ref())); +} diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer-empty-address.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer-empty-address.js new file mode 100644 index 0000000000..f6254ec43e --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer-empty-address.js @@ -0,0 +1,16 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.alloc(256, 'x'); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, () => client.send(buf, client.address().port, onMessage)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js new file mode 100644 index 0000000000..a95018d14b --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.alloc(256, 'x'); +const offset = 20; +const len = buf.length - offset; + +const onMessage = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, () => client.send(buf, offset, len, + client.address().port, + onMessage)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer-length.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length.js new file mode 100644 index 0000000000..f570ecb20d --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); +const offset = 20; +const len = buf.length - offset; + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, () => client.send(buf, offset, len, + client.address().port, + '127.0.0.1', + messageSent)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer.js new file mode 100644 index 0000000000..11f8208de9 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, () => client.send(buf, + client.address().port, + common.localhostIPv4, + onMessage)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js new file mode 100644 index 0000000000..37e9e04c44 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', function() { + const port = this.address().port; + client.send([buf1, buf2], port, messageSent); +}); + +client.on('message', common.mustCall(function onMessage(buf) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer.js b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer.js new file mode 100644 index 0000000000..09f01f6e8a --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustCall((err, bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', () => { + const port = client.address().port; + client.send([buf1, buf2], port, common.localhostIPv4, messageSent); +}); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-recursive.js b/test/js/node/test/parallel/test-dgram-send-callback-recursive.js new file mode 100644 index 0000000000..1a4c7c84fc --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-recursive.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); +const chunk = 'abc'; +let received = 0; +let sent = 0; +const limit = 10; +let async = false; +let port; + +function onsend() { + if (sent++ < limit) { + client.send(chunk, 0, chunk.length, port, common.localhostIPv4, onsend); + } else { + assert.strictEqual(async, true); + } +} + +client.on('listening', function() { + port = this.address().port; + + process.nextTick(() => { + async = true; + }); + + onsend(); +}); + +client.on('message', (buf, info) => { + received++; + if (received === limit) { + client.close(); + } +}); + +client.on('close', common.mustCall(function() { + assert.strictEqual(received, limit); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-empty-array.js b/test/js/node/test/parallel/test-dgram-send-empty-array.js new file mode 100644 index 0000000000..178b72bb12 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-empty-array.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +let interval; + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.alloc(0); + assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); + clearInterval(interval); + client.close(); +})); + +client.on('listening', common.mustCall(function() { + interval = setInterval(function() { + client.send([], client.address().port, common.localhostIPv4); + }, 10); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-empty-buffer.js b/test/js/node/test/parallel/test-dgram-send-empty-buffer.js new file mode 100644 index 0000000000..76b014b893 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-empty-buffer.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + const port = this.address().port; + + client.on('message', common.mustCall(function onMessage(buffer) { + assert.strictEqual(buffer.length, 0); + clearInterval(interval); + client.close(); + })); + + const buf = Buffer.alloc(0); + const interval = setInterval(function() { + client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall()); + }, 10); +})); diff --git a/test/js/node/test/parallel/test-dgram-send-empty-packet.js b/test/js/node/test/parallel/test-dgram-send-empty-packet.js new file mode 100644 index 0000000000..eb4f79e8aa --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-empty-packet.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + + client.on('message', common.mustCall(callback)); + + const port = this.address().port; + const buf = Buffer.alloc(1); + + const interval = setInterval(function() { + client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall(callback)); + }, 10); + + function callback(firstArg) { + // If client.send() callback, firstArg should be null. + // If client.on('message') listener, firstArg should be a 0-length buffer. + if (firstArg instanceof Buffer) { + assert.strictEqual(firstArg.length, 0); + clearInterval(interval); + client.close(); + } + } +})); diff --git a/test/js/node/test/parallel/test-dgram-send-multi-buffer-copy.js b/test/js/node/test/parallel/test-dgram-send-multi-buffer-copy.js new file mode 100644 index 0000000000..2ee87494b0 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-multi-buffer-copy.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const onMessage = common.mustCall(function(err, bytes) { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', function() { + const toSend = [buf1, buf2]; + client.send(toSend, this.address().port, common.localhostIPv4, onMessage); + toSend.splice(0, 2); +}); + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-multi-string-array.js b/test/js/node/test/parallel/test-dgram-send-multi-string-array.js new file mode 100644 index 0000000000..8d73a6d183 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-multi-string-array.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const socket = dgram.createSocket('udp4'); +const data = ['foo', 'bar', 'baz']; + +socket.on('message', common.mustCall((msg, rinfo) => { + socket.close(); + assert.deepStrictEqual(msg.toString(), data.join('')); +})); + +socket.bind(() => socket.send(data, socket.address().port, 'localhost')); diff --git a/test/js/node/test/parallel/test-dgram-udp4.js b/test/js/node/test/parallel/test-dgram-udp4.js new file mode 100644 index 0000000000..c7ee34b2c1 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-udp4.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const message_to_send = 'A message to send'; + +const server = dgram.createSocket('udp4'); +server.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, common.localhostIPv4); + assert.strictEqual(msg.toString(), message_to_send.toString()); + server.send(msg, 0, msg.length, rinfo.port, rinfo.address); +})); +server.on('listening', common.mustCall(() => { + const client = dgram.createSocket('udp4'); + const port = server.address().port; + client.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, common.localhostIPv4); + assert.strictEqual(rinfo.port, port); + assert.strictEqual(msg.toString(), message_to_send.toString()); + client.close(); + server.close(); + })); + client.send(message_to_send, + 0, + message_to_send.length, + port, + 'localhost'); + client.on('close', common.mustCall()); +})); +server.on('close', common.mustCall()); +server.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-unref-in-cluster.js b/test/js/node/test/parallel/test-dgram-unref-in-cluster.js new file mode 100644 index 0000000000..40833a129d --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-unref-in-cluster.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (common.isWindows) + common.skip('dgram clustering is currently not supported on Windows.'); + +if (cluster.isPrimary) { + cluster.fork(); +} else { + const socket = dgram.createSocket('udp4'); + socket.unref(); + socket.bind(); + socket.on('listening', common.mustCall(() => { + const sockets = process.getActiveResourcesInfo().filter((item) => { + return item === 'UDPWrap'; + }); + assert.ok(sockets.length === 0); + process.disconnect(); + })); +} diff --git a/test/js/node/test/parallel/net-server-unref.test.js b/test/js/node/test/parallel/test-dgram-unref.js similarity index 74% rename from test/js/node/test/parallel/net-server-unref.test.js rename to test/js/node/test/parallel/test-dgram-unref.js index 2067d6cbb9..930cbf095c 100644 --- a/test/js/node/test/parallel/net-server-unref.test.js +++ b/test/js/node/test/parallel/test-dgram-unref.js @@ -1,6 +1,3 @@ -//#FILE: test-net-server-unref.js -//#SHA1: bb2f989bf01182d804d6a8a0d0f33950f357c617 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,18 +19,22 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const net = require("net"); +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); -test("net server unref", () => { - const s = net.createServer(); - s.listen(0); +{ + // Test the case of unref()'ing a socket with a handle. + const s = dgram.createSocket('udp4'); + s.bind(); s.unref(); +} - const mockCallback = jest.fn(); - setTimeout(mockCallback, 1000).unref(); +{ + // Test the case of unref()'ing a socket with no handle. + const s = dgram.createSocket('udp4'); - expect(mockCallback).not.toHaveBeenCalled(); -}); + s.close(common.mustCall(() => s.unref())); +} -//<#END_FILE: test-net-server-unref.js +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-bind-store.js b/test/js/node/test/parallel/test-diagnostics-channel-bind-store.js new file mode 100644 index 0000000000..81fb299c2f --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-bind-store.js @@ -0,0 +1,108 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dc = require('diagnostics_channel'); +const { AsyncLocalStorage } = require('async_hooks'); + +let n = 0; +const thisArg = new Date(); +const inputs = [ + { foo: 'bar' }, + { baz: 'buz' }, +]; + +const channel = dc.channel('test'); + +// Bind a storage directly to published data +const store1 = new AsyncLocalStorage(); +channel.bindStore(store1); +let store1bound = true; + +// Bind a store with transformation of published data +const store2 = new AsyncLocalStorage(); +channel.bindStore(store2, common.mustCall((data) => { + assert.strictEqual(data, inputs[n]); + return { data }; +}, 4)); + +// Regular subscribers should see publishes from runStores calls +channel.subscribe(common.mustCall((data) => { + if (store1bound) { + assert.deepStrictEqual(data, store1.getStore()); + } + assert.deepStrictEqual({ data }, store2.getStore()); + assert.strictEqual(data, inputs[n]); +}, 4)); + +// Verify stores are empty before run +assert.strictEqual(store1.getStore(), undefined); +assert.strictEqual(store2.getStore(), undefined); + +channel.runStores(inputs[n], common.mustCall(function(a, b) { + // Verify this and argument forwarding + assert.strictEqual(this, thisArg); + assert.strictEqual(a, 1); + assert.strictEqual(b, 2); + + // Verify store 1 state matches input + assert.strictEqual(store1.getStore(), inputs[n]); + + // Verify store 2 state has expected transformation + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); + + // Should support nested contexts + n++; + channel.runStores(inputs[n], common.mustCall(function() { + // Verify this and argument forwarding + assert.strictEqual(this, undefined); + + // Verify store 1 state matches input + assert.strictEqual(store1.getStore(), inputs[n]); + + // Verify store 2 state has expected transformation + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); + })); + n--; + + // Verify store 1 state matches input + assert.strictEqual(store1.getStore(), inputs[n]); + + // Verify store 2 state has expected transformation + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); +}), thisArg, 1, 2); + +// Verify stores are empty after run +assert.strictEqual(store1.getStore(), undefined); +assert.strictEqual(store2.getStore(), undefined); + +// Verify unbinding works +assert.ok(channel.unbindStore(store1)); +store1bound = false; + +// Verify unbinding a store that is not bound returns false +assert.ok(!channel.unbindStore(store1)); + +n++; +channel.runStores(inputs[n], common.mustCall(() => { + // Verify after unbinding store 1 will remain undefined + assert.strictEqual(store1.getStore(), undefined); + + // Verify still bound store 2 receives expected data + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); +})); + +// Contain transformer errors and emit on next tick +const fail = new Error('fail'); +channel.bindStore(store1, () => { + throw fail; +}); + +let calledRunStores = false; +process.once('uncaughtException', common.mustCall((err) => { + assert.strictEqual(calledRunStores, true); + assert.strictEqual(err, fail); +})); + +channel.runStores(inputs[n], common.mustCall()); +calledRunStores = true; diff --git a/test/js/node/test/parallel/test-diagnostics-channel-has-subscribers.js b/test/js/node/test/parallel/test-diagnostics-channel-has-subscribers.js new file mode 100644 index 0000000000..de37267555 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-has-subscribers.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { channel, hasSubscribers } = require('diagnostics_channel'); + +const dc = channel('test'); +assert.ok(!hasSubscribers('test')); + +dc.subscribe(() => {}); +assert.ok(hasSubscribers('test')); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js b/test/js/node/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js new file mode 100644 index 0000000000..9498419b80 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const input = { + foo: 'bar' +}; + +// Should not have named channel +assert.ok(!dc.hasSubscribers('test')); + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel('test'); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +channel.subscribe(subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +assert.ok(channel.unsubscribe(subscriber)); +assert.ok(!channel.hasSubscribers); + +// unsubscribe() should return false when subscriber is not found +assert.ok(!channel.unsubscribe(subscriber)); + +assert.throws(() => { + channel.subscribe(null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-pub-sub.js b/test/js/node/test/parallel/test-diagnostics-channel-pub-sub.js new file mode 100644 index 0000000000..a7232ab58c --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-pub-sub.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const name = 'test'; +const input = { + foo: 'bar' +}; + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel(name); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +dc.subscribe(name, subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +assert.ok(dc.unsubscribe(name, subscriber)); +assert.ok(!channel.hasSubscribers); + +// unsubscribe() should return false when subscriber is not found +assert.ok(!dc.unsubscribe(name, subscriber)); + +assert.throws(() => { + dc.subscribe(name, null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); + +// Reaching zero subscribers should not delete from the channels map as there +// will be no more weakref to incRef if another subscribe happens while the +// channel object itself exists. +channel.subscribe(subscriber); +channel.unsubscribe(subscriber); +channel.subscribe(subscriber); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js b/test/js/node/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js new file mode 100644 index 0000000000..b0c5ab2480 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const input = { + foo: 'bar' +}; + +const channel = dc.channel('fail'); + +const error = new Error('nope'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err, error); +})); + +channel.subscribe(common.mustCall((message, name) => { + throw error; +})); + +// The failing subscriber should not stop subsequent subscribers from running +channel.subscribe(common.mustCall()); + +// Publish should continue without throwing +const fn = common.mustCall(); +channel.publish(input); +fn(); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-sync-unsubscribe.js b/test/js/node/test/parallel/test-diagnostics-channel-sync-unsubscribe.js new file mode 100644 index 0000000000..87bf44249f --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-sync-unsubscribe.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); +const dc = require('node:diagnostics_channel'); + +const channel_name = 'test:channel'; +const published_data = 'some message'; + +const onMessageHandler = common.mustCall(() => dc.unsubscribe(channel_name, onMessageHandler)); + +dc.subscribe(channel_name, onMessageHandler); + +// This must not throw. +dc.channel(channel_name).publish(published_data); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-error.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-error.js new file mode 100644 index 0000000000..672500e768 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-error.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedError = new Error('test'); +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(check), + asyncEnd: common.mustCall(check), + error: common.mustCall((found) => { + check(found); + assert.deepStrictEqual(found.error, expectedError); + }) +}; + +channel.subscribe(handlers); + +channel.traceCallback(function(cb, err) { + assert.deepStrictEqual(this, thisArg); + setImmediate(cb, err); +}, 0, input, thisArg, common.mustCall((err, res) => { + assert.strictEqual(err, expectedError); + assert.strictEqual(res, undefined); +}), expectedError); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js new file mode 100644 index 0000000000..874433efd2 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); +const store = new AsyncLocalStorage(); + +const firstContext = { foo: 'bar' }; +const secondContext = { baz: 'buz' }; + +channel.start.bindStore(store, common.mustCall(() => { + return firstContext; +})); + +channel.asyncStart.bindStore(store, common.mustCall(() => { + return secondContext; +})); + +assert.strictEqual(store.getStore(), undefined); +channel.traceCallback(common.mustCall((cb) => { + assert.deepStrictEqual(store.getStore(), firstContext); + setImmediate(cb); +}), 0, {}, null, common.mustCall(() => { + assert.deepStrictEqual(store.getStore(), secondContext); +})); +assert.strictEqual(store.getStore(), undefined); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback.js new file mode 100644 index 0000000000..d306f0f51b --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedResult = { foo: 'bar' }; +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +function checkAsync(found) { + check(found); + assert.strictEqual(found.error, undefined); + assert.deepStrictEqual(found.result, expectedResult); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(checkAsync), + asyncEnd: common.mustCall(checkAsync), + error: common.mustNotCall() +}; + +channel.subscribe(handlers); + +channel.traceCallback(function(cb, err, res) { + assert.deepStrictEqual(this, thisArg); + setImmediate(cb, err, res); +}, 0, input, thisArg, common.mustCall((err, res) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(res, expectedResult); +}), null, expectedResult); + +assert.throws(() => { + channel.traceCallback(common.mustNotCall(), 0, input, thisArg, 1, 2, 3); +}, /"callback" argument must be of type function/); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js new file mode 100644 index 0000000000..5292a6fe09 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('node:timers/promises'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); +const store = new AsyncLocalStorage(); + +const firstContext = { foo: 'bar' }; +const secondContext = { baz: 'buz' }; + +channel.start.bindStore(store, common.mustCall(() => { + return firstContext; +})); + +channel.asyncStart.bindStore(store, common.mustNotCall(() => { + return secondContext; +})); + +assert.strictEqual(store.getStore(), undefined); +channel.tracePromise(common.mustCall(async () => { + assert.deepStrictEqual(store.getStore(), firstContext); + await setTimeout(1); + // Should _not_ switch to second context as promises don't have an "after" + // point at which to do a runStores. + assert.deepStrictEqual(store.getStore(), firstContext); +})); +assert.strictEqual(store.getStore(), undefined); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise.js new file mode 100644 index 0000000000..20892ca40f --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedResult = { foo: 'bar' }; +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +function checkAsync(found) { + check(found); + assert.strictEqual(found.error, undefined); + assert.deepStrictEqual(found.result, expectedResult); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(checkAsync), + asyncEnd: common.mustCall(checkAsync), + error: common.mustNotCall() +}; + +channel.subscribe(handlers); + +channel.tracePromise(function(value) { + assert.deepStrictEqual(this, thisArg); + return Promise.resolve(value); +}, input, thisArg, expectedResult).then( + common.mustCall((value) => { + assert.deepStrictEqual(value, expectedResult); + }), + common.mustNotCall() +); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js new file mode 100644 index 0000000000..0965bf3fb4 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedError = new Error('test'); +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustNotCall(), + asyncEnd: common.mustNotCall(), + error: common.mustCall((found) => { + check(found); + assert.deepStrictEqual(found.error, expectedError); + }) +}; + +channel.subscribe(handlers); +try { + channel.traceSync(function(err) { + assert.deepStrictEqual(this, thisArg); + assert.strictEqual(err, expectedError); + throw err; + }, input, thisArg, expectedError); + + throw new Error('It should not reach this error'); +} catch (error) { + assert.deepStrictEqual(error, expectedError); +} diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-run-stores.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-run-stores.js new file mode 100644 index 0000000000..3ffe5e6720 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-run-stores.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); +const store = new AsyncLocalStorage(); + +const context = { foo: 'bar' }; + +channel.start.bindStore(store, common.mustCall(() => { + return context; +})); + +assert.strictEqual(store.getStore(), undefined); +channel.traceSync(common.mustCall(() => { + assert.deepStrictEqual(store.getStore(), context); +})); +assert.strictEqual(store.getStore(), undefined); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync.js new file mode 100644 index 0000000000..b28b47256b --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedResult = { foo: 'bar' }; +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; +const arg = { baz: 'buz' }; + +function check(found) { + assert.strictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall((found) => { + check(found); + assert.strictEqual(found.result, expectedResult); + }), + asyncStart: common.mustNotCall(), + asyncEnd: common.mustNotCall(), + error: common.mustNotCall() +}; + +assert.strictEqual(channel.start.hasSubscribers, false); +channel.subscribe(handlers); +assert.strictEqual(channel.start.hasSubscribers, true); +const result1 = channel.traceSync(function(arg1) { + assert.strictEqual(arg1, arg); + assert.strictEqual(this, thisArg); + return expectedResult; +}, input, thisArg, arg); +assert.strictEqual(result1, expectedResult); + +channel.unsubscribe(handlers); +assert.strictEqual(channel.start.hasSubscribers, false); +const result2 = channel.traceSync(function(arg1) { + assert.strictEqual(arg1, arg); + assert.strictEqual(this, thisArg); + return expectedResult; +}, input, thisArg, arg); +assert.strictEqual(result2, expectedResult); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-udp.js b/test/js/node/test/parallel/test-diagnostics-channel-udp.js new file mode 100644 index 0000000000..79869c6d80 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-udp.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const dc = require('diagnostics_channel'); + +const udpSocketChannel = dc.channel('udp.socket'); + +const isUDPSocket = (socket) => socket instanceof dgram.Socket; + +udpSocketChannel.subscribe(common.mustCall(({ socket }) => { + assert.strictEqual(isUDPSocket(socket), true); +})); +const socket = dgram.createSocket('udp4'); +socket.close(); diff --git a/test/js/node/test/parallel/test-domain-crypto.js b/test/js/node/test/parallel/test-domain-crypto.js new file mode 100644 index 0000000000..e0a470bd9d --- /dev/null +++ b/test/js/node/test/parallel/test-domain-crypto.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('node compiled without OpenSSL.'); + +const crypto = require('crypto'); + +// Pollution of global is intentional as part of test. +common.allowGlobals(require('domain')); +// See https://github.com/nodejs/node/commit/d1eff9ab +global.domain = require('domain'); + +// Should not throw a 'TypeError: undefined is not a function' exception +crypto.randomBytes(8); +crypto.randomBytes(8, common.mustSucceed()); +const buf = Buffer.alloc(8); +crypto.randomFillSync(buf); +crypto.pseudoRandomBytes(8); +crypto.pseudoRandomBytes(8, common.mustSucceed()); +crypto.pbkdf2('password', 'salt', 8, 8, 'sha1', common.mustSucceed()); diff --git a/test/js/node/test/parallel/test-domain-ee-error-listener.js b/test/js/node/test/parallel/test-domain-ee-error-listener.js new file mode 100644 index 0000000000..69041c7523 --- /dev/null +++ b/test/js/node/test/parallel/test-domain-ee-error-listener.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain').create(); +const EventEmitter = require('events'); + +domain.on('error', common.mustNotCall()); + +const ee = new EventEmitter(); + +const plainObject = { justAn: 'object' }; +ee.once('error', common.mustCall((err) => { + assert.deepStrictEqual(err, plainObject); +})); +ee.emit('error', plainObject); + +const err = new Error('test error'); +ee.once('error', common.expectsError(err)); +ee.emit('error', err); diff --git a/test/js/node/test/parallel/test-domain-nested-throw.js b/test/js/node/test/parallel/test-domain-nested-throw.js new file mode 100644 index 0000000000..ec016ada72 --- /dev/null +++ b/test/js/node/test/parallel/test-domain-nested-throw.js @@ -0,0 +1,101 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const domain = require('domain'); + +if (process.argv[2] !== 'child') { + parent(); + return; +} + +function parent() { + const node = process.execPath; + const spawn = require('child_process').spawn; + const opt = { stdio: 'inherit' }; + const child = spawn(node, [__filename, 'child'], opt); + child.on('exit', function(c) { + assert(!c); + console.log('ok'); + }); +} + +let gotDomain1Error = false; +let gotDomain2Error = false; + +let threw1 = false; +let threw2 = false; + +function throw1() { + threw1 = true; + throw new Error('handled by domain1'); +} + +function throw2() { + threw2 = true; + throw new Error('handled by domain2'); +} + +function inner(throw1, throw2) { + const domain1 = domain.createDomain(); + + domain1.on('error', function(err) { + if (gotDomain1Error) { + console.error('got domain 1 twice'); + process.exit(1); + } + gotDomain1Error = true; + throw2(); + }); + + domain1.run(function() { + throw1(); + }); +} + +function outer() { + const domain2 = domain.createDomain(); + + domain2.on('error', function(err) { + if (gotDomain2Error) { + console.error('got domain 2 twice'); + process.exit(1); + } + gotDomain2Error = true; + }); + + domain2.run(function() { + inner(throw1, throw2); + }); +} + +process.on('exit', function() { + assert(gotDomain1Error); + assert(gotDomain2Error); + assert(threw1); + assert(threw2); + console.log('ok'); +}); + +outer(); diff --git a/test/js/node/test/parallel/test-domain-vm-promise-isolation.js b/test/js/node/test/parallel/test-domain-vm-promise-isolation.js new file mode 100644 index 0000000000..41aed1ee33 --- /dev/null +++ b/test/js/node/test/parallel/test-domain-vm-promise-isolation.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const vm = require('vm'); + +// A promise created in a VM should not include a domain field but +// domains should still be able to propagate through them. +// +// See; https://github.com/nodejs/node/issues/40999 + +const context = vm.createContext({}); + +function run(code) { + const d = domain.createDomain(); + d.run(common.mustCall(() => { + const p = vm.runInContext(code, context)(); + assert.strictEqual(p.domain, undefined); + p.then(common.mustCall(() => { + assert.strictEqual(process.domain, d); + })); + })); +} + +for (let i = 0; i < 1000; i++) { + run('async () => null'); +} diff --git a/test/js/node/test/parallel/test-dsa-fips-invalid-key.js b/test/js/node/test/parallel/test-dsa-fips-invalid-key.js new file mode 100644 index 0000000000..05cc1d143a --- /dev/null +++ b/test/js/node/test/parallel/test-dsa-fips-invalid-key.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasFipsCrypto) + common.skip('node compiled without FIPS OpenSSL.'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const input = 'hello'; + +const dsapri = fixtures.readKey('dsa_private_1025.pem'); +const sign = crypto.createSign('SHA1'); +sign.update(input); + +assert.throws(function() { + sign.sign(dsapri); +}, /PEM_read_bio_PrivateKey failed/); diff --git a/test/js/node/test/parallel/test-error-prepare-stack-trace.js b/test/js/node/test/parallel/test-error-prepare-stack-trace.js new file mode 100644 index 0000000000..1ad29efb16 --- /dev/null +++ b/test/js/node/test/parallel/test-error-prepare-stack-trace.js @@ -0,0 +1,30 @@ +'use strict'; + +require('../common'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const assert = require('assert'); + +// Verify that the default Error.prepareStackTrace is present. +assert.strictEqual(typeof Error.prepareStackTrace, 'function'); + +// Error.prepareStackTrace() can be overridden. +{ + let prepareCalled = false; + Error.prepareStackTrace = (_error, trace) => { + prepareCalled = true; + }; + try { + throw new Error('foo'); + } catch (err) { + err.stack; // eslint-disable-line no-unused-expressions + } + assert(prepareCalled); +} + +if (process.argv[2] !== 'child') { + // Verify that the above test still passes when source-maps support is + // enabled. + spawnSyncAndExitWithoutError( + process.execPath, + ['--enable-source-maps', __filename, 'child']); +} diff --git a/test/js/node/test/parallel/test-eslint-alphabetize-errors.js b/test/js/node/test/parallel/test-eslint-alphabetize-errors.js new file mode 100644 index 0000000000..18c3aed3af --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-alphabetize-errors.js @@ -0,0 +1,63 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/alphabetize-errors'); + +new RuleTester().run('alphabetize-errors', rule, { + valid: [ + { code: ` + E('AAA', 'foo'); + E('BBB', 'bar'); + E('CCC', 'baz'); + `, options: [{ checkErrorDeclarations: true }] }, + ` + E('AAA', 'foo'); + E('CCC', 'baz'); + E('BBB', 'bar'); + `, + `const { + codes: { + ERR_A, + ERR_B, + }, + } = require("internal/errors")`, + ], + invalid: [ + { + code: ` + E('BBB', 'bar'); + E('AAA', 'foo'); + E('CCC', 'baz'); + `, + options: [{ checkErrorDeclarations: true }], + errors: [{ message: 'Out of ASCIIbetical order - BBB >= AAA', line: 3 }] + }, + { + code: `const { + codes: { + ERR_B, + ERR_A, + }, + } = require("internal/errors")`, + errors: [{ message: 'Out of ASCIIbetical order - ERR_B >= ERR_A', line: 4 }] + }, + { + code: 'const internalErrors = require("internal/errors")', + errors: [{ message: /Use destructuring/ }] + }, + { + code: 'const {codes} = require("internal/errors")', + errors: [{ message: /Use destructuring/ }] + }, + { + code: 'const {codes:{ERR_A}} = require("internal/errors")', + errors: [{ message: /Use multiline destructuring/ }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-alphabetize-primordials.js b/test/js/node/test/parallel/test-eslint-alphabetize-primordials.js new file mode 100644 index 0000000000..3f63e1ecbd --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-alphabetize-primordials.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/alphabetize-primordials'); + +new RuleTester() + .run('alphabetize-primordials', rule, { + valid: [ + 'new Array()', + '"use strict";const {\nArray\n} = primordials;', + '"use strict";const {\n\tArray,\n\tDate,\n} = primordials', + '"use strict";const {\nDate,Array\n} = notPrimordials', + '"use strict";const {\nDate,globalThis:{Array}\n} = primordials', + '"use strict";const {\nBigInt,globalThis:{Array,Date,SharedArrayBuffer,parseInt}\n} = primordials', + '"use strict";const {\nFunctionPrototypeBind,Uint32Array,globalThis:{SharedArrayBuffer}\n} = primordials', + { + code: '"use strict";const fs = require("fs");const {\nArray\n} = primordials', + options: [{ enforceTopPosition: false }], + }, + ], + invalid: [ + { + code: '"use strict";const {Array} = primordials;', + errors: [{ message: /destructuring from primordials should be multiline/ }], + }, + { + code: '"use strict";const fs = require("fs");const {Date,Array} = primordials', + errors: [ + { message: /destructuring from primordials should be multiline/ }, + { message: /destructuring from primordials should be the first expression/ }, + { message: /Date >= Array/ }, + ], + }, + { + code: 'function fn() {"use strict";const {\nArray,\n} = primordials}', + errors: [{ message: /destructuring from primordials should be the first expression/ }], + }, + { + code: '"use strict";const {\nDate,Array} = primordials', + errors: [{ message: /Date >= Array/ }], + }, + { + code: '"use strict";const {\nglobalThis:{Date, Array}} = primordials', + errors: [{ message: /Date >= Array/ }], + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-async-iife-no-unused-result.js b/test/js/node/test/parallel/test-eslint-async-iife-no-unused-result.js new file mode 100644 index 0000000000..9f74b65868 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-async-iife-no-unused-result.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/async-iife-no-unused-result'); + +const message = 'The result of an immediately-invoked async function needs ' + + 'to be used (e.g. with `.then(common.mustCall())`)'; + +const tester = new RuleTester(); +tester.run('async-iife-no-unused-result', rule, { + valid: [ + '(() => {})()', + '(async () => {})', + '(async () => {})().then()', + '(async () => {})().catch()', + '(function () {})()', + '(async function () {})', + '(async function () {})().then()', + '(async function () {})().catch()', + ], + invalid: [ + { + code: '(async () => {})()', + errors: [{ message }], + }, + { + code: '(async function() {})()', + errors: [{ message }], + }, + { + code: "const common = require('../common');(async () => {})()", + errors: [{ message }], + output: "const common = require('../common');(async () => {})()" + + '.then(common.mustCall())', + }, + { + code: "const common = require('../common');(async function() {})()", + errors: [{ message }], + output: "const common = require('../common');(async function() {})()" + + '.then(common.mustCall())', + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-avoid-prototype-pollution.js b/test/js/node/test/parallel/test-eslint-avoid-prototype-pollution.js new file mode 100644 index 0000000000..c6b0fe6386 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-avoid-prototype-pollution.js @@ -0,0 +1,334 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/avoid-prototype-pollution'); + +new RuleTester() + .run('property-descriptor-no-prototype-pollution', rule, { + valid: [ + 'ObjectDefineProperties({}, {})', + 'ObjectCreate(null, {})', + 'ObjectDefineProperties({}, { key })', + 'ObjectCreate(null, { key })', + 'ObjectDefineProperties({}, { ...spread })', + 'ObjectCreate(null, { ...spread })', + 'ObjectDefineProperties({}, { key: valueDescriptor })', + 'ObjectCreate(null, { key: valueDescriptor })', + 'ObjectDefineProperties({}, { key: { ...{}, __proto__: null } })', + 'ObjectCreate(null, { key: { ...{}, __proto__: null } })', + 'ObjectDefineProperties({}, { key: { __proto__: null } })', + 'ObjectCreate(null, { key: { __proto__: null } })', + 'ObjectDefineProperties({}, { key: { __proto__: null, enumerable: true } })', + 'ObjectCreate(null, { key: { __proto__: null, enumerable: true } })', + 'ObjectDefineProperties({}, { key: { "__proto__": null } })', + 'ObjectCreate(null, { key: { "__proto__": null } })', + 'ObjectDefineProperties({}, { key: { \'__proto__\': null } })', + 'ObjectCreate(null, { key: { \'__proto__\': null } })', + 'ObjectDefineProperty({}, "key", ObjectCreate(null))', + 'ReflectDefineProperty({}, "key", ObjectCreate(null))', + 'ObjectDefineProperty({}, "key", valueDescriptor)', + 'ReflectDefineProperty({}, "key", valueDescriptor)', + 'ObjectDefineProperty({}, "key", { __proto__: null })', + 'ReflectDefineProperty({}, "key", { __proto__: null })', + 'ObjectDefineProperty({}, "key", { __proto__: null, enumerable: true })', + 'ReflectDefineProperty({}, "key", { __proto__: null, enumerable: true })', + 'ObjectDefineProperty({}, "key", { "__proto__": null })', + 'ReflectDefineProperty({}, "key", { "__proto__": null })', + 'ObjectDefineProperty({}, "key", { \'__proto__\': null })', + 'ReflectDefineProperty({}, "key", { \'__proto__\': null })', + 'async function myFn() { return { __proto__: null } }', + 'async function myFn() { function myFn() { return {} } return { __proto__: null } }', + 'const myFn = async function myFn() { return { __proto__: null } }', + 'const myFn = async function () { return { __proto__: null } }', + 'const myFn = async () => { return { __proto__: null } }', + 'const myFn = async () => ({ __proto__: null })', + 'function myFn() { return {} }', + 'const myFn = function myFn() { return {} }', + 'const myFn = function () { return {} }', + 'const myFn = () => { return {} }', + 'const myFn = () => ({})', + 'StringPrototypeReplace("some string", "some string", "some replacement")', + 'StringPrototypeReplaceAll("some string", "some string", "some replacement")', + 'StringPrototypeSplit("some string", "some string")', + 'new Proxy({}, otherObject)', + 'new Proxy({}, someFactory())', + 'new Proxy({}, { __proto__: null })', + 'new Proxy({}, { __proto__: null, ...{} })', + 'async function name(){return await SafePromiseAll([])}', + 'async function name(){const val = await SafePromiseAll([])}', + ], + invalid: [ + { + code: 'ObjectDefineProperties({}, ObjectGetOwnPropertyDescriptors({}))', + errors: [{ message: /prototype pollution/ }], + }, + { + code: 'ObjectCreate(null, ObjectGetOwnPropertyDescriptors({}))', + errors: [{ message: /prototype pollution/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: {} })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: {} })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { [void 0]: { ...{ __proto__: null } } } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { [void 0]: { ...{ __proto__: null } } } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { __proto__: Object.prototype } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { __proto__: Object.prototype } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { [`__proto__`]: null } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { [`__proto__`]: null } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { enumerable: true } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { enumerable: true } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", {})', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", {})', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", ObjectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: 'ObjectDefineProperty({}, "key", { __proto__: null,...ObjectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ReflectDefineProperty({}, "key", ObjectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: + 'ReflectDefineProperty({}, "key", { __proto__: null,...ObjectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ObjectDefineProperty({}, "key", ReflectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: + 'ObjectDefineProperty({}, "key", { __proto__: null,...ReflectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ReflectDefineProperty({}, "key", ReflectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: + 'ReflectDefineProperty({}, "key", { __proto__: null,...ReflectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ObjectDefineProperty({}, "key", { __proto__: Object.prototype })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", { __proto__: Object.prototype })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", { [`__proto__`]: null })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", { [`__proto__`]: null })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", { enumerable: true })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", { enumerable: true })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'async function myFn(){ return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'async function myFn(){ async function someOtherFn() { return { __proto__: null } } return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'async function myFn(){ if (true) { return {} } return { __proto__: null } }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async function myFn(){ return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async function (){ return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async () => { return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async () => ({})', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'RegExpPrototypeTest(/some regex/, "some string")', + errors: [{ + message: /looks up the "exec" property/, + suggestions: [{ + desc: 'Use RegexpPrototypeExec instead', + output: 'RegExpPrototypeExec(/some regex/, "some string") !== null', + }], + }], + }, + { + code: 'RegExpPrototypeSymbolMatch(/some regex/, "some string")', + errors: [{ message: /looks up the "exec" property/ }], + }, + { + code: 'RegExpPrototypeSymbolMatchAll(/some regex/, "some string")', + errors: [{ message: /looks up the "exec" property/ }], + }, + { + code: 'RegExpPrototypeSymbolSearch(/some regex/, "some string")', + errors: [{ message: /SafeStringPrototypeSearch/ }], + }, + { + code: 'StringPrototypeMatch("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.match property/ }], + }, + { + code: 'let v = StringPrototypeMatch("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.match property/ }], + }, + { + code: 'let v = StringPrototypeMatch("some string", new RegExp("some regex"))', + errors: [{ message: /looks up the Symbol\.match property/ }], + }, + { + code: 'StringPrototypeMatchAll("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.matchAll property/ }], + }, + { + code: 'let v = StringPrototypeMatchAll("some string", new RegExp("some regex"))', + errors: [{ message: /looks up the Symbol\.matchAll property/ }], + }, + { + code: 'StringPrototypeReplace("some string", /some regex/, "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeReplace("some string", new RegExp("some regex"), "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeReplaceAll("some string", /some regex/, "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeReplaceAll("some string", new RegExp("some regex"), "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeSearch("some string", /some regex/)', + errors: [{ message: /SafeStringPrototypeSearch/ }], + }, + { + code: 'StringPrototypeSplit("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.split property/ }], + }, + { + code: 'new Proxy({}, {})', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'new Proxy({}, { [`__proto__`]: null })', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'new Proxy({}, { __proto__: Object.prototype })', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'new Proxy({}, { ...{ __proto__: null } })', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'PromisePrototypeCatch(promise, ()=>{})', + errors: [{ message: /\bPromisePrototypeThen\b/ }] + }, + { + code: 'PromiseAll([])', + errors: [{ message: /\bSafePromiseAll\b/ }] + }, + { + code: 'async function fn(){await SafePromiseAll([])}', + errors: [{ message: /\bSafePromiseAllReturnVoid\b/ }] + }, + { + code: 'async function fn(){await SafePromiseAllSettled([])}', + errors: [{ message: /\bSafePromiseAllSettledReturnVoid\b/ }] + }, + { + code: 'PromiseAllSettled([])', + errors: [{ message: /\bSafePromiseAllSettled\b/ }] + }, + { + code: 'PromiseAny([])', + errors: [{ message: /\bSafePromiseAny\b/ }] + }, + { + code: 'PromiseRace([])', + errors: [{ message: /\bSafePromiseRace\b/ }] + }, + { + code: 'ArrayPrototypeConcat([])', + errors: [{ message: /\bisConcatSpreadable\b/ }] + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-crypto-check.js b/test/js/node/test/parallel/test-eslint-crypto-check.js new file mode 100644 index 0000000000..2b2c0c2dd1 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-crypto-check.js @@ -0,0 +1,77 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/crypto-check'); + +const message = 'Please add a hasCrypto check to allow this test to be ' + + 'skipped when Node is built "--without-ssl".'; + +new RuleTester().run('crypto-check', rule, { + valid: [ + 'foo', + 'crypto', + ` + if (!common.hasCrypto) { + common.skip("missing crypto"); + } + require("crypto"); + `, + ` + if (!common.hasCrypto) { + common.skip("missing crypto"); + } + internalBinding("crypto"); + `, + ], + invalid: [ + { + code: 'require("common")\n' + + 'require("crypto")\n' + + 'if (!common.hasCrypto) {\n' + + ' common.skip("missing crypto");\n' + + '}', + errors: [{ message }] + }, + { + code: 'require("common")\n' + + 'require("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'require("crypto")' + }, + { + code: 'require("common")\n' + + 'if (common.foo) {}\n' + + 'require("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'if (common.foo) {}\n' + + 'require("crypto")' + }, + { + code: 'require("common")\n' + + 'if (common.foo) {}\n' + + 'internalBinding("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'if (common.foo) {}\n' + + 'internalBinding("crypto")' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-documented-deprecation-codes.js b/test/js/node/test/parallel/test-eslint-documented-deprecation-codes.js new file mode 100644 index 0000000000..dc3dc46b7e --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-documented-deprecation-codes.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.hasIntl) + common.skip('missing Intl'); +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/documented-deprecation-codes'); + +const mdFile = 'doc/api/deprecations.md'; + +const invalidCode = 'UNDOCUMENTED INVALID CODE'; + +new RuleTester().run('documented-deprecation-codes', rule, { + valid: [ + ` + deprecate(function() { + return this.getHeaders(); + }, 'OutgoingMessage.prototype._headers is deprecated', 'DEP0066') + `, + ], + invalid: [ + { + code: ` + deprecate(function foo(){}, 'bar', '${invalidCode}'); + `, + errors: [ + { + message: `"${invalidCode}" does not match the expected pattern`, + line: 2 + }, + { + message: `"${invalidCode}" is not documented in ${mdFile}`, + line: 2 + }, + ] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-documented-errors.js b/test/js/node/test/parallel/test-eslint-documented-errors.js new file mode 100644 index 0000000000..03131306d7 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-documented-errors.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/documented-errors'); + +const invalidCode = 'UNDOCUMENTED ERROR CODE'; + +new RuleTester().run('documented-errors', rule, { + valid: [ + ` + E('ERR_ASSERTION', 'foo'); + `, + ], + invalid: [ + { + code: ` + E('${invalidCode}', 'bar'); + `, + errors: [ + { + message: `"${invalidCode}" is not documented in doc/api/errors.md`, + line: 2 + }, + { + message: + `doc/api/errors.md does not have an anchor for "${invalidCode}"`, + line: 2 + }, + ] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-duplicate-requires.js b/test/js/node/test/parallel/test-eslint-duplicate-requires.js new file mode 100644 index 0000000000..f2a11b37ca --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-duplicate-requires.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const { RuleTester } = require('../../tools/eslint/node_modules/eslint'); +const rule = require('../../tools/eslint-rules/no-duplicate-requires'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}).run('no-duplicate-requires', rule, { + valid: [ + { + code: 'require("a"); require("b"); (function() { require("a"); });', + }, + { + code: 'require(a); require(a);', + }, + ], + invalid: [ + { + code: 'require("a"); require("a");', + errors: [{ message: '\'a\' require is duplicated.' }], + }, + ], +}); diff --git a/test/js/node/test/parallel/test-eslint-eslint-check.js b/test/js/node/test/parallel/test-eslint-eslint-check.js new file mode 100644 index 0000000000..ca34497c32 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-eslint-check.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/eslint-check'); + +const message = 'Please add a skipIfEslintMissing() call to allow this ' + + 'test to be skipped when Node.js is built ' + + 'from a source tarball.'; + +new RuleTester().run('eslint-check', rule, { + valid: [ + 'foo;', + 'require("common")\n' + + 'common.skipIfEslintMissing();\n' + + 'require("../../tools/eslint/node_modules/eslint")', + ], + invalid: [ + { + code: 'require("common")\n' + + 'require("../../tools/eslint/node_modules/eslint").RuleTester', + errors: [{ message }], + output: 'require("common")\n' + + 'common.skipIfEslintMissing();\n' + + 'require("../../tools/eslint/node_modules/eslint").RuleTester' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-inspector-check.js b/test/js/node/test/parallel/test-eslint-inspector-check.js new file mode 100644 index 0000000000..c60dcf0874 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-inspector-check.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/inspector-check'); + +const message = 'Please add a skipIfInspectorDisabled() call to allow this ' + + 'test to be skipped when Node is built ' + + '\'--without-inspector\'.'; + +new RuleTester().run('inspector-check', rule, { + valid: [ + 'foo;', + 'require("common")\n' + + 'common.skipIfInspectorDisabled();\n' + + 'require("inspector")', + ], + invalid: [ + { + code: 'require("common")\n' + + 'require("inspector")', + errors: [{ message }], + output: 'require("common")\n' + + 'common.skipIfInspectorDisabled();\n' + + 'require("inspector")' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-lowercase-name-for-primitive.js b/test/js/node/test/parallel/test-eslint-lowercase-name-for-primitive.js new file mode 100644 index 0000000000..f8029d7c8b --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-lowercase-name-for-primitive.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/lowercase-name-for-primitive'); + +new RuleTester().run('lowercase-name-for-primitive', rule, { + valid: [ + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "a", ["string", "number"])', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "string")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "number")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "boolean")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "null")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "undefined")', + ], + invalid: [ + { + code: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'Number')", + errors: [{ message: 'primitive should use lowercase: Number' }], + output: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'number')", + }, + { + code: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'STRING')", + errors: [{ message: 'primitive should use lowercase: STRING' }], + output: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'string')", + }, + { + code: "new e.TypeError('ERR_INVALID_ARG_TYPE', a, ['String','Number'])", + errors: [ + { message: 'primitive should use lowercase: String' }, + { message: 'primitive should use lowercase: Number' }, + ], + output: "new e.TypeError('ERR_INVALID_ARG_TYPE', a, ['string','number'])", + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-no-array-destructuring.js b/test/js/node/test/parallel/test-eslint-no-array-destructuring.js new file mode 100644 index 0000000000..3f9b6e0094 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-no-array-destructuring.js @@ -0,0 +1,139 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const { RuleTester } = require('../../tools/eslint/node_modules/eslint'); +const rule = require('../../tools/eslint-rules/no-array-destructuring'); + +const USE_OBJ_DESTRUCTURING = + 'Use object destructuring instead of array destructuring.'; +const USE_ARRAY_METHODS = + 'Use primordials.ArrayPrototypeSlice to avoid unsafe array iteration.'; + +new RuleTester() + .run('no-array-destructuring', rule, { + valid: [ + 'const first = [1, 2, 3][0];', + 'const {0:first} = [1, 2, 3];', + '({1:elem} = array);', + 'function name(param, { 0: key, 1: value },) {}', + ], + invalid: [ + { + code: 'const [Array] = args;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const {0:Array} = args;' + }, + { + code: 'const [ , res] = args;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const { 1:res} = args;', + }, + { + code: '[, elem] = options;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '({ 1:elem} = options);', + }, + { + code: 'const {values:[elem]} = options;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const {values:{0:elem}} = options;', + }, + { + code: '[[[elem]]] = options;', + errors: [ + { message: USE_OBJ_DESTRUCTURING }, + { message: USE_OBJ_DESTRUCTURING }, + { message: USE_OBJ_DESTRUCTURING }, + ], + output: '({0:[[elem]]} = options);', + }, + { + code: '[, ...rest] = options;', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'for(const [key, value] of new Map);', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'for(const {0:key, 1:value} of new Map);', + }, + { + code: 'let [first,,,fourth] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let {0:first,3:fourth} = array;', + }, + { + code: 'let [,second,,fourth] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let {1:second,3:fourth} = array;', + }, + { + code: 'let [ ,,,fourth ] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let { 3:fourth } = array;', + }, + { + code: 'let [,,,fourth, fifth,, minorFall, majorLift,...music] = arr;', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'function map([key, value]) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value}) {}', + }, + { + code: 'function map([key, value],) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value},) {}', + }, + { + code: '(function([key, value]) {})', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '(function({0:key, 1:value}) {})', + }, + { + code: '(function([key, value] = [null, 0]) {})', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '(function({0:key, 1:value} = [null, 0]) {})', + }, + { + code: 'function map([key, ...values]) {}', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'function map([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value}, ...args) {}', + }, + { + code: 'async function map([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'async function map({0:key, 1:value}, ...args) {}', + }, + { + code: 'async function* generator([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'async function* generator({0:key, 1:value}, ...args) {}', + }, + { + code: 'function* generator([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function* generator({0:key, 1:value}, ...args) {}', + }, + { + code: 'const cb = ([key, value], ...args) => {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const cb = ({0:key, 1:value}, ...args) => {}', + }, + { + code: 'class name{ method([key], ...args){} }', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'class name{ method({0:key}, ...args){} }', + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-no-unescaped-regexp-dot.js b/test/js/node/test/parallel/test-eslint-no-unescaped-regexp-dot.js new file mode 100644 index 0000000000..457b76a2fc --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-no-unescaped-regexp-dot.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/no-unescaped-regexp-dot'); + +new RuleTester().run('no-unescaped-regexp-dot', rule, { + valid: [ + '/foo/', + String.raw`/foo\./`, + '/.+/', + '/.*/', + '/.?/', + '/.{5}/', + String.raw`/\\\./`, + ], + invalid: [ + { + code: '/./', + errors: [{ message: 'Unescaped dot character in regular expression' }] + }, + { + code: String.raw`/\\./`, + errors: [{ message: 'Unescaped dot character in regular expression' }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-non-ascii-character.js b/test/js/node/test/parallel/test-eslint-non-ascii-character.js new file mode 100644 index 0000000000..2d71fda279 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-non-ascii-character.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/non-ascii-character'); + +new RuleTester().run('non-ascii-characters', rule, { + valid: [ + { + code: 'console.log("fhqwhgads")', + options: [] + }, + ], + invalid: [ + { + code: 'console.log("μ")', + options: [], + errors: [{ message: "Non-ASCII character 'μ' detected." }], + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-assert-iferror.js b/test/js/node/test/parallel/test-eslint-prefer-assert-iferror.js new file mode 100644 index 0000000000..cd8f46146d --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-assert-iferror.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-assert-iferror'); + +new RuleTester().run('prefer-assert-iferror', rule, { + valid: [ + 'assert.ifError(err);', + 'if (err) throw somethingElse;', + 'throw err;', + 'if (err) { throw somethingElse; }', + ], + invalid: [ + { + code: 'require("assert");\n' + + 'if (err) throw err;', + errors: [{ message: 'Use assert.ifError(err) instead.' }], + output: 'require("assert");\n' + + 'assert.ifError(err);' + }, + { + code: 'require("assert");\n' + + 'if (error) { throw error; }', + errors: [{ message: 'Use assert.ifError(error) instead.' }], + output: 'require("assert");\n' + + 'assert.ifError(error);' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-assert-methods.js b/test/js/node/test/parallel/test-eslint-prefer-assert-methods.js new file mode 100644 index 0000000000..a77ffd01d4 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-assert-methods.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-assert-methods'); + +new RuleTester().run('prefer-assert-methods', rule, { + valid: [ + 'assert.strictEqual(foo, bar);', + 'assert(foo === bar && baz);', + 'assert.notStrictEqual(foo, bar);', + 'assert(foo !== bar && baz);', + 'assert.equal(foo, bar);', + 'assert(foo == bar && baz);', + 'assert.notEqual(foo, bar);', + 'assert(foo != bar && baz);', + 'assert.ok(foo);', + 'assert.ok(foo != bar);', + 'assert.ok(foo === bar && baz);', + ], + invalid: [ + { + code: 'assert(foo == bar);', + errors: [{ + message: "'assert.equal' should be used instead of '=='" + }], + output: 'assert.equal(foo, bar);' + }, + { + code: 'assert(foo === bar);', + errors: [{ + message: "'assert.strictEqual' should be used instead of '==='" + }], + output: 'assert.strictEqual(foo, bar);' + }, + { + code: 'assert(foo != bar);', + errors: [{ + message: "'assert.notEqual' should be used instead of '!='" + }], + output: 'assert.notEqual(foo, bar);' + }, + { + code: 'assert(foo !== bar);', + errors: [{ + message: "'assert.notStrictEqual' should be used instead of '!=='" + }], + output: 'assert.notStrictEqual(foo, bar);' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-common-mustnotcall.js b/test/js/node/test/parallel/test-eslint-prefer-common-mustnotcall.js new file mode 100644 index 0000000000..a805b3c4ae --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-common-mustnotcall.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-common-mustnotcall'); + +const message = 'Please use common.mustNotCall(msg) instead of ' + + 'common.mustCall(fn, 0) or common.mustCall(0).'; + +new RuleTester().run('prefer-common-mustnotcall', rule, { + valid: [ + 'common.mustNotCall(fn)', + 'common.mustCall(fn)', + 'common.mustCall(fn, 1)', + ], + invalid: [ + { + code: 'common.mustCall(fn, 0)', + errors: [{ message }] + }, + { + code: 'common.mustCall(0)', + errors: [{ message }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-common-mustsucceed.js b/test/js/node/test/parallel/test-eslint-prefer-common-mustsucceed.js new file mode 100644 index 0000000000..134d8bbc05 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-common-mustsucceed.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-common-mustsucceed'); + +const msg1 = 'Please use common.mustSucceed instead of ' + + 'common.mustCall(assert.ifError).'; +const msg2 = 'Please use common.mustSucceed instead of ' + + 'common.mustCall with assert.ifError.'; + +new RuleTester().run('prefer-common-mustsucceed', rule, { + valid: [ + 'foo((err) => assert.ifError(err))', + 'foo(function(err) { assert.ifError(err) })', + 'foo(assert.ifError)', + 'common.mustCall((err) => err)', + ], + invalid: [ + { + code: 'common.mustCall(assert.ifError)', + errors: [{ message: msg1 }] + }, + { + code: 'common.mustCall((err) => assert.ifError(err))', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall((e) => assert.ifError(e))', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall(function(e) { assert.ifError(e); })', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall(function(e) { return assert.ifError(e); })', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall(function(e) {{ assert.ifError(e); }})', + errors: [{ message: msg2 }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-primordials.js b/test/js/node/test/parallel/test-eslint-prefer-primordials.js new file mode 100644 index 0000000000..61c84cbadd --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-primordials.js @@ -0,0 +1,265 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-primordials'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}) + .run('prefer-primordials', rule, { + valid: [ + 'new Array()', + 'JSON.stringify({})', + 'class A { *[Symbol.iterator] () { yield "a"; } }', + 'const a = { *[Symbol.iterator] () { yield "a"; } }', + 'Object.defineProperty(o, Symbol.toStringTag, { value: "o" })', + 'parseInt("10")', + ` + const { Reflect } = primordials; + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + { + code: 'const { Array } = primordials; new Array()', + options: [{ name: 'Array' }] + }, + { + code: 'const { JSONStringify } = primordials; JSONStringify({})', + options: [{ name: 'JSON' }] + }, + { + code: 'const { SymbolFor } = primordials; SymbolFor("xxx")', + options: [{ name: 'Symbol' }] + }, + { + code: ` + const { SymbolIterator } = primordials; + class A { *[SymbolIterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }] + }, + { + code: ` + const { Symbol } = primordials; + const a = { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol', ignore: ['iterator'] }] + }, + { + code: ` + const { ObjectDefineProperty, Symbol } = primordials; + ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" }); + const val = Symbol.toStringTag; + const { toStringTag } = Symbol; + `, + options: [{ name: 'Symbol', ignore: ['toStringTag'] }] + }, + { + code: 'const { Symbol } = primordials; Symbol.for("xxx")', + options: [{ name: 'Symbol', ignore: ['for'] }] + }, + { + code: 'const { NumberParseInt } = primordials; NumberParseInt("xxx")', + options: [{ name: 'parseInt', into: 'Number' }] + }, + { + code: ` + const { ReflectOwnKeys } = primordials; + module.exports = function() { + ReflectOwnKeys({}) + } + `, + options: [{ name: 'Reflect' }], + }, + { + code: 'const { Map } = primordials; new Map()', + options: [{ name: 'Map', into: 'Safe' }], + }, + { + code: ` + const { Function } = primordials; + const rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + }, + { + code: ` + const { Function } = primordials; + let rename; + rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + }, + { + code: 'function identifier() {}', + options: [{ name: 'identifier' }] + }, + { + code: 'function* identifier() {}', + options: [{ name: 'identifier' }] + }, + { + code: 'class identifier {}', + options: [{ name: 'identifier' }] + }, + { + code: 'new class { identifier(){} }', + options: [{ name: 'identifier' }] + }, + { + code: 'const a = { identifier: \'4\' }', + options: [{ name: 'identifier' }] + }, + { + code: 'identifier:{const a = 4}', + options: [{ name: 'identifier' }] + }, + { + code: 'switch(0){case identifier:}', + options: [{ name: 'identifier' }] + }, + ], + invalid: [ + { + code: 'new Array()', + options: [{ name: 'Array' }], + errors: [{ message: /const { Array } = primordials/ }] + }, + { + code: 'JSON.parse("{}")', + options: [{ name: 'JSON' }], + errors: [ + { message: /const { JSONParse } = primordials/ }, + ] + }, + { + code: 'const { JSON } = primordials; JSON.parse("{}")', + options: [{ name: 'JSON' }], + errors: [{ message: /const { JSONParse } = primordials/ }] + }, + { + code: 'Symbol.for("xxx")', + options: [{ name: 'Symbol' }], + errors: [ + { message: /const { SymbolFor } = primordials/ }, + ] + }, + { + code: 'const { Symbol } = primordials; Symbol.for("xxx")', + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolFor } = primordials/ }] + }, + { + code: ` + const { Symbol } = primordials; + class A { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolIterator } = primordials/ }] + }, + { + code: ` + const { Symbol } = primordials; + const a = { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolIterator } = primordials/ }] + }, + { + code: ` + const { ObjectDefineProperty, Symbol } = primordials; + ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" }) + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolToStringTag } = primordials/ }] + }, + { + code: ` + const { Number } = primordials; + Number.parseInt('10') + `, + options: [{ name: 'Number' }], + errors: [{ message: /const { NumberParseInt } = primordials/ }] + }, + { + code: 'parseInt("10")', + options: [{ name: 'parseInt', into: 'Number' }], + errors: [{ message: /const { NumberParseInt } = primordials/ }] + }, + { + code: ` + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + options: [{ name: 'Reflect' }], + errors: [{ message: /const { ReflectOwnKeys } = primordials/ }] + }, + { + code: ` + const { Reflect } = primordials; + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + options: [{ name: 'Reflect' }], + errors: [{ message: /const { ReflectOwnKeys } = primordials/ }] + }, + { + code: 'new Map()', + options: [{ name: 'Map', into: 'Safe' }], + errors: [{ message: /const { SafeMap } = primordials/ }] + }, + { + code: ` + const { Function } = primordials; + const noop = Function.prototype; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { FunctionPrototype } = primordials/ }] + }, + { + code: ` + const obj = { Function }; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + { + code: ` + const rename = Function; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + { + code: ` + const rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + { + code: ` + let rename; + rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-prefer-proto.js b/test/js/node/test/parallel/test-eslint-prefer-proto.js new file mode 100644 index 0000000000..7e927c967b --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-proto.js @@ -0,0 +1,69 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-proto'); + +new RuleTester().run('prefer-common-mustsucceed', rule, { + valid: [ + '({ __proto__: null })', + 'const x = { __proto__: null };', + ` + class X { + field = { __proto__: null }; + + constructor() { + this.x = { __proto__: X }; + } + } + `, + 'foo({ __proto__: Array.prototype })', + '({ "__proto__": null })', + "({ '__proto__': null })", + 'ObjectCreate(null, undefined)', + 'Object.create(null, undefined)', + 'Object.create(null, undefined, undefined)', + 'ObjectCreate(null, descriptors)', + 'Object.create(null, descriptors)', + 'ObjectCreate(Foo.prototype, descriptors)', + 'Object.create(Foo.prototype, descriptors)', + ], + invalid: [ + { + code: 'ObjectCreate(null)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'Object.create(null)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'ObjectCreate(null,)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'Object.create(null,)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'ObjectCreate(Foo)', + output: '{ __proto__: Foo }', + errors: [{ messageId: 'error', data: { value: 'Foo' } }], + }, + { + code: 'Object.create(Foo)', + output: '{ __proto__: Foo }', + errors: [{ messageId: 'error', data: { value: 'Foo' } }], + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-util-format-errors.js b/test/js/node/test/parallel/test-eslint-prefer-util-format-errors.js new file mode 100644 index 0000000000..3410265e9f --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-util-format-errors.js @@ -0,0 +1,32 @@ +'use strict'; + +/* eslint-disable no-template-curly-in-string */ + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-util-format-errors'); + +new RuleTester() + .run('prefer-util-format-errors', rule, { + valid: [ + 'E(\'ABC\', \'abc\');', + 'E(\'ABC\', (arg1, arg2) => `${arg2}${arg1}`);', + 'E(\'ABC\', (arg1, arg2) => `${arg1}{arg2.something}`);', + 'E(\'ABC\', (arg1, arg2) => fn(arg1, arg2));', + ], + invalid: [ + { + code: 'E(\'ABC\', (arg1, arg2) => `${arg1}${arg2}`);', + errors: [{ + message: 'Please use a printf-like formatted string that ' + + 'util.format can consume.' + }] + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-require-common-first.js b/test/js/node/test/parallel/test-eslint-require-common-first.js new file mode 100644 index 0000000000..ef19f95b97 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-require-common-first.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/require-common-first'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}).run('require-common-first', rule, { + valid: [ + { + code: 'require("common")\n' + + 'require("assert")' + }, + ], + invalid: [ + { + code: 'require("assert")\n' + + 'require("common")', + errors: [{ message: 'Mandatory module "common" must be loaded ' + + 'before any other modules.' }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-required-modules.js b/test/js/node/test/parallel/test-eslint-required-modules.js new file mode 100644 index 0000000000..4704163a38 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-required-modules.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/required-modules'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}).run('required-modules', rule, { + valid: [ + { + code: 'require("common")', + options: [{ common: 'common' }] + }, + { + code: 'foo', + options: [] + }, + { + code: 'require("common")', + options: [{ common: 'common(/index\\.(m)?js)?$' }] + }, + { + code: 'require("common/index.js")', + options: [{ common: 'common(/index\\.(m)?js)?$' }] + }, + ], + invalid: [ + { + code: 'foo', + options: [{ common: 'common' }], + errors: [{ message: 'Mandatory module "common" must be loaded.' }] + }, + { + code: 'require("common/fixtures.js")', + options: [{ common: 'common(/index\\.(m)?js)?$' }], + errors: [{ + message: + 'Mandatory module "common" must be loaded.' + }] + }, + { + code: 'require("somethingElse")', + options: [{ common: 'common' }], + errors: [{ message: 'Mandatory module "common" must be loaded.' }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eval-strict-referenceerror.js b/test/js/node/test/parallel/test-eval-strict-referenceerror.js new file mode 100644 index 0000000000..97f2b15ba0 --- /dev/null +++ b/test/js/node/test/parallel/test-eval-strict-referenceerror.js @@ -0,0 +1,27 @@ +/* eslint-disable strict */ +require('../common'); + +// In Node.js 0.10, a bug existed that caused strict functions to not capture +// their environment when evaluated. When run in 0.10 `test()` fails with a +// `ReferenceError`. See https://github.com/nodejs/node/issues/2245 for details. + +const assert = require('assert'); + +function test() { + + const code = [ + 'var foo = {m: 1};', + '', + 'function bar() {', + '\'use strict\';', + 'return foo; // foo isn\'t captured in 0.10', + '};', + ].join('\n'); + + eval(code); + + return bar(); // eslint-disable-line no-undef + +} + +assert.deepStrictEqual(test(), { m: 1 }); diff --git a/test/js/node/test/parallel/test-eval.js b/test/js/node/test/parallel/test-eval.js new file mode 100644 index 0000000000..46a4b7c546 --- /dev/null +++ b/test/js/node/test/parallel/test-eval.js @@ -0,0 +1,7 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Verify that eval is allowed by default. +assert.strictEqual(eval('"eval"'), 'eval'); diff --git a/test/js/node/test/parallel/event-emitter-add-listeners.test.js b/test/js/node/test/parallel/test-event-emitter-add-listeners.js similarity index 53% rename from test/js/node/test/parallel/event-emitter-add-listeners.test.js rename to test/js/node/test/parallel/test-event-emitter-add-listeners.js index fec7a8fdd2..f42d1f2487 100644 --- a/test/js/node/test/parallel/event-emitter-add-listeners.test.js +++ b/test/js/node/test/parallel/test-event-emitter-add-listeners.js @@ -1,6 +1,3 @@ -//#FILE: test-event-emitter-add-listeners.js -//#SHA1: 25d8611a8cf3694d26e53bb90f760beeb3bb1946 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,70 +19,68 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const EventEmitter = require("events"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); -test("EventEmitter addListener functionality", () => { +{ const ee = new EventEmitter(); const events_new_listener_emitted = []; const listeners_new_listener_emitted = []; // Sanity check - expect(ee.addListener).toBe(ee.on); + assert.strictEqual(ee.addListener, ee.on); - ee.on("newListener", function (event, listener) { + ee.on('newListener', function(event, listener) { // Don't track newListener listeners. - if (event === "newListener") return; + if (event === 'newListener') + return; events_new_listener_emitted.push(event); listeners_new_listener_emitted.push(listener); }); - const hello = jest.fn((a, b) => { - expect(a).toBe("a"); - expect(b).toBe("b"); + const hello = common.mustCall(function(a, b) { + assert.strictEqual(a, 'a'); + assert.strictEqual(b, 'b'); }); - ee.once("newListener", function (name, listener) { - expect(name).toBe("hello"); - expect(listener).toBe(hello); - expect(this.listeners("hello")).toEqual([]); + ee.once('newListener', function(name, listener) { + assert.strictEqual(name, 'hello'); + assert.strictEqual(listener, hello); + assert.deepStrictEqual(this.listeners('hello'), []); }); - ee.on("hello", hello); - ee.once("foo", () => { - throw new Error("This should not be called"); - }); - expect(events_new_listener_emitted).toEqual(["hello", "foo"]); - expect(listeners_new_listener_emitted).toEqual([hello, expect.any(Function)]); + ee.on('hello', hello); + ee.once('foo', assert.fail); + assert.deepStrictEqual(['hello', 'foo'], events_new_listener_emitted); + assert.deepStrictEqual([hello, assert.fail], listeners_new_listener_emitted); - ee.emit("hello", "a", "b"); - expect(hello).toHaveBeenCalledTimes(1); -}); + ee.emit('hello', 'a', 'b'); +} -test("setMaxListeners with 0 does not throw", () => { +// Just make sure that this doesn't throw: +{ const f = new EventEmitter(); - expect(() => { - f.setMaxListeners(0); - }).not.toThrow(); -}); -test("newListener event and listener order", () => { + f.setMaxListeners(0); +} + +{ const listen1 = () => {}; const listen2 = () => {}; const ee = new EventEmitter(); - ee.once("newListener", function () { - expect(ee.listeners("hello")).toEqual([]); - ee.once("newListener", function () { - expect(ee.listeners("hello")).toEqual([]); + ee.once('newListener', function() { + assert.deepStrictEqual(ee.listeners('hello'), []); + ee.once('newListener', function() { + assert.deepStrictEqual(ee.listeners('hello'), []); }); - ee.on("hello", listen2); + ee.on('hello', listen2); }); - ee.on("hello", listen1); + ee.on('hello', listen1); // The order of listeners on an event is not always the order in which the // listeners were added. - expect(ee.listeners("hello")).toEqual([listen2, listen1]); -}); - -//<#END_FILE: test-event-emitter-add-listeners.js + assert.deepStrictEqual(ee.listeners('hello'), [listen2, listen1]); +} diff --git a/test/js/node/test/parallel/test-event-emitter-check-listener-leaks.js b/test/js/node/test/parallel/test-event-emitter-check-listener-leaks.js new file mode 100644 index 0000000000..04ef3ddab1 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-check-listener-leaks.js @@ -0,0 +1,103 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); + +// default +{ + const e = new events.EventEmitter(); + + for (let i = 0; i < 10; i++) { + e.on('default', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.default, 'warned')); + e.on('default', common.mustNotCall()); + assert.ok(e._events.default.warned); + + // symbol + const symbol = Symbol('symbol'); + e.setMaxListeners(1); + e.on(symbol, common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events[symbol], 'warned')); + e.on(symbol, common.mustNotCall()); + assert.ok(Object.hasOwn(e._events[symbol], 'warned')); + + // specific + e.setMaxListeners(5); + for (let i = 0; i < 5; i++) { + e.on('specific', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.specific, 'warned')); + e.on('specific', common.mustNotCall()); + assert.ok(e._events.specific.warned); + + // only one + e.setMaxListeners(1); + e.on('only one', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events['only one'], 'warned')); + e.on('only one', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events['only one'], 'warned')); + + // unlimited + e.setMaxListeners(0); + for (let i = 0; i < 1000; i++) { + e.on('unlimited', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.unlimited, 'warned')); +} + +// process-wide +{ + events.EventEmitter.defaultMaxListeners = 42; + const e = new events.EventEmitter(); + + for (let i = 0; i < 42; ++i) { + e.on('fortytwo', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.fortytwo, 'warned')); + e.on('fortytwo', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.fortytwo, 'warned')); + delete e._events.fortytwo.warned; + + events.EventEmitter.defaultMaxListeners = 44; + e.on('fortytwo', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events.fortytwo, 'warned')); + e.on('fortytwo', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.fortytwo, 'warned')); +} + +// But _maxListeners still has precedence over defaultMaxListeners +{ + events.EventEmitter.defaultMaxListeners = 42; + const e = new events.EventEmitter(); + e.setMaxListeners(1); + e.on('uno', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events.uno, 'warned')); + e.on('uno', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.uno, 'warned')); + + // chainable + assert.strictEqual(e, e.setMaxListeners(1)); +} diff --git a/test/js/node/test/parallel/test-event-emitter-emit-context.js b/test/js/node/test/parallel/test-event-emitter-emit-context.js new file mode 100644 index 0000000000..e4c73cadc5 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-emit-context.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +// Test emit called by other context +const EE = new EventEmitter(); + +// Works as expected if the context has no `constructor.name` +{ + const ctx = { __proto__: null }; + assert.throws( + () => EE.emit.call(ctx, 'error', new Error('foo')), + common.expectsError({ name: 'Error', message: 'foo' }) + ); +} + +assert.strictEqual(EE.emit.call({}, 'foo'), false); diff --git a/test/js/node/test/parallel/test-event-emitter-error-monitor.js b/test/js/node/test/parallel/test-event-emitter-error-monitor.js new file mode 100644 index 0000000000..8254fc6254 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-error-monitor.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const EE = new EventEmitter(); +const theErr = new Error('MyError'); + +EE.on( + EventEmitter.errorMonitor, + common.mustCall(function onErrorMonitor(e) { + assert.strictEqual(e, theErr); + }, 3) +); + +// Verify with no error listener +assert.throws( + () => EE.emit('error', theErr), theErr +); + +// Verify with error listener +EE.once('error', common.mustCall((e) => assert.strictEqual(e, theErr))); +EE.emit('error', theErr); + + +// Verify it works with once +process.nextTick(() => EE.emit('error', theErr)); +assert.rejects(EventEmitter.once(EE, 'notTriggered'), theErr).then(common.mustCall()); + +// Only error events trigger error monitor +EE.on('aEvent', common.mustCall()); +EE.emit('aEvent'); diff --git a/test/js/node/test/parallel/test-event-emitter-get-max-listeners.js b/test/js/node/test/parallel/test-event-emitter-get-max-listeners.js new file mode 100644 index 0000000000..43f9f0cebc --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-get-max-listeners.js @@ -0,0 +1,19 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const emitter = new EventEmitter(); + +assert.strictEqual(emitter.getMaxListeners(), EventEmitter.defaultMaxListeners); + +emitter.setMaxListeners(0); +assert.strictEqual(emitter.getMaxListeners(), 0); + +emitter.setMaxListeners(3); +assert.strictEqual(emitter.getMaxListeners(), 3); + +// https://github.com/nodejs/node/issues/523 - second call should not throw. +const recv = {}; +EventEmitter.prototype.on.call(recv, 'event', () => {}); +EventEmitter.prototype.on.call(recv, 'event', () => {}); diff --git a/test/js/node/test/parallel/test-event-emitter-listener-count.js b/test/js/node/test/parallel/test-event-emitter-listener-count.js new file mode 100644 index 0000000000..117d38f575 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-listener-count.js @@ -0,0 +1,18 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const emitter = new EventEmitter(); +emitter.on('foo', () => {}); +emitter.on('foo', () => {}); +emitter.on('baz', () => {}); +// Allow any type +emitter.on(123, () => {}); + +assert.strictEqual(EventEmitter.listenerCount(emitter, 'foo'), 2); +assert.strictEqual(emitter.listenerCount('foo'), 2); +assert.strictEqual(emitter.listenerCount('bar'), 0); +assert.strictEqual(emitter.listenerCount('baz'), 1); +assert.strictEqual(emitter.listenerCount(123), 1); diff --git a/test/js/node/test/parallel/event-emitter-set-max-listeners-side-effects.test.js b/test/js/node/test/parallel/test-event-emitter-method-names.js similarity index 68% rename from test/js/node/test/parallel/event-emitter-set-max-listeners-side-effects.test.js rename to test/js/node/test/parallel/test-event-emitter-method-names.js index 101ccfa80b..684024d027 100644 --- a/test/js/node/test/parallel/event-emitter-set-max-listeners-side-effects.test.js +++ b/test/js/node/test/parallel/test-event-emitter-method-names.js @@ -1,6 +1,3 @@ -//#FILE: test-event-emitter-set-max-listeners-side-effects.js -//#SHA1: 319371e261c5cc46348475cdd789eb9ee56839d8 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,16 +19,17 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const events = require("events"); +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); -test("EventEmitter setMaxListeners should not have side effects", () => { - const e = new events.EventEmitter(); - - expect(e._events).not.toBeInstanceOf(Object); - expect(Object.keys(e._events)).toEqual([]); - e.setMaxListeners(5); - expect(Object.keys(e._events)).toEqual([]); +const E = events.EventEmitter.prototype; +assert.strictEqual(E.constructor.name, 'EventEmitter'); +assert.strictEqual(E.on, E.addListener); // Same method. +assert.strictEqual(E.off, E.removeListener); // Same method. +Object.getOwnPropertyNames(E).forEach(function(name) { + if (name === 'constructor' || name === 'on' || name === 'off') return; + if (typeof E[name] !== 'function') return; + assert.strictEqual(E[name].name, name); }); - -//<#END_FILE: test-event-emitter-set-max-listeners-side-effects.js diff --git a/test/js/node/test/parallel/test-event-emitter-modify-in-emit.js b/test/js/node/test/parallel/test-event-emitter-modify-in-emit.js new file mode 100644 index 0000000000..995fa01d11 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-modify-in-emit.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +let callbacks_called = []; + +const e = new events.EventEmitter(); + +function callback1() { + callbacks_called.push('callback1'); + e.on('foo', callback2); + e.on('foo', callback3); + e.removeListener('foo', callback1); +} + +function callback2() { + callbacks_called.push('callback2'); + e.removeListener('foo', callback2); +} + +function callback3() { + callbacks_called.push('callback3'); + e.removeListener('foo', callback3); +} + +e.on('foo', callback1); +assert.strictEqual(e.listeners('foo').length, 1); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 2); +assert.deepStrictEqual(['callback1'], callbacks_called); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 0); +assert.deepStrictEqual(['callback1', 'callback2', 'callback3'], + callbacks_called); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 0); +assert.deepStrictEqual(['callback1', 'callback2', 'callback3'], + callbacks_called); + +e.on('foo', callback1); +e.on('foo', callback2); +assert.strictEqual(e.listeners('foo').length, 2); +e.removeAllListeners('foo'); +assert.strictEqual(e.listeners('foo').length, 0); + +// Verify that removing callbacks while in emit allows emits to propagate to +// all listeners +callbacks_called = []; + +e.on('foo', callback2); +e.on('foo', callback3); +assert.strictEqual(e.listeners('foo').length, 2); +e.emit('foo'); +assert.deepStrictEqual(['callback2', 'callback3'], callbacks_called); +assert.strictEqual(e.listeners('foo').length, 0); diff --git a/test/js/node/test/parallel/process-uptime.test.js b/test/js/node/test/parallel/test-event-emitter-num-args.js similarity index 60% rename from test/js/node/test/parallel/process-uptime.test.js rename to test/js/node/test/parallel/test-event-emitter-num-args.js index f776e4d2a0..8bb2274303 100644 --- a/test/js/node/test/parallel/process-uptime.test.js +++ b/test/js/node/test/parallel/test-event-emitter-num-args.js @@ -1,6 +1,3 @@ -//#FILE: test-process-uptime.js -//#SHA1: 98140b3c8b495ef62c519ca900eeb15f1ef5b5aa -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,23 +19,36 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); -test("process.uptime() returns a reasonable value", () => { - console.error(process.uptime()); - // Add some wiggle room for different platforms. - // Verify that the returned value is in seconds - - // 15 seconds should be a good estimate. - expect(process.uptime()).toBeLessThanOrEqual(15); +const e = new events.EventEmitter(); +const num_args_emitted = []; + +e.on('numArgs', function() { + const numArgs = arguments.length; + num_args_emitted.push(numArgs); }); -test("process.uptime() increases over time", async () => { - const original = process.uptime(); - - await new Promise(resolve => setTimeout(resolve, 10)); - - const uptime = process.uptime(); - expect(uptime).toBeGreaterThan(original); +e.on('foo', function() { + num_args_emitted.push(arguments.length); }); -//<#END_FILE: test-process-uptime.js +e.on('foo', function() { + num_args_emitted.push(arguments.length); +}); + +e.emit('numArgs'); +e.emit('numArgs', null); +e.emit('numArgs', null, null); +e.emit('numArgs', null, null, null); +e.emit('numArgs', null, null, null, null); +e.emit('numArgs', null, null, null, null, null); + +e.emit('foo', null, null, null, null); + +process.on('exit', function() { + assert.deepStrictEqual(num_args_emitted, [0, 1, 2, 3, 4, 5, 4, 4]); +}); diff --git a/test/js/node/test/parallel/test-event-emitter-prepend.js b/test/js/node/test/parallel/test-event-emitter-prepend.js new file mode 100644 index 0000000000..ffe8544911 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-prepend.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const myEE = new EventEmitter(); +let m = 0; +// This one comes last. +myEE.on('foo', common.mustCall(() => assert.strictEqual(m, 2))); + +// This one comes second. +myEE.prependListener('foo', common.mustCall(() => assert.strictEqual(m++, 1))); + +// This one comes first. +myEE.prependOnceListener('foo', + common.mustCall(() => assert.strictEqual(m++, 0))); + +myEE.emit('foo'); + +// Test fallback if prependListener is undefined. +const stream = require('stream'); + +delete EventEmitter.prototype.prependListener; + +function Writable() { + this.writable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +const w = new Writable(); +const r = new Readable(); +r.pipe(w); diff --git a/test/js/node/test/parallel/test-event-emitter-set-max-listeners-side-effects.js b/test/js/node/test/parallel/test-event-emitter-set-max-listeners-side-effects.js new file mode 100644 index 0000000000..8e66e999a5 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-set-max-listeners-side-effects.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const e = new events.EventEmitter(); + +assert(!(e._events instanceof Object)); +assert.deepStrictEqual(Object.keys(e._events), []); +e.setMaxListeners(5); +assert.deepStrictEqual(Object.keys(e._events), []); diff --git a/test/js/node/test/parallel/test-event-emitter-special-event-names.js b/test/js/node/test/parallel/test-event-emitter-special-event-names.js new file mode 100644 index 0000000000..f34faba946 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-special-event-names.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const ee = new EventEmitter(); +const handler = () => {}; + +assert.deepStrictEqual(ee.eventNames(), []); + +assert.strictEqual(ee._events.hasOwnProperty, undefined); +assert.strictEqual(ee._events.toString, undefined); + +ee.on('__proto__', handler); +ee.on('__defineGetter__', handler); +ee.on('toString', handler); + +assert.deepStrictEqual(ee.eventNames(), [ + '__proto__', + '__defineGetter__', + 'toString', +]); + +assert.deepStrictEqual(ee.listeners('__proto__'), [handler]); +assert.deepStrictEqual(ee.listeners('__defineGetter__'), [handler]); +assert.deepStrictEqual(ee.listeners('toString'), [handler]); + +ee.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); +})); +ee.emit('__proto__', 1); + +process.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); +})); +process.emit('__proto__', 1); diff --git a/test/js/node/test/parallel/event-emitter-subclass.test.js b/test/js/node/test/parallel/test-event-emitter-subclass.js similarity index 62% rename from test/js/node/test/parallel/event-emitter-subclass.test.js rename to test/js/node/test/parallel/test-event-emitter-subclass.js index a48d9e8fdd..a6ef54e5fa 100644 --- a/test/js/node/test/parallel/event-emitter-subclass.test.js +++ b/test/js/node/test/parallel/test-event-emitter-subclass.js @@ -1,6 +1,3 @@ -//#FILE: test-event-emitter-subclass.js -//#SHA1: e7f7e379fcf89318168b9e5d9f38c4af371ada8e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,8 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const EventEmitter = require("events").EventEmitter; +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events').EventEmitter; Object.setPrototypeOf(MyEE.prototype, EventEmitter.prototype); Object.setPrototypeOf(MyEE, EventEmitter); @@ -35,33 +34,24 @@ function MyEE(cb) { EventEmitter.call(this); } -test("MyEE callback is called", () => { - const callback = jest.fn(); - const myee = new MyEE(callback); - expect(callback).toHaveBeenCalledTimes(1); -}); +const myee = new MyEE(common.mustCall()); Object.setPrototypeOf(ErrorEE.prototype, EventEmitter.prototype); Object.setPrototypeOf(ErrorEE, EventEmitter); function ErrorEE() { - this.emit("error", new Error("blerg")); + this.emit('error', new Error('blerg')); } -test("ErrorEE throws error", () => { - expect(() => { - new ErrorEE(); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); +assert.throws(function() { + new ErrorEE(); +}, /blerg/); + +process.on('exit', function() { + assert(!(myee._events instanceof Object)); + assert.deepStrictEqual(Object.keys(myee._events), []); + console.log('ok'); }); -test("MyEE _events is empty after removeAllListeners", () => { - const myee = new MyEE(() => {}); - expect(myee._events).not.toBeInstanceOf(Object); - expect(Object.keys(myee._events)).toEqual([]); -}); function MyEE2() { EventEmitter.call(this); @@ -69,13 +59,9 @@ function MyEE2() { MyEE2.prototype = new EventEmitter(); -test("MyEE2 instances do not share listeners", () => { - const ee1 = new MyEE2(); - const ee2 = new MyEE2(); +const ee1 = new MyEE2(); +const ee2 = new MyEE2(); - ee1.on("x", () => {}); +ee1.on('x', () => {}); - expect(ee2.listenerCount("x")).toBe(0); -}); - -//<#END_FILE: test-event-emitter-subclass.js +assert.strictEqual(ee2.listenerCount('x'), 0); diff --git a/test/js/node/test/parallel/test-event-emitter-symbols.js b/test/js/node/test/parallel/test-event-emitter-symbols.js new file mode 100644 index 0000000000..98d44ff3fa --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-symbols.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const ee = new EventEmitter(); +const foo = Symbol('foo'); +const listener = common.mustCall(); + +ee.on(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), [listener]); + +ee.emit(foo); + +ee.removeAllListeners(); +assert.deepStrictEqual(ee.listeners(foo), []); + +ee.on(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), [listener]); + +ee.removeListener(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), []); diff --git a/test/js/node/test/parallel/test-event-target.js b/test/js/node/test/parallel/test-event-target.js new file mode 100644 index 0000000000..12246b15ae --- /dev/null +++ b/test/js/node/test/parallel/test-event-target.js @@ -0,0 +1,21 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const eventPhases = { + 'NONE': 0, + 'CAPTURING_PHASE': 1, + 'AT_TARGET': 2, + 'BUBBLING_PHASE': 3 +}; + +for (const [prop, value] of Object.entries(eventPhases)) { + // Check if the value of the property matches the expected value + assert.strictEqual(Event[prop], value, `Expected Event.${prop} to be ${value}, but got ${Event[prop]}`); + + const desc = Object.getOwnPropertyDescriptor(Event, prop); + assert.strictEqual(desc.writable, false, `${prop} should not be writable`); + assert.strictEqual(desc.configurable, false, `${prop} should not be configurable`); + assert.strictEqual(desc.enumerable, true, `${prop} should be enumerable`); +} diff --git a/test/js/node/test/parallel/test-events-getmaxlisteners.js b/test/js/node/test/parallel/test-events-getmaxlisteners.js new file mode 100644 index 0000000000..05b4e75b72 --- /dev/null +++ b/test/js/node/test/parallel/test-events-getmaxlisteners.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const { getMaxListeners, EventEmitter, defaultMaxListeners, setMaxListeners } = require('node:events'); + +{ + const ee = new EventEmitter(); + assert.strictEqual(getMaxListeners(ee), defaultMaxListeners); + setMaxListeners(101, ee); + assert.strictEqual(getMaxListeners(ee), 101); +} + +{ + const et = new EventTarget(); + assert.strictEqual(getMaxListeners(et), defaultMaxListeners); + setMaxListeners(101, et); + assert.strictEqual(getMaxListeners(et), 101); +} diff --git a/test/js/node/test/parallel/test-events-list.js b/test/js/node/test/parallel/test-events-list.js new file mode 100644 index 0000000000..4e589b07f2 --- /dev/null +++ b/test/js/node/test/parallel/test-events-list.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const EE = new EventEmitter(); +const m = () => {}; +EE.on('foo', () => {}); +assert.deepStrictEqual(['foo'], EE.eventNames()); +EE.on('bar', m); +assert.deepStrictEqual(['foo', 'bar'], EE.eventNames()); +EE.removeListener('bar', m); +assert.deepStrictEqual(['foo'], EE.eventNames()); +const s = Symbol('s'); +EE.on(s, m); +assert.deepStrictEqual(['foo', s], EE.eventNames()); +EE.removeListener(s, m); +assert.deepStrictEqual(['foo'], EE.eventNames()); diff --git a/test/js/node/test/parallel/test-events-uncaught-exception-stack.js b/test/js/node/test/parallel/test-events-uncaught-exception-stack.js new file mode 100644 index 0000000000..e330f254ae --- /dev/null +++ b/test/js/node/test/parallel/test-events-uncaught-exception-stack.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +// Tests that the error stack where the exception was thrown is *not* appended. + +process.on('uncaughtException', common.mustCall((err) => { + const lines = err.stack.split('\n'); + assert.strictEqual(lines[0], 'Error'); + lines.slice(1).forEach((line) => { + assert.match(line, /^ {4}at/); + }); +})); + +new EventEmitter().emit('error', new Error()); diff --git a/test/js/node/test/parallel/test-eventsource-disabled.js b/test/js/node/test/parallel/test-eventsource-disabled.js new file mode 100644 index 0000000000..ade4f51d99 --- /dev/null +++ b/test/js/node/test/parallel/test-eventsource-disabled.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(typeof EventSource, 'undefined'); diff --git a/test/js/node/test/parallel/test-eventtarget-once-twice.js b/test/js/node/test/parallel/test-eventtarget-once-twice.js new file mode 100644 index 0000000000..3358bab90e --- /dev/null +++ b/test/js/node/test/parallel/test-eventtarget-once-twice.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const { once } = require('events'); + +const et = new EventTarget(); +(async function() { + await once(et, 'foo'); + await once(et, 'foo'); +})().then(common.mustCall()); + +et.dispatchEvent(new Event('foo')); +setImmediate(() => { + et.dispatchEvent(new Event('foo')); +}); diff --git a/test/js/node/test/parallel/test-exception-handler.js b/test/js/node/test/parallel/test-exception-handler.js new file mode 100644 index 0000000000..ca25ccd6ef --- /dev/null +++ b/test/js/node/test/parallel/test-exception-handler.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const MESSAGE = 'catch me if you can'; + +process.on('uncaughtException', common.mustCall((e) => { + console.log('uncaught exception! 1'); + assert.strictEqual(MESSAGE, e.message); +})); + +process.on('uncaughtException', common.mustCall((e) => { + console.log('uncaught exception! 2'); + assert.strictEqual(MESSAGE, e.message); +})); + +setTimeout(() => { + throw new Error(MESSAGE); +}, 10); diff --git a/test/js/node/test/parallel/test-exception-handler2.js b/test/js/node/test/parallel/test-exception-handler2.js new file mode 100644 index 0000000000..ae95d45245 --- /dev/null +++ b/test/js/node/test/parallel/test-exception-handler2.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.on('uncaughtException', function(err) { + console.log(`Caught exception: ${err}`); +}); + +setTimeout(common.mustCall(function() { + console.log('This will still run.'); +}), 50); + +// Intentionally cause an exception, but don't catch it. +nonexistentFunc(); // eslint-disable-line no-undef +assert.fail('This will not run.'); diff --git a/test/js/node/test/parallel/test-fetch.mjs b/test/js/node/test/parallel/test-fetch.mjs new file mode 100644 index 0000000000..bbdb7130ed --- /dev/null +++ b/test/js/node/test/parallel/test-fetch.mjs @@ -0,0 +1,36 @@ +import * as common from '../common/index.mjs'; + +import assert from 'assert'; +import events from 'events'; +import http from 'http'; + +assert.strictEqual(typeof globalThis.fetch, 'function'); +assert.strictEqual(typeof globalThis.FormData, 'function'); +assert.strictEqual(typeof globalThis.Headers, 'function'); +assert.strictEqual(typeof globalThis.Request, 'function'); +assert.strictEqual(typeof globalThis.Response, 'function'); + +{ + const asyncFunction = async function() {}.constructor; + + assert.ok(!(fetch instanceof asyncFunction)); + assert.notStrictEqual(Reflect.getPrototypeOf(fetch), Reflect.getPrototypeOf(async function() {})); + assert.strictEqual(Reflect.getPrototypeOf(fetch), Reflect.getPrototypeOf(function() {})); +} + +const server = http.createServer(common.mustCall((req, res) => { + res.end('Hello world'); +})); +server.listen(0); +await events.once(server, 'listening'); +const port = server.address().port; + +const response = await fetch(`http://localhost:${port}`); + +assert(response instanceof Response); +assert.strictEqual(response.status, 200); +assert.strictEqual(response.statusText, 'OK'); +const body = await response.text(); +assert.strictEqual(body, 'Hello world'); + +server.close(); diff --git a/test/js/node/test/parallel/test-file-read-noexist.js b/test/js/node/test/parallel/test-file-read-noexist.js new file mode 100644 index 0000000000..7293113f22 --- /dev/null +++ b/test/js/node/test/parallel/test-file-read-noexist.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +const filename = fixtures.path('does_not_exist.txt'); +fs.readFile(filename, 'latin1', common.mustCall(function(err, content) { + assert.ok(err); + assert.strictEqual(err.code, 'ENOENT'); +})); diff --git a/test/js/node/test/parallel/test-file-validate-mode-flag.js b/test/js/node/test/parallel/test-file-validate-mode-flag.js new file mode 100644 index 0000000000..62a9ef2ca2 --- /dev/null +++ b/test/js/node/test/parallel/test-file-validate-mode-flag.js @@ -0,0 +1,40 @@ +'use strict'; + +// Checks for crash regression: https://github.com/nodejs/node/issues/37430 + +const common = require('../common'); +const assert = require('assert'); +const { + open, + openSync, + promises: { + open: openPromise, + }, +} = require('fs'); + +// These should throw, not crash. +const invalid = 4_294_967_296; + +assert.throws(() => open(__filename, invalid, common.mustNotCall()), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.throws(() => open(__filename, 0, invalid, common.mustNotCall()), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.throws(() => openSync(__filename, invalid), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.throws(() => openSync(__filename, 0, invalid), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.rejects(openPromise(__filename, invalid), { + code: 'ERR_OUT_OF_RANGE' +}).then(common.mustCall()); + +assert.rejects(openPromise(__filename, 0, invalid), { + code: 'ERR_OUT_OF_RANGE' +}).then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-filehandle-close.js b/test/js/node/test/parallel/test-filehandle-close.js new file mode 100644 index 0000000000..6e55f3f06d --- /dev/null +++ b/test/js/node/test/parallel/test-filehandle-close.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +// Test that using FileHandle.close to close an already-closed fd fails +// with EBADF. + +(async function() { + const fh = await fs.promises.open(__filename); + fs.closeSync(fh.fd); + + await assert.rejects(() => fh.close(), { + code: 'EBADF', + syscall: 'close' + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-buffertype-writesync.js b/test/js/node/test/parallel/test-fs-buffertype-writesync.js new file mode 100644 index 0000000000..5649a00569 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-buffertype-writesync.js @@ -0,0 +1,16 @@ +'use strict'; +require('../common'); + +// This test ensures that writeSync throws for invalid data input. + +const assert = require('assert'); +const fs = require('fs'); + +[ + true, false, 0, 1, Infinity, () => {}, {}, [], undefined, null, +].forEach((value) => { + assert.throws( + () => fs.writeSync(1, value), + { message: /"buffer"/, code: 'ERR_INVALID_ARG_TYPE' } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-chmod-mask.js b/test/js/node/test/parallel/test-fs-chmod-mask.js new file mode 100644 index 0000000000..53f1931be4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-chmod-mask.js @@ -0,0 +1,89 @@ +'use strict'; + +// This tests that the lower bits of mode > 0o777 still works in fs APIs. + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +let mode; +// On Windows chmod is only able to manipulate write permission +if (common.isWindows) { + mode = 0o444; // read-only +} else { + mode = 0o777; +} + +const maskToIgnore = 0o10000; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function test(mode, asString) { + const suffix = asString ? 'str' : 'num'; + const input = asString ? + (mode | maskToIgnore).toString(8) : (mode | maskToIgnore); + + { + const file = tmpdir.resolve(`chmod-async-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + + fs.chmod(file, input, common.mustSucceed(() => { + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + })); + } + + { + const file = tmpdir.resolve(`chmodSync-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + + fs.chmodSync(file, input); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + } + + { + const file = tmpdir.resolve(`fchmod-async-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + fs.open(file, 'w', common.mustSucceed((fd) => { + fs.fchmod(fd, input, common.mustSucceed(() => { + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.close(fd, assert.ifError); + })); + })); + } + + { + const file = tmpdir.resolve(`fchmodSync-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + const fd = fs.openSync(file, 'w'); + + fs.fchmodSync(fd, input); + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + + fs.close(fd, assert.ifError); + } + + if (fs.lchmod) { + const link = tmpdir.resolve(`lchmod-src-${suffix}`); + const file = tmpdir.resolve(`lchmod-dest-${suffix}`); + fs.writeFileSync(file, 'test', 'utf-8'); + fs.symlinkSync(file, link); + + fs.lchmod(link, input, common.mustSucceed(() => { + assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode); + })); + } + + if (fs.lchmodSync) { + const link = tmpdir.resolve(`lchmodSync-src-${suffix}`); + const file = tmpdir.resolve(`lchmodSync-dest-${suffix}`); + fs.writeFileSync(file, 'test', 'utf-8'); + fs.symlinkSync(file, link); + + fs.lchmodSync(link, input); + assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode); + } +} + +test(mode, true); +test(mode, false); diff --git a/test/js/node/test/parallel/test-fs-chown-type-check.js b/test/js/node/test/parallel/test-fs-chown-type-check.js new file mode 100644 index 0000000000..0ca78aa86e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-chown-type-check.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.chown(i, 1, 1, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync(i, 1, 1), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +[false, 'test', {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.chown('not_a_file_that_exists', i, 1, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chown('not_a_file_that_exists', 1, i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync('not_a_file_that_exists', i, 1), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync('not_a_file_that_exists', 1, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-constants.js b/test/js/node/test/parallel/test-fs-constants.js new file mode 100644 index 0000000000..49bcabd808 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-constants.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +// Check if the two constants accepted by chmod() on Windows are defined. +assert.notStrictEqual(fs.constants.S_IRUSR, undefined); +assert.notStrictEqual(fs.constants.S_IWUSR, undefined); diff --git a/test/js/node/test/parallel/test-fs-copyfile-respect-permissions.js b/test/js/node/test/parallel/test-fs-copyfile-respect-permissions.js new file mode 100644 index 0000000000..d668ec63ec --- /dev/null +++ b/test/js/node/test/parallel/test-fs-copyfile-respect-permissions.js @@ -0,0 +1,58 @@ +'use strict'; + +// Test that fs.copyFile() respects file permissions. +// Ref: https://github.com/nodejs/node/issues/26936 + +const common = require('../common'); + +if (!common.isWindows && process.getuid() === 0) + common.skip('as this test should not be run as `root`'); + +if (common.isIBMi) + common.skip('IBMi has a different access permission mechanism'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const fs = require('fs'); + +let n = 0; + +function beforeEach() { + n++; + const source = tmpdir.resolve(`source${n}`); + const dest = tmpdir.resolve(`dest${n}`); + fs.writeFileSync(source, 'source'); + fs.writeFileSync(dest, 'dest'); + fs.chmodSync(dest, '444'); + + const check = (err) => { + const expected = ['EACCES', 'EPERM']; + assert(expected.includes(err.code), `${err.code} not in ${expected}`); + assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'dest'); + return true; + }; + + return { source, dest, check }; +} + +// Test synchronous API. +{ + const { source, dest, check } = beforeEach(); + assert.throws(() => { fs.copyFileSync(source, dest); }, check); +} + +// Test promises API. +{ + const { source, dest, check } = beforeEach(); + (async () => { + await assert.rejects(fs.promises.copyFile(source, dest), check); + })().then(common.mustCall()); +} + +// Test callback API. +{ + const { source, dest, check } = beforeEach(); + fs.copyFile(source, dest, common.mustCall(check)); +} diff --git a/test/js/node/test/parallel/test-fs-empty-readStream.js b/test/js/node/test/parallel/test-fs-empty-readStream.js new file mode 100644 index 0000000000..7cbe4d5005 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-empty-readStream.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const emptyFile = fixtures.path('empty.txt'); + +fs.open(emptyFile, 'r', common.mustSucceed((fd) => { + const read = fs.createReadStream(emptyFile, { fd }); + + read.once('data', common.mustNotCall('data event should not emit')); + + read.once('end', common.mustCall()); +})); + +fs.open(emptyFile, 'r', common.mustSucceed((fd) => { + const read = fs.createReadStream(emptyFile, { fd }); + + read.pause(); + + read.once('data', common.mustNotCall('data event should not emit')); + + read.once('end', common.mustNotCall('end event should not emit')); + + setTimeout(common.mustCall(() => { + assert.strictEqual(read.isPaused(), true); + }), common.platformTimeout(50)); +})); diff --git a/test/js/node/test/parallel/test-fs-existssync-false.js b/test/js/node/test/parallel/test-fs-existssync-false.js new file mode 100644 index 0000000000..e81e6c7a31 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-existssync-false.js @@ -0,0 +1,34 @@ +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +// This test ensures that fs.existsSync doesn't incorrectly return false. +// (especially on Windows) +// https://github.com/nodejs/node-v0.x-archive/issues/3739 + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +let dir = path.resolve(tmpdir.path); + +// Make sure that the tmp directory is clean +tmpdir.refresh(); + +// Make a long path. +for (let i = 0; i < 50; i++) { + dir = `${dir}/1234567890`; + try { + fs.mkdirSync(dir, '0777'); + } catch (e) { + if (e.code !== 'EEXIST') { + throw e; + } + } +} + +// Test if file exists synchronously +assert(fs.existsSync(dir), 'Directory is not accessible'); + +// Test if file exists asynchronously +fs.access(dir, common.mustSucceed()); diff --git a/test/js/node/test/parallel/test-fs-fmap.js b/test/js/node/test/parallel/test-fs-fmap.js new file mode 100644 index 0000000000..c4298f0d0e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-fmap.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const { + O_CREAT = 0, + O_RDONLY = 0, + O_TRUNC = 0, + O_WRONLY = 0, + UV_FS_O_FILEMAP = 0 +} = fs.constants; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Run this test on all platforms. While UV_FS_O_FILEMAP is only available on +// Windows, it should be silently ignored on other platforms. + +const filename = tmpdir.resolve('fmap.txt'); +const text = 'Memory File Mapping Test'; + +const mw = UV_FS_O_FILEMAP | O_TRUNC | O_CREAT | O_WRONLY; +const mr = UV_FS_O_FILEMAP | O_RDONLY; + +fs.writeFileSync(filename, text, { flag: mw }); +const r1 = fs.readFileSync(filename, { encoding: 'utf8', flag: mr }); +assert.strictEqual(r1, text); diff --git a/test/js/node/test/parallel/stream2-finish-pipe.test.js b/test/js/node/test/parallel/test-fs-fsync.js similarity index 54% rename from test/js/node/test/parallel/stream2-finish-pipe.test.js rename to test/js/node/test/parallel/test-fs-fsync.js index db9e55b8f6..6168c08d5b 100644 --- a/test/js/node/test/parallel/stream2-finish-pipe.test.js +++ b/test/js/node/test/parallel/test-fs-fsync.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-finish-pipe.js -//#SHA1: dfe174476d312542a54b9574955bddf0ad35aa9e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,33 +19,40 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const stream = require("stream"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); -test("stream2 finish pipe", done => { - const r = new stream.Readable(); - r._read = function (size) { - r.push(Buffer.allocUnsafe(size)); +const fs = require('fs'); + +const fileFixture = fixtures.path('a.js'); +const fileTemp = tmpdir.resolve('a.js'); + +// Copy fixtures to temp. +tmpdir.refresh(); +fs.copyFileSync(fileFixture, fileTemp); + +fs.open(fileTemp, 'a', 0o777, common.mustSucceed((fd) => { + fs.fdatasyncSync(fd); + + fs.fsyncSync(fd); + + fs.fdatasync(fd, common.mustSucceed(() => { + fs.fsync(fd, common.mustSucceed(() => { + fs.closeSync(fd); + })); + })); +})); + +['', false, null, undefined, {}, []].forEach((input) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' }; - - const w = new stream.Writable(); - w._write = function (data, encoding, cb) { - process.nextTick(cb, null); - }; - - r.pipe(w); - - // end() must be called in nextTick or a WRITE_AFTER_END error occurs. - process.nextTick(() => { - // This might sound unrealistic, but it happens in net.js. When - // socket.allowHalfOpen === false, EOF will cause .destroySoon() call which - // ends the writable side of net.Socket. - w.end(); - // We need to wait for the 'finish' event to ensure the test completes successfully - w.on("finish", () => { - done(); - }); - }); + assert.throws(() => fs.fdatasync(input), errObj); + assert.throws(() => fs.fdatasyncSync(input), errObj); + assert.throws(() => fs.fsync(input), errObj); + assert.throws(() => fs.fsyncSync(input), errObj); }); - -//<#END_FILE: test-stream2-finish-pipe.js diff --git a/test/js/node/test/parallel/test-fs-link.js b/test/js/node/test/parallel/test-fs-link.js new file mode 100644 index 0000000000..df5f606d0c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-link.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test creating and reading hard link +const srcPath = tmpdir.resolve('hardlink-target.txt'); +const dstPath = tmpdir.resolve('link1.js'); +fs.writeFileSync(srcPath, 'hello world'); + +function callback(err) { + assert.ifError(err); + const dstContent = fs.readFileSync(dstPath, 'utf8'); + assert.strictEqual(dstContent, 'hello world'); +} + +fs.link(srcPath, dstPath, common.mustCall(callback)); + +// test error outputs + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.link(i, '', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.link('', i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.linkSync(i, ''), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.linkSync('', i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/http-bind-twice.test.js b/test/js/node/test/parallel/test-fs-long-path.js similarity index 56% rename from test/js/node/test/parallel/http-bind-twice.test.js rename to test/js/node/test/parallel/test-fs-long-path.js index 99917ffc27..11724a88dc 100644 --- a/test/js/node/test/parallel/http-bind-twice.test.js +++ b/test/js/node/test/parallel/test-fs-long-path.js @@ -1,6 +1,3 @@ -//#FILE: test-http-bind-twice.js -//#SHA1: 71319f0a5445d1ea7644e952ec4048d8996be1bc -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,34 +19,34 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +if (!common.isWindows) + common.skip('this test is Windows-specific.'); -test("HTTP server bind twice", async () => { - const server1 = http.createServer(jest.fn()); - const server1ListenPromise = new Promise(resolve => { - server1.listen(0, "127.0.0.1", resolve); - }); +const fs = require('fs'); +const path = require('path'); - await server1ListenPromise; +const tmpdir = require('../common/tmpdir'); - const server2 = http.createServer(jest.fn()); - const server2ErrorPromise = new Promise(resolve => { - server2.on("error", resolve); - }); +// Make a path that will be at least 260 chars long. +const fileNameLen = Math.max(260 - tmpdir.path.length - 1, 1); +const fileName = tmpdir.resolve('x'.repeat(fileNameLen)); +const fullPath = path.resolve(fileName); - server2.listen(server1.address().port, "127.0.0.1"); +tmpdir.refresh(); - const error = await server2ErrorPromise; - - expect(error).toEqual( - expect.objectContaining({ - code: "EADDRINUSE", - message: expect.any(String), - }), - ); - - await new Promise(resolve => server1.close(resolve)); +console.log({ + filenameLength: fileName.length, + fullPathLength: fullPath.length }); -//<#END_FILE: test-http-bind-twice.js +fs.writeFile(fullPath, 'ok', common.mustSucceed(() => { + fs.stat(fullPath, common.mustSucceed()); + + // Tests https://github.com/nodejs/node/issues/39721 + fs.realpath.native(fullPath, common.mustSucceed()); + + // Tests https://github.com/nodejs/node/issues/51031 + fs.promises.realpath(fullPath).then(common.mustCall(), common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-fs-make-callback.js b/test/js/node/test/parallel/test-fs-make-callback.js new file mode 100644 index 0000000000..d9341ab06f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-make-callback.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const callbackThrowValues = [null, true, false, 0, 1, 'foo', /foo/, [], {}]; + +const { sep } = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function testMakeCallback(cb) { + return function() { + // fs.mkdtemp() calls makeCallback() on its third argument + fs.mkdtemp(`${tmpdir.path}${sep}`, {}, cb); + }; +} + +function invalidCallbackThrowsTests() { + callbackThrowValues.forEach((value) => { + assert.throws(testMakeCallback(value), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} + +invalidCallbackThrowsTests(); diff --git a/test/js/node/test/parallel/test-fs-makeStatsCallback.js b/test/js/node/test/parallel/test-fs-makeStatsCallback.js new file mode 100644 index 0000000000..953fc065e8 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-makeStatsCallback.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const callbackThrowValues = [null, true, false, 0, 1, 'foo', /foo/, [], {}]; + +function testMakeStatsCallback(cb) { + return function() { + // fs.stat() calls makeStatsCallback() on its second argument + fs.stat(__filename, cb); + }; +} + +// Verify the case where a callback function is provided +testMakeStatsCallback(common.mustCall())(); + +function invalidCallbackThrowsTests() { + callbackThrowValues.forEach((value) => { + assert.throws(testMakeStatsCallback(value), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} + +invalidCallbackThrowsTests(); diff --git a/test/js/node/test/parallel/test-fs-mkdir-recursive-eaccess.js b/test/js/node/test/parallel/test-fs-mkdir-recursive-eaccess.js new file mode 100644 index 0000000000..034a230948 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-mkdir-recursive-eaccess.js @@ -0,0 +1,70 @@ +'use strict'; + +// Test that mkdir with recursive option returns appropriate error +// when executed on folder it does not have permission to access. +// Ref: https://github.com/nodejs/node/issues/31481 + +const common = require('../common'); + +if (!common.isWindows && process.getuid() === 0) + common.skip('as this test should not be run as `root`'); + +if (common.isIBMi) + common.skip('IBMi has a different access permission mechanism'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +let n = 0; + +function makeDirectoryReadOnly(dir) { + let accessErrorCode = 'EACCES'; + if (common.isWindows) { + accessErrorCode = 'EPERM'; + execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC,AD,WD)"`); + } else { + fs.chmodSync(dir, '444'); + } + return accessErrorCode; +} + +function makeDirectoryWritable(dir) { + if (common.isWindows) { + execSync(`icacls ${dir} /remove:d "everyone"`); + } +} + +// Synchronous API should return an EACCESS error with path populated. +{ + const dir = tmpdir.resolve(`mkdirp_${n++}`); + fs.mkdirSync(dir); + const codeExpected = makeDirectoryReadOnly(dir); + let err = null; + try { + fs.mkdirSync(path.join(dir, '/foo'), { recursive: true }); + } catch (_err) { + err = _err; + } + makeDirectoryWritable(dir); + assert(err); + assert.strictEqual(err.code, codeExpected); + assert(err.path); +} + +// Asynchronous API should return an EACCESS error with path populated. +{ + const dir = tmpdir.resolve(`mkdirp_${n++}`); + fs.mkdirSync(dir); + const codeExpected = makeDirectoryReadOnly(dir); + fs.mkdir(path.join(dir, '/bar'), { recursive: true }, (err) => { + makeDirectoryWritable(dir); + assert(err); + assert.strictEqual(err.code, codeExpected); + assert(err.path); + }); +} diff --git a/test/js/node/test/parallel/test-fs-mkdtemp-prefix-check.js b/test/js/node/test/parallel/test-fs-mkdtemp-prefix-check.js new file mode 100644 index 0000000000..af5cb6ab7d --- /dev/null +++ b/test/js/node/test/parallel/test-fs-mkdtemp-prefix-check.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const prefixValues = [undefined, null, 0, true, false, 1]; + +function fail(value) { + assert.throws( + () => { + fs.mkdtempSync(value, {}); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +function failAsync(value) { + assert.throws( + () => { + fs.mkdtemp(value, common.mustNotCall()); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +for (const prefixValue of prefixValues) { + fail(prefixValue); + failAsync(prefixValue); +} diff --git a/test/js/node/test/parallel/test-fs-non-number-arguments-throw.js b/test/js/node/test/parallel/test-fs-non-number-arguments-throw.js new file mode 100644 index 0000000000..dbcec683cd --- /dev/null +++ b/test/js/node/test/parallel/test-fs-non-number-arguments-throw.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const tempFile = tmpdir.resolve('fs-non-number-arguments-throw'); + +tmpdir.refresh(); +fs.writeFileSync(tempFile, 'abc\ndef'); + +// A sanity check when using numbers instead of strings +const sanity = 'def'; +const saneEmitter = fs.createReadStream(tempFile, { start: 4, end: 6 }); + +assert.throws( + () => { + fs.createReadStream(tempFile, { start: '4', end: 6 }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws( + () => { + fs.createReadStream(tempFile, { start: 4, end: '6' }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws( + () => { + fs.createWriteStream(tempFile, { start: '4' }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +saneEmitter.on('data', common.mustCall(function(data) { + assert.strictEqual( + sanity, data.toString('utf8'), + `read ${data.toString('utf8')} instead of ${sanity}`); +})); diff --git a/test/js/node/test/parallel/test-fs-open-mode-mask.js b/test/js/node/test/parallel/test-fs-open-mode-mask.js new file mode 100644 index 0000000000..a79591c0ae --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open-mode-mask.js @@ -0,0 +1,40 @@ +'use strict'; + +// This tests that the lower bits of mode > 0o777 still works in fs.open(). + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const mode = common.isWindows ? 0o444 : 0o644; + +const maskToIgnore = 0o10000; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function test(mode, asString) { + const suffix = asString ? 'str' : 'num'; + const input = asString ? + (mode | maskToIgnore).toString(8) : (mode | maskToIgnore); + + { + const file = tmpdir.resolve(`openSync-${suffix}.txt`); + const fd = fs.openSync(file, 'w+', input); + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.closeSync(fd); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + } + + { + const file = tmpdir.resolve(`open-${suffix}.txt`); + fs.open(file, 'w+', input, common.mustSucceed((fd) => { + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.closeSync(fd); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + })); + } +} + +test(mode, true); +test(mode, false); diff --git a/test/js/node/test/parallel/test-fs-open-no-close.js b/test/js/node/test/parallel/test-fs-open-no-close.js new file mode 100644 index 0000000000..41b58f5ef7 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open-no-close.js @@ -0,0 +1,31 @@ +'use strict'; + +// Refs: https://github.com/nodejs/node/issues/34266 +// Failing to close a file should not keep the event loop open. + +const common = require('../common'); +const assert = require('assert'); + +const fs = require('fs'); + +const debuglog = (arg) => { + console.log(new Date().toLocaleString(), arg); +}; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +let openFd; + +fs.open(`${tmpdir.path}/dummy`, 'wx+', common.mustCall((err, fd) => { + debuglog('fs open() callback'); + assert.ifError(err); + openFd = fd; +})); +debuglog('waiting for callback'); + +process.on('beforeExit', common.mustCall(() => { + if (openFd) { + fs.closeSync(openFd); + } +})); diff --git a/test/js/node/test/parallel/test-fs-open-numeric-flags.js b/test/js/node/test/parallel/test-fs-open-numeric-flags.js new file mode 100644 index 0000000000..3237b2764e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open-numeric-flags.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); + +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// O_WRONLY without O_CREAT shall fail with ENOENT +const pathNE = tmpdir.resolve('file-should-not-exist'); +assert.throws( + () => fs.openSync(pathNE, fs.constants.O_WRONLY), + (e) => e.code === 'ENOENT' +); diff --git a/test/js/node/test/parallel/test-fs-open.js b/test/js/node/test/parallel/test-fs-open.js new file mode 100644 index 0000000000..0855e521f7 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open.js @@ -0,0 +1,120 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +let caughtException = false; + +try { + // Should throw ENOENT, not EBADF + // see https://github.com/joyent/node/pull/1228 + fs.openSync('/8hvftyuncxrt/path/to/file/that/does/not/exist', 'r'); +} catch (e) { + assert.strictEqual(e.code, 'ENOENT'); + caughtException = true; +} +assert.strictEqual(caughtException, true); + +fs.openSync(__filename); + +fs.open(__filename, common.mustSucceed()); + +fs.open(__filename, 'r', common.mustSucceed()); + +fs.open(__filename, 'rs', common.mustSucceed()); + +fs.open(__filename, 'r', 0, common.mustSucceed()); + +fs.open(__filename, 'r', null, common.mustSucceed()); + +async function promise() { + await fs.promises.open(__filename); + await fs.promises.open(__filename, 'r'); +} + +promise().then(common.mustCall()).catch(common.mustNotCall()); + +assert.throws( + () => fs.open(__filename, 'r', 'boom', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError' + } +); + +for (const extra of [[], ['r'], ['r', 0], ['r', 0, 'bad callback']]) { + assert.throws( + () => fs.open(__filename, ...extra), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +} + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.open(i, 'r', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.openSync(i, 'r', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.rejects( + fs.promises.open(i, 'r'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ).then(common.mustCall()); +}); + +// Check invalid modes. +[false, [], {}].forEach((mode) => { + assert.throws( + () => fs.open(__filename, 'r', mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); + assert.throws( + () => fs.openSync(__filename, 'r', mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); + assert.rejects( + fs.promises.open(__filename, 'r', mode), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ).then(common.mustCall()); +}); diff --git a/test/js/node/test/parallel/test-fs-options-immutable.js b/test/js/node/test/parallel/test-fs-options-immutable.js new file mode 100644 index 0000000000..fffdecdc87 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-options-immutable.js @@ -0,0 +1,70 @@ +'use strict'; +const common = require('../common'); + +// These tests make sure that the `options` object passed to these functions are +// never altered. +// +// Refer: https://github.com/nodejs/node/issues/7655 + +const fs = require('fs'); + +const options = common.mustNotMutateObjectDeep({}); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +fs.readFile(__filename, options, common.mustSucceed()); +fs.readFileSync(__filename, options); + +fs.readdir(__dirname, options, common.mustSucceed()); +fs.readdirSync(__dirname, options); + +if (common.canCreateSymLink()) { + const sourceFile = tmpdir.resolve('test-readlink'); + const linkFile = tmpdir.resolve('test-readlink-link'); + + fs.writeFileSync(sourceFile, ''); + fs.symlinkSync(sourceFile, linkFile); + + fs.readlink(linkFile, options, common.mustSucceed()); + fs.readlinkSync(linkFile, options); +} + +{ + const fileName = tmpdir.resolve('writeFile'); + fs.writeFileSync(fileName, 'ABCD', options); + fs.writeFile(fileName, 'ABCD', options, common.mustSucceed()); +} + +{ + const fileName = tmpdir.resolve('appendFile'); + fs.appendFileSync(fileName, 'ABCD', options); + fs.appendFile(fileName, 'ABCD', options, common.mustSucceed()); +} + +if (!common.isIBMi) { // IBMi does not support fs.watch() + const watch = fs.watch(__filename, options, common.mustNotCall()); + watch.close(); +} + +{ + fs.watchFile(__filename, options, common.mustNotCall()); + fs.unwatchFile(__filename); +} + +{ + fs.realpathSync(__filename, options); + fs.realpath(__filename, options, common.mustSucceed()); +} + +{ + const tempFileName = tmpdir.resolve('mkdtemp-'); + fs.mkdtempSync(tempFileName, options); + fs.mkdtemp(tempFileName, options, common.mustSucceed()); +} + +{ + const fileName = tmpdir.resolve('streams'); + fs.WriteStream(fileName, options).once('open', common.mustCall(() => { + fs.ReadStream(fileName, options).destroy(); + })).end(); +} diff --git a/test/js/node/test/parallel/test-fs-promises-exists.js b/test/js/node/test/parallel/test-fs-promises-exists.js new file mode 100644 index 0000000000..b9bbeee1de --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-exists.js @@ -0,0 +1,9 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fsPromises = require('fs/promises'); + +assert.strictEqual(fsPromises, fs.promises); +assert.strictEqual(fsPromises.constants, fs.constants); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-append-file.js b/test/js/node/test/parallel/test-fs-promises-file-handle-append-file.js new file mode 100644 index 0000000000..90bb6e3925 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-append-file.js @@ -0,0 +1,44 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.appendFile method. + +const fs = require('fs'); +const { open } = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +async function validateAppendBuffer() { + const filePath = path.resolve(tmpDir, 'tmp-append-file-buffer.txt'); + const fileHandle = await open(filePath, 'a'); + const buffer = Buffer.from('a&Dp'.repeat(100), 'utf8'); + + await fileHandle.appendFile(buffer); + const appendedFileData = fs.readFileSync(filePath); + assert.deepStrictEqual(appendedFileData, buffer); + + await fileHandle.close(); +} + +async function validateAppendString() { + const filePath = path.resolve(tmpDir, 'tmp-append-file-string.txt'); + const fileHandle = await open(filePath, 'a'); + const string = 'x~yz'.repeat(100); + + await fileHandle.appendFile(string); + const stringAsBuffer = Buffer.from(string, 'utf8'); + const appendedFileData = fs.readFileSync(filePath); + assert.deepStrictEqual(appendedFileData, stringAsBuffer); + + await fileHandle.close(); +} + +validateAppendBuffer() + .then(validateAppendString) + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-chmod.js b/test/js/node/test/parallel/test-fs-promises-file-handle-chmod.js new file mode 100644 index 0000000000..5c7414a9b1 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-chmod.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.chmod method. + +const fs = require('fs'); +const { open } = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +async function validateFilePermission() { + const filePath = path.resolve(tmpDir, 'tmp-chmod.txt'); + const fileHandle = await open(filePath, 'w+', 0o444); + // File created with r--r--r-- 444 + const statsBeforeMod = fs.statSync(filePath); + assert.strictEqual(statsBeforeMod.mode & 0o444, 0o444); + + let expectedAccess; + const newPermissions = 0o765; + + if (common.isWindows) { + // Chmod in Windows will only toggle read only/write access. The + // fs.Stats.mode in Windows is computed using read/write + // bits (not exec). Read-only at best returns 444; r/w 666. + // Refer: /deps/uv/src/win/fs.cfs; + expectedAccess = 0o664; + } else { + expectedAccess = newPermissions; + } + + // Change the permissions to rwxr--r-x + await fileHandle.chmod(newPermissions); + const statsAfterMod = fs.statSync(filePath); + assert.deepStrictEqual(statsAfterMod.mode & expectedAccess, expectedAccess); + + await fileHandle.close(); +} + +validateFilePermission().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-dispose.js b/test/js/node/test/parallel/test-fs-promises-file-handle-dispose.js new file mode 100644 index 0000000000..406430eef4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-dispose.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const { promises: fs } = require('fs'); + +async function doOpen() { + const fh = await fs.open(__filename); + fh.on('close', common.mustCall()); + await fh[Symbol.asyncDispose](); +} + +doOpen().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-read-worker.js b/test/js/node/test/parallel/test-fs-promises-file-handle-read-worker.js new file mode 100644 index 0000000000..7ae881801a --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-read-worker.js @@ -0,0 +1,54 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const file = tmpdir.resolve('read_stream_filehandle_worker.txt'); +const input = 'hello world'; +const { Worker, isMainThread, workerData } = require('worker_threads'); + +if (isMainThread || !workerData) { + tmpdir.refresh(); + fs.writeFileSync(file, input); + + fs.promises.open(file, 'r').then((handle) => { + handle.on('close', common.mustNotCall()); + new Worker(__filename, { + workerData: { handle }, + transferList: [handle] + }); + }); + fs.promises.open(file, 'r').then(async (handle) => { + try { + fs.createReadStream(null, { fd: handle }); + assert.throws(() => { + new Worker(__filename, { + workerData: { handle }, + transferList: [handle] + }); + }, { + code: 25, + name: 'DataCloneError', + }); + } finally { + await handle.close(); + } + }); +} else { + let output = ''; + + const handle = workerData.handle; + handle.on('close', common.mustCall()); + const stream = fs.createReadStream(null, { fd: handle }); + + stream.on('data', common.mustCallAtLeast((data) => { + output += data; + })); + + stream.on('end', common.mustCall(() => { + handle.close(); + assert.strictEqual(output, input); + })); + + stream.on('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-stat.js b/test/js/node/test/parallel/test-fs-promises-file-handle-stat.js new file mode 100644 index 0000000000..c989c405c5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-stat.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.stat method. + +const { open } = require('fs').promises; +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); + +tmpdir.refresh(); + +async function validateStat() { + const filePath = tmpdir.resolve('tmp-read-file.txt'); + const fileHandle = await open(filePath, 'w+'); + const stats = await fileHandle.stat(); + assert.ok(stats.mtime instanceof Date); + await fileHandle.close(); +} + +validateStat() + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-truncate.js b/test/js/node/test/parallel/test-fs-promises-file-handle-truncate.js new file mode 100644 index 0000000000..687f8c2725 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-truncate.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { open, readFile } = require('fs').promises; +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +async function validateTruncate() { + const text = 'Hello world'; + const filename = tmpdir.resolve('truncate-file.txt'); + const fileHandle = await open(filename, 'w+'); + + const buffer = Buffer.from(text, 'utf8'); + await fileHandle.write(buffer, 0, buffer.length); + + assert.strictEqual((await readFile(filename)).toString(), text); + + await fileHandle.truncate(5); + assert.strictEqual((await readFile(filename)).toString(), 'Hello'); + + await fileHandle.close(); +} + +validateTruncate().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-write.js b/test/js/node/test/parallel/test-fs-promises-file-handle-write.js new file mode 100644 index 0000000000..7f3d12d481 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-write.js @@ -0,0 +1,77 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.write method. + +const fs = require('fs'); +const { open } = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +async function validateWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from('Hello world'.repeat(100), 'utf8'); + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(buffer, readFileData); + + await fileHandle.close(); +} + +async function validateEmptyWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-empty-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from(''); // empty buffer + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(buffer, readFileData); + + await fileHandle.close(); +} + +async function validateNonUint8ArrayWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-data-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from('Hello world', 'utf8').toString('base64'); + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(Buffer.from(buffer, 'utf8'), readFileData); + + await fileHandle.close(); +} + +async function validateNonStringValuesWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-non-string-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const nonStringValues = [ + 123, {}, new Map(), null, undefined, 0n, () => {}, Symbol(), true, + new String('notPrimitive'), + { toString() { return 'amObject'; } }, + { [Symbol.toPrimitive]: (hint) => 'amObject' }, + ]; + for (const nonStringValue of nonStringValues) { + await assert.rejects( + fileHandle.write(nonStringValue), + { message: /"buffer"/, code: 'ERR_INVALID_ARG_TYPE' } + ); + } + + await fileHandle.close(); +} + +Promise.all([ + validateWrite(), + validateEmptyWrite(), + validateNonUint8ArrayWrite(), + validateNonStringValuesWrite(), +]).then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-readfile-empty.js b/test/js/node/test/parallel/test-fs-promises-readfile-empty.js new file mode 100644 index 0000000000..ef15a26811 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-readfile-empty.js @@ -0,0 +1,17 @@ +'use strict'; +require('../common'); + +const assert = require('assert'); +const { promises: fs } = require('fs'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('empty.txt'); + +fs.readFile(fn) + .then(assert.ok); + +fs.readFile(fn, 'utf8') + .then(assert.strictEqual.bind(this, '')); + +fs.readFile(fn, { encoding: 'utf8' }) + .then(assert.strictEqual.bind(this, '')); diff --git a/test/js/node/test/parallel/test-fs-promises-readfile-with-fd.js b/test/js/node/test/parallel/test-fs-promises-readfile-with-fd.js new file mode 100644 index 0000000000..b5d1c26168 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-readfile-with-fd.js @@ -0,0 +1,34 @@ +'use strict'; + +// This test makes sure that `readFile()` always reads from the current +// position of the file, instead of reading from the beginning of the file. + +const common = require('../common'); +const assert = require('assert'); +const { writeFileSync } = require('fs'); +const { open } = require('fs').promises; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fn = tmpdir.resolve('test.txt'); +writeFileSync(fn, 'Hello World'); + +async function readFileTest() { + const handle = await open(fn, 'r'); + + /* Read only five bytes, so that the position moves to five. */ + const buf = Buffer.alloc(5); + const { bytesRead } = await handle.read(buf, 0, 5, null); + assert.strictEqual(bytesRead, 5); + assert.strictEqual(buf.toString(), 'Hello'); + + /* readFile() should read from position five, instead of zero. */ + assert.strictEqual((await handle.readFile()).toString(), ' World'); + + await handle.close(); +} + + +readFileTest() + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-writefile-typedarray.js b/test/js/node/test/parallel/test-fs-promises-writefile-typedarray.js new file mode 100644 index 0000000000..32d9cffa23 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-writefile-typedarray.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const fsPromises = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +const dest = path.resolve(tmpDir, 'tmp.txt'); +// Use a file size larger than `kReadFileMaxChunkSize`. +const buffer = Buffer.from('012'.repeat(2 ** 14)); + +(async () => { + for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { + const array = new Constructor(buffer.buffer); + await fsPromises.writeFile(dest, array); + const data = await fsPromises.readFile(dest); + assert.deepStrictEqual(data, buffer); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-writefile-with-fd.js b/test/js/node/test/parallel/test-fs-promises-writefile-with-fd.js new file mode 100644 index 0000000000..7cb9eeeca9 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-writefile-with-fd.js @@ -0,0 +1,35 @@ +'use strict'; + +// This test makes sure that `writeFile()` always writes from the current +// position of the file, instead of truncating the file. + +const common = require('../common'); +const assert = require('assert'); +const { readFileSync } = require('fs'); +const { open } = require('fs').promises; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fn = tmpdir.resolve('test.txt'); + +async function writeFileTest() { + const handle = await open(fn, 'w'); + + /* Write only five bytes, so that the position moves to five. */ + const buf = Buffer.from('Hello'); + const { bytesWritten } = await handle.write(buf, 0, 5, null); + assert.strictEqual(bytesWritten, 5); + + /* Write some more with writeFile(). */ + await handle.writeFile('World'); + + /* New content should be written at position five, instead of zero. */ + assert.strictEqual(readFileSync(fn).toString(), 'HelloWorld'); + + await handle.close(); +} + + +writeFileTest() + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promisified.js b/test/js/node/test/parallel/test-fs-promisified.js new file mode 100644 index 0000000000..63430f64a8 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promisified.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const { promisify } = require('util'); + +const read = promisify(fs.read); +const write = promisify(fs.write); +const exists = promisify(fs.exists); + +{ + const fd = fs.openSync(__filename, 'r'); + read(fd, Buffer.alloc(1024), 0, 1024, null).then(common.mustCall((obj) => { + assert.strictEqual(typeof obj.bytesRead, 'number'); + assert(obj.buffer instanceof Buffer); + fs.closeSync(fd); + })); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +{ + const filename = tmpdir.resolve('write-promise.txt'); + const fd = fs.openSync(filename, 'w'); + write(fd, Buffer.from('foobar')).then(common.mustCall((obj) => { + assert.strictEqual(typeof obj.bytesWritten, 'number'); + assert.strictEqual(obj.buffer.toString(), 'foobar'); + fs.closeSync(fd); + })); +} + +{ + exists(__filename).then(common.mustCall((x) => { + assert.strictEqual(x, true); + })); +} diff --git a/test/js/node/test/parallel/test-fs-read-file-assert-encoding.js b/test/js/node/test/parallel/test-fs-read-file-assert-encoding.js new file mode 100644 index 0000000000..77cd0e3ab1 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-file-assert-encoding.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const encoding = 'foo-8'; +const filename = 'bar.txt'; +assert.throws( + () => fs.readFile(filename, { encoding }, common.mustNotCall()), + { code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' } +); diff --git a/test/js/node/test/parallel/test-fs-read-file-sync-hostname.js b/test/js/node/test/parallel/test-fs-read-file-sync-hostname.js new file mode 100644 index 0000000000..599f48b6cc --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-file-sync-hostname.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.isLinux) + common.skip('Test is linux specific.'); + +const assert = require('assert'); +const fs = require('fs'); + +// Test to make sure reading a file under the /proc directory works. See: +// https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0 +const hostname = fs.readFileSync('/proc/sys/kernel/hostname'); +assert.ok(hostname.length > 0); diff --git a/test/js/node/test/parallel/test-fs-read-offset-null.js b/test/js/node/test/parallel/test-fs-read-offset-null.js new file mode 100644 index 0000000000..012c94e41e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-offset-null.js @@ -0,0 +1,64 @@ +'use strict'; + + +// Test to assert the desired functioning of fs.read +// when {offset:null} is passed as options parameter + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fsPromises = fs.promises; +const fixtures = require('../common/fixtures'); +const filepath = fixtures.path('x.txt'); + +const buf = Buffer.alloc(1); +// Reading only one character, hence buffer of one byte is enough. + +// Tests are done by making sure the first letter in buffer is +// same as first letter in file. +// 120 is the ascii code of letter x. + +// Tests for callback API. +fs.open(filepath, 'r', common.mustSucceed((fd) => { + fs.read(fd, { offset: null, buffer: buf }, + common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(buffer[0], 120); + fs.close(fd, common.mustSucceed(() => {})); + })); +})); + +fs.open(filepath, 'r', common.mustSucceed((fd) => { + fs.read(fd, buf, { offset: null }, + common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(buffer[0], 120); + fs.close(fd, common.mustSucceed(() => {})); + })); +})); + +let filehandle = null; + +// Tests for promises api +(async () => { + filehandle = await fsPromises.open(filepath, 'r'); + const readObject = await filehandle.read(buf, { offset: null }); + assert.strictEqual(readObject.buffer[0], 120); +})() +.finally(() => filehandle?.close()) +.then(common.mustCall()); + +// Undocumented: omitted position works the same as position === null +(async () => { + filehandle = await fsPromises.open(filepath, 'r'); + const readObject = await filehandle.read(buf, null, buf.length); + assert.strictEqual(readObject.buffer[0], 120); +})() +.finally(() => filehandle?.close()) +.then(common.mustCall()); + +(async () => { + filehandle = await fsPromises.open(filepath, 'r'); + const readObject = await filehandle.read(buf, null, buf.length, 0); + assert.strictEqual(readObject.buffer[0], 120); +})() +.finally(() => filehandle?.close()) +.then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-read-optional-params.js b/test/js/node/test/parallel/test-fs-read-optional-params.js new file mode 100644 index 0000000000..e89f86ee5f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-optional-params.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const assert = require('assert'); +const filepath = fixtures.path('x.txt'); + +const expected = Buffer.from('xyz\n'); +const defaultBufferAsync = Buffer.alloc(16384); +const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); + +function testValid(message, ...options) { + const paramsMsg = `${message} (as params)`; + const paramsFilehandle = fs.openSync(filepath, 'r'); + fs.read(paramsFilehandle, ...options, common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(bytesRead, expected.byteLength, paramsMsg); + assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength, paramsMsg); + fs.closeSync(paramsFilehandle); + })); + + const optionsMsg = `${message} (as options)`; + const optionsFilehandle = fs.openSync(filepath, 'r'); + fs.read(optionsFilehandle, bufferAsOption, ...options, common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(bytesRead, expected.byteLength, optionsMsg); + assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength, optionsMsg); + fs.closeSync(optionsFilehandle); + })); +} + +testValid('Not passing in any object'); +testValid('Passing in a null', null); +testValid('Passing in an empty object', common.mustNotMutateObjectDeep({})); +testValid('Passing in an object', common.mustNotMutateObjectDeep({ + offset: 0, + length: bufferAsOption.byteLength, + position: 0, +})); diff --git a/test/js/node/test/parallel/test-fs-read-promises-optional-params.js b/test/js/node/test/parallel/test-fs-read-promises-optional-params.js new file mode 100644 index 0000000000..f9007a69ba --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-promises-optional-params.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const read = require('util').promisify(fs.read); +const assert = require('assert'); +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); + +const expected = Buffer.from('xyz\n'); +const defaultBufferAsync = Buffer.alloc(16384); +const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); + +read(fd, common.mustNotMutateObjectDeep({})) + .then(function({ bytesRead, buffer }) { + assert.strictEqual(bytesRead, expected.byteLength); + assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength); + }) + .then(common.mustCall()); + +read(fd, bufferAsOption, common.mustNotMutateObjectDeep({ position: 0 })) + .then(function({ bytesRead, buffer }) { + assert.strictEqual(bytesRead, expected.byteLength); + assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength); + }) + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-read-stream-autoClose.js b/test/js/node/test/parallel/test-fs-read-stream-autoClose.js new file mode 100644 index 0000000000..728d4f5eee --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-autoClose.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const writeFile = tmpdir.resolve('write-autoClose.txt'); +tmpdir.refresh(); + +const file = fs.createWriteStream(writeFile, { autoClose: true }); + +file.on('finish', common.mustCall(() => { + assert.strictEqual(file.destroyed, false); +})); +file.end('asd'); diff --git a/test/js/node/test/parallel/test-fs-read-stream-double-close.js b/test/js/node/test/parallel/test-fs-read-stream-double-close.js new file mode 100644 index 0000000000..32117a0a1e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-double-close.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); + +{ + const s = fs.createReadStream(__filename); + + s.close(common.mustCall()); + s.close(common.mustCall()); +} + +{ + const s = fs.createReadStream(__filename); + + // This is a private API, but it is worth testing. close calls this + s.destroy(null, common.mustCall()); + s.destroy(null, common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-fs-read-stream-encoding.js b/test/js/node/test/parallel/test-fs-read-stream-encoding.js new file mode 100644 index 0000000000..8eeaee6572 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-encoding.js @@ -0,0 +1,17 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const stream = require('stream'); +const fixtures = require('../common/fixtures'); +const encoding = 'base64'; + +const example = fixtures.path('x.txt'); +const assertStream = new stream.Writable({ + write: function(chunk, enc, next) { + const expected = Buffer.from('xyz'); + assert(chunk.equals(expected)); + } +}); +assertStream.setDefaultEncoding(encoding); +fs.createReadStream(example, encoding).pipe(assertStream); diff --git a/test/js/node/test/parallel/test-fs-read-stream-fd-leak.js b/test/js/node/test/parallel/test-fs-read-stream-fd-leak.js new file mode 100644 index 0000000000..81712def76 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-fd-leak.js @@ -0,0 +1,62 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +let openCount = 0; +const _fsopen = fs.open; +const _fsclose = fs.close; + +const loopCount = 50; +const totalCheck = 50; +const emptyTxt = fixtures.path('empty.txt'); + +fs.open = function() { + openCount++; + return _fsopen.apply(null, arguments); +}; + +fs.close = function() { + openCount--; + return _fsclose.apply(null, arguments); +}; + +function testLeak(endFn, callback) { + console.log(`testing for leaks from fs.createReadStream().${endFn}()...`); + + let i = 0; + let check = 0; + + function checkFunction() { + if (openCount !== 0 && check < totalCheck) { + check++; + setTimeout(checkFunction, 100); + return; + } + + assert.strictEqual( + openCount, + 0, + `no leaked file descriptors using ${endFn}() (got ${openCount})` + ); + + openCount = 0; + callback && setTimeout(callback, 100); + } + + setInterval(function() { + const s = fs.createReadStream(emptyTxt); + s[endFn](); + + if (++i === loopCount) { + clearTimeout(this); + setTimeout(checkFunction, 100); + } + }, 2); +} + +testLeak('close', function() { + testLeak('destroy'); +}); diff --git a/test/js/node/test/parallel/test-fs-read-stream-fd.js b/test/js/node/test/parallel/test-fs-read-stream-fd.js new file mode 100644 index 0000000000..f40d35b967 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-fd.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const file = tmpdir.resolve('read_stream_fd_test.txt'); +const input = 'hello world'; + +let output = ''; +tmpdir.refresh(); +fs.writeFileSync(file, input); + +const fd = fs.openSync(file, 'r'); +const stream = fs.createReadStream(null, { fd: fd, encoding: 'utf8' }); + +assert.strictEqual(stream.path, undefined); + +stream.on('data', common.mustCallAtLeast((data) => { + output += data; +})); + +process.on('exit', () => { + assert.strictEqual(output, input); +}); diff --git a/test/js/node/test/parallel/test-fs-read-stream-resume.js b/test/js/node/test/parallel/test-fs-read-stream-resume.js new file mode 100644 index 0000000000..36e3d809cd --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-resume.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +const fs = require('fs'); + +const file = fixtures.path('x.txt'); +let data = ''; +let first = true; + +const stream = fs.createReadStream(file); +stream.setEncoding('utf8'); +stream.on('data', common.mustCallAtLeast(function(chunk) { + data += chunk; + if (first) { + first = false; + stream.resume(); + } +})); + +process.nextTick(function() { + stream.pause(); + setTimeout(function() { + stream.resume(); + }, 100); +}); + +process.on('exit', function() { + assert.strictEqual(data, 'xyz\n'); +}); diff --git a/test/js/node/test/parallel/test-fs-read-zero-length.js b/test/js/node/test/parallel/test-fs-read-zero-length.js new file mode 100644 index 0000000000..ac2efc73f5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-zero-length.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); +const bufferAsync = Buffer.alloc(0); +const bufferSync = Buffer.alloc(0); + +fs.read(fd, bufferAsync, 0, 0, 0, common.mustCall((err, bytesRead) => { + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(bufferAsync, Buffer.alloc(0)); +})); + +const r = fs.readSync(fd, bufferSync, 0, 0, 0); +assert.deepStrictEqual(bufferSync, Buffer.alloc(0)); +assert.strictEqual(r, 0); diff --git a/test/js/node/test/parallel/test-fs-readdir-buffer.js b/test/js/node/test/parallel/test-fs-readdir-buffer.js new file mode 100644 index 0000000000..54b7353cb1 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readdir-buffer.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); + +if (!common.isMacOS) { + common.skip('this tests works only on MacOS'); +} + +const assert = require('assert'); + +fs.readdir( + Buffer.from('/dev'), + { withFileTypes: true, encoding: 'buffer' }, + common.mustCall((e, d) => { + assert.strictEqual(e, null); + }) +); diff --git a/test/js/node/test/parallel/test-fs-readdir-recursive.js b/test/js/node/test/parallel/test-fs-readdir-recursive.js new file mode 100644 index 0000000000..f32e600d2a --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readdir-recursive.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const net = require('net'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const server = net.createServer().listen(common.PIPE, common.mustCall(() => { + // The process should not crash + // See https://github.com/nodejs/node/issues/52159 + fs.readdirSync(tmpdir.path, { recursive: true }); + server.close(); +})); diff --git a/test/js/node/test/parallel/test-fs-readdir-ucs2.js b/test/js/node/test/parallel/test-fs-readdir-ucs2.js new file mode 100644 index 0000000000..264858ec6a --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readdir-ucs2.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +if (!common.isLinux) + common.skip('Test is linux specific.'); + +const path = require('path'); +const fs = require('fs'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const filename = '\uD83D\uDC04'; +const root = Buffer.from(`${tmpdir.path}${path.sep}`); +const filebuff = Buffer.from(filename, 'ucs2'); +const fullpath = Buffer.concat([root, filebuff]); + +try { + fs.closeSync(fs.openSync(fullpath, 'w+')); +} catch (e) { + if (e.code === 'EINVAL') + common.skip('test requires filesystem that supports UCS2'); + throw e; +} + +fs.readdir(tmpdir.path, 'ucs2', common.mustSucceed((list) => { + assert.strictEqual(list.length, 1); + const fn = list[0]; + assert.deepStrictEqual(Buffer.from(fn, 'ucs2'), filebuff); + assert.strictEqual(fn, filename); +})); diff --git a/test/js/node/test/parallel/test-fs-readfile-empty.js b/test/js/node/test/parallel/test-fs-readfile-empty.js new file mode 100644 index 0000000000..f6303777b2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-empty.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// Trivial test of fs.readFile on an empty file. +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('empty.txt'); + +fs.readFile(fn, common.mustCall((err, data) => { + assert.ok(data); +})); + +fs.readFile(fn, 'utf8', common.mustCall((err, data) => { + assert.strictEqual(data, ''); +})); + +fs.readFile(fn, { encoding: 'utf8' }, common.mustCall((err, data) => { + assert.strictEqual(data, ''); +})); + +assert.ok(fs.readFileSync(fn)); +assert.strictEqual(fs.readFileSync(fn, 'utf8'), ''); diff --git a/test/js/node/test/parallel/test-fs-readfile-eof.js b/test/js/node/test/parallel/test-fs-readfile-eof.js new file mode 100644 index 0000000000..94354b915b --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-eof.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs/promises'); +const childType = ['child-encoding', 'child-non-encoding']; + +if (process.argv[2] === childType[0]) { + fs.readFile('/dev/stdin', 'utf8').then((data) => { + process.stdout.write(data); + }); + return; +} else if (process.argv[2] === childType[1]) { + fs.readFile('/dev/stdin').then((data) => { + process.stdout.write(data); + }); + return; +} + +const data1 = 'Hello'; +const data2 = 'World'; +const expected = `${data1}\n${data2}\n`; + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); + +function test(child) { + const cmd = `(echo ${data1}; sleep 0.5; echo ${data2}) | ${node} ${f} ${child}`; + exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + expected, + `expected to read(${child === childType[0] ? 'with' : 'without'} encoding): '${expected}' but got: '${stdout}'`); + assert.strictEqual( + stderr, + '', + `expected not to read anything from stderr but got: '${stderr}'`); + })); +} + +test(childType[0]); +test(childType[1]); diff --git a/test/js/node/test/parallel/test-fs-readfile-fd.js b/test/js/node/test/parallel/test-fs-readfile-fd.js new file mode 100644 index 0000000000..1779d9f97d --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-fd.js @@ -0,0 +1,94 @@ +'use strict'; +const common = require('../common'); + +// Test fs.readFile using a file descriptor. + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); +const fn = fixtures.path('empty.txt'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +tempFd(function(fd, close) { + fs.readFile(fd, function(err, data) { + assert.ok(data); + close(); + }); +}); + +tempFd(function(fd, close) { + fs.readFile(fd, 'utf8', function(err, data) { + assert.strictEqual(data, ''); + close(); + }); +}); + +tempFdSync(function(fd) { + assert.ok(fs.readFileSync(fd)); +}); + +tempFdSync(function(fd) { + assert.strictEqual(fs.readFileSync(fd, 'utf8'), ''); +}); + +function tempFd(callback) { + fs.open(fn, 'r', function(err, fd) { + assert.ifError(err); + callback(fd, function() { + fs.close(fd, function(err) { + assert.ifError(err); + }); + }); + }); +} + +function tempFdSync(callback) { + const fd = fs.openSync(fn, 'r'); + callback(fd); + fs.closeSync(fd); +} + +{ + // This test makes sure that `readFile()` always reads from the current + // position of the file, instead of reading from the beginning of the file, + // when used with file descriptors. + + const filename = tmpdir.resolve('test.txt'); + fs.writeFileSync(filename, 'Hello World'); + + { + // Tests the fs.readFileSync(). + const fd = fs.openSync(filename, 'r'); + + // Read only five bytes, so that the position moves to five. + const buf = Buffer.alloc(5); + assert.strictEqual(fs.readSync(fd, buf, 0, 5), 5); + assert.strictEqual(buf.toString(), 'Hello'); + + // readFileSync() should read from position five, instead of zero. + assert.strictEqual(fs.readFileSync(fd).toString(), ' World'); + + fs.closeSync(fd); + } + + { + // Tests the fs.readFile(). + fs.open(filename, 'r', common.mustSucceed((fd) => { + const buf = Buffer.alloc(5); + + // Read only five bytes, so that the position moves to five. + fs.read(fd, buf, 0, 5, null, common.mustSucceed((bytes) => { + assert.strictEqual(bytes, 5); + assert.strictEqual(buf.toString(), 'Hello'); + + fs.readFile(fd, common.mustSucceed((data) => { + // readFile() should read from position five, instead of zero. + assert.strictEqual(data.toString(), ' World'); + + fs.closeSync(fd); + })); + })); + })); + } +} diff --git a/test/js/node/test/parallel/test-fs-readfile-pipe-large.js b/test/js/node/test/parallel/test-fs-readfile-pipe-large.js new file mode 100644 index 0000000000..4376774bb4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-pipe-large.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +// Simulate `cat readfile.js | node readfile.js` + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs'); + +if (process.argv[2] === 'child') { + fs.readFile('/dev/stdin', function(er, data) { + assert.ifError(er); + process.stdout.write(data); + }); + return; +} + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('readfile_pipe_large_test.txt'); +const dataExpected = 'a'.repeat(999999); +tmpdir.refresh(); +fs.writeFileSync(filename, dataExpected); + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); +const cmd = `cat ${filename} | ${node} ${f} child`; +exec(cmd, { maxBuffer: 1000000 }, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + dataExpected, + `expect it reads the file and outputs 999999 'a' but got : ${stdout}` + ); + assert.strictEqual( + stderr, + '', + `expect that it does not write to stderr, but got : ${stderr}` + ); + console.log('ok'); +})); diff --git a/test/js/node/test/parallel/test-fs-readfile-pipe.js b/test/js/node/test/parallel/test-fs-readfile-pipe.js new file mode 100644 index 0000000000..79d5699fef --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-pipe.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Simulate `cat readfile.js | node readfile.js` + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs'); + +if (process.argv[2] === 'child') { + fs.readFile('/dev/stdin', common.mustSucceed((data) => { + process.stdout.write(data); + })); + return; +} + +const fixtures = require('../common/fixtures'); + +const filename = fixtures.path('readfile_pipe_test.txt'); +const dataExpected = fs.readFileSync(filename).toString(); + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); +const cmd = `cat ${filename} | ${node} ${f} child`; +exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + dataExpected, + `expected to read: '${dataExpected}' but got: '${stdout}'`); + assert.strictEqual( + stderr, + '', + `expected not to read anything from stderr but got: '${stderr}'`); + console.log('ok'); +})); diff --git a/test/js/node/test/parallel/regression-object-prototype.test.js b/test/js/node/test/parallel/test-fs-readfile-unlink.js similarity index 64% rename from test/js/node/test/parallel/regression-object-prototype.test.js rename to test/js/node/test/parallel/test-fs-readfile-unlink.js index aae31cdf28..1688567fd6 100644 --- a/test/js/node/test/parallel/regression-object-prototype.test.js +++ b/test/js/node/test/parallel/test-fs-readfile-unlink.js @@ -1,6 +1,3 @@ -//#FILE: test-regression-object-prototype.js -//#SHA1: 198fbbc217f8034fb1b81ce137b6b09a19acd0de -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,22 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -/* eslint-disable node-core/require-common-first, node-core/required-modules */ -"use strict"; +'use strict'; +const common = require('../common'); -test("Object prototype modification regression", () => { - const consoleSpy = jest.spyOn(console, "log"); +// Test that unlink succeeds immediately after readFile completes. - Object.prototype.xadsadsdasasdxx = function () {}; +const assert = require('assert'); +const fs = require('fs'); - expect(consoleSpy).not.toHaveBeenCalled(); +const tmpdir = require('../common/tmpdir'); - console.log("puts after"); +const fileName = tmpdir.resolve('test.bin'); +const buf = Buffer.alloc(512 * 1024, 42); - expect(consoleSpy).toHaveBeenCalledWith("puts after"); - expect(consoleSpy).toHaveBeenCalledTimes(1); +tmpdir.refresh(); - consoleSpy.mockRestore(); -}); +fs.writeFileSync(fileName, buf); -//<#END_FILE: test-regression-object-prototype.js +fs.readFile(fileName, common.mustSucceed((data) => { + assert.strictEqual(data.length, buf.length); + assert.strictEqual(buf[0], 42); + + // Unlink should not throw. This is part of the test. It used to throw on + // Windows due to a bug. + fs.unlinkSync(fileName); +})); diff --git a/test/js/node/test/parallel/fs-readfile-zero-byte-liar.test.js b/test/js/node/test/parallel/test-fs-readfile-zero-byte-liar.js similarity index 72% rename from test/js/node/test/parallel/fs-readfile-zero-byte-liar.test.js rename to test/js/node/test/parallel/test-fs-readfile-zero-byte-liar.js index 0e3799ec0e..a3ba3985ab 100644 --- a/test/js/node/test/parallel/fs-readfile-zero-byte-liar.test.js +++ b/test/js/node/test/parallel/test-fs-readfile-zero-byte-liar.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-readfile-zero-byte-liar.js -//#SHA1: ddca2f114cf32b03f36405a21c81058e7a1f0c18 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,13 +19,15 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; - -const fs = require("fs"); +'use strict'; +const common = require('../common'); // Test that readFile works even when stat returns size 0. -const dataExpected = fs.readFileSync(__filename, "utf8"); +const assert = require('assert'); +const fs = require('fs'); + +const dataExpected = fs.readFileSync(__filename, 'utf8'); // Sometimes stat returns size=0, but it's a lie. fs._fstat = fs.fstat; @@ -42,20 +41,15 @@ fs.fstat = (fd, cb) => { }); }; -fs.fstatSync = fd => { +fs.fstatSync = (fd) => { const st = fs._fstatSync(fd); st.size = 0; return st; }; -test("readFileSync works with zero byte liar", () => { - const d = fs.readFileSync(__filename, "utf8"); - expect(d).toBe(dataExpected); -}); +const d = fs.readFileSync(__filename, 'utf8'); +assert.strictEqual(d, dataExpected); -test("readFile works with zero byte liar", async () => { - const d = await fs.promises.readFile(__filename, "utf8"); - expect(d).toBe(dataExpected); -}); - -//<#END_FILE: test-fs-readfile-zero-byte-liar.js +fs.readFile(__filename, 'utf8', common.mustCall((er, d) => { + assert.strictEqual(d, dataExpected); +})); diff --git a/test/js/node/test/parallel/test-fs-readfilesync-enoent.js b/test/js/node/test/parallel/test-fs-readfilesync-enoent.js new file mode 100644 index 0000000000..baf87ff990 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfilesync-enoent.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); + +// This test is only relevant on Windows. +if (!common.isWindows) + common.skip('Windows specific test.'); + +// This test ensures fs.realpathSync works on properly on Windows without +// throwing ENOENT when the path involves a fileserver. +// https://github.com/nodejs/node-v0.x-archive/issues/3542 + +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +function test(p) { + const result = fs.realpathSync(p); + assert.strictEqual(result.toLowerCase(), path.resolve(p).toLowerCase()); + + fs.realpath(p, common.mustSucceed((result) => { + assert.strictEqual(result.toLowerCase(), path.resolve(p).toLowerCase()); + })); +} + +test(`//${os.hostname()}/c$/Windows/System32`); +test(`//${os.hostname()}/c$/Windows`); +test(`//${os.hostname()}/c$/`); +test(`\\\\${os.hostname()}\\c$\\`); +test('C:\\'); +test('C:'); +test(process.env.windir); diff --git a/test/js/node/test/parallel/test-fs-readfilesync-pipe-large.js b/test/js/node/test/parallel/test-fs-readfilesync-pipe-large.js new file mode 100644 index 0000000000..5450337c4f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfilesync-pipe-large.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); + +// Simulate `cat readfile.js | node readfile.js` + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs'); + +if (process.argv[2] === 'child') { + process.stdout.write(fs.readFileSync('/dev/stdin', 'utf8')); + return; +} + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('readfilesync_pipe_large_test.txt'); +const dataExpected = 'a'.repeat(999999); +tmpdir.refresh(); +fs.writeFileSync(filename, dataExpected); + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); +const cmd = `cat ${filename} | ${node} ${f} child`; +exec( + cmd, + { maxBuffer: 1000000 }, + common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout, dataExpected); + assert.strictEqual(stderr, ''); + console.log('ok'); + }) +); diff --git a/test/js/node/test/parallel/test-fs-readlink-type-check.js b/test/js/node/test/parallel/test-fs-readlink-type-check.js new file mode 100644 index 0000000000..58d431308c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readlink-type-check.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.readlink(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.readlinkSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-readv-promises.js b/test/js/node/test/parallel/test-fs-readv-promises.js new file mode 100644 index 0000000000..cdfc3d3b21 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readv-promises.js @@ -0,0 +1,63 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs').promises; +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; +const exptectedBuff = Buffer.from(expected); + +let cnt = 0; +function getFileName() { + return tmpdir.resolve(`readv_promises_${++cnt}.txt`); +} + +const allocateEmptyBuffers = (combinedLength) => { + const bufferArr = []; + // Allocate two buffers, each half the size of exptectedBuff + bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); + bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); + + return bufferArr; +}; + +(async () => { + { + const filename = getFileName(); + await fs.writeFile(filename, exptectedBuff); + const handle = await fs.open(filename, 'r'); + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const expectedLength = exptectedBuff.length; + + let { bytesRead, buffers } = await handle.readv([Buffer.from('')], + null); + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(buffers, [Buffer.from('')]); + + ({ bytesRead, buffers } = await handle.readv(bufferArr, null)); + assert.strictEqual(bytesRead, expectedLength); + assert.deepStrictEqual(buffers, bufferArr); + assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename))); + handle.close(); + } + + { + const filename = getFileName(); + await fs.writeFile(filename, exptectedBuff); + const handle = await fs.open(filename, 'r'); + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const expectedLength = exptectedBuff.length; + + let { bytesRead, buffers } = await handle.readv([Buffer.from('')]); + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(buffers, [Buffer.from('')]); + + ({ bytesRead, buffers } = await handle.readv(bufferArr)); + assert.strictEqual(bytesRead, expectedLength); + assert.deepStrictEqual(buffers, bufferArr); + assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename))); + handle.close(); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-readv-sync.js b/test/js/node/test/parallel/test-fs-readv-sync.js new file mode 100644 index 0000000000..548f54cbb9 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readv-sync.js @@ -0,0 +1,92 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; + +const exptectedBuff = Buffer.from(expected); +const expectedLength = exptectedBuff.length; + +const filename = tmpdir.resolve('readv_sync.txt'); +fs.writeFileSync(filename, exptectedBuff); + +const allocateEmptyBuffers = (combinedLength) => { + const bufferArr = []; + // Allocate two buffers, each half the size of exptectedBuff + bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); + bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); + + return bufferArr; +}; + +// fs.readvSync with array of buffers with all parameters +{ + const fd = fs.openSync(filename, 'r'); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + + let read = fs.readvSync(fd, [Buffer.from('')], 0); + assert.strictEqual(read, 0); + + read = fs.readvSync(fd, bufferArr, 0); + assert.strictEqual(read, expectedLength); + + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); +} + +// fs.readvSync with array of buffers without position +{ + const fd = fs.openSync(filename, 'r'); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + + let read = fs.readvSync(fd, [Buffer.from('')]); + assert.strictEqual(read, 0); + + read = fs.readvSync(fd, bufferArr); + assert.strictEqual(read, expectedLength); + + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); +} + +/** + * Testing with incorrect arguments + */ +const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined]; + +{ + const fd = fs.openSync(filename, 'r'); + + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readvSync(fd, wrongInput, null), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } + + fs.closeSync(fd); +} + +{ + // fs.readv with wrong fd argument + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readvSync(wrongInput), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } +} diff --git a/test/js/node/test/parallel/test-fs-readv.js b/test/js/node/test/parallel/test-fs-readv.js new file mode 100644 index 0000000000..111719a7ef --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readv.js @@ -0,0 +1,93 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; + +let cnt = 0; +const getFileName = () => tmpdir.resolve(`readv_${++cnt}.txt`); +const exptectedBuff = Buffer.from(expected); + +const allocateEmptyBuffers = (combinedLength) => { + const bufferArr = []; + // Allocate two buffers, each half the size of exptectedBuff + bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); + bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); + + return bufferArr; +}; + +const getCallback = (fd, bufferArr) => { + return common.mustSucceed((bytesRead, buffers) => { + assert.deepStrictEqual(bufferArr, buffers); + const expectedLength = exptectedBuff.length; + assert.deepStrictEqual(bytesRead, expectedLength); + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(exptectedBuff)); + }); +}; + +// fs.readv with array of buffers with all parameters +{ + const filename = getFileName(); + const fd = fs.openSync(filename, 'w+'); + fs.writeSync(fd, exptectedBuff); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const callback = getCallback(fd, bufferArr); + + fs.readv(fd, bufferArr, 0, callback); +} + +// fs.readv with array of buffers without position +{ + const filename = getFileName(); + fs.writeFileSync(filename, exptectedBuff); + const fd = fs.openSync(filename, 'r'); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const callback = getCallback(fd, bufferArr); + + fs.readv(fd, bufferArr, callback); +} + +/** + * Testing with incorrect arguments + */ +const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined]; + +{ + const filename = getFileName(2); + fs.writeFileSync(filename, exptectedBuff); + const fd = fs.openSync(filename, 'r'); + + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readv(fd, wrongInput, null, common.mustNotCall()), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } + + fs.closeSync(fd); +} + +{ + // fs.readv with wrong fd argument + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readv(wrongInput, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } +} diff --git a/test/js/node/test/parallel/test-fs-realpath-on-substed-drive.js b/test/js/node/test/parallel/test-fs-realpath-on-substed-drive.js new file mode 100644 index 0000000000..aea53f642f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-realpath-on-substed-drive.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +if (!common.isWindows) + common.skip('Test for Windows only'); + +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const fs = require('fs'); +const spawnSync = require('child_process').spawnSync; + +let result; + +// Create a subst drive +const driveLetters = 'ABCDEFGHIJKLMNOPQRSTUWXYZ'; +let drive; +let i; +for (i = 0; i < driveLetters.length; ++i) { + drive = `${driveLetters[i]}:`; + result = spawnSync('subst', [drive, fixtures.fixturesDir]); + if (result.status === 0) + break; +} +if (i === driveLetters.length) + common.skip('Cannot create subst drive'); + +// Schedule cleanup (and check if all callbacks where called) +process.on('exit', function() { + spawnSync('subst', ['/d', drive]); +}); + +// test: +const filename = `${drive}\\empty.js`; +const filenameBuffer = Buffer.from(filename); + +result = fs.realpathSync(filename); +assert.strictEqual(result, filename); + +result = fs.realpathSync(filename, 'buffer'); +assert(Buffer.isBuffer(result)); +assert(result.equals(filenameBuffer)); + +fs.realpath(filename, common.mustSucceed((result) => { + assert.strictEqual(result, filename); +})); + +fs.realpath(filename, 'buffer', common.mustSucceed((result) => { + assert(Buffer.isBuffer(result)); + assert(result.equals(filenameBuffer)); +})); diff --git a/test/js/node/test/parallel/test-fs-realpath-pipe.js b/test/js/node/test/parallel/test-fs-realpath-pipe.js new file mode 100644 index 0000000000..f637642ca2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-realpath-pipe.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); + +const { spawnSync } = require('child_process'); + +for (const code of [ + `require('fs').realpath('/dev/stdin', (err, resolvedPath) => { + if (err) { + console.error(err); + process.exit(1); + } + if (resolvedPath) { + process.exit(2); + } + });`, + `try { + if (require('fs').realpathSync('/dev/stdin')) { + process.exit(2); + } + } catch (e) { + console.error(e); + process.exit(1); + }`, +]) { + const child = spawnSync(process.execPath, ['-e', code], { + stdio: 'pipe' + }); + if (child.status !== 2) { + console.log(code); + console.log(child.stderr.toString()); + } + assert.strictEqual(child.status, 2); +} diff --git a/test/js/node/test/parallel/test-fs-rmdir-type-check.js b/test/js/node/test/parallel/test-fs-rmdir-type-check.js new file mode 100644 index 0000000000..7014ce27f8 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-rmdir-type-check.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.rmdir(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.rmdirSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/fs-empty-readstream.test.js b/test/js/node/test/parallel/test-fs-sir-writes-alot.js similarity index 50% rename from test/js/node/test/parallel/fs-empty-readstream.test.js rename to test/js/node/test/parallel/test-fs-sir-writes-alot.js index cc018a90b7..7d213d6c6f 100644 --- a/test/js/node/test/parallel/fs-empty-readstream.test.js +++ b/test/js/node/test/parallel/test-fs-sir-writes-alot.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-empty-readStream.js -//#SHA1: 979f558eb3c86e2d897cb766be1f300bbb0cbf8c -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -23,46 +20,51 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; +require('../common'); const fs = require('fs'); -const path = require('path'); +const assert = require('assert'); -const emptyFile = path.join(__dirname, '..', 'fixtures', 'empty.txt'); +const tmpdir = require('../common/tmpdir'); -test('read stream on empty file should not emit data event', (done) => { - fs.open(emptyFile, 'r', (err, fd) => { - expect(err).toBeNull(); - const read = fs.createReadStream(emptyFile, { fd }); +const filename = tmpdir.resolve('out.txt'); - const dataHandler = jest.fn(); - read.once('data', dataHandler); +tmpdir.refresh(); - read.once('end', () => { - expect(dataHandler).not.toHaveBeenCalled(); - done(); - }); +const fd = fs.openSync(filename, 'w'); + +const line = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'; + +const N = 10240; +let complete = 0; + +for (let i = 0; i < N; i++) { + // Create a new buffer for each write. Before the write is actually + // executed by the thread pool, the buffer will be collected. + const buffer = Buffer.from(line); + fs.write(fd, buffer, 0, buffer.length, null, function(er, written) { + complete++; + if (complete === N) { + fs.closeSync(fd); + const s = fs.createReadStream(filename); + s.on('data', testBuffer); + } }); +} + +let bytesChecked = 0; + +function testBuffer(b) { + for (let i = 0; i < b.length; i++) { + bytesChecked++; + if (b[i] !== 'a'.charCodeAt(0) && b[i] !== '\n'.charCodeAt(0)) { + throw new Error(`invalid char ${i},${b[i]}`); + } + } +} + +process.on('exit', function() { + // Probably some of the writes are going to overlap, so we can't assume + // that we get (N * line.length). Let's just make sure we've checked a + // few... + assert.ok(bytesChecked > 1000); }); - -test('paused read stream on empty file should not emit data or end events', (done) => { - fs.open(emptyFile, 'r', (err, fd) => { - expect(err).toBeNull(); - const read = fs.createReadStream(emptyFile, { fd }); - - read.pause(); - - const dataHandler = jest.fn(); - read.once('data', dataHandler); - - const endHandler = jest.fn(); - read.once('end', endHandler); - - setTimeout(() => { - expect(read.isPaused()).toBe(true); - expect(dataHandler).not.toHaveBeenCalled(); - expect(endHandler).not.toHaveBeenCalled(); - done(); - }, 50); - }); -}); - -//<#END_FILE: test-fs-empty-readStream.js diff --git a/test/js/node/test/parallel/test-fs-stat-bigint.js b/test/js/node/test/parallel/test-fs-stat-bigint.js new file mode 100644 index 0000000000..0a2bea92e5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-stat-bigint.js @@ -0,0 +1,247 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const promiseFs = require('fs').promises; +const tmpdir = require('../common/tmpdir'); +const { isDate } = require('util').types; +const { inspect } = require('util'); + +tmpdir.refresh(); + +let testIndex = 0; + +function getFilename() { + const filename = tmpdir.resolve(`test-file-${++testIndex}`); + fs.writeFileSync(filename, 'test'); + return filename; +} + +function verifyStats(bigintStats, numStats, allowableDelta) { + // allowableDelta: It's possible that the file stats are updated between the + // two stat() calls so allow for a small difference. + for (const key of Object.keys(numStats)) { + const val = numStats[key]; + if (isDate(val)) { + const time = val.getTime(); + const time2 = bigintStats[key].getTime(); + assert( + time - time2 <= allowableDelta, + `difference of ${key}.getTime() should <= ${allowableDelta}.\n` + + `Number version ${time}, BigInt version ${time2}n`); + } else if (key === 'mode') { + assert.strictEqual(bigintStats[key], BigInt(val)); + assert.strictEqual( + bigintStats.isBlockDevice(), + numStats.isBlockDevice() + ); + assert.strictEqual( + bigintStats.isCharacterDevice(), + numStats.isCharacterDevice() + ); + assert.strictEqual( + bigintStats.isDirectory(), + numStats.isDirectory() + ); + assert.strictEqual( + bigintStats.isFIFO(), + numStats.isFIFO() + ); + assert.strictEqual( + bigintStats.isFile(), + numStats.isFile() + ); + assert.strictEqual( + bigintStats.isSocket(), + numStats.isSocket() + ); + assert.strictEqual( + bigintStats.isSymbolicLink(), + numStats.isSymbolicLink() + ); + } else if (key.endsWith('Ms')) { + const nsKey = key.replace('Ms', 'Ns'); + const msFromBigInt = bigintStats[key]; + const nsFromBigInt = bigintStats[nsKey]; + const msFromBigIntNs = Number(nsFromBigInt / (10n ** 6n)); + const msFromNum = numStats[key]; + + assert( + msFromNum - Number(msFromBigInt) <= allowableDelta, + `Number version ${key} = ${msFromNum}, ` + + `BigInt version ${key} = ${msFromBigInt}n, ` + + `Allowable delta = ${allowableDelta}`); + + assert( + msFromNum - Number(msFromBigIntNs) <= allowableDelta, + `Number version ${key} = ${msFromNum}, ` + + `BigInt version ${nsKey} = ${nsFromBigInt}n` + + ` = ${msFromBigIntNs}ms, Allowable delta = ${allowableDelta}`); + } else if (Number.isSafeInteger(val)) { + assert.strictEqual( + bigintStats[key], BigInt(val), + `${inspect(bigintStats[key])} !== ${inspect(BigInt(val))}\n` + + `key=${key}, val=${val}` + ); + } else { + assert( + Number(bigintStats[key]) - val < 1, + `${key} is not a safe integer, difference should < 1.\n` + + `Number version ${val}, BigInt version ${bigintStats[key]}n`); + } + } +} + +const runSyncTest = (func, arg) => { + const startTime = process.hrtime.bigint(); + const bigintStats = func(arg, common.mustNotMutateObjectDeep({ bigint: true })); + const numStats = func(arg); + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); +}; + +{ + const filename = getFilename(); + runSyncTest(fs.statSync, filename); +} + +if (!common.isWindows) { + const filename = getFilename(); + const link = `${filename}-link`; + fs.symlinkSync(filename, link); + runSyncTest(fs.lstatSync, link); +} + +{ + const filename = getFilename(); + const fd = fs.openSync(filename, 'r'); + runSyncTest(fs.fstatSync, fd); + fs.closeSync(fd); +} + +{ + assert.throws( + () => fs.statSync('does_not_exist'), + { code: 'ENOENT' }); + assert.strictEqual( + fs.statSync('does_not_exist', common.mustNotMutateObjectDeep({ throwIfNoEntry: false })), + undefined); +} + +{ + assert.throws( + () => fs.lstatSync('does_not_exist'), + { code: 'ENOENT' }); + assert.strictEqual( + fs.lstatSync('does_not_exist', common.mustNotMutateObjectDeep({ throwIfNoEntry: false })), + undefined); +} + +{ + assert.throws( + () => fs.fstatSync(9999), + { code: 'EBADF' }); + assert.throws( + () => fs.fstatSync(9999, common.mustNotMutateObjectDeep({ throwIfNoEntry: false })), + { code: 'EBADF' }); +} + +const runCallbackTest = (func, arg, done) => { + const startTime = process.hrtime.bigint(); + func(arg, common.mustNotMutateObjectDeep({ bigint: true }), common.mustCall((err, bigintStats) => { + func(arg, common.mustCall((err, numStats) => { + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); + if (done) { + done(); + } + })); + })); +}; + +{ + const filename = getFilename(); + runCallbackTest(fs.stat, filename); +} + +if (!common.isWindows) { + const filename = getFilename(); + const link = `${filename}-link`; + fs.symlinkSync(filename, link); + runCallbackTest(fs.lstat, link); +} + +{ + const filename = getFilename(); + const fd = fs.openSync(filename, 'r'); + runCallbackTest(fs.fstat, fd, () => { fs.closeSync(fd); }); +} + +const runPromiseTest = async (func, arg) => { + const startTime = process.hrtime.bigint(); + const bigintStats = await func(arg, common.mustNotMutateObjectDeep({ bigint: true })); + const numStats = await func(arg); + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); +}; + +{ + const filename = getFilename(); + runPromiseTest(promiseFs.stat, filename); +} + +if (!common.isWindows) { + const filename = getFilename(); + const link = `${filename}-link`; + fs.symlinkSync(filename, link); + runPromiseTest(promiseFs.lstat, link); +} + +(async function() { + const filename = getFilename(); + const handle = await promiseFs.open(filename, 'r'); + const startTime = process.hrtime.bigint(); + const bigintStats = await handle.stat(common.mustNotMutateObjectDeep({ bigint: true })); + const numStats = await handle.stat(); + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); + await handle.close(); +})().then(common.mustCall()); + +{ + // These two tests have an equivalent in ./test-fs-stat.js + + // BigIntStats Date properties can be set before reading them + fs.stat(__filename, { bigint: true }, common.mustSucceed((s) => { + s.atime = 2; + s.mtime = 3; + s.ctime = 4; + s.birthtime = 5; + + assert.strictEqual(s.atime, 2); + assert.strictEqual(s.mtime, 3); + assert.strictEqual(s.ctime, 4); + assert.strictEqual(s.birthtime, 5); + })); + + // BigIntStats Date properties can be set after reading them + fs.stat(__filename, { bigint: true }, common.mustSucceed((s) => { + // eslint-disable-next-line no-unused-expressions + s.atime, s.mtime, s.ctime, s.birthtime; + + s.atime = 2; + s.mtime = 3; + s.ctime = 4; + s.birthtime = 5; + + assert.strictEqual(s.atime, 2); + assert.strictEqual(s.mtime, 3); + assert.strictEqual(s.ctime, 4); + assert.strictEqual(s.birthtime, 5); + })); +} diff --git a/test/js/node/test/parallel/test-fs-stream-double-close.js b/test/js/node/test/parallel/test-fs-stream-double-close.js new file mode 100644 index 0000000000..8c0037b243 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-stream-double-close.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +test1(fs.createReadStream(__filename)); +test2(fs.createReadStream(__filename)); +test3(fs.createReadStream(__filename)); + +test1(fs.createWriteStream(`${tmpdir.path}/dummy1`)); +test2(fs.createWriteStream(`${tmpdir.path}/dummy2`)); +test3(fs.createWriteStream(`${tmpdir.path}/dummy3`)); + +function test1(stream) { + stream.destroy(); + stream.destroy(); +} + +function test2(stream) { + stream.destroy(); + stream.on('open', common.mustCall(function(fd) { + stream.destroy(); + })); +} + +function test3(stream) { + stream.on('open', common.mustCall(function(fd) { + stream.destroy(); + stream.destroy(); + })); +} diff --git a/test/js/node/test/parallel/http-eof-on-connect.test.js b/test/js/node/test/parallel/test-fs-symlink-buffer-path.js similarity index 55% rename from test/js/node/test/parallel/http-eof-on-connect.test.js rename to test/js/node/test/parallel/test-fs-symlink-buffer-path.js index 1161c1f40c..ecad001d5e 100644 --- a/test/js/node/test/parallel/http-eof-on-connect.test.js +++ b/test/js/node/test/parallel/test-fs-symlink-buffer-path.js @@ -1,6 +1,3 @@ -//#FILE: test-http-eof-on-connect.js -//#SHA1: c243d7ad215d84d88b20dfeea40976155f00a2bb -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,34 +19,41 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +if (!common.canCreateSymLink()) + common.skip('insufficient privileges'); -const net = require("net"); -const http = require("http"); +const fixtures = require('../common/fixtures'); -// This is a regression test for https://github.com/joyent/node/issues/44 -// It is separate from test-http-malformed-request.js because it is only -// reproducible on the first packet on the first connection to a server. +const assert = require('assert'); +const fs = require('fs'); -test("EOF on connect", async () => { - const server = http.createServer(jest.fn()); - server.listen(0); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); - await new Promise(resolve => { - server.on("listening", () => { - const client = net.createConnection(server.address().port, "127.0.0.1"); +// Test creating and reading symbolic link +const linkData = fixtures.path('/cycles/root.js'); +const linkPath = tmpdir.resolve('symlink1.js'); - client.on("connect", () => { - client.destroy(); - }); +let linkTime; +let fileTime; - client.on("close", () => { - server.close(resolve); - }); - }); - }); +// Refs: https://github.com/nodejs/node/issues/34514 +fs.symlinkSync(Buffer.from(linkData), linkPath); - expect(server.listeners("request")[0]).not.toHaveBeenCalled(); +fs.lstat(linkPath, common.mustSucceed((stats) => { + linkTime = stats.mtime.getTime(); +})); + +fs.stat(linkPath, common.mustSucceed((stats) => { + fileTime = stats.mtime.getTime(); +})); + +fs.readlink(linkPath, common.mustSucceed((destination) => { + assert.strictEqual(destination, linkData); +})); + +process.on('exit', () => { + assert.notStrictEqual(linkTime, fileTime); }); - -//<#END_FILE: test-http-eof-on-connect.js diff --git a/test/js/node/test/parallel/http-contentlength0.test.js b/test/js/node/test/parallel/test-fs-symlink-dir-junction-relative.js similarity index 53% rename from test/js/node/test/parallel/http-contentlength0.test.js rename to test/js/node/test/parallel/test-fs-symlink-dir-junction-relative.js index 4a6a7fec72..01ef8c8bdc 100644 --- a/test/js/node/test/parallel/http-contentlength0.test.js +++ b/test/js/node/test/parallel/test-fs-symlink-dir-junction-relative.js @@ -1,6 +1,3 @@ -//#FILE: test-http-contentLength0.js -//#SHA1: d85b0cc3dcfcff522ffbeddacf89111897b80c02 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,35 +19,40 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +// Test creating and resolving relative junction or symbolic link -// Simple test of Node's HTTP Client choking on a response -// with a 'Content-Length: 0 ' response header. -// I.E. a space character after the 'Content-Length' throws an `error` event. +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); -test("HTTP Client handles Content-Length: 0 with space", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "Content-Length": "0 " }); - res.end(); - }); +const tmpdir = require('../common/tmpdir'); - await new Promise(resolve => { - server.listen(0, resolve); - }); +const linkPath1 = tmpdir.resolve('junction1'); +const linkPath2 = tmpdir.resolve('junction2'); +const linkTarget = fixtures.fixturesDir; +const linkData = fixtures.fixturesDir; - const { port } = server.address(); +tmpdir.refresh(); - await new Promise(resolve => { - const request = http.request({ port }, response => { - expect(response.statusCode).toBe(200); - server.close(); - response.resume(); - resolve(); - }); +// Test fs.symlink() +fs.symlink(linkData, linkPath1, 'junction', common.mustSucceed(() => { + verifyLink(linkPath1); +})); - request.end(); - }); -}); +// Test fs.symlinkSync() +fs.symlinkSync(linkData, linkPath2, 'junction'); +verifyLink(linkPath2); -//<#END_FILE: test-http-contentLength0.js +function verifyLink(linkPath) { + const stats = fs.lstatSync(linkPath); + assert.ok(stats.isSymbolicLink()); + + const data1 = fs.readFileSync(`${linkPath}/x.txt`, 'ascii'); + const data2 = fs.readFileSync(`${linkTarget}/x.txt`, 'ascii'); + assert.strictEqual(data1, data2); + + // Clean up. + fs.unlinkSync(linkPath); +} diff --git a/test/js/node/test/parallel/fs-readfile-unlink.test.js b/test/js/node/test/parallel/test-fs-symlink-dir-junction.js similarity index 51% rename from test/js/node/test/parallel/fs-readfile-unlink.test.js rename to test/js/node/test/parallel/test-fs-symlink-dir-junction.js index 85475f6488..3990467c6f 100644 --- a/test/js/node/test/parallel/fs-readfile-unlink.test.js +++ b/test/js/node/test/parallel/test-fs-symlink-dir-junction.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-readfile-unlink.js -//#SHA1: a7107747d7901dfcc1ffd0e7adbf548412a1016a -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,31 +19,45 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); +const tmpdir = require('../common/tmpdir'); -// Test that unlink succeeds immediately after readFile completes. +// Test creating and reading symbolic link +const linkData = fixtures.path('cycles/'); +const linkPath = tmpdir.resolve('cycles_link'); -test("unlink succeeds immediately after readFile completes", async () => { - const tmpdir = path.join(os.tmpdir(), "node-test-fs-readfile-unlink"); - await fs.promises.mkdir(tmpdir, { recursive: true }); +tmpdir.refresh(); - const fileName = path.join(tmpdir, "test.bin"); - const buf = Buffer.alloc(512 * 1024, 42); +fs.symlink(linkData, linkPath, 'junction', common.mustSucceed(() => { + fs.lstat(linkPath, common.mustSucceed((stats) => { + assert.ok(stats.isSymbolicLink()); - await fs.promises.writeFile(fileName, buf); + fs.readlink(linkPath, common.mustSucceed((destination) => { + assert.strictEqual(destination, linkData); - const data = await fs.promises.readFile(fileName); + fs.unlink(linkPath, common.mustSucceed(() => { + assert(!fs.existsSync(linkPath)); + assert(fs.existsSync(linkData)); + })); + })); + })); +})); - expect(data.length).toBe(buf.length); - expect(data[0]).toBe(42); +// Test invalid symlink +{ + const linkData = fixtures.path('/not/exists/dir'); + const linkPath = tmpdir.resolve('invalid_junction_link'); - // Unlink should not throw. This is part of the test. It used to throw on - // Windows due to a bug. - await expect(fs.promises.unlink(fileName)).resolves.toBeUndefined(); -}); + fs.symlink(linkData, linkPath, 'junction', common.mustSucceed(() => { + assert(!fs.existsSync(linkPath)); -//<#END_FILE: test-fs-readfile-unlink.js + fs.unlink(linkPath, common.mustSucceed(() => { + assert(!fs.existsSync(linkPath)); + })); + })); +} diff --git a/test/js/node/test/parallel/test-fs-symlink-dir.js b/test/js/node/test/parallel/test-fs-symlink-dir.js new file mode 100644 index 0000000000..690e3302ed --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-dir.js @@ -0,0 +1,81 @@ +'use strict'; +const common = require('../common'); + +// Test creating a symbolic link pointing to a directory. +// Ref: https://github.com/nodejs/node/pull/23724 +// Ref: https://github.com/nodejs/node/issues/23596 + + +if (!common.canCreateSymLink()) + common.skip('insufficient privileges'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const fsPromises = fs.promises; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const linkTargets = [ + 'relative-target', + tmpdir.resolve('absolute-target'), +]; +const linkPaths = [ + path.relative(process.cwd(), tmpdir.resolve('relative-path')), + tmpdir.resolve('absolute-path'), +]; + +function testSync(target, path) { + fs.symlinkSync(target, path); + fs.readdirSync(path); +} + +function testAsync(target, path) { + fs.symlink(target, path, common.mustSucceed(() => { + fs.readdirSync(path); + })); +} + +async function testPromises(target, path) { + await fsPromises.symlink(target, path); + fs.readdirSync(path); +} + +for (const linkTarget of linkTargets) { + fs.mkdirSync(tmpdir.resolve(linkTarget)); + for (const linkPath of linkPaths) { + testSync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-sync`); + testAsync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-async`); + testPromises(linkTarget, `${linkPath}-${path.basename(linkTarget)}-promises`) + .then(common.mustCall()); + } +} + +// Test invalid symlink +{ + function testSync(target, path) { + fs.symlinkSync(target, path); + assert(!fs.existsSync(path)); + } + + function testAsync(target, path) { + fs.symlink(target, path, common.mustSucceed(() => { + assert(!fs.existsSync(path)); + })); + } + + async function testPromises(target, path) { + await fsPromises.symlink(target, path); + assert(!fs.existsSync(path)); + } + + for (const linkTarget of linkTargets.map((p) => p + '-broken')) { + for (const linkPath of linkPaths) { + testSync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-sync`); + testAsync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-async`); + testPromises(linkTarget, `${linkPath}-${path.basename(linkTarget)}-promises`) + .then(common.mustCall()); + } + } +} diff --git a/test/js/node/test/parallel/test-fs-symlink-longpath.js b/test/js/node/test/parallel/test-fs-symlink-longpath.js new file mode 100644 index 0000000000..f3586317c2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-longpath.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const tmpDir = tmpdir.path; +const longPath = path.join(...[tmpDir].concat(Array(30).fill('1234567890'))); +fs.mkdirSync(longPath, { recursive: true }); + +// Test if we can have symlinks to files and folders with long filenames +const targetDirectory = path.join(longPath, 'target-directory'); +fs.mkdirSync(targetDirectory); +const pathDirectory = path.join(tmpDir, 'new-directory'); +fs.symlink(targetDirectory, pathDirectory, 'dir', common.mustSucceed(() => { + assert(fs.existsSync(pathDirectory)); +})); + +const targetFile = path.join(longPath, 'target-file'); +fs.writeFileSync(targetFile, 'data'); +const pathFile = path.join(tmpDir, 'new-file'); +fs.symlink(targetFile, pathFile, common.mustSucceed(() => { + assert(fs.existsSync(pathFile)); +})); diff --git a/test/js/node/test/parallel/fs-truncate-clear-file-zero.test.js b/test/js/node/test/parallel/test-fs-truncate-clear-file-zero.js similarity index 57% rename from test/js/node/test/parallel/fs-truncate-clear-file-zero.test.js rename to test/js/node/test/parallel/test-fs-truncate-clear-file-zero.js index 8dc9566b1f..234e65e580 100644 --- a/test/js/node/test/parallel/fs-truncate-clear-file-zero.test.js +++ b/test/js/node/test/parallel/test-fs-truncate-clear-file-zero.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-truncate-clear-file-zero.js -//#SHA1: 28aa057c9903ea2436c340ccecfec093c647714c -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,46 +19,38 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); // This test ensures that `fs.truncate` opens the file with `r+` and not `w`, // which had earlier resulted in the target file's content getting zeroed out. // https://github.com/nodejs/node-v0.x-archive/issues/6233 -const filename = path.join(os.tmpdir(), "truncate-file.txt"); +const assert = require('assert'); +const fs = require('fs'); -beforeEach(() => { - // Clean up any existing test file - try { - fs.unlinkSync(filename); - } catch (err) { - if (err.code !== "ENOENT") throw err; - } -}); +const filename = `${tmpdir.path}/truncate-file.txt`; -test("fs.truncateSync", () => { - fs.writeFileSync(filename, "0123456789"); - expect(fs.readFileSync(filename, "utf8")).toBe("0123456789"); +tmpdir.refresh(); + +// Synchronous test. +{ + fs.writeFileSync(filename, '0123456789'); + assert.strictEqual(fs.readFileSync(filename).toString(), '0123456789'); fs.truncateSync(filename, 5); - expect(fs.readFileSync(filename, "utf8")).toBe("01234"); -}); + assert.strictEqual(fs.readFileSync(filename).toString(), '01234'); +} -test("fs.truncate", async () => { - fs.writeFileSync(filename, "0123456789"); - expect(fs.readFileSync(filename, "utf8")).toBe("0123456789"); - - await new Promise((resolve, reject) => { - fs.truncate(filename, 5, err => { - if (err) reject(err); - else resolve(); - }); - }); - - expect(fs.readFileSync(filename, "utf8")).toBe("01234"); -}); - -//<#END_FILE: test-fs-truncate-clear-file-zero.js +// Asynchronous test. +{ + fs.writeFileSync(filename, '0123456789'); + assert.strictEqual(fs.readFileSync(filename).toString(), '0123456789'); + fs.truncate( + filename, + 5, + common.mustSucceed(() => { + assert.strictEqual(fs.readFileSync(filename).toString(), '01234'); + }) + ); +} diff --git a/test/js/node/test/parallel/test-fs-truncate-sync.js b/test/js/node/test/parallel/test-fs-truncate-sync.js new file mode 100644 index 0000000000..66250cf438 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-truncate-sync.js @@ -0,0 +1,21 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const tmp = tmpdir.path; + +tmpdir.refresh(); + +const filename = path.resolve(tmp, 'truncate-sync-file.txt'); + +fs.writeFileSync(filename, 'hello world', 'utf8'); + +const fd = fs.openSync(filename, 'r+'); + +fs.truncateSync(fd, 5); +assert(fs.readFileSync(fd).equals(Buffer.from('hello'))); + +fs.closeSync(fd); +fs.unlinkSync(filename); diff --git a/test/js/node/test/parallel/test-fs-unlink-type-check.js b/test/js/node/test/parallel/test-fs-unlink-type-check.js new file mode 100644 index 0000000000..006e9ad734 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-unlink-type-check.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.unlink(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.unlinkSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-utimes-y2K38.js b/test/js/node/test/parallel/test-fs-utimes-y2K38.js new file mode 100644 index 0000000000..9e42e90feb --- /dev/null +++ b/test/js/node/test/parallel/test-fs-utimes-y2K38.js @@ -0,0 +1,66 @@ +'use strict'; +const common = require('../common'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const fs = require('fs'); + +// Check for Y2K38 support. For Windows, assume it's there. Windows +// doesn't have `touch` and `date -r` which are used in the check for support. +if (!common.isWindows) { + const testFilePath = `${tmpdir.path}/y2k38-test`; + const testFileDate = '204001020304'; + const { spawnSync } = require('child_process'); + const touchResult = spawnSync('touch', + ['-t', testFileDate, testFilePath], + { encoding: 'utf8' }); + if (touchResult.status !== 0) { + common.skip('File system appears to lack Y2K38 support (touch failed)'); + } + + // On some file systems that lack Y2K38 support, `touch` will succeed but + // the time will be incorrect. + const dateResult = spawnSync('date', + ['-r', testFilePath, '+%Y%m%d%H%M'], + { encoding: 'utf8' }); + if (dateResult.status === 0) { + if (dateResult.stdout.trim() !== testFileDate) { + common.skip('File system appears to lack Y2k38 support (date failed)'); + } + } else { + // On some platforms `date` may not support the `-r` option. Usually + // this will result in a non-zero status and usage information printed. + // In this case optimistically proceed -- the earlier `touch` succeeded + // but validation that the file has the correct time is not easily possible. + assert.match(dateResult.stderr, /[Uu]sage:/); + } +} + +// Ref: https://github.com/nodejs/node/issues/13255 +const path = `${tmpdir.path}/test-utimes-precision`; +fs.writeFileSync(path, ''); + +const Y2K38_mtime = 2 ** 31; +fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime); +const Y2K38_stats = fs.statSync(path); +assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime); + +if (common.isWindows) { + // This value would get converted to (double)1713037251359.9998 + const truncate_mtime = 1713037251360; + fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000); + const truncate_stats = fs.statSync(path); + assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime); + + // test Y2K38 for windows + // This value if treaded as a `signed long` gets converted to -2135622133469. + // POSIX systems stores timestamps in {long t_sec, long t_usec}. + // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv + // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement. + const overflow_mtime = 2159345162531; + fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000); + const overflow_stats = fs.statSync(path); + assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime); +} diff --git a/test/js/node/test/parallel/test-fs-watch-abort-signal.js b/test/js/node/test/parallel/test-fs-watch-abort-signal.js new file mode 100644 index 0000000000..33936d2d49 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-abort-signal.js @@ -0,0 +1,30 @@ +// Flags: --expose-internals +'use strict'; + +// Verify that AbortSignal integration works for fs.watch + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + + +{ + // Signal aborted after creating the watcher + const file = fixtures.path('empty.js'); + const ac = new AbortController(); + const { signal } = ac; + const watcher = fs.watch(file, { signal }); + watcher.once('close', common.mustCall()); + setImmediate(() => ac.abort()); +} +{ + // Signal aborted before creating the watcher + const file = fixtures.path('empty.js'); + const signal = AbortSignal.abort(); + const watcher = fs.watch(file, { signal }); + watcher.once('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-fs-watch-close-when-destroyed.js b/test/js/node/test/parallel/test-fs-watch-close-when-destroyed.js new file mode 100644 index 0000000000..afa5307e4d --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-close-when-destroyed.js @@ -0,0 +1,48 @@ +'use strict'; + +// This tests that closing a watcher when the underlying handle is +// already destroyed will result in a noop instead of a crash. + +const common = require('../common'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); + +tmpdir.refresh(); +const root = tmpdir.resolve('watched-directory'); +fs.mkdirSync(root); + +const watcher = fs.watch(root, { persistent: false, recursive: false }); + +// The following listeners may or may not be invoked. + +watcher.addListener('error', () => { + setTimeout( + () => { watcher.close(); }, // Should not crash if it's invoked + common.platformTimeout(10) + ); +}); + +watcher.addListener('change', () => { + setTimeout( + () => { watcher.close(); }, + common.platformTimeout(10) + ); +}); + +fs.rmdirSync(root); +// Wait for the listener to hit +setTimeout( + common.mustCall(), + common.platformTimeout(100) +); diff --git a/test/js/node/test/parallel/test-fs-watch-encoding.js b/test/js/node/test/parallel/test-fs-watch-encoding.js new file mode 100644 index 0000000000..758c6c26be --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-encoding.js @@ -0,0 +1,95 @@ +'use strict'; + +// This test is a bit more complicated than it ideally needs to be to work +// around issues on OS X and SmartOS. +// +// On OS X, watch events are subject to peculiar timing oddities such that an +// event might fire out of order. The synchronous refreshing of the tmp +// directory might trigger an event on the watchers that are instantiated after +// it! +// +// On SmartOS, the watch events fire but the filename is null. + +const common = require('../common'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fn = '新建文夹件.txt'; +const a = tmpdir.resolve(fn); + +const watchers = new Set(); + +function registerWatcher(watcher) { + watchers.add(watcher); +} + +function unregisterWatcher(watcher) { + watcher.close(); + watchers.delete(watcher); + if (watchers.size === 0) { + clearInterval(interval); + } +} + +{ + // Test that using the `encoding` option has the expected result. + const watcher = fs.watch( + tmpdir.path, + { encoding: 'hex' }, + (event, filename) => { + if (['e696b0e5bbbae69687e5a4b9e4bbb62e747874', null].includes(filename)) + done(watcher); + } + ); + registerWatcher(watcher); +} + +{ + // Test that in absence of `encoding` option has the expected result. + const watcher = fs.watch( + tmpdir.path, + (event, filename) => { + if ([fn, null].includes(filename)) + done(watcher); + } + ); + registerWatcher(watcher); +} + +{ + // Test that using the `encoding` option has the expected result. + const watcher = fs.watch( + tmpdir.path, + { encoding: 'buffer' }, + (event, filename) => { + if (filename instanceof Buffer && filename.toString('utf8') === fn) + done(watcher); + else if (filename === null) + done(watcher); + } + ); + registerWatcher(watcher); +} + +const done = common.mustCall(unregisterWatcher, watchers.size); + +// OS X and perhaps other systems can have surprising race conditions with +// file events. So repeat the operation in case it is missed the first time. +const interval = setInterval(() => { + const fd = fs.openSync(a, 'w+'); + fs.closeSync(fd); + fs.unlinkSync(a); +}, common.platformTimeout(100)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-existing-subfolder.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-existing-subfolder.js new file mode 100644 index 0000000000..628ca4b2fd --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-existing-subfolder.js @@ -0,0 +1,58 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Add a file to subfolder of a watching folder + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-4'); +fs.mkdirSync(testDirectory); + +const file = 'folder-5'; +const filePath = path.join(testDirectory, file); +fs.mkdirSync(filePath); + +const subfolderPath = path.join(filePath, 'subfolder-6'); +fs.mkdirSync(subfolderPath); + +const childrenFile = 'file-7.txt'; +const childrenAbsolutePath = path.join(subfolderPath, childrenFile); +const relativePath = path.join(file, path.basename(subfolderPath), childrenFile); + +const watcher = fs.watch(testDirectory, { recursive: true }); +let watcherClosed = false; +watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === relativePath) { + watcher.close(); + watcherClosed = true; + } +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.writeFileSync(childrenAbsolutePath, 'world'); +}, common.platformTimeout(200)); + +process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-new-folder.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-new-folder.js new file mode 100644 index 0000000000..2f91c968f7 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-new-folder.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Add a file to newly created folder to already watching folder + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-3'); +fs.mkdirSync(testDirectory); + +const filePath = path.join(testDirectory, 'folder-3'); + +const childrenFile = 'file-4.txt'; +const childrenAbsolutePath = path.join(filePath, childrenFile); +const childrenRelativePath = path.join(path.basename(filePath), childrenFile); + +const watcher = fs.watch(testDirectory, { recursive: true }); +let watcherClosed = false; +watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + assert.ok(filename === path.basename(filePath) || filename === childrenRelativePath); + + if (filename === childrenRelativePath) { + watcher.close(); + watcherClosed = true; + } +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.mkdirSync(filePath); + fs.writeFileSync(childrenAbsolutePath, 'world'); +}, common.platformTimeout(200)); + +process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file-with-url.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-with-url.js new file mode 100644 index 0000000000..ee726961c4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-with-url.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const { pathToFileURL } = require('url'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Add a file to already watching folder, and use URL as the path + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-5'); + fs.mkdirSync(testDirectory); + + const filePath = path.join(testDirectory, 'file-8.txt'); + const url = pathToFileURL(testDirectory); + + const watcher = fs.watch(url, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(filePath, 'world'); + + process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file.js new file mode 100644 index 0000000000..27b933871c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Add a file to already watching folder + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-1'); +fs.mkdirSync(testDirectory); + +const testFile = path.join(testDirectory, 'file-1.txt'); + +const watcher = fs.watch(testDirectory, { recursive: true }); +let watcherClosed = false; +watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + watcherClosed = true; + } +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.writeFileSync(testFile, 'world'); +}, common.platformTimeout(200)); + +process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-folder.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-folder.js new file mode 100644 index 0000000000..1851a7850f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-folder.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Add a folder to already watching folder + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-2'); + fs.mkdirSync(testDirectory); + + const testFile = path.join(testDirectory, 'folder-2'); + + const watcher = fs.watch(testDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.mkdirSync(testFile); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-assert-leaks.js b/test/js/node/test/parallel/test-fs-watch-recursive-assert-leaks.js new file mode 100644 index 0000000000..f5950e38ce --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-assert-leaks.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Assert recursive watch does not leak handles +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-7'); +const filePath = path.join(testDirectory, 'only-file.txt'); +fs.mkdirSync(testDirectory); + +let watcherClosed = false; +const watcher = fs.watch(testDirectory, { recursive: true }); +watcher.on('change', common.mustCallAtLeast(async (event, filename) => { + await setTimeout(common.platformTimeout(100)); + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + await setTimeout(common.platformTimeout(100)); + assert(!process._getActiveHandles().some((handle) => handle.constructor.name === 'StatWatcher')); +})); + +process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +(async () => { + await setTimeout(200); + fs.writeFileSync(filePath, 'content'); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-delete.js b/test/js/node/test/parallel/test-fs-watch-recursive-delete.js new file mode 100644 index 0000000000..8e78ad54d6 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-delete.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); + +if (common.isSunOS) + common.skip('SunOS behaves differently'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +tmpdir.refresh(); + +fs.mkdirSync(tmpdir.resolve('./parent/child'), { recursive: true }); + +fs.writeFileSync(tmpdir.resolve('./parent/child/test.tmp'), 'test'); + +const toWatch = tmpdir.resolve('./parent'); + +const onFileUpdate = common.mustCallAtLeast((eventType, filename) => { + // We are only checking for the filename to avoid having Windows, Linux and Mac specific assertions + if (fs.readdirSync(tmpdir.resolve('./parent')).length === 0) { + watcher.close(); + } +}, 1); + +const watcher = fs.watch(toWatch, { recursive: true }, onFileUpdate); + +// We must wait a bit `fs.rm()` to let the watcher be set up properly +setTimeout(() => { + fs.rm(tmpdir.resolve('./parent/child'), { recursive: true }, common.mustCall()); +}, common.platformTimeout(500)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js b/test/js/node/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js new file mode 100644 index 0000000000..145b3314f2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); + +if (!common.isLinux) + common.skip('This test can run only on Linux'); + +// Test that the watcher do not crash if the file "disappears" while +// watch is being set up. + +const path = require('node:path'); +const fs = require('node:fs'); +const { spawn } = require('node:child_process'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +const watcher = fs.watch(testDir, { recursive: true }); +watcher.on('change', function(event, filename) { + // This console.log makes the error happen + // do not remove + console.log(filename, event); +}); + +const testFile = path.join(testDir, 'a'); +const child = spawn(process.argv[0], ['-e', `const fs = require('node:fs'); for (let i = 0; i < 10000; i++) { const fd = fs.openSync('${testFile}', 'w'); fs.writeSync(fd, Buffer.from('hello')); fs.rmSync('${testFile}') }`], { + stdio: 'inherit' +}); + +child.on('exit', function() { + watcher.close(); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-symlink.js b/test/js/node/test/parallel/test-fs-watch-recursive-symlink.js new file mode 100644 index 0000000000..602ec58eab --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-symlink.js @@ -0,0 +1,100 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Add a recursive symlink to the parent folder + + const testDirectory = fs.mkdtempSync(testDir + path.sep); + + // Do not use `testDirectory` as base. It will hang the tests. + const rootDirectory = path.join(testDirectory, 'test-1'); + fs.mkdirSync(rootDirectory); + + const filePath = path.join(rootDirectory, 'file.txt'); + + const symlinkFolder = path.join(rootDirectory, 'symlink-folder'); + fs.symlinkSync(rootDirectory, symlinkFolder); + + + const watcher = fs.watch(rootDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + assert.ok(event === 'rename', `Received ${event}`); + assert.ok(filename === path.basename(symlinkFolder) || filename === path.basename(filePath), `Received ${filename}`); + + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(filePath, 'world'); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); + +(async () => { + // This test checks how a symlink to outside the tracking folder can trigger change + // tmp/sub-directory/tracking-folder/symlink-folder -> tmp/sub-directory + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + + const subDirectory = path.join(rootDirectory, 'sub-directory'); + fs.mkdirSync(subDirectory); + + const trackingSubDirectory = path.join(subDirectory, 'tracking-folder'); + fs.mkdirSync(trackingSubDirectory); + + const symlinkFolder = path.join(trackingSubDirectory, 'symlink-folder'); + fs.symlinkSync(subDirectory, symlinkFolder); + + const forbiddenFile = path.join(subDirectory, 'forbidden.txt'); + const acceptableFile = path.join(trackingSubDirectory, 'acceptable.txt'); + + const watcher = fs.watch(trackingSubDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + // macOS will only change the following events: + // { event: 'rename', filename: 'symlink-folder' } + // { event: 'rename', filename: 'acceptable.txt' } + assert.ok(event === 'rename', `Received ${event}`); + assert.ok(filename === path.basename(symlinkFolder) || filename === path.basename(acceptableFile), `Received ${filename}`); + + if (filename === path.basename(acceptableFile)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(forbiddenFile, 'world'); + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(acceptableFile, 'acceptable'); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-sync-write.js b/test/js/node/test/parallel/test-fs-watch-recursive-sync-write.js new file mode 100644 index 0000000000..ecc380d190 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-sync-write.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const { watch, writeFileSync } = require('node:fs'); +const { join } = require('node:path'); +const tmpdir = require('../common/tmpdir.js'); +const assert = require('assert'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +tmpdir.refresh(); + +const tmpDir = tmpdir.path; +const filename = join(tmpDir, 'test.file'); + +const keepalive = setTimeout(() => { + throw new Error('timed out'); +}, common.platformTimeout(30_000)); + +const watcher = watch(tmpDir, { recursive: true }, common.mustCall((eventType, _filename) => { + clearTimeout(keepalive); + watcher.close(); + assert.strictEqual(eventType, 'rename'); + assert.strictEqual(join(tmpDir, _filename), filename); +})); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + writeFileSync(filename, 'foobar2'); +}, common.platformTimeout(200)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-update-file.js b/test/js/node/test/parallel/test-fs-watch-recursive-update-file.js new file mode 100644 index 0000000000..7100b015ab --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-update-file.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Watch a folder and update an already existing file in it. + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-0'); +fs.mkdirSync(testDirectory); + +const testFile = path.join(testDirectory, 'file-1.txt'); +fs.writeFileSync(testFile, 'hello'); + +const watcher = fs.watch(testDirectory, { recursive: true }); +watcher.on('change', common.mustCallAtLeast(function(event, filename) { + // Libuv inconsistenly emits a rename event for the file we are watching + assert.ok(event === 'change' || event === 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + } +})); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.writeFileSync(testFile, 'hello'); +}, common.platformTimeout(200)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-validate.js b/test/js/node/test/parallel/test-fs-watch-recursive-validate.js new file mode 100644 index 0000000000..09eccc2d7e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-validate.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Handle non-boolean values for options.recursive + + if (!common.isWindows && !common.isMacOS) { + assert.throws(() => { + const testsubdir = fs.mkdtempSync(testDir + path.sep); + fs.watch(testsubdir, { recursive: '1' }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-watch-file.js b/test/js/node/test/parallel/test-fs-watch-recursive-watch-file.js new file mode 100644 index 0000000000..3449db8e59 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-watch-file.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Watch a file (not a folder) using fs.watch + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-6'); + fs.mkdirSync(testDirectory); + + const filePath = path.join(testDirectory, 'only-file.txt'); + fs.writeFileSync(filePath, 'hello'); + + const watcher = fs.watch(filePath, { recursive: true }); + let watcherClosed = false; + let interval; + watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'change'); + + if (filename === path.basename(filePath)) { + clearInterval(interval); + interval = null; + watcher.close(); + watcherClosed = true; + } + }); + + interval = setInterval(() => { + fs.writeFileSync(filePath, 'world'); + }, common.platformTimeout(10)); + + process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + assert.strictEqual(interval, null); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-ref-unref.js b/test/js/node/test/parallel/test-fs-watch-ref-unref.js new file mode 100644 index 0000000000..e51ecaf5b2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-ref-unref.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); + +const watcher = fs.watch(__filename, common.mustNotCall()); + +watcher.unref(); + +setTimeout( + common.mustCall(() => { + watcher.ref(); + watcher.unref(); + }), + common.platformTimeout(100) +); diff --git a/test/js/node/test/parallel/test-fs-watch-stop-sync.js b/test/js/node/test/parallel/test-fs-watch-stop-sync.js new file mode 100644 index 0000000000..7f0882e489 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-stop-sync.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); + +// This test checks that the `stop` event is emitted asynchronously. +// +// If it isn't asynchronous, then the listener will be called during the +// execution of `watch.stop()`. That would be a bug. +// +// If it is asynchronous, then the listener will be removed before the event is +// emitted. + +const fs = require('fs'); + +const listener = common.mustNotCall( + 'listener should have been removed before the event was emitted' +); + +const watch = fs.watchFile(__filename, common.mustNotCall()); +watch.once('stop', listener); +watch.stop(); +watch.removeListener('stop', listener); diff --git a/test/js/node/test/parallel/test-fs-watch.js b/test/js/node/test/parallel/test-fs-watch.js new file mode 100644 index 0000000000..a494b9f8df --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch.js @@ -0,0 +1,98 @@ +'use strict'; +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// Tests if `filename` is provided to watcher on supported platforms + +const fs = require('fs'); +const assert = require('assert'); +const { join } = require('path'); + +class WatchTestCase { + constructor(shouldInclude, dirName, fileName, field) { + this.dirName = dirName; + this.fileName = fileName; + this.field = field; + this.shouldSkip = !shouldInclude; + } + get dirPath() { return tmpdir.resolve(this.dirName); } + get filePath() { return join(this.dirPath, this.fileName); } +} + +const cases = [ + // Watch on a file should callback with a filename on supported systems + new WatchTestCase( + common.isLinux || common.isMacOS || common.isWindows || common.isAIX, + 'watch1', + 'foo', + 'filePath' + ), + // Watch on a directory should callback with a filename on supported systems + new WatchTestCase( + common.isLinux || common.isMacOS || common.isWindows, + 'watch2', + 'bar', + 'dirPath' + ), +]; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +for (const testCase of cases) { + if (testCase.shouldSkip) continue; + fs.mkdirSync(testCase.dirPath); + // Long content so it's actually flushed. + const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4); + fs.writeFileSync(testCase.filePath, content1); + + let interval; + const pathToWatch = testCase[testCase.field]; + const watcher = fs.watch(pathToWatch); + watcher.on('error', (err) => { + if (interval) { + clearInterval(interval); + interval = null; + } + assert.fail(err); + }); + watcher.on('close', common.mustCall(() => { + watcher.close(); // Closing a closed watcher should be a noop + })); + watcher.on('change', common.mustCall(function(eventType, argFilename) { + if (interval) { + clearInterval(interval); + interval = null; + } + if (common.isMacOS) + assert.strictEqual(['rename', 'change'].includes(eventType), true); + else + assert.strictEqual(eventType, 'change'); + assert.strictEqual(argFilename, testCase.fileName); + + watcher.close(); + + // We document that watchers cannot be used anymore when it's closed, + // here we turn the methods into noops instead of throwing + watcher.close(); // Closing a closed watcher should be a noop + })); + + // Long content so it's actually flushed. toUpperCase so there's real change. + const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4); + interval = setInterval(() => { + fs.writeFileSync(testCase.filePath, ''); + fs.writeFileSync(testCase.filePath, content2); + }, 100); +} + +[false, 1, {}, [], null, undefined].forEach((input) => { + assert.throws( + () => fs.watch(input, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-watchfile-ref-unref.js b/test/js/node/test/parallel/test-fs-watchfile-ref-unref.js new file mode 100644 index 0000000000..4ac2691ea9 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watchfile-ref-unref.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); + +const fs = require('fs'); +const assert = require('assert'); + +const uncalledListener = common.mustNotCall(); +const uncalledListener2 = common.mustNotCall(); +const watcher = fs.watchFile(__filename, uncalledListener); + +watcher.unref(); +watcher.unref(); +watcher.ref(); +watcher.unref(); +watcher.ref(); +watcher.ref(); +watcher.unref(); + +fs.unwatchFile(__filename, uncalledListener); + +// Watch the file with two different listeners. +fs.watchFile(__filename, uncalledListener); +const watcher2 = fs.watchFile(__filename, uncalledListener2); + +setTimeout( + common.mustCall(() => { + fs.unwatchFile(__filename, common.mustNotCall()); + assert.strictEqual(watcher2.listenerCount('change'), 2); + fs.unwatchFile(__filename, uncalledListener); + assert.strictEqual(watcher2.listenerCount('change'), 1); + watcher2.unref(); + }), + common.platformTimeout(100) +); diff --git a/test/js/node/test/parallel/test-fs-write-file-buffer.js b/test/js/node/test/parallel/test-fs-write-file-buffer.js new file mode 100644 index 0000000000..ec6c60e1a4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-file-buffer.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const util = require('util'); +const fs = require('fs'); + +let data = [ + '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH', + 'Bw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/', + '2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e', + 'Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAQABADASIAAhEBAxEB/8QA', + 'HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF', + 'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK', + 'FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1', + 'dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG', + 'x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB', + 'AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC', + 'AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom', + 'JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE', + 'hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU', + '1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhfBUFl/wk', + 'OmPqKJJZw3aiZFBw4z93jnkkc9u9dj8XLfSI/EBt7DTo7ea2Ox5YXVo5FC7g', + 'Tjq24nJPXNVtO0KATRvNHCIg3zoWJWQHqp+o4pun+EtJ0zxBq8mnLJa2d1L5', + '0NvnKRjJBUE5PAx3NYxxUY0pRtvYHSc5Ka2X9d7H/9k=']; + +data = data.join('\n'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const buf = Buffer.from(data, 'base64'); +fs.writeFileSync(tmpdir.resolve('test.jpg'), buf); + +util.log('Done!'); diff --git a/test/js/node/test/parallel/test-fs-write-file-invalid-path.js b/test/js/node/test/parallel/test-fs-write-file-invalid-path.js new file mode 100644 index 0000000000..aaa7eacde5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-file-invalid-path.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +if (!common.isWindows) + common.skip('This test is for Windows only.'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const DATA_VALUE = 'hello'; + +// Refs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx +// Ignore '/', '\\' and ':' +const RESERVED_CHARACTERS = '<>"|?*'; + +[...RESERVED_CHARACTERS].forEach((ch) => { + const pathname = tmpdir.resolve(`somefile_${ch}`); + assert.throws( + () => { + fs.writeFileSync(pathname, DATA_VALUE); + }, + /^Error: ENOENT: no such file or directory, open '.*'$/, + `failed with '${ch}'`); +}); + +// Test for ':' (NTFS data streams). +// Refs: https://msdn.microsoft.com/en-us/library/windows/desktop/bb540537.aspx +const pathname = tmpdir.resolve('foo:bar'); +fs.writeFileSync(pathname, DATA_VALUE); + +let content = ''; +const fileDataStream = fs.createReadStream(pathname, { + encoding: 'utf8' +}); + +fileDataStream.on('data', (data) => { + content += data; +}); + +fileDataStream.on('end', common.mustCall(() => { + assert.strictEqual(content, DATA_VALUE); +})); diff --git a/test/js/node/test/parallel/test-fs-write-no-fd.js b/test/js/node/test/parallel/test-fs-write-no-fd.js new file mode 100644 index 0000000000..576457203e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-no-fd.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +assert.throws(function() { + fs.write(null, Buffer.allocUnsafe(1), 0, 1, common.mustNotCall()); +}, /TypeError/); + +assert.throws(function() { + fs.write(null, '1', 0, 1, common.mustNotCall()); +}, /TypeError/); diff --git a/test/js/node/test/parallel/test-fs-write-stream-close-without-callback.js b/test/js/node/test/parallel/test-fs-write-stream-close-without-callback.js new file mode 100644 index 0000000000..7bf83cd809 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-stream-close-without-callback.js @@ -0,0 +1,12 @@ +'use strict'; + +require('../common'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const s = fs.createWriteStream(tmpdir.resolve('nocallback')); + +s.end('hello world'); +s.close(); diff --git a/test/js/node/test/parallel/test-fs-write-stream-encoding.js b/test/js/node/test/parallel/test-fs-write-stream-encoding.js new file mode 100644 index 0000000000..f06fae923c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-stream-encoding.js @@ -0,0 +1,35 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const stream = require('stream'); +const tmpdir = require('../common/tmpdir'); +const firstEncoding = 'base64'; +const secondEncoding = 'latin1'; + +const examplePath = fixtures.path('x.txt'); +const dummyPath = tmpdir.resolve('x.txt'); + +tmpdir.refresh(); + +const exampleReadStream = fs.createReadStream(examplePath, { + encoding: firstEncoding +}); + +const dummyWriteStream = fs.createWriteStream(dummyPath, { + encoding: firstEncoding +}); + +exampleReadStream.pipe(dummyWriteStream).on('finish', function() { + const assertWriteStream = new stream.Writable({ + write: function(chunk, enc, next) { + const expected = Buffer.from('xyz\n'); + assert(chunk.equals(expected)); + } + }); + assertWriteStream.setDefaultEncoding(secondEncoding); + fs.createReadStream(dummyPath, { + encoding: secondEncoding + }).pipe(assertWriteStream); +}); diff --git a/test/js/node/test/parallel/fs-write-stream-end.test.js b/test/js/node/test/parallel/test-fs-write-stream-end.js similarity index 56% rename from test/js/node/test/parallel/fs-write-stream-end.test.js rename to test/js/node/test/parallel/test-fs-write-stream-end.js index a73488cb3b..7f0cc65540 100644 --- a/test/js/node/test/parallel/fs-write-stream-end.test.js +++ b/test/js/node/test/parallel/test-fs-write-stream-end.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-write-stream-end.js -//#SHA1: a4194cfb1f416f5fddd5edc55b7d867db14a5320 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,55 +19,41 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const fs = require("fs"); -const path = require("path"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); -const tmpdir = path.join(__dirname, "tmp"); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); -beforeAll(() => { - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("end without data", done => { - const file = path.join(tmpdir, "write-end-test0.txt"); +{ + const file = tmpdir.resolve('write-end-test0.txt'); const stream = fs.createWriteStream(file); stream.end(); - stream.on("close", () => { - done(); - }); -}); + stream.on('close', common.mustCall()); +} -test("end with data", done => { - const file = path.join(tmpdir, "write-end-test1.txt"); +{ + const file = tmpdir.resolve('write-end-test1.txt'); const stream = fs.createWriteStream(file); - stream.end("a\n", "utf8"); - stream.on("close", () => { - const content = fs.readFileSync(file, "utf8"); - expect(content).toBe("a\n"); - done(); - }); -}); + stream.end('a\n', 'utf8'); + stream.on('close', common.mustCall(function() { + const content = fs.readFileSync(file, 'utf8'); + assert.strictEqual(content, 'a\n'); + })); +} -test("end triggers open and finish events", done => { - const file = path.join(tmpdir, "write-end-test2.txt"); +{ + const file = tmpdir.resolve('write-end-test2.txt'); const stream = fs.createWriteStream(file); stream.end(); let calledOpen = false; - stream.on("open", () => { + stream.on('open', () => { calledOpen = true; }); - stream.on("finish", () => { - expect(calledOpen).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-fs-write-stream-end.js + stream.on('finish', common.mustCall(() => { + assert.strictEqual(calledOpen, true); + })); +} diff --git a/test/js/node/test/parallel/http-url.parse-auth.test.js b/test/js/node/test/parallel/test-fs-write-stream.js similarity index 56% rename from test/js/node/test/parallel/http-url.parse-auth.test.js rename to test/js/node/test/parallel/test-fs-write-stream.js index 439d699393..a3dccf7cdc 100644 --- a/test/js/node/test/parallel/http-url.parse-auth.test.js +++ b/test/js/node/test/parallel/test-fs-write-stream.js @@ -1,6 +1,3 @@ -//#FILE: test-http-url.parse-auth.js -//#SHA1: 97f9b1c737c705489b2d6402750034291a9f6f63 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,35 +19,48 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); -test("http url parse auth", async () => { - function check(request) { - // The correct authorization header is be passed - expect(request.headers.authorization).toBe("Basic dXNlcjpwYXNzOg=="); - } +const tmpdir = require('../common/tmpdir'); - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); +const file = tmpdir.resolve('write.txt'); + +tmpdir.refresh(); + +{ + const stream = fs.WriteStream(file); + const _fs_close = fs.close; + + fs.close = function(fd) { + assert.ok(fd, 'fs.close must not be called without an undefined fd.'); + fs.close = _fs_close; + fs.closeSync(fd); + }; + stream.destroy(); +} + +{ + const stream = fs.createWriteStream(file); + + stream.on('drain', function() { + assert.fail('\'drain\' event must not be emitted before ' + + 'stream.write() has been called at least once.'); }); + stream.destroy(); +} - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - // username = "user", password = "pass:" - const testURL = url.parse(`http://user:pass%3A@localhost:${port}`); - - // make the request - http.request(testURL).end(); - resolve(); - }); +// Throws if data is not of type Buffer. +{ + const stream = fs.createWriteStream(file); + stream.on('error', common.mustNotCall()); + assert.throws(() => { + stream.write(42); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' }); -}); - -//<#END_FILE: test-http-url.parse-auth.js + stream.destroy(); +} diff --git a/test/js/node/test/parallel/fs-write-sync.test.js b/test/js/node/test/parallel/test-fs-write-sync.js similarity index 66% rename from test/js/node/test/parallel/fs-write-sync.test.js rename to test/js/node/test/parallel/test-fs-write-sync.js index 5e277e75e5..733892c35e 100644 --- a/test/js/node/test/parallel/fs-write-sync.test.js +++ b/test/js/node/test/parallel/test-fs-write-sync.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-write-sync.js -//#SHA1: 4ae5fa7550eefe258b9c1de798f4a4092e9d15d1 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,23 +19,17 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const filename = tmpdir.resolve('write.txt'); -const filename = path.join(os.tmpdir(), "write.txt"); +tmpdir.refresh(); -beforeEach(() => { - try { - fs.unlinkSync(filename); - } catch (err) { - // Ignore errors if file doesn't exist - } -}); - -test("fs.writeSync with various parameter combinations", () => { - const parameters = [Buffer.from("bár"), 0, Buffer.byteLength("bár")]; +{ + const parameters = [Buffer.from('bár'), 0, Buffer.byteLength('bár')]; // The first time fs.writeSync is called with all parameters provided. // After that, each pop in the cycle removes the final parameter. So: @@ -46,21 +37,19 @@ test("fs.writeSync with various parameter combinations", () => { // - The 3rd time fs.writeSync with a buffer, without the offset and length // parameters. while (parameters.length > 0) { - const fd = fs.openSync(filename, "w"); + const fd = fs.openSync(filename, 'w'); - let written = fs.writeSync(fd, ""); - expect(written).toBe(0); + let written = fs.writeSync(fd, ''); + assert.strictEqual(written, 0); - fs.writeSync(fd, "foo"); + fs.writeSync(fd, 'foo'); written = fs.writeSync(fd, ...parameters); - expect(written).toBeGreaterThan(3); + assert.ok(written > 3); fs.closeSync(fd); - expect(fs.readFileSync(filename, "utf-8")).toBe("foobár"); + assert.strictEqual(fs.readFileSync(filename, 'utf-8'), 'foobár'); parameters.pop(); } -}); - -//<#END_FILE: test-fs-write-sync.js +} diff --git a/test/js/node/test/parallel/test-fs-writestream-open-write.js b/test/js/node/test/parallel/test-fs-writestream-open-write.js new file mode 100644 index 0000000000..af02d90ae6 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-writestream-open-write.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const { strictEqual } = require('assert'); +const fs = require('fs'); + +// Regression test for https://github.com/nodejs/node/issues/51993 + +tmpdir.refresh(); + +const file = tmpdir.resolve('test-fs-writestream-open-write.txt'); + +const w = fs.createWriteStream(file); + +w.on('open', common.mustCall(() => { + w.write('hello'); + + process.nextTick(() => { + w.write('world'); + w.end(); + }); +})); + +w.on('close', common.mustCall(() => { + strictEqual(fs.readFileSync(file, 'utf8'), 'helloworld'); + fs.unlinkSync(file); +})); diff --git a/test/js/node/test/parallel/test-global-domexception.js b/test/js/node/test/parallel/test-global-domexception.js new file mode 100644 index 0000000000..d19b5a5e4f --- /dev/null +++ b/test/js/node/test/parallel/test-global-domexception.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +assert.strictEqual(typeof DOMException, 'function'); + +assert.throws(() => { + atob('我要抛错!'); +}, DOMException); diff --git a/test/js/node/test/parallel/test-global-encoder.js b/test/js/node/test/parallel/test-global-encoder.js new file mode 100644 index 0000000000..0e98bc806c --- /dev/null +++ b/test/js/node/test/parallel/test-global-encoder.js @@ -0,0 +1,8 @@ +'use strict'; + +require('../common'); +const { strictEqual } = require('assert'); +const util = require('util'); + +strictEqual(TextDecoder, util.TextDecoder); +strictEqual(TextEncoder, util.TextEncoder); diff --git a/test/js/node/test/parallel/test-global-webcrypto.js b/test/js/node/test/parallel/test-global-webcrypto.js new file mode 100644 index 0000000000..9eb18ca9d1 --- /dev/null +++ b/test/js/node/test/parallel/test-global-webcrypto.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +/* eslint-disable no-restricted-properties */ +assert.strictEqual(globalThis.crypto, crypto.webcrypto); +assert.strictEqual(Crypto, crypto.webcrypto.constructor); +assert.strictEqual(SubtleCrypto, crypto.webcrypto.subtle.constructor); diff --git a/test/js/node/test/parallel/test-handle-wrap-close-abort.js b/test/js/node/test/parallel/test-handle-wrap-close-abort.js new file mode 100644 index 0000000000..b91f9dd349 --- /dev/null +++ b/test/js/node/test/parallel/test-handle-wrap-close-abort.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +process.on('uncaughtException', common.mustCall(2)); + +setTimeout(function() { + process.nextTick(function() { + const c = setInterval(function() { + clearInterval(c); + throw new Error('setInterval'); + }, 1); + }); + setTimeout(function() { + throw new Error('setTimeout'); + }, 1); +}, 1); diff --git a/test/js/node/test/parallel/console-not-call-tostring.test.js b/test/js/node/test/parallel/test-http-abort-before-end.js similarity index 72% rename from test/js/node/test/parallel/console-not-call-tostring.test.js rename to test/js/node/test/parallel/test-http-abort-before-end.js index f43e7dbde6..5577f256ca 100644 --- a/test/js/node/test/parallel/console-not-call-tostring.test.js +++ b/test/js/node/test/parallel/test-http-abort-before-end.js @@ -1,6 +1,3 @@ -//#FILE: test-console-not-call-toString.js -//#SHA1: e0bf3a601442b76f12f657e53df54690d2fe21fa -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,18 +19,25 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const http = require('http'); -test("util.inspect does not call toString", () => { - function func() {} - let toStringCalled = false; - func.toString = function () { - toStringCalled = true; - }; +const server = http.createServer(common.mustNotCall()); - require("util").inspect(func); +server.listen(0, common.mustCall(() => { + const req = http.request({ + method: 'GET', + host: '127.0.0.1', + port: server.address().port + }); - expect(toStringCalled).toBe(false); -}); + req.on('abort', common.mustCall(() => { + server.close(); + })); -//<#END_FILE: test-console-not-call-toString.js + req.on('error', common.mustNotCall()); + + req.abort(); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-agent-false.js b/test/js/node/test/parallel/test-http-agent-false.js new file mode 100644 index 0000000000..2f4505ef66 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-false.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +// Sending `agent: false` when `port: null` is also passed in (i.e. the result +// of a `url.parse()` call with the default port used, 80 or 443), should not +// result in an assertion error... +const opts = { + host: '127.0.0.1', + port: null, + path: '/', + method: 'GET', + agent: false +}; + +// We just want an "error" (no local HTTP server on port 80) or "response" +// to happen (user happens ot have HTTP server running on port 80). +// As long as the process doesn't crash from a C++ assertion then we're good. +const req = http.request(opts); + +// Will be called by either the response event or error event, not both +const oneResponse = common.mustCall(); +req.on('response', oneResponse); +req.on('error', oneResponse); +req.end(); diff --git a/test/js/node/test/parallel/test-http-agent-keepalive-delay.js b/test/js/node/test/parallel/test-http-agent-keepalive-delay.js new file mode 100644 index 0000000000..b5edd78b66 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-keepalive-delay.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const { Agent } = require('_http_agent'); + +const agent = new Agent({ + keepAlive: true, + keepAliveMsecs: 1000, +}); + +const server = http.createServer(common.mustCall((req, res) => { + res.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const createConnection = agent.createConnection; + agent.createConnection = (options, ...args) => { + assert.strictEqual(options.keepAlive, true); + assert.strictEqual(options.keepAliveInitialDelay, agent.keepAliveMsecs); + return createConnection.call(agent, options, ...args); + }; + http.get({ + host: 'localhost', + port: server.address().port, + agent: agent, + path: '/' + }, common.mustCall((res) => { + // for emit end event + res.on('data', () => {}); + res.on('end', () => { + server.close(); + }); + })); +})); diff --git a/test/js/node/test/parallel/http-agent-no-protocol.test.js b/test/js/node/test/parallel/test-http-agent-no-protocol.js similarity index 65% rename from test/js/node/test/parallel/http-agent-no-protocol.test.js rename to test/js/node/test/parallel/test-http-agent-no-protocol.js index 99d0acc26f..d1eaf242a5 100644 --- a/test/js/node/test/parallel/http-agent-no-protocol.test.js +++ b/test/js/node/test/parallel/test-http-agent-no-protocol.js @@ -1,6 +1,3 @@ -//#FILE: test-http-agent-no-protocol.js -//#SHA1: f1b40623163271a500c87971bf996466e006130e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,21 +19,14 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); - -test("http agent with no protocol", async () => { - const serverCallback = jest.fn((req, res) => { - res.end(); - }); - - const server = http.createServer(serverCallback); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", resolve); - }); +'use strict'; +const common = require('../common'); +const http = require('http'); +const url = require('url'); +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, '127.0.0.1', common.mustCall(() => { const opts = url.parse(`http://127.0.0.1:${server.address().port}/`); // Remove the `protocol` field… the `http` module should fall back @@ -44,20 +34,8 @@ test("http agent with no protocol", async () => { opts.agent = new http.Agent(); opts.agent.protocol = null; - const responseCallback = jest.fn(res => { + http.get(opts, common.mustCall((res) => { res.resume(); server.close(); - }); - - await new Promise(resolve => { - http.get(opts, res => { - responseCallback(res); - resolve(); - }); - }); - - expect(serverCallback).toHaveBeenCalledTimes(1); - expect(responseCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-agent-no-protocol.js + })); +})); diff --git a/test/js/node/test/parallel/test-http-agent-null.js b/test/js/node/test/parallel/test-http-agent-null.js new file mode 100644 index 0000000000..0f87d09813 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-null.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, common.mustCall(() => { + const options = { + agent: null, + port: server.address().port + }; + http.get(options, common.mustCall((res) => { + res.resume(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http-agent-uninitialized-with-handle.js b/test/js/node/test/parallel/test-http-agent-uninitialized-with-handle.js new file mode 100644 index 0000000000..77f0177173 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-uninitialized-with-handle.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); + +const agent = new http.Agent({ + keepAlive: true, +}); +const socket = new net.Socket(); +// If _handle exists then internals assume a couple methods exist. +socket._handle = { + ref() { }, + readStart() { }, +}; + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, common.mustCall(() => { + const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); + + // Manually add the socket without a _handle. + agent.freeSockets[agent.getName(req)] = [socket]; + // Now force the agent to use the socket and check that _handle exists before + // calling asyncReset(). + agent.addRequest(req, {}); + req.on('response', common.mustCall(() => { + server.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-agent-uninitialized.js b/test/js/node/test/parallel/test-http-agent-uninitialized.js new file mode 100644 index 0000000000..dbb38e3b0f --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-uninitialized.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); + +const agent = new http.Agent({ + keepAlive: true, +}); +const socket = new net.Socket(); + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, common.mustCall(() => { + const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); + + // Manually add the socket without a _handle. + agent.freeSockets[agent.getName(req)] = [socket]; + // Now force the agent to use the socket and check that _handle exists before + // calling asyncReset(). + agent.addRequest(req, {}); + req.on('response', common.mustCall(() => { + server.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-allow-req-after-204-res.js b/test/js/node/test/parallel/test-http-allow-req-after-204-res.js new file mode 100644 index 0000000000..84dd876985 --- /dev/null +++ b/test/js/node/test/parallel/test-http-allow-req-after-204-res.js @@ -0,0 +1,61 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); + +// first 204 or 304 works, subsequent anything fails +const codes = [204, 200]; + +const countdown = new Countdown(codes.length, () => server.close()); + +const server = http.createServer(common.mustCall((req, res) => { + const code = codes.shift(); + assert.strictEqual(typeof code, 'number'); + assert.ok(code > 0); + res.writeHead(code, {}); + res.end(); +}, codes.length)); + +function nextRequest() { + + const request = http.get({ + port: server.address().port, + path: '/' + }, common.mustCall((response) => { + response.on('end', common.mustCall(() => { + if (countdown.dec()) { + // throws error: + nextRequest(); + // TODO: investigate why this does not work fine even though it should. + // works just fine: + // process.nextTick(nextRequest); + } + })); + response.resume(); + })); + request.end(); +} + +server.listen(0, nextRequest); diff --git a/test/js/node/test/parallel/test-http-bind-twice.js b/test/js/node/test/parallel/test-http-bind-twice.js new file mode 100644 index 0000000000..50834cc5cb --- /dev/null +++ b/test/js/node/test/parallel/test-http-bind-twice.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server1 = http.createServer(common.mustNotCall()); +server1.listen(0, '127.0.0.1', common.mustCall(function() { + const server2 = http.createServer(common.mustNotCall()); + server2.listen(this.address().port, '127.0.0.1', common.mustNotCall()); + + server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + server1.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http-buffer-sanity.js b/test/js/node/test/parallel/test-http-buffer-sanity.js new file mode 100644 index 0000000000..05c027fd48 --- /dev/null +++ b/test/js/node/test/parallel/test-http-buffer-sanity.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const bufferSize = 5 * 1024 * 1024; +let measuredSize = 0; + +const buffer = Buffer.allocUnsafe(bufferSize); +for (let i = 0; i < buffer.length; i++) { + buffer[i] = i % 256; +} + +const server = http.Server(function(req, res) { + server.close(); + + let i = 0; + + req.on('data', (d) => { + measuredSize += d.length; + for (let j = 0; j < d.length; j++) { + assert.strictEqual(d[j], buffer[i]); + i++; + } + }); + + req.on('end', common.mustCall(() => { + assert.strictEqual(measuredSize, bufferSize); + res.writeHead(200); + res.write('thanks'); + res.end(); + })); +}); + +server.listen(0, common.mustCall(() => { + const req = http.request({ + port: server.address().port, + method: 'POST', + path: '/', + headers: { 'content-length': buffer.length } + }, common.mustCall((res) => { + res.setEncoding('utf8'); + let data = ''; + res.on('data', (chunk) => data += chunk); + res.on('end', common.mustCall(() => { + assert.strictEqual(data, 'thanks'); + })); + })); + req.end(buffer); +})); diff --git a/test/js/node/test/parallel/test-http-chunk-problem.js b/test/js/node/test/parallel/test-http-chunk-problem.js new file mode 100644 index 0000000000..a3c354aecd --- /dev/null +++ b/test/js/node/test/parallel/test-http-chunk-problem.js @@ -0,0 +1,96 @@ +'use strict'; +// http://groups.google.com/group/nodejs/browse_thread/thread/f66cd3c960406919 +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); + +if (process.argv[2] === 'request') { + const http = require('http'); + const options = { + port: +process.argv[3], + path: '/' + }; + + http.get(options, (res) => { + res.pipe(process.stdout); + }); + + return; +} + +if (process.argv[2] === 'shasum') { + const crypto = require('crypto'); + const shasum = crypto.createHash('sha1'); + process.stdin.on('data', (d) => { + shasum.update(d); + }); + + process.stdin.on('close', () => { + process.stdout.write(shasum.digest('hex')); + }); + + return; +} + +const http = require('http'); +const cp = require('child_process'); + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('big'); +let server; + +function executeRequest(cb) { + cp.exec([`"${process.execPath}"`, + `"${__filename}"`, + 'request', + server.address().port, + '|', + `"${process.execPath}"`, + `"${__filename}"`, + 'shasum' ].join(' '), + (err, stdout, stderr) => { + if (stderr.trim() !== '') { + console.log(stderr); + } + assert.ifError(err); + assert.strictEqual(stdout.slice(0, 40), + '8c206a1a87599f532ce68675536f0b1546900d7a'); + cb(); + } + ); +} + + +tmpdir.refresh(); + +common.createZeroFilledFile(filename); + +server = http.createServer(function(req, res) { + res.writeHead(200); + + // Create the subprocess + const cat = cp.spawn('cat', [filename]); + + // Stream the data through to the response as binary chunks + cat.stdout.on('data', (data) => { + res.write(data); + }); + + cat.stdout.on('end', () => res.end()); + + // End the response on exit (and log errors) + cat.on('exit', (code) => { + if (code !== 0) { + console.error(`subprocess exited with code ${code}`); + process.exit(1); + } + }); + +}); + +server.listen(0, () => { + executeRequest(() => server.close()); +}); diff --git a/test/js/node/test/parallel/test-http-chunked-smuggling.js b/test/js/node/test/parallel/test-http-chunked-smuggling.js new file mode 100644 index 0000000000..dbad45e1be --- /dev/null +++ b/test/js/node/test/parallel/test-http-chunked-smuggling.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const assert = require('assert'); + +// Verify that invalid chunk extensions cannot be used to perform HTTP request +// smuggling attacks. + +const server = http.createServer(common.mustCall((request, response) => { + assert.notStrictEqual(request.url, '/admin'); + response.end('hello world'); +}), 1); + +server.listen(0, common.mustCall(start)); + +function start() { + const sock = net.connect(server.address().port); + + sock.write('' + + 'GET / HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n' + + '\r\n' + + '2;\n' + + 'xx\r\n' + + '4c\r\n' + + '0\r\n' + + '\r\n' + + 'GET /admin HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n' + + '\r\n' + + '0\r\n' + + '\r\n' + ); + + sock.resume(); + sock.on('end', common.mustCall(function() { + server.close(); + })); +} diff --git a/test/js/node/test/parallel/net-buffersize.test.js b/test/js/node/test/parallel/test-http-chunked.js similarity index 59% rename from test/js/node/test/parallel/net-buffersize.test.js rename to test/js/node/test/parallel/test-http-chunked.js index 5701356648..cfa34e3afe 100644 --- a/test/js/node/test/parallel/net-buffersize.test.js +++ b/test/js/node/test/parallel/test-http-chunked.js @@ -1,6 +1,3 @@ -//#FILE: test-net-buffersize.js -//#SHA1: b6b1298dc9f836252e5fcdcee680116d50da7651 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,39 +19,30 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const net = require("net"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const fixtures = require('../common/fixtures'); +const UTF8_STRING = fixtures.utf8TestText; -const iter = 10; - -test("net buffer size", async () => { - const server = net.createServer(socket => { - socket.on("readable", () => { - socket.read(); - }); - - socket.on("end", () => { +const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf8' }); + res.end(UTF8_STRING, 'utf8'); +})); +server.listen(0, common.mustCall(() => { + let data = ''; + http.get({ + path: '/', + host: 'localhost', + port: server.address().port + }, common.mustCall((x) => { + x.setEncoding('utf8'); + x.on('data', (c) => data += c); + x.on('end', common.mustCall(() => { + assert.strictEqual(typeof data, 'string'); + assert.strictEqual(UTF8_STRING, data); server.close(); - }); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const client = net.connect(server.address().port); - - client.on("finish", () => { - expect(client.bufferSize).toBe(0); - resolve(); - }); - - for (let i = 1; i < iter; i++) { - client.write("a"); - expect(client.bufferSize).toBe(i); - } - - client.end(); - }); - }); -}); - -//<#END_FILE: test-net-buffersize.js + })); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-abort2.js b/test/js/node/test/parallel/test-http-client-abort2.js new file mode 100644 index 0000000000..bc4b0e4083 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-abort2.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + res.end('Hello'); +})); + +server.listen(0, common.mustCall(() => { + const options = { port: server.address().port }; + const req = http.get(options, common.mustCall((res) => { + res.on('data', (data) => { + req.abort(); + server.close(); + }); + })); +})); diff --git a/test/js/node/test/parallel/test-http-client-close-with-default-agent.js b/test/js/node/test/parallel/test-http-client-close-with-default-agent.js new file mode 100644 index 0000000000..ea1e1481ba --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-close-with-default-agent.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(function(req, res) { + res.writeHead(200); + res.end(); +}); + +server.listen(0, common.mustCall(() => { + const req = http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.resume(); + server.close(); + }); + + req.end(); +})); + +// This timer should never go off as the server will close the socket +setTimeout(common.mustNotCall(), common.platformTimeout(1000)).unref(); diff --git a/test/js/node/test/parallel/test-http-client-defaults.js b/test/js/node/test/parallel/test-http-client-defaults.js new file mode 100644 index 0000000000..43419d1dfd --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-defaults.js @@ -0,0 +1,23 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const ClientRequest = require('http').ClientRequest; + +{ + const req = new ClientRequest({ createConnection: () => {} }); + assert.strictEqual(req.path, '/'); + assert.strictEqual(req.method, 'GET'); +} + +{ + const req = new ClientRequest({ method: '', createConnection: () => {} }); + assert.strictEqual(req.path, '/'); + assert.strictEqual(req.method, 'GET'); +} + +{ + const req = new ClientRequest({ path: '', createConnection: () => {} }); + assert.strictEqual(req.path, '/'); + assert.strictEqual(req.method, 'GET'); +} diff --git a/test/js/node/test/parallel/test-http-client-encoding.js b/test/js/node/test/parallel/test-http-client-encoding.js new file mode 100644 index 0000000000..a4701cdbd0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-encoding.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer((req, res) => { + res.end('ok'); + server.close(); +}).listen(0, common.mustCall(() => { + http.request({ + port: server.address().port, + encoding: 'utf8' + }, common.mustCall((res) => { + let data = ''; + res.on('data', (chunk) => data += chunk); + res.on('end', common.mustCall(() => assert.strictEqual(data, 'ok'))); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-get-url.js b/test/js/node/test/parallel/test-http-client-get-url.js new file mode 100644 index 0000000000..3b091a72ed --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-get-url.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); +const testPath = '/foo?bar'; + +const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.method, 'GET'); + assert.strictEqual(req.url, testPath); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); +}, 3)); + +server.listen(0, common.localhostIPv4, common.mustCall(() => { + const u = `http://${common.localhostIPv4}:${server.address().port}${testPath}`; + http.get(u, common.mustCall(() => { + http.get(url.parse(u), common.mustCall(() => { + http.get(new URL(u), common.mustCall(() => { + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-client-input-function.js b/test/js/node/test/parallel/test-http-client-input-function.js new file mode 100644 index 0000000000..3a2f93aa0e --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-input-function.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200); + res.end('hello world'); + })).listen(0, '127.0.0.1'); + + server.on('listening', common.mustCall(() => { + const req = new http.ClientRequest(server.address(), common.mustCall((response) => { + let body = ''; + response.setEncoding('utf8'); + response.on('data', (chunk) => { + body += chunk; + }); + + response.on('end', common.mustCall(() => { + assert.strictEqual(body, 'hello world'); + server.close(); + })); + })); + + req.end(); + })); +} diff --git a/test/js/node/test/parallel/test-http-client-keep-alive-hint.js b/test/js/node/test/parallel/test-http-client-keep-alive-hint.js new file mode 100644 index 0000000000..2618dfd552 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-keep-alive-hint.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer( + { keepAliveTimeout: common.platformTimeout(60000) }, + function(req, res) { + req.resume(); + res.writeHead(200, { 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=1' }); + res.end('FOO'); + } +); + +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + + res.resume(); + server.close(); + }); +})); + + +// This timer should never go off as the agent will parse the hint and terminate earlier +setTimeout(common.mustNotCall(), common.platformTimeout(3000)).unref(); diff --git a/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js b/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js new file mode 100644 index 0000000000..e6e0bac1bb --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer((req, res) => { + res.end(); +}).listen(0, common.mustCall(() => { + const agent = new http.Agent({ + maxSockets: 1, + keepAlive: true + }); + + const port = server.address().port; + + const post = http.request({ + agent, + method: 'POST', + port, + }, common.mustCall((res) => { + res.resume(); + })); + + // What happens here is that the server `end`s the response before we send + // `something`, and the client thought that this is a green light for sending + // next GET request + post.write(Buffer.alloc(16 * 1024, 'X')); + setTimeout(() => { + post.end('something'); + }, 100); + + http.request({ + agent, + method: 'GET', + port, + }, common.mustCall((res) => { + server.close(); + res.connection.end(); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-pipe-end.js b/test/js/node/test/parallel/test-http-client-pipe-end.js new file mode 100644 index 0000000000..ee88ce3d96 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-pipe-end.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// See https://github.com/joyent/node/issues/3257 + +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(function(req, res) { + req.resume(); + req.once('end', function() { + res.writeHead(200); + res.end(); + server.close(); + }); +}); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +server.listen(common.PIPE, function() { + const req = http.request({ + socketPath: common.PIPE, + headers: { 'Content-Length': '1' }, + method: 'POST', + path: '/' + }); + + req.write('.'); + + sched(function() { req.end(); }, 5); +}); + +// Schedule a callback after `ticks` event loop ticks +function sched(cb, ticks) { + function fn() { + if (--ticks) + setImmediate(fn); + else + cb(); + } + setImmediate(fn); +} diff --git a/test/js/node/test/parallel/test-http-client-race-2.js b/test/js/node/test/parallel/test-http-client-race-2.js new file mode 100644 index 0000000000..951b8e0d74 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-race-2.js @@ -0,0 +1,112 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +// +// Slight variation on test-http-client-race to test for another race +// condition involving the parsers FreeList used internally by http.Client. +// + +const body1_s = '1111111111111111'; +const body2_s = '22222'; +const body3_s = '3333333333333333333'; + +const server = http.createServer(function(req, res) { + const pathname = url.parse(req.url).pathname; + + let body; + switch (pathname) { + case '/1': body = body1_s; break; + case '/2': body = body2_s; break; + default: body = body3_s; + } + + res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Content-Length': body.length + }); + res.end(body); +}); +server.listen(0); + +let body1 = ''; +let body2 = ''; +let body3 = ''; + +server.on('listening', function() { + // + // Client #1 is assigned Parser #1 + // + const req1 = http.get({ port: this.address().port, path: '/1' }); + req1.on('response', function(res1) { + res1.setEncoding('utf8'); + + res1.on('data', function(chunk) { + body1 += chunk; + }); + + res1.on('end', function() { + // + // Delay execution a little to allow the 'close' event to be processed + // (required to trigger this bug!) + // + setTimeout(function() { + // + // The bug would introduce itself here: Client #2 would be allocated the + // parser that previously belonged to Client #1. But we're not finished + // with Client #1 yet! + // + // At this point, the bug would manifest itself and crash because the + // internal state of the parser was no longer valid for use by Client #1 + // + const req2 = http.get({ port: server.address().port, path: '/2' }); + req2.on('response', function(res2) { + res2.setEncoding('utf8'); + res2.on('data', function(chunk) { body2 += chunk; }); + res2.on('end', function() { + + // + // Just to be really sure we've covered all our bases, execute a + // request using client2. + // + const req3 = http.get({ port: server.address().port, path: '/3' }); + req3.on('response', function(res3) { + res3.setEncoding('utf8'); + res3.on('data', function(chunk) { body3 += chunk; }); + res3.on('end', function() { server.close(); }); + }); + }); + }); + }, 500); + }); + }); +}); + +process.on('exit', function() { + assert.strictEqual(body1_s, body1); + assert.strictEqual(body2_s, body2); + assert.strictEqual(body3_s, body3); +}); diff --git a/test/js/node/test/parallel/microtask-queue-integration.test.js b/test/js/node/test/parallel/test-http-client-race.js similarity index 50% rename from test/js/node/test/parallel/microtask-queue-integration.test.js rename to test/js/node/test/parallel/test-http-client-race.js index 7d0dc16d64..60b6b49737 100644 --- a/test/js/node/test/parallel/microtask-queue-integration.test.js +++ b/test/js/node/test/parallel/test-http-client-race.js @@ -1,6 +1,3 @@ -//#FILE: test-microtask-queue-integration.js -//#SHA1: bc144f7d64d2ed682489718745be5883122cf323 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,60 +19,51 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); -const implementations = [ - function (fn) { - Promise.resolve().then(fn); - }, -]; +const body1_s = '1111111111111111'; +const body2_s = '22222'; -let expected = 0; -let done = 0; - -afterAll(() => { - expect(done).toBe(expected); +const server = http.createServer(function(req, res) { + const body = url.parse(req.url).pathname === '/1' ? body1_s : body2_s; + res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Content-Length': body.length + }); + res.end(body); }); +server.listen(0); -function testMicrotask(scheduleMicrotask) { - return new Promise(resolve => { - let nextTickCalled = false; - expected++; +let body1 = ''; +let body2 = ''; - scheduleMicrotask(() => { - process.nextTick(() => { - nextTickCalled = true; +server.on('listening', function() { + const req1 = http.request({ port: this.address().port, path: '/1' }); + req1.end(); + req1.on('response', function(res1) { + res1.setEncoding('utf8'); + + res1.on('data', function(chunk) { + body1 += chunk; + }); + + res1.on('end', function() { + const req2 = http.request({ port: server.address().port, path: '/2' }); + req2.end(); + req2.on('response', function(res2) { + res2.setEncoding('utf8'); + res2.on('data', function(chunk) { body2 += chunk; }); + res2.on('end', function() { server.close(); }); }); - - setTimeout(() => { - expect(nextTickCalled).toBe(true); - done++; - resolve(); - }, 0); }); }); -} - -test("first tick case", async () => { - await Promise.all(implementations.map(testMicrotask)); }); -test("tick callback case", async () => { - await new Promise(resolve => { - setTimeout(async () => { - await Promise.all( - implementations.map( - impl => - new Promise(resolve => { - process.nextTick(() => { - testMicrotask(impl).then(resolve); - }); - }), - ), - ); - resolve(); - }, 0); - }); +process.on('exit', function() { + assert.strictEqual(body1_s, body1); + assert.strictEqual(body2_s, body2); }); - -//<#END_FILE: test-microtask-queue-integration.js diff --git a/test/js/node/test/parallel/test-http-client-read-in-error.js b/test/js/node/test/parallel/test-http-client-read-in-error.js new file mode 100644 index 0000000000..5e38e49c1f --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-read-in-error.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); +const http = require('http'); + +class Agent extends http.Agent { + createConnection() { + const socket = new net.Socket(); + + socket.on('error', function() { + socket.push('HTTP/1.1 200\r\n\r\n'); + }); + + let onNewListener; + socket.on('newListener', onNewListener = (name) => { + if (name !== 'error') + return; + socket.removeListener('newListener', onNewListener); + + // Let other listeners to be set up too + process.nextTick(() => { + this.breakSocket(socket); + }); + }); + + return socket; + } + + breakSocket(socket) { + socket.emit('error', new Error('Intentional error')); + } +} + +const agent = new Agent(); + +http.request({ + agent +}).once('error', function() { + console.log('ignore'); + this.on('data', common.mustNotCall()); +}); diff --git a/test/js/node/test/parallel/test-http-client-res-destroyed.js b/test/js/node/test/parallel/test-http-client-res-destroyed.js new file mode 100644 index 0000000000..188ab06c15 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-res-destroyed.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.end('asd'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ + port: server.address().port + }, common.mustCall((res) => { + assert.strictEqual(res.destroyed, false); + res.destroy(); + assert.strictEqual(res.destroyed, true); + res.on('close', common.mustCall(() => { + server.close(); + })); + })); + })); +} + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.end('asd'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ + port: server.address().port + }, common.mustCall((res) => { + assert.strictEqual(res.destroyed, false); + res.on('close', common.mustCall(() => { + assert.strictEqual(res.destroyed, true); + server.close(); + })).resume(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http-client-timeout-agent.js b/test/js/node/test/parallel/test-http-client-timeout-agent.js new file mode 100644 index 0000000000..66c0c0f6b9 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-timeout-agent.js @@ -0,0 +1,94 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +let requests_sent = 0; +let requests_done = 0; +const options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', +}; + +const server = http.createServer((req, res) => { + const m = /\/(.*)/.exec(req.url); + const reqid = parseInt(m[1], 10); + if (reqid % 2) { + // Do not reply the request + } else { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write(reqid.toString()); + res.end(); + } +}); + +server.listen(0, options.host, function() { + options.port = this.address().port; + + for (requests_sent = 0; requests_sent < 30; requests_sent += 1) { + options.path = `/${requests_sent}`; + const req = http.request(options); + req.id = requests_sent; + req.on('response', (res) => { + res.on('data', function(data) { + console.log(`res#${this.req.id} data:${data}`); + }); + res.on('end', function(data) { + console.log(`res#${this.req.id} end`); + requests_done += 1; + req.destroy(); + }); + }); + req.on('close', function() { + console.log(`req#${this.id} close`); + }); + req.on('error', function() { + console.log(`req#${this.id} error`); + this.destroy(); + }); + req.setTimeout(50, function() { + console.log(`req#${this.id} timeout`); + this.abort(); + requests_done += 1; + }); + req.end(); + } + + setTimeout(function maybeDone() { + if (requests_done >= requests_sent) { + setTimeout(() => { + server.close(); + }, 100); + } else { + setTimeout(maybeDone, 100); + } + }, 100); +}); + +process.on('exit', () => { + console.error(`done=${requests_done} sent=${requests_sent}`); + // Check that timeout on http request was not called too much + assert.strictEqual(requests_done, requests_sent); +}); diff --git a/test/js/node/test/parallel/test-http-client-timeout-connect-listener.js b/test/js/node/test/parallel/test-http-client-timeout-connect-listener.js new file mode 100644 index 0000000000..ea09aff718 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-timeout-connect-listener.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that `ClientRequest.prototype.setTimeout()` does +// not add a listener for the `'connect'` event to the socket if the +// socket is already connected. + +const assert = require('assert'); +const http = require('http'); + +// Maximum allowed value for timeouts. +const timeout = 2 ** 31 - 1; + +const server = http.createServer((req, res) => { + res.end(); +}); + +server.listen(0, common.mustCall(() => { + const agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); + const options = { port: server.address().port, agent: agent }; + + doRequest(options, common.mustCall(() => { + const req = doRequest(options, common.mustCall(() => { + agent.destroy(); + server.close(); + })); + + req.on('socket', common.mustCall((socket) => { + assert.strictEqual(socket.listenerCount('connect'), 0); + })); + })); +})); + +function doRequest(options, callback) { + const req = http.get(options, (res) => { + res.on('end', callback); + res.resume(); + }); + + req.setTimeout(timeout); + return req; +} diff --git a/test/js/node/test/parallel/test-http-client-timeout.js b/test/js/node/test/parallel/test-http-client-timeout.js new file mode 100644 index 0000000000..b83bd1ddf0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-timeout.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', + path: '/' +}; + +const server = http.createServer(function(req, res) { + // This space intentionally left blank +}); + +server.listen(0, options.host, function() { + options.port = this.address().port; + const req = http.request(options, function(res) { + // This space intentionally left blank + }); + req.on('close', function() { + assert.strictEqual(req.destroyed, true); + server.close(); + }); + function destroy() { + req.destroy(); + } + const s = req.setTimeout(1, destroy); + assert.ok(s instanceof http.ClientRequest); + req.on('error', destroy); + req.end(); +}); diff --git a/test/js/node/test/parallel/http-client-timeout-event.test.js b/test/js/node/test/parallel/test-http-client-upload-buf.js similarity index 55% rename from test/js/node/test/parallel/http-client-timeout-event.test.js rename to test/js/node/test/parallel/test-http-client-upload-buf.js index bfff317e47..1c75612c80 100644 --- a/test/js/node/test/parallel/http-client-timeout-event.test.js +++ b/test/js/node/test/parallel/test-http-client-upload-buf.js @@ -1,6 +1,3 @@ -//#FILE: test-http-client-timeout-event.js -//#SHA1: b4aeb9d5d97b5ffa46c8c281fbc04d052857b08f -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,44 +19,46 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); -const options = { - method: "GET", - port: undefined, - host: "127.0.0.1", - path: "/", -}; +const N = 1024; -test("http client timeout event", async () => { - const server = http.createServer(); +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(req.method, 'POST'); + let bytesReceived = 0; - await new Promise(resolve => { - server.listen(0, options.host, () => { - options.port = server.address().port; - const req = http.request(options); - - req.on("error", () => { - // This space is intentionally left blank - }); - - req.on("close", () => { - expect(req.destroyed).toBe(true); - server.close(); - resolve(); - }); - - req.setTimeout(1); - req.on("timeout", () => { - req.end(() => { - setTimeout(() => { - req.destroy(); - }, 100); - }); - }); - }); + req.on('data', function(chunk) { + bytesReceived += chunk.length; }); -}); -//<#END_FILE: test-http-client-timeout-event.js + req.on('end', common.mustCall(function() { + assert.strictEqual(N, bytesReceived); + console.log('request complete from server'); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); + })); +})); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'POST', + path: '/' + }, common.mustCall(function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + console.log(chunk); + }); + res.on('end', common.mustCall(function() { + server.close(); + })); + })); + + req.write(Buffer.allocUnsafe(N)); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-upload.js b/test/js/node/test/parallel/test-http-client-upload.js new file mode 100644 index 0000000000..830c37da8e --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-upload.js @@ -0,0 +1,67 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(req.method, 'POST'); + req.setEncoding('utf8'); + + let sent_body = ''; + + req.on('data', function(chunk) { + console.log(`server got: ${JSON.stringify(chunk)}`); + sent_body += chunk; + }); + + req.on('end', common.mustCall(function() { + assert.strictEqual(sent_body, '1\n2\n3\n'); + console.log('request complete from server'); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); + })); +})); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'POST', + path: '/' + }, common.mustCall(function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + console.log(chunk); + }); + res.on('end', common.mustCall(function() { + server.close(); + })); + })); + + req.write('1\n'); + req.write('2\n'); + req.write('3\n'); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-with-create-connection.js b/test/js/node/test/parallel/test-http-client-with-create-connection.js new file mode 100644 index 0000000000..5c99de6c49 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-with-create-connection.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +let count = 0; +let server1; +let server2; + +function request(options) { + count++; + http.get({ + ...options, + createConnection: (...args) => { + return net.connect(...args); + } + }, (res) => { + res.resume(); + res.on('end', () => { + if (--count === 0) { + server1.close(); + server2.close(); + } + }); + }); +} + +server1 = http.createServer((req, res) => { + res.end('ok'); +}).listen(common.PIPE, () => { + server2 = http.createServer((req, res) => { + res.end('ok'); + }).listen(() => { + request({ + path: '/', + socketPath: common.PIPE, + }); + + request({ + socketPath: common.PIPE, + }); + + request({ + path: '/', + port: server2.address().port, + }); + + request({ + port: server2.address().port, + }); + }); +}); diff --git a/test/js/node/test/parallel/test-http-contentLength0.js b/test/js/node/test/parallel/test-http-contentLength0.js new file mode 100644 index 0000000000..975e2abe88 --- /dev/null +++ b/test/js/node/test/parallel/test-http-contentLength0.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const http = require('http'); + +// Simple test of Node's HTTP Client choking on a response +// with a 'Content-Length: 0 ' response header. +// I.E. a space character after the 'Content-Length' throws an `error` event. + + +const s = http.createServer(function(req, res) { + res.writeHead(200, { 'Content-Length': '0 ' }); + res.end(); +}); +s.listen(0, function() { + + const request = http.request({ port: this.address().port }, (response) => { + console.log(`STATUS: ${response.statusCode}`); + s.close(); + response.resume(); + }); + + request.end(); +}); diff --git a/test/js/node/test/parallel/http-agent-null.test.js b/test/js/node/test/parallel/test-http-date-header.js similarity index 64% rename from test/js/node/test/parallel/http-agent-null.test.js rename to test/js/node/test/parallel/test-http-date-header.js index c44c03f788..169af2bf2a 100644 --- a/test/js/node/test/parallel/http-agent-null.test.js +++ b/test/js/node/test/parallel/test-http-date-header.js @@ -1,6 +1,3 @@ -//#FILE: test-http-agent-null.js -//#SHA1: 65fb22d32bae2a7eecc4242b5b2d2d693849641c -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,35 +19,37 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); -test("http.get with null agent", async () => { - const server = http.createServer((req, res) => { - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const options = { - agent: null, - port: server.address().port, - }; - - const responsePromise = new Promise(resolve => { - http.get(options, res => { - res.resume(); - resolve(res); - }); - }); - - await expect(responsePromise).resolves.toBeDefined(); - - await new Promise(resolve => { - server.close(resolve); +const testResBody = 'other stuff!\n'; + +const server = http.createServer((req, res) => { + assert.ok(!('date' in req.headers), + 'Request headers contained a Date.'); + res.writeHead(200, { + 'Content-Type': 'text/plain' }); + res.end(testResBody); }); +server.listen(0); -//<#END_FILE: test-http-agent-null.js + +server.addListener('listening', () => { + const options = { + port: server.address().port, + path: '/', + method: 'GET' + }; + const req = http.request(options, (res) => { + assert.ok('date' in res.headers, + 'Response headers didn\'t contain a Date.'); + res.addListener('end', () => { + server.close(); + }); + res.resume(); + }); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http-decoded-auth.js b/test/js/node/test/parallel/test-http-decoded-auth.js new file mode 100644 index 0000000000..7b09f47cc7 --- /dev/null +++ b/test/js/node/test/parallel/test-http-decoded-auth.js @@ -0,0 +1,48 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const testCases = [ + { + username: 'test@test"', + password: '123456^', + expected: 'dGVzdEB0ZXN0IjoxMjM0NTZe' + }, + { + username: 'test%40test', + password: '123456', + expected: 'dGVzdEB0ZXN0OjEyMzQ1Ng==' + }, + { + username: 'not%3Agood', + password: 'god', + expected: 'bm90Omdvb2Q6Z29k' + }, + { + username: 'not%22good', + password: 'g%5Eod', + expected: 'bm90Imdvb2Q6Z15vZA==' + }, + { + username: 'test1234::::', + password: 'mypass', + expected: 'dGVzdDEyMzQ6Ojo6Om15cGFzcw==' + }, +]; + +for (const testCase of testCases) { + const server = http.createServer(function(request, response) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, `Basic ${testCase.expected}`); + response.writeHead(200, {}); + response.end('ok'); + server.close(); + }); + + server.listen(0, function() { + // make the request + const url = new URL(`http://${testCase.username}:${testCase.password}@localhost:${this.address().port}`); + http.request(url).end(); + }); +} diff --git a/test/js/node/test/parallel/http-response-readable.test.js b/test/js/node/test/parallel/test-http-default-encoding.js similarity index 62% rename from test/js/node/test/parallel/http-response-readable.test.js rename to test/js/node/test/parallel/test-http-default-encoding.js index 4af51a42e5..0c0de0808a 100644 --- a/test/js/node/test/parallel/http-response-readable.test.js +++ b/test/js/node/test/parallel/test-http-default-encoding.js @@ -1,6 +1,3 @@ -//#FILE: test-http-response-readable.js -//#SHA1: bfdd12475c68879668c3019c685001244559fb20 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,28 +19,40 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); -test("HTTP response readable state", async () => { - const testServer = new http.Server((req, res) => { +const expected = 'This is a unicode text: سلام'; +let result = ''; + +const server = http.Server((req, res) => { + req.setEncoding('utf8'); + req.on('data', (chunk) => { + result += chunk; + }).on('end', () => { res.writeHead(200); - res.end("Hello world"); + res.end('hello world\n'); + server.close(); }); - await new Promise(resolve => { - testServer.listen(0, () => { - const port = testServer.address().port; - http.get({ port }, res => { - expect(res.readable).toBe(true); - res.on("end", () => { - expect(res.readable).toBe(false); - testServer.close(resolve); - }); - res.resume(); - }); - }); - }); }); -//<#END_FILE: test-http-response-readable.js +server.listen(0, function() { + http.request({ + port: this.address().port, + path: '/', + method: 'POST' + }, (res) => { + console.log(res.statusCode); + res.resume(); + }).on('error', (e) => { + console.log(e.message); + process.exit(1); + }).end(expected); +}); + +process.on('exit', () => { + assert.strictEqual(result, expected); +}); diff --git a/test/js/node/test/parallel/test-http-end-throw-socket-handling.js b/test/js/node/test/parallel/test-http-end-throw-socket-handling.js new file mode 100644 index 0000000000..956096270e --- /dev/null +++ b/test/js/node/test/parallel/test-http-end-throw-socket-handling.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); + +// Make sure that throwing in 'end' handler doesn't lock +// up the socket forever. +// +// This is NOT a good way to handle errors in general, but all +// the same, we should not be so brittle and easily broken. + +const http = require('http'); +const countdown = new Countdown(10, () => server.close()); + +const server = http.createServer((req, res) => { + countdown.dec(); + res.end('ok'); +}); + +server.listen(0, common.mustCall(() => { + for (let i = 0; i < 10; i++) { + const options = { port: server.address().port }; + const req = http.request(options, (res) => { + res.resume(); + res.on('end', common.mustCall(() => { + throw new Error('gleep glorp'); + })); + }); + req.end(); + } +})); + +process.on('uncaughtException', common.mustCall(10)); diff --git a/test/js/node/test/parallel/test-http-eof-on-connect.js b/test/js/node/test/parallel/test-http-eof-on-connect.js new file mode 100644 index 0000000000..5e885bb91a --- /dev/null +++ b/test/js/node/test/parallel/test-http-eof-on-connect.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const http = require('http'); + +// This is a regression test for https://github.com/joyent/node/issues/44 +// It is separate from test-http-malformed-request.js because it is only +// reproducible on the first packet on the first connection to a server. + +const server = http.createServer(common.mustNotCall()); +server.listen(0); + +server.on('listening', function() { + net.createConnection(this.address().port).on('connect', function() { + this.destroy(); + }).on('close', function() { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-extra-response.js b/test/js/node/test/parallel/test-http-extra-response.js new file mode 100644 index 0000000000..6d1a770487 --- /dev/null +++ b/test/js/node/test/parallel/test-http-extra-response.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +// If an HTTP server is broken and sends data after the end of the response, +// node should ignore it and drop the connection. +// Demos this bug: https://github.com/joyent/node/issues/680 + +const body = 'hello world\r\n'; +const fullResponse = + 'HTTP/1.1 500 Internal Server Error\r\n' + + `Content-Length: ${body.length}\r\n` + + 'Content-Type: text/plain\r\n' + + 'Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n' + + 'Host: 10.20.149.2\r\n' + + 'Access-Control-Allow-Credentials: true\r\n' + + 'Server: badly broken/0.1 (OS NAME)\r\n' + + '\r\n' + + body; + +const server = net.createServer(function(socket) { + let postBody = ''; + + socket.setEncoding('utf8'); + + socket.on('data', function(chunk) { + postBody += chunk; + + if (postBody.includes('\r\n')) { + socket.write(fullResponse); + socket.end(fullResponse); + } + }); + + socket.on('error', function(err) { + assert.strictEqual(err.code, 'ECONNRESET'); + }); +}); + + +server.listen(0, common.mustCall(function() { + http.get({ port: this.address().port }, common.mustCall(function(res) { + let buffer = ''; + console.log(`Got res code: ${res.statusCode}`); + + res.setEncoding('utf8'); + res.on('data', function(chunk) { + buffer += chunk; + }); + + res.on('end', common.mustCall(function() { + console.log(`Response ended, read ${buffer.length} bytes`); + assert.strictEqual(body, buffer); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-full-response.js b/test/js/node/test/parallel/test-http-full-response.js new file mode 100644 index 0000000000..d08e091ebd --- /dev/null +++ b/test/js/node/test/parallel/test-http-full-response.js @@ -0,0 +1,81 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +// This test requires the program 'ab' +const http = require('http'); +const exec = require('child_process').exec; + +const bodyLength = 12345; + +const body = 'c'.repeat(bodyLength); + +const server = http.createServer(function(req, res) { + res.writeHead(200, { + 'Content-Length': bodyLength, + 'Content-Type': 'text/plain' + }); + res.end(body); +}); + +function runAb(opts, callback) { + const command = `ab ${opts} http://127.0.0.1:${server.address().port}/`; + exec(command, function(err, stdout, stderr) { + if (err) { + if (/ab|apr/i.test(stderr)) { + common.printSkipMessage(`problem spawning \`ab\`.\n${stderr}`); + process.reallyExit(0); + } + throw err; + } + + let m = /Document Length:\s*(\d+) bytes/i.exec(stdout); + const documentLength = parseInt(m[1]); + + m = /Complete requests:\s*(\d+)/i.exec(stdout); + const completeRequests = parseInt(m[1]); + + m = /HTML transferred:\s*(\d+) bytes/i.exec(stdout); + const htmlTransferred = parseInt(m[1]); + + assert.strictEqual(bodyLength, documentLength); + assert.strictEqual(completeRequests * documentLength, htmlTransferred); + + if (callback) callback(); + }); +} + +server.listen(0, common.mustCall(function() { + runAb('-c 1 -n 10', common.mustCall(function() { + console.log('-c 1 -n 10 okay'); + + runAb('-c 1 -n 100', common.mustCall(function() { + console.log('-c 1 -n 100 okay'); + + runAb('-c 1 -n 1000', common.mustCall(function() { + console.log('-c 1 -n 1000 okay'); + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/http-get-pipeline-problem.test.js b/test/js/node/test/parallel/test-http-get-pipeline-problem.js similarity index 52% rename from test/js/node/test/parallel/http-get-pipeline-problem.test.js rename to test/js/node/test/parallel/test-http-get-pipeline-problem.js index ec0750f018..b8b11e7e77 100644 --- a/test/js/node/test/parallel/http-get-pipeline-problem.test.js +++ b/test/js/node/test/parallel/test-http-get-pipeline-problem.js @@ -1,6 +1,3 @@ -//#FILE: test-http-get-pipeline-problem.js -//#SHA1: 422a6c8ae8350bb70627efee734652421322b1bd -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,77 +19,82 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // In previous versions of Node.js (e.g., 0.6.0), this sort of thing would halt // after http.globalAgent.maxSockets number of files. // See https://groups.google.com/forum/#!topic/nodejs-dev/V5fB69hFa9o -const fixtures = require("../common/fixtures"); -const http = require("http"); -const fs = require("fs"); -const tmpdir = require("../common/tmpdir"); +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http = require('http'); +const fs = require('fs'); +const Countdown = require('../common/countdown'); http.globalAgent.maxSockets = 1; +const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); -const image = fixtures.readSync("/person.jpg"); +const image = fixtures.readSync('/person.jpg'); console.log(`image.length = ${image.length}`); const total = 10; +const responseCountdown = new Countdown(total, common.mustCall(() => { + checkFiles(); + server.close(); +})); -test("HTTP GET pipeline problem", async () => { - const server = http.createServer((req, res) => { - setTimeout(() => { - res.writeHead(200, { - "content-type": "image/jpeg", - connection: "close", - "content-length": image.length, - }); - res.end(image); - }, 1); - }); +const server = http.Server(function(req, res) { + setTimeout(function() { + res.writeHead(200, { + 'content-type': 'image/jpeg', + 'connection': 'close', + 'content-length': image.length + }); + res.end(image); + }, 1); +}); - await new Promise(resolve => server.listen(0, resolve)); - const serverAddress = server.address(); +server.listen(0, function() { + for (let i = 0; i < total; i++) { + (function() { + const x = i; - const requests = Array.from({ length: total }, (_, i) => { - return new Promise((resolve, reject) => { const opts = { - port: serverAddress.port, - headers: { connection: "close" }, + port: server.address().port, + headers: { connection: 'close' } }; - http - .get(opts, res => { - console.error(`recv ${i}`); - const s = fs.createWriteStream(`${tmpdir.path}/${i}.jpg`); - res.pipe(s); + http.get(opts, function(res) { + console.error(`recv ${x}`); + const s = fs.createWriteStream(`${tmpdir.path}/${x}.jpg`); + res.pipe(s); - s.on("finish", () => { - console.error(`done ${i}`); - resolve(); - }); - }) - .on("error", reject); - }); - }); + s.on('finish', function() { + console.error(`done ${x}`); + responseCountdown.dec(); + }); + }).on('error', function(e) { + console.error('error! ', e.message); + throw e; + }); + })(); + } +}); - await Promise.all(requests); - - // Check files +function checkFiles() { + // Should see 1.jpg, 2.jpg, ..., 100.jpg in tmpDir const files = fs.readdirSync(tmpdir.path); - expect(files.length).toBeGreaterThanOrEqual(total); + assert(total <= files.length); for (let i = 0; i < total; i++) { const fn = `${i}.jpg`; - expect(files).toContain(fn); + assert.ok(files.includes(fn), `couldn't find '${fn}'`); const stat = fs.statSync(`${tmpdir.path}/${fn}`); - expect(stat.size).toBe(image.length); + assert.strictEqual( + image.length, stat.size, + `size doesn't match on '${fn}'. Got ${stat.size} bytes`); } - - server.close(); -}); - -//<#END_FILE: test-http-get-pipeline-problem.js +} diff --git a/test/js/node/test/parallel/http-url.parse-path.test.js b/test/js/node/test/parallel/test-http-head-request.js similarity index 59% rename from test/js/node/test/parallel/http-url.parse-path.test.js rename to test/js/node/test/parallel/test-http-head-request.js index 21b456b66f..26d490d357 100644 --- a/test/js/node/test/parallel/http-url.parse-path.test.js +++ b/test/js/node/test/parallel/test-http-head-request.js @@ -1,6 +1,3 @@ -//#FILE: test-http-url.parse-path.js -//#SHA1: 9eb246a6c09b70b76260a83bec4bb25452d38b7d -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,33 +19,39 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); +'use strict'; +const common = require('../common'); +const http = require('http'); -test("http request url parsing", async () => { - function check(request) { - // A path should come over - expect(request.url).toBe("/asdf"); - } +const body = 'hello world\n'; - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); +function test(headers) { + const server = http.createServer(function(req, res) { + console.error('req: %s headers: %j', req.method, headers); + res.writeHead(200, headers); + res.end(); server.close(); }); - await new Promise(resolve => { - server.listen(0, () => { - const testURL = url.parse(`http://localhost:${server.address().port}/asdf`); + server.listen(0, common.mustCall(function() { + const request = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(response) { + console.error('response start'); + response.on('end', common.mustCall(function() { + console.error('response end'); + })); + response.resume(); + })); + request.end(); + })); +} - // make the request - http.request(testURL).end(); - resolve(); - }); - }); +test({ + 'Transfer-Encoding': 'chunked' +}); +test({ + 'Content-Length': body.length }); - -//<#END_FILE: test-http-url.parse-path.js diff --git a/test/js/node/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js b/test/js/node/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js new file mode 100644 index 0000000000..5ebd9f8a90 --- /dev/null +++ b/test/js/node/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request with data to res.end, +// it does not send any body but the response is sent +// anyway. + +const server = http.createServer(function(req, res) { + res.end('FAIL'); // broken: sends FAIL from hot path. +}); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + server.close(); + })); + res.resume(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/cluster-disconnect-with-no-workers.test.js b/test/js/node/test/parallel/test-http-head-response-has-no-body-end.js similarity index 64% rename from test/js/node/test/parallel/cluster-disconnect-with-no-workers.test.js rename to test/js/node/test/parallel/test-http-head-response-has-no-body-end.js index 01c67b2877..824a1bafe3 100644 --- a/test/js/node/test/parallel/cluster-disconnect-with-no-workers.test.js +++ b/test/js/node/test/parallel/test-http-head-response-has-no-body-end.js @@ -1,6 +1,3 @@ -//#FILE: test-cluster-disconnect-with-no-workers.js -//#SHA1: 06b4a0a662491bd9593c303307535a635d69d53e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,26 +19,30 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const http = require('http'); -let disconnected = false; +// This test is to make sure that when the HTTP server +// responds to a HEAD request with data to res.end, +// it does not send any body. -test("cluster.disconnect with no workers", async () => { - const disconnectPromise = new Promise(resolve => { - cluster.disconnect(() => { - disconnected = true; - resolve(); - }); - }); - - // Assert that callback is not sometimes synchronous - expect(disconnected).toBe(false); - - await disconnectPromise; - - // Assert that the callback was called - expect(disconnected).toBe(true); +const server = http.createServer(function(req, res) { + res.writeHead(200); + res.end('FAIL'); // broken: sends FAIL from hot path. }); +server.listen(0); -//<#END_FILE: test-cluster-disconnect-with-no-workers.js +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + server.close(); + })); + res.resume(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/beforeexit-event-exit.test.js b/test/js/node/test/parallel/test-http-head-response-has-no-body.js similarity index 62% rename from test/js/node/test/parallel/beforeexit-event-exit.test.js rename to test/js/node/test/parallel/test-http-head-response-has-no-body.js index c7a0ad2a9c..bd96d7161b 100644 --- a/test/js/node/test/parallel/beforeexit-event-exit.test.js +++ b/test/js/node/test/parallel/test-http-head-response-has-no-body.js @@ -1,6 +1,3 @@ -//#FILE: test-beforeexit-event-exit.js -//#SHA1: 27b6351612c5ec4f51d3317ca1c89511b833eaab -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,23 +19,30 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const http = require('http'); -test("beforeExit event should not be called when process.exit() is called", () => { - const mockBeforeExit = jest.fn(); - process.on("beforeExit", mockBeforeExit); +// This test is to make sure that when the HTTP server +// responds to a HEAD request, it does not send any body. +// In this case it was sending '0\r\n\r\n' - // Spy on process.exit to prevent actual exit - const exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {}); - - process.exit(); - - expect(mockBeforeExit).not.toHaveBeenCalled(); - expect(exitSpy).toHaveBeenCalled(); - - // Clean up - process.removeListener("beforeExit", mockBeforeExit); - exitSpy.mockRestore(); +const server = http.createServer(function(req, res) { + res.writeHead(200); // broken: defaults to TE chunked + res.end(); }); +server.listen(0); -//<#END_FILE: test-beforeexit-event-exit.js +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + server.close(); + })); + res.resume(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-header-owstext.js b/test/js/node/test/parallel/test-http-header-owstext.js new file mode 100644 index 0000000000..bc094137a2 --- /dev/null +++ b/test/js/node/test/parallel/test-http-header-owstext.js @@ -0,0 +1,49 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that the http-parser strips leading and trailing OWS from +// header values. It sends the header values in chunks to force the parser to +// build the string up through multiple calls to on_header_value(). + +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +function check(hdr, snd, rcv) { + const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.headers[hdr], rcv); + req.pipe(res); + })); + + server.listen(0, common.mustCall(function() { + const client = net.connect(this.address().port, start); + function start() { + client.write('GET / HTTP/1.1\r\n' + hdr + ':', drain); + } + + function drain() { + if (snd.length === 0) { + return client.write('\r\nConnection: close\r\n\r\n'); + } + client.write(snd.shift(), drain); + } + + const bufs = []; + client.on('data', function(chunk) { + bufs.push(chunk); + }); + client.on('end', common.mustCall(function() { + const head = Buffer.concat(bufs) + .toString('latin1') + .split('\r\n')[0]; + assert.strictEqual(head, 'HTTP/1.1 200 OK'); + server.close(); + })); + })); +} + +check('host', [' \t foo.com\t'], 'foo.com'); +check('host', [' \t foo\tcom\t'], 'foo\tcom'); +check('host', [' \t', ' ', ' foo.com\t', '\t '], 'foo.com'); +check('host', [' \t', ' \t'.repeat(100), '\t '], ''); +check('host', [' \t', ' - - - - ', '\t '], '- - - -'); diff --git a/test/js/node/test/parallel/test-http-highwatermark.js b/test/js/node/test/parallel/test-http-highwatermark.js new file mode 100644 index 0000000000..79d9c46a55 --- /dev/null +++ b/test/js/node/test/parallel/test-http-highwatermark.js @@ -0,0 +1,52 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const http = require('http'); + +// These test cases to check socketOnDrain where needPause becomes false. +// When send large response enough to exceed highWaterMark, it expect the socket +// to be paused and res.write would be failed. +// And it should be resumed when outgoingData falls below highWaterMark. + +let requestReceived = 0; + +const server = http.createServer(function(req, res) { + const id = ++requestReceived; + const enoughToDrain = req.connection.writableHighWaterMark; + const body = 'x'.repeat(enoughToDrain * 100); + + if (id === 1) { + // Case of needParse = false + req.connection.once('pause', common.mustCall(() => { + assert(req.connection._paused, '_paused must be true because it exceeds' + + 'highWaterMark by second request'); + })); + } else { + // Case of needParse = true + const resume = req.connection.parser.resume.bind(req.connection.parser); + req.connection.parser.resume = common.mustCall((...args) => { + const paused = req.connection._paused; + assert(!paused, '_paused must be false because it become false by ' + + 'socketOnDrain when outgoingData falls below ' + + 'highWaterMark'); + return resume(...args); + }); + } + assert(!res.write(body), 'res.write must return false because it will ' + + 'exceed highWaterMark on this call'); + res.end(); +}).on('listening', () => { + const c = net.createConnection(server.address().port, () => { + c.write('GET / HTTP/1.1\r\n\r\n'); + c.write('GET / HTTP/1.1\r\n\r\n', + () => setImmediate(() => c.resume())); + c.end(); + }); + + c.on('end', () => { + server.close(); + }); +}); + +server.listen(0); diff --git a/test/js/node/test/parallel/test-http-host-headers.js b/test/js/node/test/parallel/test-http-host-headers.js new file mode 100644 index 0000000000..97d200ade1 --- /dev/null +++ b/test/js/node/test/parallel/test-http-host-headers.js @@ -0,0 +1,96 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const httpServer = http.createServer(reqHandler); + +function reqHandler(req, res) { + if (req.url === '/setHostFalse5') { + assert.strictEqual(req.headers.host, undefined); + } else { + assert.strictEqual( + req.headers.host, `localhost:${this.address().port}`, + `Wrong host header for req[${req.url}]: ${req.headers.host}`); + } + res.writeHead(200, {}); + res.end('ok'); +} + +testHttp(); + +function testHttp() { + + let counter = 0; + + function cb(res) { + counter--; + if (counter === 0) { + httpServer.close(); + } + res.resume(); + } + + httpServer.listen(0, (er) => { + assert.ifError(er); + http.get({ + method: 'GET', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()); + + http.request({ + method: 'GET', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + + http.request({ + method: 'POST', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + + http.request({ + method: 'PUT', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + + http.request({ + method: 'DELETE', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + }); +} diff --git a/test/js/node/test/parallel/test-http-keep-alive-timeout-custom.js b/test/js/node/test/parallel/test-http-keep-alive-timeout-custom.js new file mode 100644 index 0000000000..a74aa5a212 --- /dev/null +++ b/test/js/node/test/parallel/test-http-keep-alive-timeout-custom.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer(common.mustCall((req, res) => { + const body = 'hello world\n'; + + res.writeHead(200, { + 'Content-Length': body.length, + 'Keep-Alive': 'timeout=50' + }); + res.write(body); + res.end(); +})); +server.keepAliveTimeout = 12010; + +const agent = new http.Agent({ maxSockets: 1, keepAlive: true }); + +server.listen(0, common.mustCall(function() { + http.get({ + path: '/', port: this.address().port, agent: agent + }, common.mustCall((response) => { + response.resume(); + assert.strictEqual( + response.headers['keep-alive'], 'timeout=50'); + server.close(); + agent.destroy(); + })); +})); diff --git a/test/js/node/test/parallel/net-during-close.test.js b/test/js/node/test/parallel/test-http-localaddress-bind-error.js similarity index 57% rename from test/js/node/test/parallel/net-during-close.test.js rename to test/js/node/test/parallel/test-http-localaddress-bind-error.js index 9edb7efc92..d4bd72bae1 100644 --- a/test/js/node/test/parallel/net-during-close.test.js +++ b/test/js/node/test/parallel/test-http-localaddress-bind-error.js @@ -1,6 +1,3 @@ -//#FILE: test-net-during-close.js -//#SHA1: c5fc5c85760b2c68679f7041ebf737e8204ca8c5 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,31 +19,34 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const net = require("net"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); -test("accessing client properties during server close", done => { - const server = net.createServer(socket => { - socket.end(); - }); - - server.listen(0, () => { - const client = net.createConnection(server.address().port); - server.close(); - - // Server connection event has not yet fired client is still attempting to - // connect. Accessing properties should not throw in this case. - expect(() => { - /* eslint-disable no-unused-expressions */ - client.remoteAddress; - client.remoteFamily; - client.remotePort; - /* eslint-enable no-unused-expressions */ - }).not.toThrow(); - - // Exit now, do not wait for the client error event. - done(); +const invalidLocalAddress = '1.2.3.4'; + +const server = http.createServer(function(req, res) { + console.log(`Connect from: ${req.connection.remoteAddress}`); + + req.on('end', function() { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(`You are from: ${req.connection.remoteAddress}`); }); + req.resume(); }); -//<#END_FILE: test-net-during-close.js +server.listen(0, '127.0.0.1', common.mustCall(function() { + http.request({ + host: 'localhost', + port: this.address().port, + path: '/', + method: 'GET', + localAddress: invalidLocalAddress + }, function(res) { + assert.fail('unexpectedly got response from server'); + }).on('error', common.mustCall(function(e) { + console.log(`client got error: ${e.message}`); + server.close(); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-malformed-request.js b/test/js/node/test/parallel/test-http-malformed-request.js new file mode 100644 index 0000000000..1f2fecc117 --- /dev/null +++ b/test/js/node/test/parallel/test-http-malformed-request.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); +const http = require('http'); +const url = require('url'); + +// Make sure no exceptions are thrown when receiving malformed HTTP +// requests. +const server = http.createServer(common.mustCall((req, res) => { + console.log(`req: ${JSON.stringify(url.parse(req.url))}`); + + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('Hello World'); + res.end(); + + server.close(); +})); +server.listen(0); + +server.on('listening', function() { + const c = net.createConnection(this.address().port); + c.on('connect', function() { + c.write('GET /hello?foo=%99bar HTTP/1.1\r\nHost: example.com\r\n\r\n'); + c.end(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-missing-header-separator-cr.js b/test/js/node/test/parallel/test-http-missing-header-separator-cr.js new file mode 100644 index 0000000000..1129ec4ed9 --- /dev/null +++ b/test/js/node/test/parallel/test-http-missing-header-separator-cr.js @@ -0,0 +1,83 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); +const net = require('net'); + +function serverHandler(server, msg) { + const client = net.connect(server.address().port, 'localhost'); + + let response = ''; + + client.on('data', common.mustCall((chunk) => { + response += chunk; + })); + + client.setEncoding('utf8'); + client.on('error', common.mustNotCall()); + client.on('end', common.mustCall(() => { + assert.strictEqual( + response, + 'HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n' + ); + server.close(); + })); + client.write(msg); + client.resume(); +} + +{ + const msg = [ + 'GET / HTTP/1.1', + 'Host: localhost', + 'Dummy: x\nContent-Length: 23', + '', + 'GET / HTTP/1.1', + 'Dummy: GET /admin HTTP/1.1', + 'Host: localhost', + '', + '', + ].join('\r\n'); + + const server = http.createServer(common.mustNotCall()); + + server.listen(0, common.mustSucceed(serverHandler.bind(null, server, msg))); +} + +{ + const msg = [ + 'POST / HTTP/1.1', + 'Host: localhost', + 'x:x\nTransfer-Encoding: chunked', + '', + '1', + 'A', + '0', + '', + '', + ].join('\r\n'); + + const server = http.createServer(common.mustNotCall()); + + server.listen(0, common.mustSucceed(serverHandler.bind(null, server, msg))); +} + +{ + const msg = [ + 'POST / HTTP/1.1', + 'Host: localhost', + 'x:\nTransfer-Encoding: chunked', + '', + '1', + 'A', + '0', + '', + '', + ].join('\r\n'); + + const server = http.createServer(common.mustNotCall()); + + server.listen(0, common.mustSucceed(serverHandler.bind(null, server, msg))); +} diff --git a/test/js/node/test/parallel/test-http-no-content-length.js b/test/js/node/test/parallel/test-http-no-content-length.js new file mode 100644 index 0000000000..a3a51c015e --- /dev/null +++ b/test/js/node/test/parallel/test-http-no-content-length.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const http = require('http'); + +const server = net.createServer(function(socket) { + // Neither Content-Length nor Connection + socket.end('HTTP/1.1 200 ok\r\n\r\nHello'); +}).listen(0, common.mustCall(function() { + http.get({ port: this.address().port }, common.mustCall(function(res) { + let body = ''; + + res.setEncoding('utf8'); + res.on('data', function(chunk) { + body += chunk; + }); + res.on('end', common.mustCall(function() { + assert.strictEqual(body, 'Hello'); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-outgoing-destroy.js b/test/js/node/test/parallel/test-http-outgoing-destroy.js new file mode 100644 index 0000000000..47d5e948ab --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-destroy.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); +const OutgoingMessage = http.OutgoingMessage; + +{ + const msg = new OutgoingMessage(); + assert.strictEqual(msg.destroyed, false); + msg.destroy(); + assert.strictEqual(msg.destroyed, true); + msg.write('asd', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + })); + msg.on('error', common.mustNotCall()); +} diff --git a/test/js/node/test/parallel/test-http-outgoing-end-types.js b/test/js/node/test/parallel/test-http-outgoing-end-types.js new file mode 100644 index 0000000000..20b443bff2 --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-end-types.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const httpServer = http.createServer(common.mustCall(function(req, res) { + httpServer.close(); + assert.throws(() => { + res.end(['Throws.']); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); + res.end(); +})); + +httpServer.listen(0, common.mustCall(function() { + http.get({ port: this.address().port }); +})); diff --git a/test/js/node/test/parallel/http-outgoing-finish.test.js b/test/js/node/test/parallel/test-http-outgoing-finish.js similarity index 67% rename from test/js/node/test/parallel/http-outgoing-finish.test.js rename to test/js/node/test/parallel/test-http-outgoing-finish.js index fa7aab9a23..0f71cccdf8 100644 --- a/test/js/node/test/parallel/http-outgoing-finish.test.js +++ b/test/js/node/test/parallel/test-http-outgoing-finish.js @@ -1,6 +1,3 @@ -//#FILE: test-http-outgoing-finish.js -//#SHA1: cd9dbce2b1b26369349c30bcd94979b354316128 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,65 +19,58 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const assert = require('assert'); -const http = require("http"); +const http = require('http'); -test("http outgoing finish", async () => { - const server = http.createServer((req, res) => { - req.resume(); - req.on("end", () => { - write(res); - }); - server.close(); +http.createServer(function(req, res) { + req.resume(); + req.on('end', function() { + write(res); }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http.request({ - port: server.address().port, - method: "PUT", - }); - write(req); - req.on("response", res => { - res.resume(); - }); - resolve(); - }); + this.close(); +}).listen(0, function() { + const req = http.request({ + port: this.address().port, + method: 'PUT' + }); + write(req); + req.on('response', function(res) { + res.resume(); }); }); -const buf = Buffer.alloc(1024 * 16, "x"); +const buf = Buffer.alloc(1024 * 16, 'x'); function write(out) { const name = out.constructor.name; let finishEvent = false; let endCb = false; // First, write until it gets some backpressure - while (out.write(buf)); + while (out.write(buf, common.mustSucceed())); // Now end, and make sure that we don't get the 'finish' event // before the tick where the cb gets called. We give it until // nextTick because this is added as a listener before the endcb // is registered. The order is not what we're testing here, just // that 'finish' isn't emitted until the stream is fully flushed. - out.on("finish", () => { + out.on('finish', function() { finishEvent = true; console.error(`${name} finish event`); - process.nextTick(() => { - expect(endCb).toBe(true); + process.nextTick(function() { + assert(endCb, `${name} got finish event before endcb!`); console.log(`ok - ${name} finishEvent`); }); }); - out.end(buf, () => { + out.end(buf, common.mustCall(function() { endCb = true; console.error(`${name} endCb`); - process.nextTick(() => { - expect(finishEvent).toBe(true); + process.nextTick(function() { + assert(finishEvent, `${name} got endCb event before finishEvent!`); console.log(`ok - ${name} endCb`); }); - }); + })); } - -//<#END_FILE: test-http-outgoing-finish.js diff --git a/test/js/node/test/parallel/test-http-outgoing-finished.js b/test/js/node/test/parallel/test-http-outgoing-finished.js new file mode 100644 index 0000000000..7da1b7429a --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-finished.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const { finished } = require('stream'); + +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer(function(req, res) { + let closed = false; + res + .on('close', common.mustCall(() => { + closed = true; + finished(res, common.mustCall(() => { + server.close(); + })); + })) + .end(); + finished(res, common.mustCall(() => { + assert.strictEqual(closed, true); + })); + +}).listen(0, function() { + http + .request({ + port: this.address().port, + method: 'GET' + }) + .on('response', function(res) { + res.resume(); + }) + .end(); +}); diff --git a/test/js/node/test/parallel/test-http-outgoing-writableFinished.js b/test/js/node/test/parallel/test-http-outgoing-writableFinished.js new file mode 100644 index 0000000000..6f84d91e71 --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-writableFinished.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(res.writableFinished, false); + res + .on('finish', common.mustCall(() => { + assert.strictEqual(res.writableFinished, true); + server.close(); + })) + .end(); +})); + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const clientRequest = http.request({ + port: server.address().port, + method: 'GET', + path: '/' + }); + + assert.strictEqual(clientRequest.writableFinished, false); + clientRequest + .on('finish', common.mustCall(() => { + assert.strictEqual(clientRequest.writableFinished, true); + })) + .end(); + assert.strictEqual(clientRequest.writableFinished, false); +})); diff --git a/test/js/node/test/parallel/test-http-outgoing-write-types.js b/test/js/node/test/parallel/test-http-outgoing-write-types.js new file mode 100644 index 0000000000..6257b87eea --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-write-types.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const httpServer = http.createServer(common.mustCall(function(req, res) { + httpServer.close(); + assert.throws(() => { + res.write(['Throws.']); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); + // should not throw + res.write('1a2b3c'); + // should not throw + res.write(new Uint8Array(1024)); + // should not throw + res.write(Buffer.from('1'.repeat(1024))); + res.end(); +})); + +httpServer.listen(0, common.mustCall(function() { + http.get({ port: this.address().port }); +})); diff --git a/test/js/node/test/parallel/test-http-pause-no-dump.js b/test/js/node/test/parallel/test-http-pause-no-dump.js new file mode 100644 index 0000000000..b794634c48 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pause-no-dump.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall(function(req, res) { + req.once('data', common.mustCall(() => { + req.pause(); + res.writeHead(200); + res.end(); + res.on('finish', common.mustCall(() => { + assert(!req._dumped); + })); + })); +})); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'POST', + path: '/' + }, common.mustCall(function(res) { + assert.strictEqual(res.statusCode, 200); + res.resume(); + res.on('end', common.mustCall(() => { + server.close(); + })); + })); + + req.end(Buffer.allocUnsafe(1024)); +})); diff --git a/test/js/node/test/parallel/test-http-pause-resume-one-end.js b/test/js/node/test/parallel/test-http-pause-resume-one-end.js new file mode 100644 index 0000000000..34bf3be478 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pause-resume-one-end.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.Server(function(req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello World\n'); + server.close(); +}); + +server.listen(0, common.mustCall(function() { + const opts = { + port: this.address().port, + headers: { connection: 'close' } + }; + + http.get(opts, common.mustCall(function(res) { + res.on('data', common.mustCall(function() { + res.pause(); + setImmediate(function() { + res.resume(); + }); + })); + + res.on('end', common.mustCall(() => { + assert.strictEqual(res.destroyed, false); + })); + assert.strictEqual(res.destroyed, false); + res.on('close', common.mustCall(() => { + assert.strictEqual(res.destroyed, true); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-pause.js b/test/js/node/test/parallel/test-http-pause.js new file mode 100644 index 0000000000..555eece2e3 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pause.js @@ -0,0 +1,78 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const expectedServer = 'Request Body from Client'; +let resultServer = ''; +const expectedClient = 'Response Body from Server'; +let resultClient = ''; + +const server = http.createServer((req, res) => { + console.error('pause server request'); + req.pause(); + setTimeout(() => { + console.error('resume server request'); + req.resume(); + req.setEncoding('utf8'); + req.on('data', (chunk) => { + resultServer += chunk; + }); + req.on('end', () => { + console.error(resultServer); + res.writeHead(200); + res.end(expectedClient); + }); + }, 100); +}); + +server.listen(0, function() { + // Anonymous function rather than arrow function to test `this` value. + assert.strictEqual(this, server); + const req = http.request({ + port: this.address().port, + path: '/', + method: 'POST' + }, (res) => { + console.error('pause client response'); + res.pause(); + setTimeout(() => { + console.error('resume client response'); + res.resume(); + res.on('data', (chunk) => { + resultClient += chunk; + }); + res.on('end', () => { + console.error(resultClient); + server.close(); + }); + }, 100); + }); + req.end(expectedServer); +}); + +process.on('exit', () => { + assert.strictEqual(resultServer, expectedServer); + assert.strictEqual(resultClient, expectedClient); +}); diff --git a/test/js/node/test/parallel/http-res-write-after-end.test.js b/test/js/node/test/parallel/test-http-pipe-fs.js similarity index 52% rename from test/js/node/test/parallel/http-res-write-after-end.test.js rename to test/js/node/test/parallel/test-http-pipe-fs.js index 45a17ed5e7..b7c1a02918 100644 --- a/test/js/node/test/parallel/http-res-write-after-end.test.js +++ b/test/js/node/test/parallel/test-http-pipe-fs.js @@ -1,6 +1,3 @@ -//#FILE: test-http-res-write-after-end.js -//#SHA1: e579896871bf375190397b4f7e99f37cc156e7c9 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,34 +19,47 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +const http = require('http'); +const fs = require('fs'); +const Countdown = require('../common/countdown'); +const NUMBER_OF_STREAMS = 2; -test("HTTP response write after end", async () => { - const server = http.Server((req, res) => { - res.on("error", error => { - expect(error).toMatchObject({ - code: "ERR_STREAM_WRITE_AFTER_END", - name: "Error", - message: expect.any(String), - }); - }); +const countdown = new Countdown(NUMBER_OF_STREAMS, () => server.close()); - res.write("This should write."); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const file = tmpdir.resolve('http-pipe-fs-test.txt'); + +const server = http.createServer(common.mustCall(function(req, res) { + const stream = fs.createWriteStream(file); + req.pipe(stream); + stream.on('close', function() { + res.writeHead(200); res.end(); - - const r = res.write("This should raise an error."); - // Write after end should return false - expect(r).toBe(false); }); +}, 2)).listen(0, function() { + http.globalAgent.maxSockets = 1; - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - server.close(resolve); + for (let i = 0; i < NUMBER_OF_STREAMS; ++i) { + const req = http.request({ + port: server.address().port, + method: 'POST', + headers: { + 'Content-Length': 5 + } + }, function(res) { + res.on('end', function() { + console.error(`res${i + 1} end`); + countdown.dec(); }); + res.resume(); }); - }); + req.on('socket', function(s) { + console.error(`req${i + 1} start`); + }); + req.end('12345'); + } }); - -//<#END_FILE: test-http-res-write-after-end.js diff --git a/test/js/node/test/parallel/test-http-pipeline-socket-parser-typeerror.js b/test/js/node/test/parallel/test-http-pipeline-socket-parser-typeerror.js new file mode 100644 index 0000000000..e092154b97 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pipeline-socket-parser-typeerror.js @@ -0,0 +1,64 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js doesn't crash because of a TypeError by +// checking in `connectionListener` that the socket still has the parser. +// https://github.com/nodejs/node/issues/3508 + +const http = require('http'); +const net = require('net'); + +let once = false; +let first = null; +let second = null; + +const chunk = Buffer.alloc(1024, 'X'); + +let size = 0; + +let more; +let done; + +const server = http + .createServer((req, res) => { + if (!once) server.close(); + once = true; + + if (first === null) { + first = res; + return; + } + if (second === null) { + second = res; + res.write(chunk); + } else { + res.end(chunk); + } + size += res.outputSize; + if (size <= req.socket.writableHighWaterMark) { + more(); + return; + } + done(); + }) + .on('upgrade', (req, socket) => { + second.end(chunk, () => { + socket.end(); + }); + first.end('hello'); + }) + .listen(0, () => { + const s = net.connect(server.address().port); + more = () => { + s.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); + }; + done = () => { + s.write( + 'GET / HTTP/1.1\r\n\r\n' + + 'GET / HTTP/1.1\r\nConnection: upgrade\r\nUpgrade: ws\r\n\r\naaa' + ); + }; + more(); + more(); + s.resume(); + }); diff --git a/test/js/node/test/parallel/test-http-proxy.js b/test/js/node/test/parallel/test-http-proxy.js new file mode 100644 index 0000000000..af3497630b --- /dev/null +++ b/test/js/node/test/parallel/test-http-proxy.js @@ -0,0 +1,107 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +const cookies = [ + 'session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT', + 'prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT', +]; + +const headers = { 'content-type': 'text/plain', + 'set-cookie': cookies, + 'hello': 'world' }; + +const backend = http.createServer(function(req, res) { + console.error('backend request'); + res.writeHead(200, headers); + res.write('hello world\n'); + res.end(); +}); + +const proxy = http.createServer(function(req, res) { + console.error(`proxy req headers: ${JSON.stringify(req.headers)}`); + http.get({ + port: backend.address().port, + path: url.parse(req.url).pathname + }, function(proxy_res) { + + console.error(`proxy res headers: ${JSON.stringify(proxy_res.headers)}`); + + assert.strictEqual(proxy_res.headers.hello, 'world'); + assert.strictEqual(proxy_res.headers['content-type'], 'text/plain'); + assert.deepStrictEqual(proxy_res.headers['set-cookie'], cookies); + + res.writeHead(proxy_res.statusCode, proxy_res.headers); + + proxy_res.on('data', function(chunk) { + res.write(chunk); + }); + + proxy_res.on('end', function() { + res.end(); + console.error('proxy res'); + }); + }); +}); + +let body = ''; + +let nlistening = 0; +function startReq() { + nlistening++; + if (nlistening < 2) return; + + http.get({ + port: proxy.address().port, + path: '/test' + }, function(res) { + console.error('got res'); + assert.strictEqual(res.statusCode, 200); + + assert.strictEqual(res.headers.hello, 'world'); + assert.strictEqual(res.headers['content-type'], 'text/plain'); + assert.deepStrictEqual(res.headers['set-cookie'], cookies); + + res.setEncoding('utf8'); + res.on('data', function(chunk) { body += chunk; }); + res.on('end', function() { + proxy.close(); + backend.close(); + console.error('closed both'); + }); + }); + console.error('client req'); +} + +console.error('listen proxy'); +proxy.listen(0, startReq); + +console.error('listen backend'); +backend.listen(0, startReq); + +process.on('exit', function() { + assert.strictEqual(body, 'hello world\n'); +}); diff --git a/test/js/node/test/parallel/test-http-request-agent.js b/test/js/node/test/parallel/test-http-request-agent.js new file mode 100644 index 0000000000..453ac380d0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-agent.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); + +// This test ensures that a http request callback is called when the agent +// option is set. +// See https://github.com/nodejs/node-v0.x-archive/issues/1531 + +const https = require('https'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = https.createServer(options, function(req, res) { + res.writeHead(200); + res.end('hello world\n'); +}); + +server.listen(0, common.mustCall(function() { + console.error('listening'); + https.get({ + agent: false, + path: '/', + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(function(res) { + console.error(res.statusCode, res.headers); + res.resume(); + server.close(); + })).on('error', function(e) { + console.error(e); + process.exit(1); + }); +})); diff --git a/test/js/node/test/parallel/test-http-request-arguments.js b/test/js/node/test/parallel/test-http-request-arguments.js new file mode 100644 index 0000000000..5cdd514fd5 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-arguments.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Test providing both a url and options, with the options partially +// replacing address and port portions of the URL provided. +{ + const server = http.createServer( + common.mustCall((req, res) => { + assert.strictEqual(req.url, '/testpath'); + res.end(); + server.close(); + }) + ); + server.listen( + 0, + common.mustCall(() => { + http.get( + 'http://example.com/testpath', + { hostname: 'localhost', port: server.address().port }, + common.mustCall((res) => { + res.resume(); + }) + ); + }) + ); +} diff --git a/test/js/node/test/parallel/test-http-request-end-twice.js b/test/js/node/test/parallel/test-http-request-end-twice.js new file mode 100644 index 0000000000..47f08fd6e4 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-end-twice.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.Server(function(req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('hello world\n'); +}); +server.listen(0, function() { + const req = http.get({ port: this.address().port }, function(res) { + res.on('end', function() { + assert.strictEqual(req.end(), req); + server.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/http-abort-before-end.test.js b/test/js/node/test/parallel/test-http-request-end.js similarity index 58% rename from test/js/node/test/parallel/http-abort-before-end.test.js rename to test/js/node/test/parallel/test-http-request-end.js index 0df59e1fcf..6f141fda1e 100644 --- a/test/js/node/test/parallel/http-abort-before-end.test.js +++ b/test/js/node/test/parallel/test-http-request-end.js @@ -1,6 +1,3 @@ -//#FILE: test-http-abort-before-end.js -//#SHA1: ccb82c66677f07f3ee815846261393edb8bfe5d4 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,39 +19,42 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); -test("HTTP request abort before end", async () => { - const server = http.createServer(jest.fn()); +const expected = 'Post Body For Test'; +const expectedStatusCode = 200; - await new Promise(resolve => { - server.listen(0, resolve); +const server = http.Server(function(req, res) { + let result = ''; + + req.setEncoding('utf8'); + req.on('data', function(chunk) { + result += chunk; }); - const req = http.request({ - method: "GET", - host: "127.0.0.1", - port: server.address().port, - }); + req.on('end', common.mustCall(() => { + assert.strictEqual(result, expected); + res.writeHead(expectedStatusCode); + res.end('hello world\n'); + server.close(); + })); - const abortPromise = new Promise(resolve => { - req.on("abort", resolve); - }); - - req.on("error", jest.fn()); - - req.abort(); - req.end(); - - await abortPromise; - - expect(server.listeners("request")[0]).not.toHaveBeenCalled(); - expect(req.listeners("error")[0]).not.toHaveBeenCalled(); - - await new Promise(resolve => { - server.close(resolve); - }); }); -//<#END_FILE: test-http-abort-before-end.js +server.listen(0, function() { + const req = http.request({ + port: this.address().port, + path: '/', + method: 'POST' + }, function(res) { + assert.strictEqual(res.statusCode, expectedStatusCode); + res.resume(); + }).on('error', common.mustNotCall()); + + const result = req.end(expected); + + assert.strictEqual(req, result); +}); diff --git a/test/js/node/test/parallel/test-http-request-large-payload.js b/test/js/node/test/parallel/test-http-request-large-payload.js new file mode 100644 index 0000000000..3be100b740 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-large-payload.js @@ -0,0 +1,26 @@ +'use strict'; +require('../common'); + +// This test ensures Node.js doesn't throw an error when making requests with +// the payload 16kb or more in size. +// https://github.com/nodejs/node/issues/2821 + +const http = require('http'); + +const server = http.createServer(function(req, res) { + res.writeHead(200); + res.end(); + + server.close(); +}); + +server.listen(0, function() { + const req = http.request({ + method: 'POST', + port: this.address().port + }); + + const payload = Buffer.alloc(16390, 'Й'); + req.write(payload); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http-request-method-delete-payload.js b/test/js/node/test/parallel/test-http-request-method-delete-payload.js new file mode 100644 index 0000000000..03728846df --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-method-delete-payload.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const http = require('http'); + +const data = 'PUT / HTTP/1.1\r\n\r\n'; + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('data', function(chunk) { + assert.strictEqual(chunk, Buffer.from(data)); + }); + res.setHeader('Content-Type', 'text/plain'); + for (let i = 0; i < req.rawHeaders.length; i += 2) { + if (req.rawHeaders[i].toLowerCase() === 'host') continue; + if (req.rawHeaders[i].toLowerCase() === 'connection') continue; + res.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`); + } + res.end(); +})).unref(); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const req = http.request({ method: 'DELETE', port }, function(res) { + res.resume(); + }); + + req.write(data); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-request-smuggling-content-length.js b/test/js/node/test/parallel/test-http-request-smuggling-content-length.js new file mode 100644 index 0000000000..4ae39b93f4 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-smuggling-content-length.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const assert = require('assert'); + +// Verify that a request with a space before the content length will result +// in a 400 Bad Request. + +const server = http.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(start)); + +function start() { + const sock = net.connect(server.address().port); + + sock.write('GET / HTTP/1.1\r\nHost: localhost:5000\r\n' + + 'Content-Length : 5\r\n\r\nhello'); + + let body = ''; + sock.setEncoding('utf8'); + sock.on('data', (chunk) => { + body += chunk; + }); + sock.on('end', common.mustCall(function() { + assert.strictEqual(body, 'HTTP/1.1 400 Bad Request\r\n' + + 'Connection: close\r\n\r\n'); + server.close(); + })); +} diff --git a/test/js/node/test/parallel/child-process-set-blocking.test.js b/test/js/node/test/parallel/test-http-res-write-after-end.js similarity index 66% rename from test/js/node/test/parallel/child-process-set-blocking.test.js rename to test/js/node/test/parallel/test-http-res-write-after-end.js index a8288fc114..285938c8fe 100644 --- a/test/js/node/test/parallel/child-process-set-blocking.test.js +++ b/test/js/node/test/parallel/test-http-res-write-after-end.js @@ -1,6 +1,3 @@ -//#FILE: test-child-process-set-blocking.js -//#SHA1: 2855d3d616c7af61fc6b84705cd05515f02bcb47 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,22 +19,27 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { spawn } = require("child_process"); -const os = require("os"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); -const SIZE = 100000; -const python = process.env.PYTHON || (os.platform() === "win32" ? "python" : "python3"); +const server = http.Server(common.mustCall(function(req, res) { + res.on('error', common.expectsError({ + code: 'ERR_STREAM_WRITE_AFTER_END', + name: 'Error' + })); -test("child process set blocking", done => { - const cp = spawn(python, ["-c", `print(${SIZE} * "C")`], { - stdio: "inherit", - }); + res.write('This should write.'); + res.end(); - cp.on("exit", code => { - expect(code).toBe(0); - done(); + const r = res.write('This should raise an error.'); + // Write after end should return false + assert.strictEqual(r, false); +})); + +server.listen(0, function() { + http.get({ port: this.address().port }, function(res) { + server.close(); }); }); - -//<#END_FILE: test-child-process-set-blocking.js diff --git a/test/js/node/test/parallel/http-date-header.test.js b/test/js/node/test/parallel/test-http-res-write-end-dont-take-array.js similarity index 51% rename from test/js/node/test/parallel/http-date-header.test.js rename to test/js/node/test/parallel/test-http-res-write-end-dont-take-array.js index b47a7163d9..8bebfc14e4 100644 --- a/test/js/node/test/parallel/http-date-header.test.js +++ b/test/js/node/test/parallel/test-http-res-write-end-dont-take-array.js @@ -1,6 +1,3 @@ -//#FILE: test-http-date-header.js -//#SHA1: e4d2a00dad7c6483d9ed328731bb04f5f431afb4 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,44 +19,55 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); -const testResBody = "other stuff!\n"; +const server = http.createServer(); -test("HTTP Date header", async () => { - const server = http.createServer((req, res) => { - expect(req.headers).not.toHaveProperty("date"); - res.writeHead(200, { - "Content-Type": "text/plain", - }); - res.end(testResBody); - }); +server.once('request', common.mustCall((req, res) => { + server.on('request', common.mustCall((req, res) => { + res.end(Buffer.from('asdf')); + })); + // `res.write()` should accept `string`. + res.write('string'); + // `res.write()` should accept `buffer`. + res.write(Buffer.from('asdf')); - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - const options = { - port, - path: "/", - method: "GET", + const expectedError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', }; - const responsePromise = new Promise((resolve, reject) => { - const req = http.request(options, res => { - expect(res.headers).toHaveProperty("date"); + // `res.write()` should not accept an Array. + assert.throws( + () => { + res.write(['array']); + }, + expectedError + ); + + // `res.end()` should not accept an Array. + assert.throws( + () => { + res.end(['moo']); + }, + expectedError + ); + + // `res.end()` should accept `string`. + res.end('string'); +})); + +server.listen(0, function() { + // Just make a request, other tests handle responses. + http.get({ port: this.address().port }, (res) => { + res.resume(); + // Do it again to test .end(Buffer); + http.get({ port: server.address().port }, (res) => { res.resume(); - res.on("end", resolve); + server.close(); }); - req.on("error", reject); - req.end(); }); - - await responsePromise; - await new Promise(resolve => server.close(resolve)); }); - -//<#END_FILE: test-http-date-header.js diff --git a/test/js/node/test/parallel/test-http-response-readable.js b/test/js/node/test/parallel/test-http-response-readable.js new file mode 100644 index 0000000000..9ecfbc4ca9 --- /dev/null +++ b/test/js/node/test/parallel/test-http-response-readable.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const testServer = new http.Server(function(req, res) { + res.writeHead(200); + res.end('Hello world'); +}); + +testServer.listen(0, function() { + http.get({ port: this.address().port }, function(res) { + assert.strictEqual(res.readable, true); + res.on('end', function() { + assert.strictEqual(res.readable, false); + testServer.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-response-writehead-returns-this.js b/test/js/node/test/parallel/test-http-response-writehead-returns-this.js new file mode 100644 index 0000000000..a62c2eca03 --- /dev/null +++ b/test/js/node/test/parallel/test-http-response-writehead-returns-this.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer((req, res) => { + res.writeHead(200, { 'a-header': 'a-header-value' }).end('abc'); +}); + +server.listen(0, () => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.headers['a-header'], 'a-header-value'); + + const chunks = []; + + res.on('data', (chunk) => chunks.push(chunk)); + res.on('end', () => { + assert.strictEqual(Buffer.concat(chunks).toString(), 'abc'); + server.close(); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-http-server-delete-parser.js b/test/js/node/test/parallel/test-http-server-delete-parser.js new file mode 100644 index 0000000000..4215ee2f9d --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-delete-parser.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); + +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('okay', common.mustCall(() => { + delete res.socket.parser; + })); + res.end(); +})); + +server.listen(0, '127.0.0.1', common.mustCall(() => { + const req = http.request({ + port: server.address().port, + host: '127.0.0.1', + method: 'GET', + }); + req.end(); +})); + +server.unref(); diff --git a/test/js/node/test/parallel/test-http-server-non-utf8-header.js b/test/js/node/test/parallel/test-http-server-non-utf8-header.js new file mode 100644 index 0000000000..8ce82ac4ca --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-non-utf8-header.js @@ -0,0 +1,69 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const nonUtf8Header = 'bår'; +const nonUtf8ToLatin1 = Buffer.from(nonUtf8Header).toString('latin1'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'content-disposition', + Buffer.from(nonUtf8Header).toString('binary'), + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} + +{ + // Test multi-value header + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'content-disposition', + [Buffer.from(nonUtf8Header).toString('binary')], + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'Content-Length', '5', + 'content-disposition', + Buffer.from(nonUtf8Header).toString('binary'), + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http-server-options-incoming-message.js b/test/js/node/test/parallel/test-http-server-options-incoming-message.js new file mode 100644 index 0000000000..d0f4a769d7 --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-options-incoming-message.js @@ -0,0 +1,41 @@ +'use strict'; + +/** + * This test covers http.Server({ IncomingMessage }) option: + * With IncomingMessage option the server should use + * the new class for creating req Object instead of the default + * http.IncomingMessage. + */ +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +class MyIncomingMessage extends http.IncomingMessage { + getUserAgent() { + return this.headers['user-agent'] || 'unknown'; + } +} + +const server = http.createServer({ + IncomingMessage: MyIncomingMessage +}, common.mustCall(function(req, res) { + assert.strictEqual(req.getUserAgent(), 'node-test'); + res.statusCode = 200; + res.end(); +})); +server.listen(); + +server.on('listening', function makeRequest() { + http.get({ + port: this.address().port, + headers: { + 'User-Agent': 'node-test' + } + }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.on('end', () => { + server.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-server-options-server-response.js b/test/js/node/test/parallel/test-http-server-options-server-response.js new file mode 100644 index 0000000000..f5adf39bed --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-options-server-response.js @@ -0,0 +1,35 @@ +'use strict'; + +/** + * This test covers http.Server({ ServerResponse }) option: + * With ServerResponse option the server should use + * the new class for creating res Object instead of the default + * http.ServerResponse. + */ +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +class MyServerResponse extends http.ServerResponse { + status(code) { + return this.writeHead(code, { 'Content-Type': 'text/plain' }); + } +} + +const server = http.Server({ + ServerResponse: MyServerResponse +}, common.mustCall(function(req, res) { + res.status(200); + res.end(); +})); +server.listen(); + +server.on('listening', function makeRequest() { + http.get({ port: this.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.on('end', () => { + server.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/http-client-encoding.test.js b/test/js/node/test/parallel/test-http-server-stale-close.js similarity index 61% rename from test/js/node/test/parallel/http-client-encoding.test.js rename to test/js/node/test/parallel/test-http-server-stale-close.js index b0455d81bb..b9322ed9fc 100644 --- a/test/js/node/test/parallel/http-client-encoding.test.js +++ b/test/js/node/test/parallel/test-http-server-stale-close.js @@ -1,6 +1,3 @@ -//#FILE: test-http-client-encoding.js -//#SHA1: a3e1a0cc1bf9352602068f323d90071d3c2d5f7d -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,35 +19,35 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +require('../common'); +const http = require('http'); +const fork = require('child_process').fork; +const assert = require('assert'); -test("HTTP client encoding", async () => { +if (process.env.NODE_TEST_FORK_PORT) { + const req = http.request({ + headers: { 'Content-Length': '42' }, + method: 'POST', + host: '127.0.0.1', + port: +process.env.NODE_TEST_FORK_PORT, + }, process.exit); + req.write('BAM'); + req.end(); +} else { const server = http.createServer((req, res) => { - res.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - const req = http.request( - { - port: port, - encoding: "utf8", - }, - res => { - let data = ""; - res.on("data", chunk => (data += chunk)); - res.on("end", () => { - expect(data).toBe("ok"); - resolve(); - }); - }, - ); - req.end(); + res.writeHead(200, { 'Content-Length': '42' }); + req.pipe(res); + assert.strictEqual(req.destroyed, false); + req.on('close', () => { + assert.strictEqual(req.destroyed, true); + server.close(); + res.end(); }); }); -}); - -//<#END_FILE: test-http-client-encoding.js + server.listen(0, function() { + fork(__filename, { + env: { ...process.env, NODE_TEST_FORK_PORT: this.address().port } + }); + }); +} diff --git a/test/js/node/test/parallel/test-http-server-write-after-end.js b/test/js/node/test/parallel/test-http-server-write-after-end.js new file mode 100644 index 0000000000..ba28771312 --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-write-after-end.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); + +// Fix for https://github.com/nodejs/node/issues/14368 + +const server = http.createServer(handle); + +function handle(req, res) { + res.on('error', common.mustNotCall()); + + res.write('hello'); + res.end(); + + setImmediate(common.mustCall(() => { + res.write('world', common.mustCall((err) => { + common.expectsError({ + code: 'ERR_STREAM_WRITE_AFTER_END', + name: 'Error' + })(err); + server.close(); + })); + })); +} + +server.listen(0, common.mustCall(() => { + http.get(`http://localhost:${server.address().port}`); +})); diff --git a/test/js/node/test/parallel/test-http-set-header-chain.js b/test/js/node/test/parallel/test-http-set-header-chain.js new file mode 100644 index 0000000000..aa9519129a --- /dev/null +++ b/test/js/node/test/parallel/test-http-set-header-chain.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const expected = { + '__proto__': null, + 'testheader1': 'foo', + 'testheader2': 'bar', + 'testheader3': 'xyz' +}; +const server = http.createServer(common.mustCall((req, res) => { + let retval = res.setHeader('testheader1', 'foo'); + + // Test that the setHeader returns the same response object. + assert.strictEqual(retval, res); + + retval = res.setHeader('testheader2', 'bar').setHeader('testheader3', 'xyz'); + // Test that chaining works for setHeader. + assert.deepStrictEqual(res.getHeaders(), expected); + res.end('ok'); +})); +server.listen(0, () => { + http.get({ port: server.address().port }, common.mustCall((res) => { + res.on('data', () => {}); + res.on('end', common.mustCall(() => { + server.close(); + })); + })); +}); diff --git a/test/js/node/test/parallel/test-http-status-code.js b/test/js/node/test/parallel/test-http-status-code.js new file mode 100644 index 0000000000..246d22c131 --- /dev/null +++ b/test/js/node/test/parallel/test-http-status-code.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const Countdown = require('../common/countdown'); + +// Simple test of Node's HTTP ServerResponse.statusCode +// ServerResponse.prototype.statusCode + +const tests = [200, 202, 300, 404, 451, 500]; +let test; +const countdown = new Countdown(tests.length, () => s.close()); + +const s = http.createServer(function(req, res) { + res.writeHead(test, { 'Content-Type': 'text/plain' }); + console.log(`--\nserver: statusCode after writeHead: ${res.statusCode}`); + assert.strictEqual(res.statusCode, test); + res.end('hello world\n'); +}); + +s.listen(0, nextTest); + + +function nextTest() { + test = tests.shift(); + + http.get({ port: s.address().port }, function(response) { + console.log(`client: expected status: ${test}`); + console.log(`client: statusCode: ${response.statusCode}`); + assert.strictEqual(response.statusCode, test); + response.on('end', function() { + if (countdown.dec()) + nextTest(); + }); + response.resume(); + }); +} diff --git a/test/js/node/test/parallel/test-http-upgrade-reconsume-stream.js b/test/js/node/test/parallel/test-http-upgrade-reconsume-stream.js new file mode 100644 index 0000000000..e712ea647b --- /dev/null +++ b/test/js/node/test/parallel/test-http-upgrade-reconsume-stream.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const http = require('http'); + +// Tests that, after the HTTP parser stopped owning a socket that emits an +// 'upgrade' event, another C++ stream can start owning it (e.g. a TLSSocket). + +const server = http.createServer(common.mustNotCall()); + +server.on('upgrade', common.mustCall((request, socket, head) => { + // This should not crash. + new tls.TLSSocket(socket); + server.close(); + socket.destroy(); +})); + +server.listen(0, common.mustCall(() => { + http.get({ + port: server.address().port, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + } + }).on('error', () => {}); +})); diff --git a/test/js/node/test/parallel/stream-end-paused.test.js b/test/js/node/test/parallel/test-http-url.parse-auth-with-header-in-request.js similarity index 58% rename from test/js/node/test/parallel/stream-end-paused.test.js rename to test/js/node/test/parallel/test-http-url.parse-auth-with-header-in-request.js index 1022f980ef..ea5793ee18 100644 --- a/test/js/node/test/parallel/stream-end-paused.test.js +++ b/test/js/node/test/parallel/test-http-url.parse-auth-with-header-in-request.js @@ -1,6 +1,3 @@ -//#FILE: test-stream-end-paused.js -//#SHA1: 4ebe901f30ba0469bb75c7f9f7ba5316ceb271a5 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,32 +19,34 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { Readable } = require("stream"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); -test("end event for paused 0-length streams", done => { - // Make sure we don't miss the end event for paused 0-length streams +function check(request) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, 'NoAuthForYOU'); +} - const stream = new Readable(); - let calledRead = false; - stream._read = function () { - expect(calledRead).toBe(false); - calledRead = true; - this.push(null); - }; - - stream.on("data", () => { - throw new Error("should not ever get data"); - }); - stream.pause(); - - setTimeout(() => { - stream.on("end", () => { - expect(calledRead).toBe(true); - done(); - }); - stream.resume(); - }, 1); +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); }); -//<#END_FILE: test-stream-end-paused.js +server.listen(0, function() { + const testURL = + url.parse(`http://asdf:qwer@localhost:${this.address().port}`); + // The test here is if you set a specific authorization header in the + // request we should not override that with basic auth + testURL.headers = { + Authorization: 'NoAuthForYOU' + }; + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/dgram-unref.test.js b/test/js/node/test/parallel/test-http-url.parse-auth.js similarity index 62% rename from test/js/node/test/parallel/dgram-unref.test.js rename to test/js/node/test/parallel/test-http-url.parse-auth.js index ff11989d7b..2bb5311586 100644 --- a/test/js/node/test/parallel/dgram-unref.test.js +++ b/test/js/node/test/parallel/test-http-url.parse-auth.js @@ -1,6 +1,3 @@ -//#FILE: test-dgram-unref.js -//#SHA1: 97b218a9107def7e2cc28e595f84f9a05a606850 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,28 +19,30 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const dgram = require("dgram"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); -test("unref() a socket with a handle", () => { - const s = dgram.createSocket("udp4"); - s.bind(); - s.unref(); - // No assertion needed, just checking that it doesn't throw +function check(request) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, 'Basic dXNlcjpwYXNzOg=='); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); }); -test("unref() a socket with no handle", done => { - const s = dgram.createSocket("udp4"); - s.close(() => { - s.unref(); - done(); - }); -}); +server.listen(0, function() { + const port = this.address().port; + // username = "user", password = "pass:" + const testURL = url.parse(`http://user:pass%3A@localhost:${port}`); -test("setTimeout should not be called", () => { - const mockCallback = jest.fn(); - setTimeout(mockCallback, 1000).unref(); - expect(mockCallback).not.toHaveBeenCalled(); + // make the request + http.request(testURL).end(); }); - -//<#END_FILE: test-dgram-unref.js diff --git a/test/js/node/test/parallel/http-url.parse-basic.test.js b/test/js/node/test/parallel/test-http-url.parse-basic.js similarity index 56% rename from test/js/node/test/parallel/http-url.parse-basic.test.js rename to test/js/node/test/parallel/test-http-url.parse-basic.js index 9d2d329d3f..71885b4bd0 100644 --- a/test/js/node/test/parallel/http-url.parse-basic.test.js +++ b/test/js/node/test/parallel/test-http-url.parse-basic.js @@ -1,6 +1,3 @@ -//#FILE: test-http-url.parse-basic.js -//#SHA1: f2f2841de1c82e38067e73196926090f350d89c6 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,44 +19,40 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); let testURL; // Make sure the basics work function check(request) { // Default method should still be 'GET' - expect(request.method).toBe("GET"); + assert.strictEqual(request.method, 'GET'); // There are no URL params, so you should not see any - expect(request.url).toBe("/"); + assert.strictEqual(request.url, '/'); // The host header should use the url.parse.hostname - expect(request.headers.host).toBe(`${testURL.hostname}:${testURL.port}`); + assert.strictEqual(request.headers.host, + `${testURL.hostname}:${testURL.port}`); } -test("HTTP URL parsing basics", async () => { - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - testURL = url.parse(`http://localhost:${server.address().port}`); - - // make the request - const clientRequest = http.request(testURL); - // Since there is a little magic with the agent - // make sure that an http request uses the http.Agent - expect(clientRequest.agent).toBeInstanceOf(http.Agent); - clientRequest.end(); - resolve(); - }); - }); +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); }); -//<#END_FILE: test-http-url.parse-basic.js +server.listen(0, function() { + testURL = url.parse(`http://localhost:${this.address().port}`); + + // make the request + const clientRequest = http.request(testURL); + // Since there is a little magic with the agent + // make sure that an http request uses the http.Agent + assert.ok(clientRequest.agent instanceof http.Agent); + clientRequest.end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-only-support-http-https-protocol.js b/test/js/node/test/parallel/test-http-url.parse-only-support-http-https-protocol.js new file mode 100644 index 0000000000..3f6633075c --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-only-support-http-https-protocol.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +const invalidUrls = [ + 'file:///whatever', + 'mailto:asdf@asdf.com', + 'ftp://www.example.com', + 'javascript:alert(\'hello\');', + 'xmpp:foo@bar.com', + 'f://some.host/path', +]; + +for (const invalid of invalidUrls) { + assert.throws( + () => { http.request(url.parse(invalid)); }, + { + code: 'ERR_INVALID_PROTOCOL', + name: 'TypeError' + } + ); +} diff --git a/test/js/node/test/parallel/net-listen-close-server.test.js b/test/js/node/test/parallel/test-http-url.parse-path.js similarity index 67% rename from test/js/node/test/parallel/net-listen-close-server.test.js rename to test/js/node/test/parallel/test-http-url.parse-path.js index 519ae0a2cb..25e4838c4a 100644 --- a/test/js/node/test/parallel/net-listen-close-server.test.js +++ b/test/js/node/test/parallel/test-http-url.parse-path.js @@ -1,6 +1,3 @@ -//#FILE: test-net-listen-close-server.js -//#SHA1: e39989e43d691f9cc91a02156112ba681366601b -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,20 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const net = require("net"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); -test("server listen and close without calling callbacks", () => { - const server = net.createServer(() => {}); - const listenSpy = jest.fn(); - const errorSpy = jest.fn(); +function check(request) { + // A path should come over + assert.strictEqual(request.url, '/asdf'); +} - server.listen(0, listenSpy); - server.on("error", errorSpy); +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); server.close(); - - expect(listenSpy).not.toHaveBeenCalled(); - expect(errorSpy).not.toHaveBeenCalled(); }); -//<#END_FILE: test-net-listen-close-server.js +server.listen(0, function() { + const testURL = url.parse(`http://localhost:${this.address().port}/asdf`); + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/http-url.parse-post.test.js b/test/js/node/test/parallel/test-http-url.parse-post.js similarity index 59% rename from test/js/node/test/parallel/http-url.parse-post.test.js rename to test/js/node/test/parallel/test-http-url.parse-post.js index 800cde5e39..db5ee78fe6 100644 --- a/test/js/node/test/parallel/http-url.parse-post.test.js +++ b/test/js/node/test/parallel/test-http-url.parse-post.js @@ -1,6 +1,3 @@ -//#FILE: test-http-url.parse-post.js -//#SHA1: e0e7f97c725fb9eaa6058365bef5021e9710e857 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,40 +19,36 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); let testURL; function check(request) { // url.parse should not mess with the method - expect(request.method).toBe("POST"); + assert.strictEqual(request.method, 'POST'); // Everything else should be right - expect(request.url).toBe("/asdf?qwer=zxcv"); + assert.strictEqual(request.url, '/asdf?qwer=zxcv'); // The host header should use the url.parse.hostname - expect(request.headers.host).toBe(`${testURL.hostname}:${testURL.port}`); + assert.strictEqual(request.headers.host, + `${testURL.hostname}:${testURL.port}`); } -test("http url parse post", async () => { - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - testURL = url.parse(`http://localhost:${server.address().port}/asdf?qwer=zxcv`); - testURL.method = "POST"; - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); }); -//<#END_FILE: test-http-url.parse-post.js +server.listen(0, function() { + testURL = url.parse(`http://localhost:${this.address().port}/asdf?qwer=zxcv`); + testURL.method = 'POST'; + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-search.js b/test/js/node/test/parallel/test-http-url.parse-search.js new file mode 100644 index 0000000000..0759c779d3 --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-search.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // A path should come over with params + assert.strictEqual(request.url, '/asdf?qwer=zxcv'); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const port = this.address().port; + const testURL = url.parse(`http://localhost:${port}/asdf?qwer=zxcv`); + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/dgram-bind.test.js b/test/js/node/test/parallel/test-http-write-empty-string.js similarity index 59% rename from test/js/node/test/parallel/dgram-bind.test.js rename to test/js/node/test/parallel/test-http-write-empty-string.js index 0bf412718a..88eff08f76 100644 --- a/test/js/node/test/parallel/dgram-bind.test.js +++ b/test/js/node/test/parallel/test-http-write-empty-string.js @@ -1,6 +1,3 @@ -//#FILE: test-dgram-bind.js -//#SHA1: 748fcd0fcb3ed5103b9072bba3019e560fb2799b -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,32 +19,36 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const dgram = require("dgram"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); -test("dgram socket bind", async () => { - const socket = dgram.createSocket("udp4"); +const http = require('http'); - await new Promise(resolve => { - socket.on("listening", () => { - expect(() => { - socket.bind(); - }).toThrow( - expect.objectContaining({ - code: "ERR_SOCKET_ALREADY_BOUND", - name: "Error", - message: expect.stringMatching(/^Socket is already bound$/), - }), - ); +const server = http.createServer(function(request, response) { + console.log(`responding to ${request.url}`); - socket.close(); - resolve(); - }); + response.writeHead(200, { 'Content-Type': 'text/plain' }); + response.write('1\n'); + response.write(''); + response.write('2\n'); + response.write(''); + response.end('3\n'); - const result = socket.bind(); // Should not throw. - - expect(result).toBe(socket); // Should have returned itself. - }); + this.close(); }); -//<#END_FILE: test-dgram-bind.js +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, common.mustCall((res) => { + let response = ''; + + assert.strictEqual(res.statusCode, 200); + res.setEncoding('ascii'); + res.on('data', (chunk) => { + response += chunk; + }); + res.on('end', common.mustCall(() => { + assert.strictEqual(response, '1\n2\n3\n'); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-zero-length-write.js b/test/js/node/test/parallel/test-http-zero-length-write.js new file mode 100644 index 0000000000..f765ad0545 --- /dev/null +++ b/test/js/node/test/parallel/test-http-zero-length-write.js @@ -0,0 +1,94 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const http = require('http'); + +const Stream = require('stream'); + +function getSrc() { + // An old-style readable stream. + // The Readable class prevents this behavior. + const src = new Stream(); + + // Start out paused, just so we don't miss anything yet. + let paused = false; + src.pause = function() { + paused = true; + }; + src.resume = function() { + paused = false; + }; + + const chunks = [ '', 'asdf', '', 'foo', '', 'bar', '' ]; + const interval = setInterval(function() { + if (paused) + return; + + const chunk = chunks.shift(); + if (chunk !== undefined) { + src.emit('data', chunk); + } else { + src.emit('end'); + clearInterval(interval); + } + }, 1); + + return src; +} + + +const expect = 'asdffoobar'; + +const server = http.createServer(function(req, res) { + let actual = ''; + req.setEncoding('utf8'); + req.on('data', function(c) { + actual += c; + }); + req.on('end', function() { + assert.strictEqual(actual, expect); + getSrc().pipe(res); + }); + server.close(); +}); + +server.listen(0, function() { + const req = http.request({ port: this.address().port, method: 'POST' }); + let actual = ''; + req.on('response', function(res) { + res.setEncoding('utf8'); + res.on('data', function(c) { + actual += c; + }); + res.on('end', function() { + assert.strictEqual(actual, expect); + }); + }); + getSrc().pipe(req); +}); + +process.on('exit', function(c) { + if (!c) console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-http-zerolengthbuffer.js b/test/js/node/test/parallel/test-http-zerolengthbuffer.js new file mode 100644 index 0000000000..c59fc18108 --- /dev/null +++ b/test/js/node/test/parallel/test-http-zerolengthbuffer.js @@ -0,0 +1,23 @@ +'use strict'; +// Serving up a zero-length buffer should work. + +const common = require('../common'); +const http = require('http'); + +const server = http.createServer((req, res) => { + const buffer = Buffer.alloc(0); + res.writeHead(200, { 'Content-Type': 'text/html', + 'Content-Length': buffer.length }); + res.end(buffer); +}); + +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, common.mustCall((res) => { + + res.on('data', common.mustNotCall()); + + res.on('end', (d) => { + server.close(); + }); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-clean-output.js b/test/js/node/test/parallel/test-http2-clean-output.js new file mode 100644 index 0000000000..27b7c338c9 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-clean-output.js @@ -0,0 +1,40 @@ +'use strict'; + +const { + hasCrypto, + mustCall, + skip +} = require('../common'); +if (!hasCrypto) + skip('missing crypto'); + +const { + strictEqual +} = require('assert'); +const { + createServer, + connect +} = require('http2'); +const { + spawnSync +} = require('child_process'); + +// Validate that there is no unexpected output when +// using http2 +if (process.argv[2] !== 'child') { + const { + stdout, stderr, status + } = spawnSync(process.execPath, [__filename, 'child'], { encoding: 'utf8' }); + strictEqual(stderr, ''); + strictEqual(stdout, ''); + strictEqual(status, 0); +} else { + const server = createServer(); + server.listen(0, mustCall(() => { + const client = connect(`http://localhost:${server.address().port}`); + client.on('connect', mustCall(() => { + client.close(); + server.close(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-client-priority-before-connect.js b/test/js/node/test/parallel/test-http2-client-priority-before-connect.js new file mode 100644 index 0000000000..7aa13a5e45 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-priority-before-connect.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.priority({}); + + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall()); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-request-listeners-warning.js b/test/js/node/test/parallel/test-http2-client-request-listeners-warning.js new file mode 100644 index 0000000000..854e9535fd --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-request-listeners-warning.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const EventEmitter = require('events'); + +// This test ensures that a MaxListenersExceededWarning isn't emitted if +// more than EventEmitter.defaultMaxListeners requests are started on a +// ClientHttp2Session before it has finished connecting. + +process.on('warning', common.mustNotCall('A warning was emitted')); + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respond(); + stream.end(); +}); + +server.listen(common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + function request() { + return new Promise((resolve, reject) => { + const stream = client.request(); + stream.on('error', reject); + stream.on('response', resolve); + stream.end(); + }); + } + + const requests = []; + for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { + requests.push(request()); + } + + Promise.all(requests).then(common.mustCall()).finally(common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-upload-reject.js b/test/js/node/test/parallel/test-http2-client-upload-reject.js new file mode 100644 index 0000000000..7234493031 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-upload-reject.js @@ -0,0 +1,49 @@ +'use strict'; + +// Verifies that uploading data from a client works + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const loc = fixtures.path('person-large.jpg'); + +assert(fs.existsSync(loc)); + +fs.readFile(loc, common.mustSucceed((data) => { + const server = http2.createServer(); + + server.on('stream', common.mustCall((stream) => { + // Wait for some data to come through. + setImmediate(() => { + stream.on('close', common.mustCall(() => { + assert.strictEqual(stream.rstCode, 0); + })); + + stream.respond({ ':status': 400 }); + stream.end(); + }); + })); + + server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 400); + })); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.pipe(req); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-upload.js b/test/js/node/test/parallel/test-http2-client-upload.js new file mode 100644 index 0000000000..d073cd94e6 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-upload.js @@ -0,0 +1,58 @@ +'use strict'; + +// Verifies that uploading data from a client works + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); +const Countdown = require('../common/countdown'); + +const loc = fixtures.path('person-large.jpg'); +let fileData; + +assert(fs.existsSync(loc)); + +fs.readFile(loc, common.mustSucceed((data) => { + fileData = data; + + const server = http2.createServer(); + let client; + + const countdown = new Countdown(2, () => { + server.close(); + client.close(); + }); + + server.on('stream', common.mustCall((stream) => { + let data = Buffer.alloc(0); + stream.on('data', (chunk) => data = Buffer.concat([data, chunk])); + stream.on('end', common.mustCall(() => { + assert.deepStrictEqual(data, fileData); + })); + // Waiting on close avoids spurious ECONNRESET seen in windows CI. + // Not sure if this is actually a bug; more details at + // https://github.com/nodejs/node/issues/20750#issuecomment-511015247 + stream.on('close', () => countdown.dec()); + stream.respond(); + stream.end(); + })); + + server.listen(0, common.mustCall(() => { + client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + + req.resume(); + req.on('end', common.mustCall()); + + const str = fs.createReadStream(loc); + str.on('end', common.mustCall()); + str.on('close', () => countdown.dec()); + str.pipe(req); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-write-before-connect.js b/test/js/node/test/parallel/test-http2-client-write-before-connect.js new file mode 100644 index 0000000000..6efefc5870 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-write-before-connect.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall((stream, headers, flags) => { + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'some data more data'); + })); + stream.respond(); + stream.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.write('some data '); + req.end('more data'); + + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall()); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-write-empty-string.js b/test/js/node/test/parallel/test-http2-client-write-empty-string.js new file mode 100644 index 0000000000..f0f0b8f012 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-write-empty-string.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); + +for (const chunkSequence of [ + [ '' ], + [ '', '' ], +]) { + const server = http2.createServer(); + server.on('stream', common.mustCall((stream, headers, flags) => { + stream.respond({ 'content-type': 'text/html' }); + + let data = ''; + stream.on('data', common.mustNotCall((chunk) => { + data += chunk.toString(); + })); + stream.on('end', common.mustCall(() => { + stream.end(`"${data}"`); + })); + })); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + + const req = client.request({ + ':method': 'POST', + ':path': '/' + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 200); + assert.strictEqual(headers['content-type'], 'text/html'); + })); + + let data = ''; + req.setEncoding('utf8'); + req.on('data', common.mustCallAtLeast((d) => data += d)); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, '""'); + server.close(); + client.close(); + })); + + for (const chunk of chunkSequence) + req.write(chunk); + req.end(); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-errors.js b/test/js/node/test/parallel/test-http2-compat-errors.js new file mode 100644 index 0000000000..18dc385422 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-errors.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +// Errors should not be reported both in Http2ServerRequest +// and Http2ServerResponse + +let expected = null; + +const server = h2.createServer(common.mustCall(function(req, res) { + res.stream.on('error', common.mustCall()); + req.on('error', common.mustNotCall()); + res.on('error', common.mustNotCall()); + req.on('aborted', common.mustCall()); + res.on('aborted', common.mustNotCall()); + + res.write('hello'); + + expected = new Error('kaboom'); + res.stream.destroy(expected); + server.close(); +})); + +server.listen(0, common.mustCall(function() { + const url = `http://localhost:${server.address().port}`; + const client = h2.connect(url, common.mustCall(() => { + const request = client.request(); + request.on('data', common.mustCall((chunk) => { + client.destroy(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-expect-continue.js b/test/js/node/test/parallel/test-http2-compat-expect-continue.js new file mode 100644 index 0000000000..d0decb1472 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-expect-continue.js @@ -0,0 +1,95 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +{ + const testResBody = 'other stuff!\n'; + + // Checks the full 100-continue flow from client sending 'expect: 100-continue' + // through server receiving it, sending back :status 100, writing the rest of + // the request to finally the client receiving to. + + const server = http2.createServer(); + + let sentResponse = false; + + server.on('request', common.mustCall((req, res) => { + res.end(testResBody); + sentResponse = true; + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + let body = ''; + + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ + ':method': 'POST', + 'expect': '100-continue' + }); + + let gotContinue = false; + req.on('continue', common.mustCall(() => { + gotContinue = true; + })); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(gotContinue, true); + assert.strictEqual(sentResponse, true); + assert.strictEqual(headers[':status'], 200); + req.end(); + })); + + req.setEncoding('utf8'); + req.on('data', common.mustCall((chunk) => { body += chunk; })); + req.on('end', common.mustCall(() => { + assert.strictEqual(body, testResBody); + client.close(); + server.close(); + })); + })); +} + +{ + // Checks the full 100-continue flow from client sending 'expect: 100-continue' + // through server receiving it and ending the request. + + const server = http2.createServer(); + + server.on('request', common.mustCall((req, res) => { + res.end(); + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ + ':path': '/', + 'expect': '100-continue' + }); + + let gotContinue = false; + req.on('continue', common.mustCall(() => { + gotContinue = true; + })); + + let gotResponse = false; + req.on('response', common.mustCall(() => { + gotResponse = true; + })); + + req.setEncoding('utf8'); + req.on('end', common.mustCall(() => { + assert.strictEqual(gotContinue, true); + assert.strictEqual(gotResponse, true); + client.close(); + server.close(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-expect-handling.js b/test/js/node/test/parallel/test-http2-compat-expect-handling.js new file mode 100644 index 0000000000..77f2275834 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-expect-handling.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const expectValue = 'meoww'; + +const server = http2.createServer(common.mustNotCall()); + +server.once('checkExpectation', common.mustCall((req, res) => { + assert.strictEqual(req.headers.expect, expectValue); + res.statusCode = 417; + res.end(); +})); + +server.listen(0, common.mustCall(() => nextTest(2))); + +function nextTest(testsToRun) { + if (!testsToRun) { + return server.close(); + } + + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + const req = client.request({ + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + 'expect': expectValue + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 417); + req.resume(); + })); + + req.on('end', common.mustCall(() => { + client.close(); + nextTest(testsToRun - 1); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-method-connect.js b/test/js/node/test/parallel/test-http2-compat-method-connect.js new file mode 100644 index 0000000000..21ad23e92b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-method-connect.js @@ -0,0 +1,40 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(() => testMethodConnect(2))); + +server.once('connect', common.mustCall((req, res) => { + assert.strictEqual(req.headers[':method'], 'CONNECT'); + res.statusCode = 405; + res.end(); +})); + +function testMethodConnect(testsToRun) { + if (!testsToRun) { + return server.close(); + } + + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + const req = client.request({ + ':method': 'CONNECT', + ':authority': `localhost:${port}` + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 405); + })); + req.resume(); + req.on('end', common.mustCall(() => { + client.close(); + testMethodConnect(testsToRun - 1); + })); + req.end(); +} diff --git a/test/js/node/test/parallel/test-http2-compat-serverrequest-pause.js b/test/js/node/test/parallel/test-http2-compat-serverrequest-pause.js new file mode 100644 index 0000000000..2abc9e3da4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverrequest-pause.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Check that pause & resume work as expected with Http2ServerRequest + +const testStr = 'Request Body from Client'; + +const server = h2.createServer(); + +server.on('request', common.mustCall((req, res) => { + let data = ''; + req.pause(); + req.setEncoding('utf8'); + req.on('data', common.mustCall((chunk) => (data += chunk))); + setTimeout(common.mustCall(() => { + assert.strictEqual(data, ''); + req.resume(); + }), common.platformTimeout(100)); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, testStr); + res.end(); + })); + + // Shouldn't throw if underlying Http2Stream no longer exists + res.on('finish', common.mustCall(() => process.nextTick(() => { + req.pause(); + req.resume(); + }))); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = h2.connect(`http://localhost:${port}`); + const request = client.request({ + ':path': '/foobar', + ':method': 'POST', + ':scheme': 'http', + ':authority': `localhost:${port}` + }); + request.resume(); + request.end(testStr); + request.on('end', common.mustCall(function() { + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverrequest-pipe.js b/test/js/node/test/parallel/test-http2-compat-serverrequest-pipe.js new file mode 100644 index 0000000000..64beb6472b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverrequest-pipe.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); + +// Piping should work as expected with createWriteStream + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const loc = fixtures.path('person-large.jpg'); +const fn = tmpdir.resolve('http2-url-tests.js'); + +const server = http2.createServer(); + +server.on('request', common.mustCall((req, res) => { + const dest = req.pipe(fs.createWriteStream(fn)); + dest.on('finish', common.mustCall(() => { + assert.strictEqual(req.complete, true); + assert.strictEqual(fs.readFileSync(loc).length, fs.readFileSync(fn).length); + fs.unlinkSync(fn); + res.end(); + })); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + + let remaining = 2; + function maybeClose() { + if (--remaining === 0) { + server.close(); + client.close(); + } + } + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall(maybeClose)); + const str = fs.createReadStream(loc); + str.on('end', common.mustCall(maybeClose)); + str.pipe(req); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-drain.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-drain.js new file mode 100644 index 0000000000..7ccbb1f4d2 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-drain.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Check that drain event is passed from Http2Stream + +const testString = 'tests'; + +const server = h2.createServer(); + +server.on('request', common.mustCall((req, res) => { + res.stream._writableState.highWaterMark = testString.length; + assert.strictEqual(res.write(testString), false); + res.on('drain', common.mustCall(() => res.end(testString))); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = h2.connect(`http://localhost:${port}`); + const request = client.request({ + ':path': '/foobar', + ':method': 'POST', + ':scheme': 'http', + ':authority': `localhost:${port}` + }); + request.resume(); + request.end(); + + let data = ''; + request.setEncoding('utf8'); + request.on('data', (chunk) => (data += chunk)); + + request.on('end', common.mustCall(function() { + assert.strictEqual(data, testString.repeat(2)); + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js new file mode 100644 index 0000000000..ce8cbe600c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +// This test case ensures that calling of res.end after sending +// 204, 205 and 304 HTTP statuses will not cause an error +// See issue: https://github.com/nodejs/node/issues/21740 + +const { + HTTP_STATUS_NO_CONTENT, + HTTP_STATUS_RESET_CONTENT, + HTTP_STATUS_NOT_MODIFIED +} = h2.constants; + +const statusWithoutBody = [ + HTTP_STATUS_NO_CONTENT, + HTTP_STATUS_RESET_CONTENT, + HTTP_STATUS_NOT_MODIFIED, +]; +const STATUS_CODES_COUNT = statusWithoutBody.length; + +const server = h2.createServer(common.mustCall(function(req, res) { + res.writeHead(statusWithoutBody.pop()); + res.end(); +}, STATUS_CODES_COUNT)); + +server.listen(0, common.mustCall(function() { + const url = `http://localhost:${server.address().port}`; + const client = h2.connect(url, common.mustCall(() => { + let responseCount = 0; + const closeAfterResponse = () => { + if (STATUS_CODES_COUNT === ++responseCount) { + client.destroy(); + server.close(); + } + }; + + for (let i = 0; i < STATUS_CODES_COUNT; i++) { + const request = client.request(); + request.on('response', common.mustCall(closeAfterResponse)); + } + + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-finished.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-finished.js new file mode 100644 index 0000000000..a42d40227c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-finished.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); +const net = require('net'); + +// Http2ServerResponse.finished +const server = h2.createServer(); +server.listen(0, common.mustCall(() => { + const port = server.address().port; + server.once('request', common.mustCall((request, response) => { + assert.ok(response.socket instanceof net.Socket); + assert.ok(response.connection instanceof net.Socket); + assert.strictEqual(response.socket, response.connection); + + response.on('finish', common.mustCall(() => { + assert.strictEqual(response.socket, undefined); + assert.strictEqual(response.connection, undefined); + process.nextTick(common.mustCall(() => { + assert.ok(response.stream); + server.close(); + })); + })); + assert.strictEqual(response.finished, false); + assert.strictEqual(response.writableEnded, false); + response.end(); + assert.strictEqual(response.finished, true); + assert.strictEqual(response.writableEnded, true); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(() => { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-flushheaders.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-flushheaders.js new file mode 100644 index 0000000000..7760bf8c7d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-flushheaders.js @@ -0,0 +1,59 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse.flushHeaders + +let serverResponse; + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + assert.strictEqual(response.headersSent, false); + assert.strictEqual(response._header, false); // Alias for headersSent + response.flushHeaders(); + assert.strictEqual(response.headersSent, true); + assert.strictEqual(response._header, true); + response.flushHeaders(); // Idempotent + + assert.throws(() => { + response.writeHead(400, { 'foo-bar': 'abc123' }); + }, { + code: 'ERR_HTTP2_HEADERS_SENT' + }); + + response.on('finish', common.mustCall(function() { + server.close(); + process.nextTick(() => { + response.flushHeaders(); // Idempotent + }); + })); + serverResponse = response; + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('response', common.mustCall(function(headers, flags) { + assert.strictEqual(headers['foo-bar'], undefined); + assert.strictEqual(headers[':status'], 200); + serverResponse.end(); + }, 1)); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-headers-send-date.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-headers-send-date.js new file mode 100644 index 0000000000..b22b1f7304 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-headers-send-date.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustCall((request, response) => { + response.sendDate = false; + response.writeHead(200); + response.end(); +})); + +server.listen(0, common.mustCall(() => { + const session = http2.connect(`http://localhost:${server.address().port}`); + const req = session.request(); + + req.on('response', common.mustCall((headers, flags) => { + assert.strictEqual('Date' in headers, false); + assert.strictEqual('date' in headers, false); + })); + + req.on('end', common.mustCall(() => { + session.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-statuscode.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-statuscode.js new file mode 100644 index 0000000000..6064a5936e --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-statuscode.js @@ -0,0 +1,76 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse should have a statusCode property + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + const expectedDefaultStatusCode = 200; + const realStatusCodes = { + continue: 100, + ok: 200, + multipleChoices: 300, + badRequest: 400, + internalServerError: 500 + }; + const fakeStatusCodes = { + tooLow: 99, + tooHigh: 600 + }; + + assert.strictEqual(response.statusCode, expectedDefaultStatusCode); + + // Setting the response.statusCode should not throw. + response.statusCode = realStatusCodes.ok; + response.statusCode = realStatusCodes.multipleChoices; + response.statusCode = realStatusCodes.badRequest; + response.statusCode = realStatusCodes.internalServerError; + + assert.throws(() => { + response.statusCode = realStatusCodes.continue; + }, { + code: 'ERR_HTTP2_INFO_STATUS_NOT_ALLOWED', + name: 'RangeError' + }); + assert.throws(() => { + response.statusCode = fakeStatusCodes.tooLow; + }, { + code: 'ERR_HTTP2_STATUS_INVALID', + name: 'RangeError' + }); + assert.throws(() => { + response.statusCode = fakeStatusCodes.tooHigh; + }, { + code: 'ERR_HTTP2_STATUS_INVALID', + name: 'RangeError' + }); + + response.on('finish', common.mustCall(function() { + server.close(); + })); + response.end(); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead-array.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead-array.js new file mode 100644 index 0000000000..a0cb65d4bf --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead-array.js @@ -0,0 +1,96 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +// Http2ServerResponse.writeHead should support arrays and nested arrays + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + const returnVal = response.writeHead(200, [ + ['foo', 'bar'], + ['foo', 'baz'], + ['ABC', 123], + ]); + assert.strictEqual(returnVal, response); + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('response', common.mustCall((headers) => { + assert.strictEqual(headers.foo, 'bar, baz'); + assert.strictEqual(headers.abc, '123'); + assert.strictEqual(headers[':status'], 200); + }, 1)); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + const returnVal = response.writeHead(200, ['foo', 'bar', 'foo', 'baz', 'ABC', 123]); + assert.strictEqual(returnVal, response); + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('response', common.mustCall((headers) => { + assert.strictEqual(headers.foo, 'bar, baz'); + assert.strictEqual(headers.abc, '123'); + assert.strictEqual(headers[':status'], 200); + }, 1)); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + try { + response.writeHead(200, ['foo', 'bar', 'ABC', 123, 'extra']); + } catch (err) { + assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); + } + + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead.js new file mode 100644 index 0000000000..8157dcbedd --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse.writeHead should override previous headers + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + response.setHeader('foo-bar', 'def456'); + + // Override + const returnVal = response.writeHead(418, { 'foo-bar': 'abc123' }); + + assert.strictEqual(returnVal, response); + + assert.throws(() => { response.writeHead(300); }, { + code: 'ERR_HTTP2_HEADERS_SENT' + }); + + response.on('finish', common.mustCall(function() { + server.close(); + process.nextTick(common.mustCall(() => { + // The stream is invalid at this point, + // and this line verifies this does not throw. + response.writeHead(300); + })); + })); + response.end(); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('response', common.mustCall(function(headers) { + assert.strictEqual(headers['foo-bar'], 'abc123'); + assert.strictEqual(headers[':status'], 418); + }, 1)); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse.js b/test/js/node/test/parallel/test-http2-compat-serverresponse.js new file mode 100644 index 0000000000..fbde58693b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse should expose convenience properties + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + assert.strictEqual(response.req, request); + + // Verify that writing to response.req is allowed. + response.req = null; + + response.on('finish', common.mustCall(function() { + process.nextTick(() => { + server.close(); + }); + })); + response.end(); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-type.js b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-type.js new file mode 100644 index 0000000000..caf5824e07 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-type.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('node:assert'); +const http2 = require('node:http2'); +const debug = require('node:util').debuglog('test'); + +const testResBody = 'response content'; + +{ + // Invalid object value + + const server = http2.createServer(); + + server.on('request', common.mustCall((req, res) => { + debug('Server sending early hints...'); + res.writeEarlyHints('this should not be here'); + + debug('Server sending full response...'); + res.end(testResBody); + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + debug('Client sending request...'); + + req.on('headers', common.mustNotCall()); + + process.on('uncaughtException', (err) => { + debug(`Caught an exception: ${JSON.stringify(err)}`); + if (err.name === 'AssertionError') throw err; + assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); + process.exit(0); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-value.js b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-value.js new file mode 100644 index 0000000000..d640f13fae --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-value.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('node:assert'); +const http2 = require('node:http2'); +const debug = require('node:util').debuglog('test'); + +const testResBody = 'response content'; + +{ + // Invalid link header value + + const server = http2.createServer(); + + server.on('request', common.mustCall((req, res) => { + debug('Server sending early hints...'); + res.writeEarlyHints({ link: BigInt(100) }); + + debug('Server sending full response...'); + res.end(testResBody); + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + debug('Client sending request...'); + + req.on('headers', common.mustNotCall()); + + process.on('uncaughtException', (err) => { + debug(`Caught an exception: ${JSON.stringify(err)}`); + if (err.name === 'AssertionError') throw err; + assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); + process.exit(0); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-write-head-destroyed.js b/test/js/node/test/parallel/test-http2-compat-write-head-destroyed.js new file mode 100644 index 0000000000..842bf0e9ab --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-write-head-destroyed.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); + +// Check that writeHead, write and end do not crash in compatibility mode + +const server = http2.createServer(common.mustCall((req, res) => { + // Destroy the stream first + req.stream.destroy(); + + res.writeHead(200); + res.write('hello '); + res.end('world'); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request(); + req.on('response', common.mustNotCall()); + req.on('close', common.mustCall((arg) => { + client.close(); + server.close(); + })); + req.resume(); +})); diff --git a/test/js/node/test/parallel/test-http2-connect-tls-with-delay.js b/test/js/node/test/parallel/test-http2-connect-tls-with-delay.js new file mode 100644 index 0000000000..0b3753ae38 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-connect-tls-with-delay.js @@ -0,0 +1,50 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const serverOptions = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = http2.createSecureServer(serverOptions, (req, res) => { + res.end(); +}); + +server.listen(0, '127.0.0.1', common.mustCall(() => { + const options = { + ALPNProtocols: ['h2'], + host: '127.0.0.1', + servername: 'localhost', + port: server.address().port, + rejectUnauthorized: false + }; + + const socket = tls.connect(options, async () => { + socket.once('readable', () => { + const client = http2.connect( + 'https://localhost:' + server.address().port, + { ...options, createConnection: () => socket } + ); + + client.once('remoteSettings', common.mustCall(() => { + const req = client.request({ + ':path': '/' + }); + req.on('data', () => req.resume()); + req.on('end', common.mustCall(() => { + client.close(); + req.close(); + server.close(); + })); + req.end(); + })); + }); + }); +})); diff --git a/test/js/node/test/parallel/test-http2-cookies.js b/test/js/node/test/parallel/test-http2-cookies.js new file mode 100644 index 0000000000..a270c1d73b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-cookies.js @@ -0,0 +1,60 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +const setCookie = [ + 'a=b', + 'c=d; Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly', + 'e=f', +]; + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + + assert.strictEqual(typeof headers.abc, 'string'); + assert.strictEqual(headers.abc, '1, 2, 3'); + assert.strictEqual(typeof headers.cookie, 'string'); + assert.strictEqual(headers.cookie, 'a=b; c=d; e=f'); + + stream.respond({ + 'content-type': 'text/html', + ':status': 200, + 'set-cookie': setCookie + }); + + stream.end('hello world'); +} + +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ + ':path': '/', + 'abc': [1, 2, 3], + 'cookie': ['a=b', 'c=d', 'e=f'], + }); + req.resume(); + + req.on('response', common.mustCall((headers) => { + assert(Array.isArray(headers['set-cookie'])); + assert.deepStrictEqual(headers['set-cookie'], setCookie); + })); + + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + req.end(); + +})); diff --git a/test/js/node/test/parallel/test-http2-date-header.js b/test/js/node/test/parallel/test-http2-date-header.js new file mode 100644 index 0000000000..2b63e1b789 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-date-header.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + // Date header is defaulted + stream.respond(); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('response', common.mustCall((headers) => { + // The date header must be set to a non-invalid value + assert.notStrictEqual((new Date()).toString(), 'Invalid Date'); + })); + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-dont-override.js b/test/js/node/test/parallel/test-http2-dont-override.js new file mode 100644 index 0000000000..3f87e14be1 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-dont-override.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const options = {}; + +const server = http2.createServer(options); + +// Options are defaulted but the options are not modified +assert.deepStrictEqual(Object.keys(options), []); + +server.on('stream', common.mustCall((stream) => { + const headers = {}; + const options = {}; + stream.respond(headers, options); + + // The headers are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(headers), []); + + // Options are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(options), []); + + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const headers = {}; + const options = {}; + + const req = client.request(headers, options); + + // The headers are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(headers), []); + + // Options are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(options), []); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-large-write-close.js b/test/js/node/test/parallel/test-http2-large-write-close.js new file mode 100644 index 0000000000..f9dee357d6 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-close.js @@ -0,0 +1,44 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +const content = Buffer.alloc(1e5, 0x44); + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}); +server.on('stream', common.mustCall((stream) => { + stream.respond({ + 'Content-Type': 'application/octet-stream', + 'Content-Length': (content.length.toString() * 2), + 'Vary': 'Accept-Encoding' + }); + + stream.write(content); + stream.write(content); + stream.end(); + stream.close(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, + { rejectUnauthorized: false }); + + const req = client.request({ ':path': '/' }); + req.end(); + + let receivedBufferLength = 0; + req.on('data', common.mustCallAtLeast((buf) => { + receivedBufferLength += buf.length; + }, 1)); + req.on('close', common.mustCall(() => { + assert.strictEqual(receivedBufferLength, content.length * 2); + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-large-write-destroy.js b/test/js/node/test/parallel/test-http2-large-write-destroy.js new file mode 100644 index 0000000000..b59c66bb04 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-destroy.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// This test will result in a crash due to a missed CHECK in C++ or +// a straight-up segfault if the C++ doesn't send RST_STREAM through +// properly when calling destroy. + +const content = Buffer.alloc(60000, 0x44); + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}); +server.on('stream', common.mustCall((stream) => { + stream.respond({ + 'Content-Type': 'application/octet-stream', + 'Content-Length': (content.length.toString() * 2), + 'Vary': 'Accept-Encoding' + }, { waitForTrailers: true }); + + stream.write(content); + stream.destroy(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, + { rejectUnauthorized: false }); + + const req = client.request({ ':path': '/' }); + req.end(); + req.resume(); // Otherwise close won't be emitted if there's pending data. + + req.on('close', common.mustCall(() => { + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js b/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js new file mode 100644 index 0000000000..bcbb1434cb --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This tests that the http2 server sends data early when it accumulates +// enough from ongoing requests to avoid DoS as mitigation for +// CVE-2019-9517 and CVE-2019-9511. +// Added by https://github.com/nodejs/node/commit/8a4a193 +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const content = fixtures.readSync('person-large.jpg'); + +const server = http2.createServer({ + maxSessionMemory: 1000 +}); +let streamCount = 0; +server.on('stream', (stream, headers) => { + stream.respond({ + 'content-type': 'image/jpeg', + ':status': 200 + }); + stream.end(content); + console.log('server sends content', ++streamCount); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}/`); + + let endCount = 0; + let finished = 0; + for (let i = 0; i < 100; i++) { + const req = client.request({ ':path': '/' }).end(); + const chunks = []; + req.on('data', (chunk) => { + chunks.push(chunk); + }); + req.on('end', common.mustCall(() => { + console.log('client receives content', ++endCount); + assert.deepStrictEqual(Buffer.concat(chunks), content); + + if (++finished === 100) { + client.close(); + server.close(); + } + })); + req.on('error', (e) => { + console.log('client error', e); + }); + } +})); diff --git a/test/js/node/test/parallel/test-http2-large-writes-session-memory-leak.js b/test/js/node/test/parallel/test-http2-large-writes-session-memory-leak.js new file mode 100644 index 0000000000..641923c06c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-writes-session-memory-leak.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/29223. +// There was a "leak" in the accounting of session memory leading +// to streams eventually failing with NGHTTP2_ENHANCE_YOUR_CALM. + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), +}); + +// Simple server that sends 200k and closes the stream. +const data200k = 'a'.repeat(200 * 1024); +server.on('stream', (stream) => { + stream.write(data200k); + stream.end(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, { + ca: fixtures.readKey('agent2-cert.pem'), + servername: 'agent2', + + // Set maxSessionMemory to 1MB so the leak causes errors faster. + maxSessionMemory: 1 + }); + + // Repeatedly create a new stream and read the incoming data. Even though we + // only have one stream active at a time, prior to the fix for #29223, + // session memory would steadily increase and we'd eventually hit the 1MB + // maxSessionMemory limit and get NGHTTP2_ENHANCE_YOUR_CALM errors trying to + // create new streams. + let streamsLeft = 50; + function newStream() { + const stream = client.request({ ':path': '/' }); + + stream.on('data', () => { }); + + stream.on('close', () => { + if (streamsLeft-- > 0) { + newStream(); + } else { + client.destroy(); + server.close(); + } + }); + } + + newStream(); +})); diff --git a/test/js/node/test/parallel/test-http2-malformed-altsvc.js b/test/js/node/test/parallel/test-http2-malformed-altsvc.js new file mode 100644 index 0000000000..28c0fb46b4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-malformed-altsvc.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const net = require('net'); +const h2test = require('../common/http2'); + +const server = http2.createServer(); +server.on('stream', common.mustNotCall()); + +const settings = new h2test.SettingsFrame(); +const settingsAck = new h2test.SettingsFrame(true); +const altsvc = new h2test.AltSvcFrame((1 << 14) + 1); + +server.listen(0, () => { + const client = net.connect(server.address().port, () => { + client.write(h2test.kClientMagic, () => { + client.write(settings.data, () => { + client.write(settingsAck.data); + // Prior to nghttp2 1.31.1, sending this malformed altsvc frame + // would cause a segfault. This test is successful if a segfault + // does not occur. + client.write(altsvc.data, common.mustCall(() => { + client.destroy(); + })); + }); + }); + }); + + // An error may or may not be emitted on the client side, we don't care + // either way if it is, but we don't want to die if it is. + client.on('error', () => {}); + client.on('close', common.mustCall(() => server.close())); + client.resume(); +}); diff --git a/test/js/node/test/parallel/test-http2-many-writes-and-destroy.js b/test/js/node/test/parallel/test-http2-many-writes-and-destroy.js new file mode 100644 index 0000000000..78db76e001 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-many-writes-and-destroy.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); + +{ + const server = http2.createServer((req, res) => { + req.pipe(res); + }); + + server.listen(0, () => { + const url = `http://localhost:${server.address().port}`; + const client = http2.connect(url); + const req = client.request({ ':method': 'POST' }); + + for (let i = 0; i < 4000; i++) { + req.write(Buffer.alloc(6)); + } + + req.on('close', common.mustCall(() => { + console.log('(req onclose)'); + server.close(); + client.close(); + })); + + req.once('data', common.mustCall(() => req.destroy())); + }); +} diff --git a/test/js/node/test/parallel/test-http2-max-session-memory-leak.js b/test/js/node/test/parallel/test-http2-max-session-memory-leak.js new file mode 100644 index 0000000000..476c605783 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-max-session-memory-leak.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/27416. +// Check that received data is accounted for correctly in the maxSessionMemory +// mechanism. + +const bodyLength = 8192; +const maxSessionMemory = 1; // 1 MiB +const requestCount = 1000; + +const server = http2.createServer({ maxSessionMemory }); +server.on('stream', (stream) => { + stream.respond(); + stream.end(); +}); + +server.listen(common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`, { + maxSessionMemory + }); + + function request() { + return new Promise((resolve, reject) => { + const stream = client.request({ + ':method': 'POST', + 'content-length': bodyLength + }); + stream.on('error', reject); + stream.on('response', resolve); + stream.end('a'.repeat(bodyLength)); + }); + } + + (async () => { + for (let i = 0; i < requestCount; i++) { + await request(); + } + + client.close(); + server.close(); + })().then(common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-methods.js b/test/js/node/test/parallel/test-http2-methods.js new file mode 100644 index 0000000000..936a264e99 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-methods.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +const methods = ['GET', 'POST', 'PATCH', 'FOO', 'A_B_C']; +let expected = methods.length; + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream, expected)); + +function onStream(stream, headers, flags) { + const method = headers[':method']; + assert.notStrictEqual(method, undefined); + assert(methods.includes(method), `method ${method} not included`); + stream.respond({ + 'content-type': 'text/html', + ':status': 200 + }); + stream.end('hello world'); +} + +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + + const headers = { ':path': '/' }; + + methods.forEach((method) => { + headers[':method'] = method; + const req = client.request(headers); + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall(() => { + if (--expected === 0) { + server.close(); + client.close(); + } + })); + req.end(); + }); +})); diff --git a/test/js/node/test/parallel/test-http2-multiplex.js b/test/js/node/test/parallel/test-http2-multiplex.js new file mode 100644 index 0000000000..4c157d0ede --- /dev/null +++ b/test/js/node/test/parallel/test-http2-multiplex.js @@ -0,0 +1,58 @@ +'use strict'; + +// Tests opening 100 concurrent simultaneous uploading streams over a single +// connection and makes sure that the data for each is appropriately echoed. + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const Countdown = require('../common/countdown'); + +const server = http2.createServer(); + +const count = 100; + +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.pipe(stream); +}, count)); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + client.setMaxListeners(100); + + const countdown = new Countdown(count, () => { + server.close(); + client.close(); + }); + + function doRequest() { + const req = client.request({ ':method': 'POST' }); + + let data = ''; + req.setEncoding('utf8'); + req.on('data', (chunk) => data += chunk); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, 'abcdefghij'); + })); + req.on('close', common.mustCall(() => countdown.dec())); + + let n = 0; + function writeChunk() { + if (n < 10) { + req.write(String.fromCharCode(97 + n)); + setTimeout(writeChunk, 10); + } else { + req.end(); + } + n++; + } + + writeChunk(); + } + + for (let n = 0; n < count; n++) + doRequest(); +})); diff --git a/test/js/node/test/parallel/test-http2-multistream-destroy-on-read-tls.js b/test/js/node/test/parallel/test-http2-multistream-destroy-on-read-tls.js new file mode 100644 index 0000000000..91cbec6b2d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-multistream-destroy-on-read-tls.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/29353. +// Test that it’s okay for an HTTP2 + TLS server to destroy a stream instance +// while reading it. + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') +}); + +const filenames = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; + +server.on('stream', common.mustCall((stream) => { + function write() { + stream.write('a'.repeat(10240)); + stream.once('drain', write); + } + write(); +}, filenames.length)); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, { + ca: fixtures.readKey('agent2-cert.pem'), + servername: 'agent2' + }); + + let destroyed = 0; + for (const entry of filenames) { + const stream = client.request({ + ':path': `/${entry}` + }); + stream.once('data', common.mustCall(() => { + stream.destroy(); + + if (++destroyed === filenames.length) { + client.destroy(); + server.close(); + } + })); + } +})); diff --git a/test/js/node/test/parallel/test-http2-no-wanttrailers-listener.js b/test/js/node/test/parallel/test-http2-no-wanttrailers-listener.js new file mode 100644 index 0000000000..09293f2584 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-no-wanttrailers-listener.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + stream.respond(undefined, { waitForTrailers: true }); + // There is no wantTrailers handler so this should close naturally + // without hanging. If the test completes without timing out, then + // it passes. + stream.end('ok'); +} + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const client = h2.connect(`http://localhost:${this.address().port}`); + const req = client.request(); + req.resume(); + req.on('trailers', common.mustNotCall()); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-options-server-response.js b/test/js/node/test/parallel/test-http2-options-server-response.js new file mode 100644 index 0000000000..6f1ae1881d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-options-server-response.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +class MyServerResponse extends h2.Http2ServerResponse { + status(code) { + return this.writeHead(code, { 'Content-Type': 'text/plain' }); + } +} + +const server = h2.createServer({ + Http2ServerResponse: MyServerResponse +}, (req, res) => { + res.status(200); + res.end(); +}); +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ ':path': '/' }); + + req.on('response', common.mustCall()); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.destroy(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-pipe-named-pipe.js b/test/js/node/test/parallel/test-http2-pipe-named-pipe.js new file mode 100644 index 0000000000..eb9b1b568c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-pipe-named-pipe.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const net = require('net'); + +// HTTP/2 servers can listen on a named pipe. + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const loc = fixtures.path('person-large.jpg'); +const fn = tmpdir.resolve('person-large.jpg'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + const dest = stream.pipe(fs.createWriteStream(fn)); + + stream.on('end', common.mustCall(() => { + stream.respond(); + stream.end(); + })); + + dest.on('finish', common.mustCall(() => { + assert.strictEqual(fs.readFileSync(fn).length, + fs.readFileSync(loc).length); + })); +})); + +server.listen(common.PIPE, common.mustCall(() => { + const client = http2.connect('http://localhost', { + createConnection(url) { + return net.connect(server.address()); + } + }); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + req.resume(); + + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.on('end', common.mustCall()); + str.pipe(req); +})); diff --git a/test/js/node/test/parallel/test-http2-pipe.js b/test/js/node/test/parallel/test-http2-pipe.js new file mode 100644 index 0000000000..ebd89e23d8 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-pipe.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); + +// Piping should work as expected with createWriteStream + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const loc = fixtures.path('person-large.jpg'); +const fn = tmpdir.resolve('http2-url-tests.js'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + const dest = stream.pipe(fs.createWriteStream(fn)); + + dest.on('finish', () => { + assert.strictEqual(fs.readFileSync(loc).length, + fs.readFileSync(fn).length); + }); + stream.respond(); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + + req.on('response', common.mustCall()); + req.resume(); + + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.on('end', common.mustCall()); + str.pipe(req); +})); diff --git a/test/js/node/test/parallel/test-http2-removed-header-stays-removed.js b/test/js/node/test/parallel/test-http2-removed-header-stays-removed.js new file mode 100644 index 0000000000..663249749a --- /dev/null +++ b/test/js/node/test/parallel/test-http2-removed-header-stays-removed.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustCall((request, response) => { + response.setHeader('date', 'snacks o clock'); + response.end(); +})); + +server.listen(0, common.mustCall(() => { + const session = http2.connect(`http://localhost:${server.address().port}`); + const req = session.request(); + req.on('response', (headers, flags) => { + assert.strictEqual(headers.date, 'snacks o clock'); + }); + req.on('end', () => { + session.close(); + server.close(); + }); +})); diff --git a/test/js/node/test/parallel/test-http2-request-remove-connect-listener.js b/test/js/node/test/parallel/test-http2-request-remove-connect-listener.js new file mode 100644 index 0000000000..61de140c22 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-request-remove-connect-listener.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + client.once('connect', common.mustCall()); + + req.on('response', common.mustCall(() => { + assert.strictEqual(client.listenerCount('connect'), 0); + })); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-request-response-proto.js b/test/js/node/test/parallel/test-http2-request-response-proto.js new file mode 100644 index 0000000000..49b15dfc70 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-request-response-proto.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const { + Http2ServerRequest, + Http2ServerResponse, +} = http2; + +const protoRequest = { __proto__: Http2ServerRequest.prototype }; +const protoResponse = { __proto__: Http2ServerResponse.prototype }; + +assert.strictEqual(protoRequest instanceof Http2ServerRequest, true); +assert.strictEqual(protoResponse instanceof Http2ServerResponse, true); diff --git a/test/js/node/test/parallel/test-http2-res-corked.js b/test/js/node/test/parallel/test-http2-res-corked.js new file mode 100644 index 0000000000..5a6c623edf --- /dev/null +++ b/test/js/node/test/parallel/test-http2-res-corked.js @@ -0,0 +1,54 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } + +// Test for Http2ServerResponse#[writableCorked,cork,uncork] + +const { strictEqual } = require('assert'); +const http2 = require('http2'); + +{ + let corksLeft = 0; + const server = http2.createServer(common.mustCall((req, res) => { + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.end(); + })); + server.listen(0, common.mustCall(() => { + const URL = `http://localhost:${server.address().port}`; + const client = http2.connect(URL); + const req = client.request(); + req.on('data', common.mustCall(2)); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-respond-file-204.js b/test/js/node/test/parallel/test-http2-respond-file-204.js new file mode 100644 index 0000000000..0c59b0e729 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-204.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const { + HTTP2_HEADER_CONTENT_TYPE, + HTTP2_HEADER_STATUS +} = http2.constants; + +const fname = fixtures.path('elipses.txt'); + +const server = http2.createServer(); +server.on('stream', (stream) => { + assert.throws(() => { + stream.respondWithFile(fname, { + [HTTP2_HEADER_STATUS]: 204, + [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain' + }); + }, { + code: 'ERR_HTTP2_PAYLOAD_FORBIDDEN', + name: 'Error', + message: 'Responses with 204 status must not have a payload' + }); + stream.respond({}); + stream.end(); +}); +server.listen(0, () => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('response', common.mustCall()); + req.on('data', common.mustNotCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http2-respond-file-compat.js b/test/js/node/test/parallel/test-http2-respond-file-compat.js new file mode 100644 index 0000000000..0205f2d0d8 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-compat.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const fixtures = require('../common/fixtures'); + +const fname = fixtures.path('elipses.txt'); + +const server = http2.createServer(common.mustCall((request, response) => { + response.stream.respondWithFile(fname); +})); +server.listen(0, () => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('response', common.mustCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); + req.resume(); +}); diff --git a/test/js/node/test/parallel/test-http2-respond-file-error-dir.js b/test/js/node/test/parallel/test-http2-respond-file-error-dir.js new file mode 100644 index 0000000000..155e005432 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-error-dir.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const assert = require('assert'); + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respondWithFile(process.cwd(), { + 'content-type': 'text/plain' + }, { + onError(err) { + common.expectsError({ + code: 'ERR_HTTP2_SEND_FILE', + name: 'Error', + message: 'Directories cannot be sent' + })(err); + + stream.respond({ ':status': 404 }); + stream.end(); + }, + statCheck: common.mustNotCall() + }); +}); +server.listen(0, () => { + + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 404); + })); + req.on('data', common.mustNotCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http2-respond-file-error-pipe-offset.js b/test/js/node/test/parallel/test-http2-respond-file-error-pipe-offset.js new file mode 100644 index 0000000000..bd043e42f4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-error-pipe-offset.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +if (common.isWindows) + common.skip('no mkfifo on Windows'); +const child_process = require('child_process'); +const fs = require('fs'); +const http2 = require('http2'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const pipeName = tmpdir.resolve('pipe'); + +const mkfifo = child_process.spawnSync('mkfifo', [ pipeName ]); +if (mkfifo.error && mkfifo.error.code === 'ENOENT') { + common.skip('missing mkfifo'); +} + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respondWithFile(pipeName, { + 'content-type': 'text/plain' + }, { + offset: 10, + onError(err) { + common.expectsError({ + code: 'ERR_HTTP2_SEND_FILE_NOSEEK', + name: 'Error', + message: 'Offset or length can only be specified for regular files' + })(err); + + stream.respond({ ':status': 404 }); + stream.end(); + }, + statCheck: common.mustNotCall() + }); +}); +server.listen(0, () => { + + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 404); + })); + req.on('data', common.mustNotCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); + +fs.writeFile(pipeName, 'Hello, world!\n', common.mustCall((err) => { + // It's possible for the reading end of the pipe to get the expected error + // and break everything down before we're finished, so allow `EPIPE` but + // no other errors. + if (err?.code !== 'EPIPE') { + assert.ifError(err); + } +})); diff --git a/test/js/node/test/parallel/test-http2-respond-with-file-connection-abort.js b/test/js/node/test/parallel/test-http2-respond-with-file-connection-abort.js new file mode 100644 index 0000000000..d5ed364570 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-with-file-connection-abort.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const net = require('net'); + +const { + HTTP2_HEADER_CONTENT_TYPE +} = http2.constants; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.on('error', (err) => assert.strictEqual(err.code, 'ECONNRESET')); + stream.respondWithFile(process.execPath, { + [HTTP2_HEADER_CONTENT_TYPE]: 'application/octet-stream' + }); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall()); + req.once('data', common.mustCall(() => { + net.Socket.prototype.destroy.call(client.socket); + server.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http2-serve-file.js b/test/js/node/test/parallel/test-http2-serve-file.js new file mode 100644 index 0000000000..7b73fe639e --- /dev/null +++ b/test/js/node/test/parallel/test-http2-serve-file.js @@ -0,0 +1,79 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const tls = require('tls'); + +const ajs_data = fixtures.readSync('a.js', 'utf8'); + +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS +} = http2.constants; + +const key = fixtures.readKey('agent8-key.pem', 'binary'); +const cert = fixtures.readKey('agent8-cert.pem', 'binary'); +const ca = fixtures.readKey('fake-startcom-root-cert.pem', 'binary'); + +const server = http2.createSecureServer({ key, cert }); + +server.on('stream', (stream, headers) => { + const name = headers[HTTP2_HEADER_PATH].slice(1); + const file = fixtures.path(name); + fs.stat(file, (err, stat) => { + if (err != null || stat.isDirectory()) { + stream.respond({ [HTTP2_HEADER_STATUS]: 404 }); + stream.end(); + } else { + stream.respond({ [HTTP2_HEADER_STATUS]: 200 }); + const str = fs.createReadStream(file); + str.pipe(stream); + } + }); +}); + +server.listen(0, () => { + + const secureContext = tls.createSecureContext({ ca }); + const client = http2.connect(`https://localhost:${server.address().port}`, + { secureContext }); + + let remaining = 2; + function maybeClose() { + if (--remaining === 0) { + client.close(); + server.close(); + } + } + + // Request for a file that does exist, response is 200 + const req1 = client.request({ [HTTP2_HEADER_PATH]: '/a.js' }, + { endStream: true }); + req1.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[HTTP2_HEADER_STATUS], 200); + })); + let req1_data = ''; + req1.setEncoding('utf8'); + req1.on('data', (chunk) => req1_data += chunk); + req1.on('end', common.mustCall(() => { + assert.strictEqual(req1_data, ajs_data); + maybeClose(); + })); + + // Request for a file that does not exist, response is 404 + const req2 = client.request({ [HTTP2_HEADER_PATH]: '/does_not_exist' }, + { endStream: true }); + req2.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[HTTP2_HEADER_STATUS], 404); + })); + req2.on('data', common.mustNotCall()); + req2.on('end', common.mustCall(() => maybeClose())); + +}); diff --git a/test/js/node/test/parallel/test-http2-server-async-dispose.js b/test/js/node/test/parallel/test-http2-server-async-dispose.js new file mode 100644 index 0000000000..4782e12e41 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-async-dispose.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); + +const server = http2.createServer(); + +server.listen(0, common.mustCall(() => { + server.on('close', common.mustCall()); + server[Symbol.asyncDispose]().then(common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-server-close-callback.js b/test/js/node/test/parallel/test-http2-server-close-callback.js new file mode 100644 index 0000000000..e4cd24ce20 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-close-callback.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const Countdown = require('../common/countdown'); +const http2 = require('http2'); + +const server = http2.createServer(); + +let session; + +const countdown = new Countdown(2, () => { + server.close(common.mustSucceed()); + session.close(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + client.on('connect', common.mustCall(() => countdown.dec())); +})); + +server.on('session', common.mustCall((s) => { + session = s; + countdown.dec(); +})); diff --git a/test/js/node/test/parallel/test-http2-server-errors.js b/test/js/node/test/parallel/test-http2-server-errors.js new file mode 100644 index 0000000000..959ddccdc7 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-errors.js @@ -0,0 +1,88 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Errors should not be reported both in Http2ServerRequest +// and Http2ServerResponse + +{ + let expected = null; + + const server = h2.createServer(); + + server.on('stream', common.mustCall(function(stream) { + stream.on('error', common.mustCall(function(err) { + assert.strictEqual(err, expected); + })); + + stream.resume(); + stream.write('hello'); + + expected = new Error('kaboom'); + stream.destroy(expected); + server.close(); + })); + + server.listen(0, common.mustCall(function() { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }; + const request = client.request(headers); + request.on('data', common.mustCall(function(chunk) { + // Cause an error on the server side + client.destroy(); + })); + request.end(); + })); + })); +} + +{ + let expected = null; + + const server = h2.createServer(); + + process.on('uncaughtException', common.mustCall(function(err) { + assert.strictEqual(err.message, 'kaboom no handler'); + })); + + server.on('stream', common.mustCall(function(stream) { + // There is no 'error' handler, and this will crash + stream.write('hello'); + stream.resume(); + + expected = new Error('kaboom no handler'); + stream.destroy(expected); + server.close(); + })); + + server.listen(0, common.mustCall(function() { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }; + const request = client.request(headers); + request.on('data', common.mustCall(function(chunk) { + // Cause an error on the server side + client.destroy(); + })); + request.end(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-server-rst-before-respond.js b/test/js/node/test/parallel/test-http2-server-rst-before-respond.js new file mode 100644 index 0000000000..d551c7121f --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-rst-before-respond.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + stream.close(); + + assert.throws(() => { + stream.additionalHeaders({ + ':status': 123, + 'abc': 123 + }); + }, { code: 'ERR_HTTP2_INVALID_STREAM' }); +} + +server.listen(0); + +server.on('listening', common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('headers', common.mustNotCall()); + req.on('close', common.mustCall(() => { + assert.strictEqual(h2.constants.NGHTTP2_NO_ERROR, req.rstCode); + server.close(); + client.close(); + })); + req.on('response', common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-session-timeout.js b/test/js/node/test/parallel/test-http2-session-timeout.js new file mode 100644 index 0000000000..c1dacdcb45 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-session-timeout.js @@ -0,0 +1,64 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const hrtime = process.hrtime.bigint; +const NS_PER_MS = 1_000_000n; + +let requests = 0; +const mustNotCall = () => { + assert.fail(`Timeout after ${requests} request(s)`); +}; + +const server = http2.createServer(); +// Disable server timeout until first request. We will set the timeout based on +// how long the first request takes. +server.timeout = 0n; + +server.on('request', (req, res) => res.end()); +server.on('timeout', mustNotCall); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = http2.connect(url); + let startTime = hrtime(); + makeReq(); + + function makeReq() { + const request = client.request({ + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }); + request.resume(); + request.end(); + + requests += 1; + + request.on('end', () => { + const diff = hrtime() - startTime; + const milliseconds = diff / NS_PER_MS; + if (server.timeout === 0n) { + // Set the timeout now. First connection will take significantly longer + // than subsequent connections, so using the duration of the first + // connection as the timeout should be robust. Double it anyway for good + // measure. + server.timeout = milliseconds * 2n; + startTime = hrtime(); + makeReq(); + } else if (milliseconds < server.timeout * 2n) { + makeReq(); + } else { + server.removeListener('timeout', mustNotCall); + server.close(); + client.close(); + } + }); + } +})); diff --git a/test/js/node/test/parallel/test-http2-socket-proxy-handler-for-has.js b/test/js/node/test/parallel/test-http2-socket-proxy-handler-for-has.js new file mode 100644 index 0000000000..a8dfbfe07a --- /dev/null +++ b/test/js/node/test/parallel/test-http2-socket-proxy-handler-for-has.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const serverOptions = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; +const server = http2.createSecureServer(serverOptions, common.mustCall( + (req, res) => { + const request = req; + assert.strictEqual(request.socket.encrypted, true); + assert.ok('encrypted' in request.socket); + res.end(); + } +)); +server.listen(common.mustCall(() => { + const port = server.address().port; + const client = http2.connect('https://localhost:' + port, { + ca: fixtures.readKey('agent1-cert.pem'), + rejectUnauthorized: false + }); + const req = client.request({}); + req.on('response', common.mustCall((headers, flags) => { + console.log(headers); + server.close(common.mustCall()); + })); + req.on('end', common.mustCall(() => { + client.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http2-status-code.js b/test/js/node/test/parallel/test-http2-status-code.js new file mode 100644 index 0000000000..d3642b4ff0 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-status-code.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const codes = [ 200, 202, 300, 400, 404, 451, 500 ]; +let test = 0; + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + const status = codes[test++]; + stream.respond({ ':status': status }, { endStream: true }); +}, 7)); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + let remaining = codes.length; + function maybeClose() { + if (--remaining === 0) { + client.close(); + server.close(); + } + } + + function doTest(expected) { + const req = client.request(); + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], expected); + })); + req.resume(); + req.on('end', common.mustCall(maybeClose)); + } + + for (let n = 0; n < codes.length; n++) + doTest(codes[n]); +})); diff --git a/test/js/node/test/parallel/test-http2-trailers-after-session-close.js b/test/js/node/test/parallel/test-http2-trailers-after-session-close.js new file mode 100644 index 0000000000..f7c7387eb0 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-trailers-after-session-close.js @@ -0,0 +1,51 @@ +'use strict'; + +// Fixes: https://github.com/nodejs/node/issues/42713 +const common = require('../common'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const assert = require('assert'); +const http2 = require('http2'); + +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, +} = http2.constants; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + server.close(); + stream.session.close(); + stream.on('wantTrailers', common.mustCall(() => { + stream.sendTrailers({ xyz: 'abc' }); + })); + + stream.respond({ [HTTP2_HEADER_STATUS]: 200 }, { waitForTrailers: true }); + stream.write('some data'); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + client.socket.on('close', common.mustCall()); + const req = client.request({ + [HTTP2_HEADER_PATH]: '/', + [HTTP2_HEADER_METHOD]: 'POST', + }); + req.end(); + req.on('response', common.mustCall()); + let data = ''; + req.on('data', (chunk) => { + data += chunk; + }); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, 'some data'); + })); + req.on('trailers', common.mustCall((headers) => { + assert.strictEqual(headers.xyz, 'abc'); + })); + req.on('close', common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-trailers.js b/test/js/node/test/parallel/test-http2-trailers.js new file mode 100644 index 0000000000..dba9aac12d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-trailers.js @@ -0,0 +1,73 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); +const body = + '

this is some data

'; +const trailerKey = 'test-trailer'; +const trailerValue = 'testing'; + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + stream.on('trailers', common.mustCall((headers) => { + assert.strictEqual(headers[trailerKey], trailerValue); + stream.end(body); + })); + stream.respond({ + 'content-type': 'text/html', + ':status': 200 + }, { waitForTrailers: true }); + stream.on('wantTrailers', () => { + stream.sendTrailers({ [trailerKey]: trailerValue }); + assert.throws( + () => stream.sendTrailers({}), + { + code: 'ERR_HTTP2_TRAILERS_ALREADY_SENT', + name: 'Error' + } + ); + }); + + assert.throws( + () => stream.sendTrailers({}), + { + code: 'ERR_HTTP2_TRAILERS_NOT_READY', + name: 'Error' + } + ); +} + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const client = h2.connect(`http://localhost:${this.address().port}`); + const req = client.request({ ':path': '/', ':method': 'POST' }, + { waitForTrailers: true }); + req.on('wantTrailers', () => { + req.sendTrailers({ [trailerKey]: trailerValue }); + }); + req.on('data', common.mustCall()); + req.on('trailers', common.mustCall((headers) => { + assert.strictEqual(headers[trailerKey], trailerValue); + })); + req.on('close', common.mustCall(() => { + assert.throws( + () => req.sendTrailers({}), + { + code: 'ERR_HTTP2_INVALID_STREAM', + name: 'Error' + } + ); + server.close(); + client.close(); + })); + req.end('data'); + +})); diff --git a/test/js/node/test/parallel/test-http2-unbound-socket-proxy.js b/test/js/node/test/parallel/test-http2-unbound-socket-proxy.js new file mode 100644 index 0000000000..74ca016944 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-unbound-socket-proxy.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const net = require('net'); + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const socket = client.socket; + const req = client.request(); + req.resume(); + req.on('close', common.mustCall(() => { + client.close(); + server.close(); + + // Tests to make sure accessing the socket proxy fails with an + // informative error. + setImmediate(common.mustCall(() => { + assert.throws(() => { + socket.example; // eslint-disable-line no-unused-expressions + }, { + code: 'ERR_HTTP2_SOCKET_UNBOUND' + }); + assert.throws(() => { + socket.example = 1; + }, { + code: 'ERR_HTTP2_SOCKET_UNBOUND' + }); + assert.throws(() => { + // eslint-disable-next-line no-unused-expressions + socket instanceof net.Socket; + }, { + code: 'ERR_HTTP2_SOCKET_UNBOUND' + }); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-write-callbacks.js b/test/js/node/test/parallel/test-http2-write-callbacks.js new file mode 100644 index 0000000000..eca7f00ea7 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-write-callbacks.js @@ -0,0 +1,37 @@ +'use strict'; + +// Verifies that write callbacks are called + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + stream.write('abc', common.mustCall(() => { + stream.end('xyz'); + })); + let actual = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => actual += chunk); + stream.on('end', common.mustCall(() => assert.strictEqual(actual, 'abcxyz'))); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ ':method': 'POST' }); + req.write('abc', common.mustCall(() => { + req.end('xyz'); + })); + let actual = ''; + req.setEncoding('utf8'); + req.on('data', (chunk) => actual += chunk); + req.on('end', common.mustCall(() => assert.strictEqual(actual, 'abcxyz'))); + req.on('close', common.mustCall(() => { + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-write-empty-string.js b/test/js/node/test/parallel/test-http2-write-empty-string.js new file mode 100644 index 0000000000..ea591176a4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-write-empty-string.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(function(request, response) { + response.writeHead(200, { 'Content-Type': 'text/plain' }); + response.write('1\n'); + response.write(''); + response.write('2\n'); + response.write(''); + response.end('3\n'); + + this.close(); +}); + +server.listen(0, common.mustCall(function() { + const client = http2.connect(`http://localhost:${this.address().port}`); + const headers = { ':path': '/' }; + const req = client.request(headers).setEncoding('ascii'); + + let res = ''; + + req.on('response', common.mustCall(function(headers) { + assert.strictEqual(headers[':status'], 200); + })); + + req.on('data', (chunk) => { + res += chunk; + }); + + req.on('end', common.mustCall(function() { + assert.strictEqual(res, '1\n2\n3\n'); + client.close(); + })); + + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http2-zero-length-header.js b/test/js/node/test/parallel/test-http2-zero-length-header.js new file mode 100644 index 0000000000..2e7876858a --- /dev/null +++ b/test/js/node/test/parallel/test-http2-zero-length-header.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(); +server.on('stream', (stream, headers) => { + assert.deepStrictEqual(headers, { + ':scheme': 'http', + ':authority': `localhost:${server.address().port}`, + ':method': 'GET', + ':path': '/', + 'bar': '', + '__proto__': null, + [http2.sensitiveHeaders]: [] + }); + stream.session.destroy(); + server.close(); +}); +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}/`); + client.request({ ':path': '/', '': 'foo', 'bar': '' }).end(); +})); diff --git a/test/js/node/test/parallel/test-http2-zero-length-write.js b/test/js/node/test/parallel/test-http2-zero-length-write.js new file mode 100644 index 0000000000..0b50715330 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-zero-length-write.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const { Readable } = require('stream'); + +function getSrc() { + const chunks = [ '', 'asdf', '', 'foo', '', 'bar', '' ]; + return new Readable({ + read() { + const chunk = chunks.shift(); + if (chunk !== undefined) + this.push(chunk); + else + this.push(null); + } + }); +} + +const expect = 'asdffoobar'; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + let actual = ''; + stream.respond(); + stream.resume(); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => actual += chunk); + stream.on('end', common.mustCall(() => { + getSrc().pipe(stream); + assert.strictEqual(actual, expect); + })); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + let actual = ''; + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + req.setEncoding('utf8'); + req.on('data', (chunk) => actual += chunk); + req.on('end', common.mustCall(() => { + assert.strictEqual(actual, expect); + server.close(); + client.close(); + })); + getSrc().pipe(req); +})); diff --git a/test/js/node/test/parallel/test-https-agent-constructor.js b/test/js/node/test/parallel/test-https-agent-constructor.js new file mode 100644 index 0000000000..69156ba0f6 --- /dev/null +++ b/test/js/node/test/parallel/test-https-agent-constructor.js @@ -0,0 +1,9 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const https = require('https'); + +assert.ok(https.Agent() instanceof https.Agent); diff --git a/test/js/node/test/parallel/test-https-agent-session-eviction.js b/test/js/node/test/parallel/test-https-agent-session-eviction.js new file mode 100644 index 0000000000..da56007105 --- /dev/null +++ b/test/js/node/test/parallel/test-https-agent-session-eviction.js @@ -0,0 +1,73 @@ +// Flags: --tls-min-v1.0 +'use strict'; + +const common = require('../common'); +const { readKey } = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const https = require('https'); +const { SSL_OP_NO_TICKET } = require('crypto').constants; + +const options = { + key: readKey('agent1-key.pem'), + cert: readKey('agent1-cert.pem'), + secureOptions: SSL_OP_NO_TICKET, + ciphers: 'RSA@SECLEVEL=0' +}; + +// Create TLS1.2 server +https.createServer(options, function(req, res) { + res.writeHead(200, { 'Connection': 'close' }); + res.end('ohai'); +}).listen(0, function() { + first(this); +}); + +// Do request and let agent cache the session +function first(server) { + const port = server.address().port; + const req = https.request({ + port: port, + rejectUnauthorized: false + }, function(res) { + res.resume(); + + server.close(function() { + faultyServer(port); + }); + }); + req.end(); +} + +// Create TLS1 server +function faultyServer(port) { + options.secureProtocol = 'TLSv1_method'; + https.createServer(options, function(req, res) { + res.writeHead(200, { 'Connection': 'close' }); + res.end('hello faulty'); + }).listen(port, function() { + second(this); + }); +} + +// Attempt to request using cached session +function second(server, session) { + const req = https.request({ + port: server.address().port, + ciphers: (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + rejectUnauthorized: false + }, function(res) { + res.resume(); + }); + + // Although we have a TLS 1.2 session to offer to the TLS 1.0 server, + // connection to the TLS 1.0 server should work. + req.on('response', common.mustCall(function(res) { + // The test is now complete for OpenSSL 1.1.0. + server.close(); + })); + + req.end(); +} diff --git a/test/js/node/test/parallel/http-head-response-has-no-body-end.test.js b/test/js/node/test/parallel/test-https-agent.js similarity index 54% rename from test/js/node/test/parallel/http-head-response-has-no-body-end.test.js rename to test/js/node/test/parallel/test-https-agent.js index 5d18311eb5..ce4bc6e5bd 100644 --- a/test/js/node/test/parallel/http-head-response-has-no-body-end.test.js +++ b/test/js/node/test/parallel/test-https-agent.js @@ -1,6 +1,3 @@ -//#FILE: test-http-head-response-has-no-body-end.js -//#SHA1: 64091937f68588f23597f106fa906d27380be005 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,41 +19,54 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); -// This test is to make sure that when the HTTP server -// responds to a HEAD request with data to res.end, -// it does not send any body. +if (!common.hasCrypto) + common.skip('missing crypto'); -test("HTTP server responds to HEAD request without sending body", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end("FAIL"); // broken: sends FAIL from hot path. - }); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const https = require('https'); - await new Promise(resolve => { - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - method: "HEAD", - path: "/", - }, - res => { - const onEnd = jest.fn(); - res.on("end", onEnd); - res.resume(); +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; - res.on("end", () => { - expect(onEnd).toHaveBeenCalledTimes(1); - server.close(resolve); - }); - }, - ); - req.end(); - }); - }); + +const server = https.Server(options, (req, res) => { + res.writeHead(200); + res.end('hello world\n'); }); -//<#END_FILE: test-http-head-response-has-no-body-end.js + +let responses = 0; +const N = 4; +const M = 4; + + +server.listen(0, () => { + for (let i = 0; i < N; i++) { + setTimeout(() => { + for (let j = 0; j < M; j++) { + https.get({ + path: '/', + port: server.address().port, + rejectUnauthorized: false + }, function(res) { + res.resume(); + assert.strictEqual(res.statusCode, 200); + if (++responses === N * M) server.close(); + }).on('error', (e) => { + throw e; + }); + } + }, i); + } +}); + + +process.on('exit', () => { + assert.strictEqual(responses, N * M); +}); diff --git a/test/js/node/test/parallel/stream2-readable-legacy-drain.test.js b/test/js/node/test/parallel/test-https-client-get-url.js similarity index 54% rename from test/js/node/test/parallel/stream2-readable-legacy-drain.test.js rename to test/js/node/test/parallel/test-https-client-get-url.js index 137ee20ab6..fb91a4f1e7 100644 --- a/test/js/node/test/parallel/stream2-readable-legacy-drain.test.js +++ b/test/js/node/test/parallel/test-https-client-get-url.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-readable-legacy-drain.js -//#SHA1: 8182fd1e12ce8538106404d39102ea69eee2e467 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,47 +19,39 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const Stream = require("stream"); -const Readable = Stream.Readable; +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +if (!common.hasCrypto) + common.skip('missing crypto'); -test("Readable stream with legacy drain", done => { - const r = new Readable(); - const N = 256; - let reads = 0; - r._read = function (n) { - return r.push(++reads === N ? null : Buffer.allocUnsafe(1)); - }; +// Disable strict server certificate validation by the client +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - const onEnd = jest.fn(); - r.on("end", onEnd); +const assert = require('assert'); +const https = require('https'); +const url = require('url'); - const w = new Stream(); - w.writable = true; - let buffered = 0; - w.write = function (c) { - buffered += c.length; - process.nextTick(drain); - return false; - }; +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; - function drain() { - expect(buffered).toBeLessThanOrEqual(3); - buffered = 0; - w.emit("drain"); - } +const server = https.createServer(options, common.mustCall((req, res) => { + assert.strictEqual(req.method, 'GET'); + assert.strictEqual(req.url, '/foo?bar'); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); +}, 3)); - const endSpy = jest.fn(); - w.end = endSpy; - - r.pipe(w); - - // Wait for the 'end' event to be emitted - r.on("end", () => { - expect(onEnd).toHaveBeenCalledTimes(1); - expect(endSpy).toHaveBeenCalledTimes(1); - done(); - }); -}); - -//<#END_FILE: test-stream2-readable-legacy-drain.js +server.listen(0, common.mustCall(() => { + const u = `https://${common.localhostIPv4}:${server.address().port}/foo?bar`; + https.get(u, common.mustCall(() => { + https.get(url.parse(u), common.mustCall(() => { + https.get(new URL(u), common.mustCall(() => { + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-https-client-renegotiation-limit.js b/test/js/node/test/parallel/test-https-client-renegotiation-limit.js new file mode 100644 index 0000000000..35fcc6bfcc --- /dev/null +++ b/test/js/node/test/parallel/test-https-client-renegotiation-limit.js @@ -0,0 +1,111 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const tls = require('tls'); +const https = require('https'); +const fixtures = require('../common/fixtures'); + +// Renegotiation as a protocol feature was dropped after TLS1.2. +tls.DEFAULT_MAX_VERSION = 'TLSv1.2'; + +// Renegotiation limits to test +const LIMITS = [0, 1, 2, 3, 5, 10, 16]; + +{ + let n = 0; + function next() { + if (n >= LIMITS.length) return; + tls.CLIENT_RENEG_LIMIT = LIMITS[n++]; + test(next); + } + next(); +} + +function test(next) { + const options = { + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem'), + }; + + const server = https.createServer(options, (req, res) => { + const conn = req.connection; + conn.on('error', (err) => { + console.error(`Caught exception: ${err}`); + assert.match(err.message, /TLS session renegotiation attack/); + conn.destroy(); + }); + res.end('ok'); + }); + + server.listen(0, () => { + const agent = https.Agent({ + keepAlive: true, + }); + + let client; + let renegs = 0; + + const options = { + rejectUnauthorized: false, + agent, + }; + + const { port } = server.address(); + + https.get(`https://localhost:${port}/`, options, (res) => { + client = res.socket; + + client.on('close', (hadErr) => { + assert.strictEqual(hadErr, false); + assert.strictEqual(renegs, tls.CLIENT_RENEG_LIMIT + 1); + server.close(); + process.nextTick(next); + }); + + client.on('error', (err) => { + console.log('CLIENT ERR', err); + throw err; + }); + + spam(); + + // Simulate renegotiation attack + function spam() { + client.renegotiate({}, (err) => { + assert.ifError(err); + assert.ok(renegs <= tls.CLIENT_RENEG_LIMIT); + setImmediate(spam); + }); + renegs++; + } + }); + + }); +} diff --git a/test/js/node/test/parallel/child-process-spawnsync-env.test.js b/test/js/node/test/parallel/test-https-connecting-to-http.js similarity index 65% rename from test/js/node/test/parallel/child-process-spawnsync-env.test.js rename to test/js/node/test/parallel/test-https-connecting-to-http.js index e6368f7e38..195ad38ed4 100644 --- a/test/js/node/test/parallel/child-process-spawnsync-env.test.js +++ b/test/js/node/test/parallel/test-https-connecting-to-http.js @@ -1,6 +1,3 @@ -//#FILE: test-child-process-spawnsync-env.js -//#SHA1: 21ad31214e1261fb3c2636bd98d36946e5be67de -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,20 +19,22 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const cp = require("child_process"); +'use strict'; +// This tests the situation where you try to connect a https client +// to an http server. You should get an error and exit. +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); -if (process.argv[2] === "child") { - console.log(process.env.foo); -} else { - test("spawnSync with custom environment", () => { - const expected = "bar"; - const child = cp.spawnSync(process.execPath, [__filename, "child"], { - env: Object.assign({}, process.env, { foo: expected }), - }); +const http = require('http'); +const https = require('https'); +const server = http.createServer(common.mustNotCall()); - expect(child.stdout.toString().trim()).toBe(expected); - }); -} +server.listen(0, common.mustCall(function() { + const req = https.get({ port: this.address().port }, common.mustNotCall()); -//<#END_FILE: test-child-process-spawnsync-env.js + req.on('error', common.mustCall(function(e) { + console.log('Got expected error: ', e.message); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-https-foafssl.js b/test/js/node/test/parallel/test-https-foafssl.js new file mode 100644 index 0000000000..d6dde97a41 --- /dev/null +++ b/test/js/node/test/parallel/test-https-foafssl.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const https = require('https'); +const spawn = require('child_process').spawn; + +const options = { + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt'), + requestCert: true, + rejectUnauthorized: false +}; + +const webIdUrl = 'URI:http://example.com/#me'; +const modulus = fixtures.readKey('rsa_cert_foafssl_b.modulus', 'ascii').replace(/\n/g, ''); +const exponent = fixtures.readKey('rsa_cert_foafssl_b.exponent', 'ascii').replace(/\n/g, ''); + +const CRLF = '\r\n'; +const body = 'hello world\n'; +let cert; + +const server = https.createServer(options, common.mustCall(function(req, res) { + console.log('got request'); + + cert = req.connection.getPeerCertificate(); + + assert.strictEqual(cert.subjectaltname, webIdUrl); + assert.strictEqual(cert.exponent, exponent); + assert.strictEqual(cert.modulus, modulus); + res.writeHead(200, { 'content-type': 'text/plain' }); + res.end(body, () => { console.log('stream finished'); }); + console.log('sent response'); +})); + +server.listen(0, function() { + const args = ['s_client', + '-quiet', + '-connect', `127.0.0.1:${this.address().port}`, + '-cert', fixtures.path('keys/rsa_cert_foafssl_b.crt'), + '-key', fixtures.path('keys/rsa_private_b.pem')]; + + const client = spawn(common.opensslCli, args); + + client.stdout.on('data', function(data) { + console.log('response received'); + const message = data.toString(); + const contents = message.split(CRLF + CRLF).pop(); + assert.strictEqual(body, contents); + server.close((e) => { + assert.ifError(e); + console.log('server closed'); + }); + console.log('server.close() called'); + }); + + client.stdin.write('GET /\r\n\r\n'); + + client.on('error', function(error) { + throw error; + }); +}); diff --git a/test/js/node/test/parallel/http-pause-resume-one-end.test.js b/test/js/node/test/parallel/test-https-localaddress-bind-error.js similarity index 52% rename from test/js/node/test/parallel/http-pause-resume-one-end.test.js rename to test/js/node/test/parallel/test-https-localaddress-bind-error.js index 52198ee8a3..57e4dd054d 100644 --- a/test/js/node/test/parallel/http-pause-resume-one-end.test.js +++ b/test/js/node/test/parallel/test-https-localaddress-bind-error.js @@ -1,6 +1,3 @@ -//#FILE: test-http-pause-resume-one-end.js -//#SHA1: 69f25ca624d470d640d6366b6df27eba31668e96 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,49 +19,44 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); -test("HTTP server pause and resume", async () => { - const server = http.Server(function (req, res) { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("Hello World\n"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const opts = { - port: server.address().port, - headers: { connection: "close" }, - }; - - await new Promise(resolve => { - http.get(opts, res => { - res.on( - "data", - jest.fn().mockImplementation(() => { - res.pause(); - setImmediate(() => { - res.resume(); - }); - }), - ); - - res.on("end", () => { - expect(res.destroyed).toBe(false); - }); - - expect(res.destroyed).toBe(false); - - res.on("close", () => { - expect(res.destroyed).toBe(true); - resolve(); - }); - }); +const assert = require('assert'); +const https = require('https'); + +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const invalidLocalAddress = '1.2.3.4'; + +const server = https.createServer(options, function(req, res) { + console.log(`Connect from: ${req.connection.remoteAddress}`); + + req.on('end', function() { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(`You are from: ${req.connection.remoteAddress}`); }); + req.resume(); }); -//<#END_FILE: test-http-pause-resume-one-end.js +server.listen(0, '127.0.0.1', common.mustCall(function() { + https.request({ + host: 'localhost', + port: this.address().port, + path: '/', + method: 'GET', + localAddress: invalidLocalAddress + }, function(res) { + assert.fail('unexpectedly got response from server'); + }).on('error', common.mustCall(function(e) { + console.log(`client got error: ${e.message}`); + server.close(); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-https-selfsigned-no-keycertsign-no-crash.js b/test/js/node/test/parallel/test-https-selfsigned-no-keycertsign-no-crash.js new file mode 100644 index 0000000000..2dd46ac878 --- /dev/null +++ b/test/js/node/test/parallel/test-https-selfsigned-no-keycertsign-no-crash.js @@ -0,0 +1,63 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// This test starts an https server and tries +// to connect to it using a self-signed certificate. +// This certificate´s keyUsage does not include the keyCertSign +// bit, which used to crash node. The test ensures node +// will not crash. Key and certificate are from #37889. +// Note: This test assumes that the connection will succeed. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const crypto = require('crypto'); + +// See #37990 for details on why this is problematic with FIPS. +if (process.config.variables.openssl_is_fips) + common.skip('Skipping as test uses non-fips compliant EC curve'); + +// This test will fail for OpenSSL < 1.1.1h +const minOpenSSL = 269488271; + +if (crypto.constants.OPENSSL_VERSION_NUMBER < minOpenSSL) + common.skip('OpenSSL < 1.1.1h'); + +const https = require('https'); +const path = require('path'); + +const key = + fixtures.readKey(path.join('selfsigned-no-keycertsign', 'key.pem')); + +const cert = + fixtures.readKey(path.join('selfsigned-no-keycertsign', 'cert.pem')); + +const serverOptions = { + key: key, + cert: cert +}; + +// Start the server +const httpsServer = https.createServer(serverOptions, (req, res) => { + res.writeHead(200); + res.end('hello world\n'); +}); +httpsServer.listen(0); + +httpsServer.on('listening', () => { + // Once the server started listening, built the client config + // with the server´s used port + const clientOptions = { + hostname: '127.0.0.1', + port: httpsServer.address().port, + ca: cert + }; + // Try to connect + const req = https.request(clientOptions, common.mustCall((res) => { + httpsServer.close(); + })); + + req.on('error', common.mustNotCall()); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-https-socket-options.js b/test/js/node/test/parallel/test-https-socket-options.js new file mode 100644 index 0000000000..b41054d5aa --- /dev/null +++ b/test/js/node/test/parallel/test-https-socket-options.js @@ -0,0 +1,85 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const https = require('https'); +const http = require('http'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const body = 'hello world\n'; + +// Try first with http server + +const server_http = http.createServer(function(req, res) { + console.log('got HTTP request'); + res.writeHead(200, { 'content-type': 'text/plain' }); + res.end(body); +}); + + +server_http.listen(0, function() { + const req = http.request({ + port: this.address().port, + rejectUnauthorized: false + }, function(res) { + server_http.close(); + res.resume(); + }); + // These methods should exist on the request and get passed down to the socket + req.setNoDelay(true); + req.setTimeout(1000, () => {}); + req.setSocketKeepAlive(true, 1000); + req.end(); +}); + +// Then try https server (requires functions to be +// mirrored in tls.js's CryptoStream) + +const server_https = https.createServer(options, function(req, res) { + console.log('got HTTPS request'); + res.writeHead(200, { 'content-type': 'text/plain' }); + res.end(body); +}); + +server_https.listen(0, function() { + const req = https.request({ + port: this.address().port, + rejectUnauthorized: false + }, function(res) { + server_https.close(); + res.resume(); + }); + // These methods should exist on the request and get passed down to the socket + req.setNoDelay(true); + req.setTimeout(1000, () => {}); + req.setSocketKeepAlive(true, 1000); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-https-truncate.js b/test/js/node/test/parallel/test-https-truncate.js new file mode 100644 index 0000000000..beed36cd7c --- /dev/null +++ b/test/js/node/test/parallel/test-https-truncate.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const https = require('https'); + +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); + +// Number of bytes discovered empirically to trigger the bug +const data = Buffer.alloc(1024 * 32 + 1); + +httpsTest(); + +function httpsTest() { + const sopt = { key, cert }; + + const server = https.createServer(sopt, function(req, res) { + res.setHeader('content-length', data.length); + res.end(data); + server.close(); + }); + + server.listen(0, function() { + const opts = { port: this.address().port, rejectUnauthorized: false }; + https.get(opts).on('response', function(res) { + test(res); + }); + }); +} + + +const test = common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + assert.strictEqual(res.readableLength, 0); + assert.strictEqual(bytes, data.length); + })); + + // Pause and then resume on each chunk, to ensure that there will be + // a lone byte hanging out at the very end. + let bytes = 0; + res.on('data', function(chunk) { + bytes += chunk.length; + this.pause(); + setTimeout(() => { this.resume(); }, 1); + }); +}); diff --git a/test/js/node/test/parallel/test-https-unix-socket-self-signed.js b/test/js/node/test/parallel/test-https-unix-socket-self-signed.js new file mode 100644 index 0000000000..9db92ac2ae --- /dev/null +++ b/test/js/node/test/parallel/test-https-unix-socket-self-signed.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fixtures = require('../common/fixtures'); +const https = require('https'); +const options = { + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem') +}; + +const server = https.createServer(options, common.mustCall((req, res) => { + res.end('bye\n'); + server.close(); +})); + +server.listen(common.PIPE, common.mustCall(() => { + https.get({ + socketPath: common.PIPE, + rejectUnauthorized: false + }); +})); diff --git a/test/js/node/test/parallel/test-icu-env.js b/test/js/node/test/parallel/test-icu-env.js new file mode 100644 index 0000000000..45b9fea8db --- /dev/null +++ b/test/js/node/test/parallel/test-icu-env.js @@ -0,0 +1,288 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { execFileSync } = require('child_process'); + +// system-icu should not be tested +const hasBuiltinICU = process.config.variables.icu_gyp_path === 'tools/icu/icu-generic.gyp'; +if (!hasBuiltinICU) + common.skip('system ICU'); + +// small-icu doesn't support non-English locales +const hasFullICU = (() => { + try { + const january = new Date(9e8); + const spanish = new Intl.DateTimeFormat('es', { month: 'long' }); + return spanish.format(january) === 'enero'; + } catch { + return false; + } +})(); +if (!hasFullICU) + common.skip('small ICU'); + +const icuVersionMajor = Number(process.config.variables.icu_ver_major ?? 0); +if (icuVersionMajor < 71) + common.skip('ICU too old'); + + +function runEnvOutside(addEnv, code, ...args) { + return execFileSync( + process.execPath, + ['-e', `process.stdout.write(String(${code}));`], + { env: { ...process.env, ...addEnv }, encoding: 'utf8' } + ); +} + +function runEnvInside(addEnv, func, ...args) { + Object.assign(process.env, addEnv); // side effects! + return func(...args); +} + +function isPack(array) { + const firstItem = array[0]; + return array.every((item) => item === firstItem); +} + +function isSet(array) { + const deduped = new Set(array); + return array.length === deduped.size; +} + + +const localesISO639 = [ + 'eng', 'cmn', 'hin', 'spa', + 'fra', 'arb', 'ben', 'rus', + 'por', 'urd', 'ind', 'deu', + 'jpn', 'pcm', 'mar', 'tel', +]; + +const locales = [ + 'en', 'zh', 'hi', 'es', + 'fr', 'ar', 'bn', 'ru', + 'pt', 'ur', 'id', 'de', + 'ja', 'pcm', 'mr', 'te', +]; + +// These must not overlap +const zones = [ + 'America/New_York', + 'UTC', + 'Asia/Irkutsk', + 'Australia/North', + 'Antarctica/South_Pole', +]; + + +assert.deepStrictEqual(Intl.getCanonicalLocales(localesISO639), locales); + + +// On some platforms these keep original locale (for example, 'January') +const enero = runEnvOutside( + { LANG: 'es' }, + 'new Intl.DateTimeFormat(undefined, { month: "long" } ).format(new Date(9e8))' +); +const janvier = runEnvOutside( + { LANG: 'fr' }, + 'new Intl.DateTimeFormat(undefined, { month: "long" } ).format(new Date(9e8))' +); +const isMockable = enero !== janvier; + +// Tests with mocked env +if (isMockable) { + assert.strictEqual( + isSet(zones.map((TZ) => runEnvOutside({ TZ }, 'new Date(333333333333).toString()'))), + true + ); + assert.strictEqual( + isSet(zones.map((TZ) => runEnvOutside({ TZ }, 'new Date(333333333333).toLocaleString()'))), + true + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG, TZ: 'Europe/Zurich' }, 'new Date(333333333333).toString()')), + [ + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Central European Standard Time)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (中欧标准时间)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (मध्य यूरोपीय मानक समय)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (hora estándar de Europa central)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (heure normale d’Europe centrale)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (توقيت وسط أوروبا الرسمي)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (মধ্য ইউরোপীয় মানক সময়)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Центральная Европа, стандартное время)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Horário Padrão da Europa Central)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (وسطی یورپ کا معیاری وقت)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Waktu Standar Eropa Tengah)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Mitteleuropäische Normalzeit)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (中央ヨーロッパ標準時)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Mídúl Yúrop Fíksd Taim)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (मध्‍य युरोपियन प्रमाण वेळ)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (సెంట్రల్ యూరోపియన్ ప్రామాణిక సమయం)', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG, TZ: 'Europe/Zurich' }, 'new Date(333333333333).toLocaleString()')), + [ + '7/25/1980, 1:35:33 AM', + '1980/7/25 01:35:33', + '25/7/1980, 1:35:33 am', + '25/7/1980, 1:35:33', + '25/07/1980 01:35:33', + '٢٥‏/٧‏/١٩٨٠، ١:٣٥:٣٣ ص', + '২৫/৭/১৯৮০, ১:৩৫:৩৩ AM', + '25.07.1980, 01:35:33', + '25/07/1980, 01:35:33', + '25/7/1980، 1:35:33 AM', + '25/7/1980, 01.35.33', + '25.7.1980, 01:35:33', + '1980/7/25 1:35:33', + '25/7/1980 01:35:33', + '२५/७/१९८०, १:३५:३३ AM', + '25/7/1980 1:35:33 AM', + ] + ); + assert.strictEqual( + runEnvOutside({ LANG: 'en' }, '["z", "ä"].sort(new Intl.Collator().compare)'), + 'ä,z' + ); + assert.strictEqual( + runEnvOutside({ LANG: 'sv' }, '["z", "ä"].sort(new Intl.Collator().compare)'), + 'z,ä' + ); + assert.deepStrictEqual( + locales.map( + (LANG) => runEnvOutside({ LANG, TZ: 'Europe/Zurich' }, 'new Intl.DateTimeFormat().format(333333333333)') + ), + [ + '7/25/1980', '1980/7/25', + '25/7/1980', '25/7/1980', + '25/07/1980', '٢٥‏/٧‏/١٩٨٠', + '২৫/৭/১৯৮০', '25.07.1980', + '25/07/1980', '25/7/1980', + '25/7/1980', '25.7.1980', + '1980/7/25', '25/7/1980', + '२५/७/१९८०', '25/7/1980', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.DisplayNames(undefined, { type: "region" }).of("CH")')), + [ + 'Switzerland', '瑞士', + 'स्विट्ज़रलैंड', 'Suiza', + 'Suisse', 'سويسرا', + 'সুইজারল্যান্ড', 'Швейцария', + 'Suíça', 'سوئٹزر لینڈ', + 'Swiss', 'Schweiz', + 'スイス', 'Swítsaland', + 'स्वित्झर्लंड', 'స్విట్జర్లాండ్', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.NumberFormat().format(275760.913)')), + [ + '275,760.913', '275,760.913', + '2,75,760.913', '275.760,913', + '275 760,913', '٢٧٥٬٧٦٠٫٩١٣', + '২,৭৫,৭৬০.৯১৩', '275 760,913', + '275.760,913', '275,760.913', + '275.760,913', '275.760,913', + '275,760.913', '275,760.913', + '२,७५,७६०.९१३', '2,75,760.913', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.PluralRules().select(0)')), + [ + 'other', 'other', 'one', 'other', + 'one', 'zero', 'one', 'many', + 'one', 'other', 'other', 'other', + 'other', 'one', 'other', 'other', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.RelativeTimeFormat().format(-586920.617, "hour")')), + [ + '586,920.617 hours ago', + '586,920.617小时前', + '5,86,920.617 घंटे पहले', + 'hace 586.920,617 horas', + 'il y a 586 920,617 heures', + 'قبل ٥٨٦٬٩٢٠٫٦١٧ ساعة', + '৫,৮৬,৯২০.৬১৭ ঘন্টা আগে', + '586 920,617 часа назад', + 'há 586.920,617 horas', + '586,920.617 گھنٹے پہلے', + '586.920,617 jam yang lalu', + 'vor 586.920,617 Stunden', + '586,920.617 時間前', + '586,920.617 áwa wé dọ́n pas', + '५,८६,९२०.६१७ तासांपूर्वी', + '5,86,920.617 గంటల క్రితం', + ] + ); +} + + +// Tests with process.env mutated inside +{ + // process.env.TZ is not intercepted in Workers + if (common.isMainThread) { + assert.strictEqual( + isSet(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toString()))), + true + ); + assert.strictEqual( + isSet(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toLocaleString()))), + true + ); + } else { + assert.strictEqual( + isPack(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toString()))), + true + ); + assert.strictEqual( + isPack(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toLocaleString()))), + true + ); + } + + assert.strictEqual( + isPack(locales.map((LANG) => runEnvInside({ LANG, TZ: 'Europe/Zurich' }, () => new Date(333333333333).toString()))), + true + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG, TZ: 'Europe/Zurich' }, () => new Date(333333333333).toLocaleString()) + )), + true + ); + assert.deepStrictEqual( + runEnvInside({ LANG: 'en' }, () => ['z', 'ä'].sort(new Intl.Collator().compare)), + runEnvInside({ LANG: 'sv' }, () => ['z', 'ä'].sort(new Intl.Collator().compare)) + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG, TZ: 'Europe/Zurich' }, () => new Intl.DateTimeFormat().format(333333333333)) + )), + true + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG }, () => new Intl.DisplayNames(undefined, { type: 'region' }).of('CH')) + )), + true + ); + assert.strictEqual( + isPack(locales.map((LANG) => runEnvInside({ LANG }, () => new Intl.NumberFormat().format(275760.913)))), + true + ); + assert.strictEqual( + isPack(locales.map((LANG) => runEnvInside({ LANG }, () => new Intl.PluralRules().select(0)))), + true + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG }, () => new Intl.RelativeTimeFormat().format(-586920.617, 'hour')) + )), + true + ); +} diff --git a/test/js/node/test/parallel/test-icu-punycode.js b/test/js/node/test/parallel/test-icu-punycode.js new file mode 100644 index 0000000000..29e88f9b9a --- /dev/null +++ b/test/js/node/test/parallel/test-icu-punycode.js @@ -0,0 +1,57 @@ +'use strict'; +// Flags: --expose-internals +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const { internalBinding } = require('internal/test/binding'); +const icu = internalBinding('icu'); +const assert = require('assert'); + +// Test hasConverter method +assert(icu.hasConverter('utf-8'), + 'hasConverter should report converter exists for utf-8'); +assert(!icu.hasConverter('x'), + 'hasConverter should report converter does not exist for x'); + +const tests = require('../fixtures/url-idna.js'); +const fixtures = require('../fixtures/icu-punycode-toascii.json'); + +{ + for (const [i, { ascii, unicode }] of tests.entries()) { + assert.strictEqual(ascii, icu.toASCII(unicode), `toASCII(${i + 1})`); + assert.strictEqual(unicode, icu.toUnicode(ascii), `toUnicode(${i + 1})`); + assert.strictEqual(ascii, icu.toASCII(icu.toUnicode(ascii)), + `toASCII(toUnicode(${i + 1}))`); + assert.strictEqual(unicode, icu.toUnicode(icu.toASCII(unicode)), + `toUnicode(toASCII(${i + 1}))`); + } +} + +{ + for (const [i, test] of fixtures.entries()) { + if (typeof test === 'string') + continue; // skip comments + const { comment, input, output } = test; + let caseComment = `case ${i + 1}`; + if (comment) + caseComment += ` (${comment})`; + if (output === null) { + assert.throws( + () => icu.toASCII(input), + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: 'Cannot convert name to ASCII' + } + ); + icu.toASCII(input, true); // Should not throw. + } else { + assert.strictEqual(icu.toASCII(input), output, `ToASCII ${caseComment}`); + assert.strictEqual(icu.toASCII(input, true), output, + `ToASCII ${caseComment} in lenient mode`); + } + icu.toUnicode(input); // Should not throw. + } +} diff --git a/test/js/node/test/parallel/test-icu-transcode.js b/test/js/node/test/parallel/test-icu-transcode.js new file mode 100644 index 0000000000..e9aced128e --- /dev/null +++ b/test/js/node/test/parallel/test-icu-transcode.js @@ -0,0 +1,90 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const buffer = require('buffer'); +const assert = require('assert'); +const orig = Buffer.from('těst ☕', 'utf8'); + +// Test Transcoding +const tests = { + 'latin1': [0x74, 0x3f, 0x73, 0x74, 0x20, 0x3f], + 'ascii': [0x74, 0x3f, 0x73, 0x74, 0x20, 0x3f], + 'ucs2': [0x74, 0x00, 0x1b, 0x01, 0x73, + 0x00, 0x74, 0x00, 0x20, 0x00, + 0x15, 0x26] +}; + +for (const test in tests) { + const dest = buffer.transcode(orig, 'utf8', test); + assert.strictEqual(dest.length, tests[test].length, `utf8->${test} length`); + for (let n = 0; n < tests[test].length; n++) + assert.strictEqual(dest[n], tests[test][n], `utf8->${test} char ${n}`); +} + +{ + const dest = buffer.transcode(Buffer.from(tests.ucs2), 'ucs2', 'utf8'); + assert.strictEqual(dest.toString(), orig.toString()); +} + +{ + const utf8 = Buffer.from('€'.repeat(4000), 'utf8'); + const ucs2 = Buffer.from('€'.repeat(4000), 'ucs2'); + const utf8_to_ucs2 = buffer.transcode(utf8, 'utf8', 'ucs2'); + const ucs2_to_utf8 = buffer.transcode(ucs2, 'ucs2', 'utf8'); + assert.deepStrictEqual(utf8, ucs2_to_utf8); + assert.deepStrictEqual(ucs2, utf8_to_ucs2); + assert.strictEqual(ucs2_to_utf8.toString('utf8'), + utf8_to_ucs2.toString('ucs2')); +} + +assert.throws( + () => buffer.transcode(null, 'utf8', 'ascii'), + { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "source" argument must be an instance of Buffer ' + + 'or Uint8Array. Received null' + } +); + +assert.throws( + () => buffer.transcode(Buffer.from('a'), 'b', 'utf8'), + /^Error: Unable to transcode Buffer \[U_ILLEGAL_ARGUMENT_ERROR\]/ +); + +assert.throws( + () => buffer.transcode(Buffer.from('a'), 'uf8', 'b'), + /^Error: Unable to transcode Buffer \[U_ILLEGAL_ARGUMENT_ERROR\]$/ +); + +assert.deepStrictEqual( + buffer.transcode(Buffer.from('hi', 'ascii'), 'ascii', 'utf16le'), + Buffer.from('hi', 'utf16le')); +assert.deepStrictEqual( + buffer.transcode(Buffer.from('hi', 'latin1'), 'latin1', 'utf16le'), + Buffer.from('hi', 'utf16le')); +assert.deepStrictEqual( + buffer.transcode(Buffer.from('hä', 'latin1'), 'latin1', 'utf16le'), + Buffer.from('hä', 'utf16le')); + +// Test that Uint8Array arguments are okay. +{ + const uint8array = new Uint8Array([...Buffer.from('hä', 'latin1')]); + assert.deepStrictEqual( + buffer.transcode(uint8array, 'latin1', 'utf16le'), + Buffer.from('hä', 'utf16le')); +} + +{ + const dest = buffer.transcode(new Uint8Array(), 'utf8', 'latin1'); + assert.strictEqual(dest.length, 0); +} + +// Test that it doesn't crash +{ + buffer.transcode(new buffer.SlowBuffer(1), 'utf16le', 'ucs2'); +} diff --git a/test/js/node/test/parallel/test-inspect-support-for-node_options.js b/test/js/node/test/parallel/test-inspect-support-for-node_options.js new file mode 100644 index 0000000000..05bb3b2c42 --- /dev/null +++ b/test/js/node/test/parallel/test-inspect-support-for-node_options.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +common.skipIfInspectorDisabled(); + +checkForInspectSupport('--inspect'); + +function checkForInspectSupport(flag) { + + const nodeOptions = JSON.stringify(flag); + const numWorkers = 2; + process.env.NODE_OPTIONS = flag; + + if (cluster.isPrimary) { + for (let i = 0; i < numWorkers; i++) { + cluster.fork(); + } + + cluster.on('online', (worker) => { + worker.disconnect(); + }); + + cluster.on('exit', common.mustCall((worker, code, signal) => { + const errMsg = `For NODE_OPTIONS ${nodeOptions}, failed to start cluster`; + assert.strictEqual(worker.exitedAfterDisconnect, true, errMsg); + }, numWorkers)); + } +} diff --git a/test/js/node/test/parallel/test-inspector-has-inspector-false.js b/test/js/node/test/parallel/test-inspector-has-inspector-false.js new file mode 100644 index 0000000000..56a50408bb --- /dev/null +++ b/test/js/node/test/parallel/test-inspector-has-inspector-false.js @@ -0,0 +1,15 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); + +if (process.features.inspector) { + common.skip('V8 inspector is enabled'); +} + +const inspector = require('internal/util/inspector'); + +inspector.sendInspectorCommand( + common.mustNotCall('Inspector callback should not be called'), + common.mustCall(1), +); diff --git a/test/js/node/test/parallel/test-inspector-stops-no-file.js b/test/js/node/test/parallel/test-inspector-stops-no-file.js new file mode 100644 index 0000000000..9ec09fb15d --- /dev/null +++ b/test/js/node/test/parallel/test-inspector-stops-no-file.js @@ -0,0 +1,16 @@ +'use strict'; +require('../common'); + +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, + [ '--inspect', 'no-such-script.js' ], + { 'stdio': 'inherit' }); + +function signalHandler() { + child.kill(); + process.exit(1); +} + +process.on('SIGINT', signalHandler); +process.on('SIGTERM', signalHandler); diff --git a/test/js/node/test/parallel/test-instanceof.js b/test/js/node/test/parallel/test-instanceof.js new file mode 100644 index 0000000000..5a8b588e7d --- /dev/null +++ b/test/js/node/test/parallel/test-instanceof.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + + +// Regression test for instanceof, see +// https://github.com/nodejs/node/issues/7592 +const F = () => {}; +F.prototype = {}; +assert({ __proto__: F.prototype } instanceof F); diff --git a/test/js/node/test/parallel/test-internal-module-require.js b/test/js/node/test/parallel/test-internal-module-require.js new file mode 100644 index 0000000000..c6e2057d3d --- /dev/null +++ b/test/js/node/test/parallel/test-internal-module-require.js @@ -0,0 +1,112 @@ +'use strict'; + +// Flags: --expose-internals +// This verifies that +// 1. We do not leak internal modules unless the --require-internals option +// is on. +// 2. We do not accidentally leak any modules to the public global scope. +// 3. Deprecated modules are properly deprecated. + +const common = require('../common'); + +if (!common.isMainThread) { + common.skip('Cannot test the existence of --expose-internals from worker'); +} + +const assert = require('assert'); +const fork = require('child_process').fork; + +const expectedPublicModules = new Set([ + '_http_agent', + '_http_client', + '_http_common', + '_http_incoming', + '_http_outgoing', + '_http_server', + '_stream_duplex', + '_stream_passthrough', + '_stream_readable', + '_stream_transform', + '_stream_wrap', + '_stream_writable', + '_tls_common', + '_tls_wrap', + 'assert', + 'async_hooks', + 'buffer', + 'child_process', + 'cluster', + 'console', + 'constants', + 'crypto', + 'dgram', + 'dns', + 'domain', + 'events', + 'fs', + 'http', + 'http2', + 'https', + 'inspector', + 'module', + 'net', + 'os', + 'path', + 'perf_hooks', + 'process', + 'punycode', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'sys', + 'timers', + 'tls', + 'trace_events', + 'tty', + 'url', + 'util', + 'v8', + 'vm', + 'worker_threads', + 'zlib', +]); + +if (process.argv[2] === 'child') { + assert(!process.execArgv.includes('--expose-internals')); + process.once('message', ({ allBuiltins }) => { + const publicModules = new Set(); + for (const id of allBuiltins) { + if (id.startsWith('internal/')) { + assert.throws(() => { + require(id); + }, { + code: 'MODULE_NOT_FOUND', + message: `Cannot find module '${id}'` + }); + } else { + require(id); + publicModules.add(id); + } + } + assert(allBuiltins.length > publicModules.size); + // Make sure all the public modules are available through + // require('module').builtinModules + assert.deepStrictEqual( + publicModules, + new Set(require('module').builtinModules) + ); + assert.deepStrictEqual(publicModules, expectedPublicModules); + }); +} else { + assert(process.execArgv.includes('--expose-internals')); + const child = fork(__filename, ['child'], { + execArgv: [] + }); + const { builtinModules } = require('module'); + // When --expose-internals is on, require('module').builtinModules + // contains internal modules. + const message = { allBuiltins: builtinModules }; + child.send(message); +} diff --git a/test/js/node/test/parallel/test-internal-process-binding.js b/test/js/node/test/parallel/test-internal-process-binding.js new file mode 100644 index 0000000000..09e3f31096 --- /dev/null +++ b/test/js/node/test/parallel/test-internal-process-binding.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(undefined, process._internalBinding); +assert.strictEqual(undefined, process.internalBinding); +assert.throws(() => { + process.binding('module_wrap'); +}, /No such module/); diff --git a/test/js/node/test/parallel/test-intl-v8BreakIterator.js b/test/js/node/test/parallel/test-intl-v8BreakIterator.js new file mode 100644 index 0000000000..257d6b2a76 --- /dev/null +++ b/test/js/node/test/parallel/test-intl-v8BreakIterator.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +assert(!('v8BreakIterator' in Intl)); +assert(!vm.runInNewContext('"v8BreakIterator" in Intl')); diff --git a/test/js/node/test/parallel/test-intl.js b/test/js/node/test/parallel/test-intl.js new file mode 100644 index 0000000000..7d1742f2c7 --- /dev/null +++ b/test/js/node/test/parallel/test-intl.js @@ -0,0 +1,163 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { execFile } = require('child_process'); + +// Does node think that i18n was enabled? +let enablei18n = process.config.variables.v8_enable_i18n_support; +if (enablei18n === undefined) { + enablei18n = 0; +} + +// Returns true if no specific locale ids were configured (i.e. "all") +// Else, returns true if loc is in the configured list +// Else, returns false +function haveLocale(loc) { + const locs = process.config.variables.icu_locales.split(','); + return locs.includes(loc); +} + +// Always run these. They should always pass, even if the locale +// param is ignored. +assert.strictEqual('Ç'.toLocaleLowerCase('el'), 'ç'); +assert.strictEqual('Ç'.toLocaleLowerCase('tr'), 'ç'); +assert.strictEqual('Ç'.toLowerCase(), 'ç'); + +assert.strictEqual('ç'.toLocaleUpperCase('el'), 'Ç'); +assert.strictEqual('ç'.toLocaleUpperCase('tr'), 'Ç'); +assert.strictEqual('ç'.toUpperCase(), 'Ç'); + +if (!common.hasIntl) { + const erMsg = + `"Intl" object is NOT present but v8_enable_i18n_support is ${enablei18n}`; + assert.strictEqual(enablei18n, 0, erMsg); + common.skip('Intl tests because Intl object not present.'); +} else { + const erMsg = + `"Intl" object is present but v8_enable_i18n_support is ${ + enablei18n}. Is this test out of date?`; + assert.strictEqual(enablei18n, 1, erMsg); + + // Construct a new date at the beginning of Unix time + const date0 = new Date(0); + + // Use the GMT time zone + const GMT = 'Etc/GMT'; + + // Construct an English formatter. Should format to "Jan 70" + const dtf = new Intl.DateTimeFormat(['en'], { + timeZone: GMT, + month: 'short', + year: '2-digit' + }); + + // If list is specified and doesn't contain 'en' then return. + if (process.config.variables.icu_locales && !haveLocale('en')) { + common.printSkipMessage( + 'detailed Intl tests because English is not listed as supported.'); + // Smoke test. Does it format anything, or fail? + console.log(`Date(0) formatted to: ${dtf.format(date0)}`); + return; + } + + // Check casing + { + assert.strictEqual('I'.toLocaleLowerCase('tr'), 'ı'); + } + + // Check with toLocaleString + { + const localeString = dtf.format(date0); + assert.strictEqual(localeString, 'Jan 70'); + } + // Options to request GMT + const optsGMT = { timeZone: GMT }; + + // Test format + { + const localeString = date0.toLocaleString(['en'], optsGMT); + assert.strictEqual(localeString, '1/1/1970, 12:00:00 AM'); + } + // number format + { + const numberFormat = new Intl.NumberFormat(['en']).format(12345.67890); + assert.strictEqual(numberFormat, '12,345.679'); + } + // If list is specified and doesn't contain 'en-US' then return. + if (process.config.variables.icu_locales && !haveLocale('en-US')) { + common.printSkipMessage('detailed Intl tests because American English is ' + + 'not listed as supported.'); + return; + } + // Number format resolved options + { + const numberFormat = new Intl.NumberFormat('en-US', { style: 'percent' }); + const resolvedOptions = numberFormat.resolvedOptions(); + assert.strictEqual(resolvedOptions.locale, 'en-US'); + assert.strictEqual(resolvedOptions.style, 'percent'); + } + // Significant Digits + { + const loc = ['en-US']; + const opts = { maximumSignificantDigits: 4 }; + const num = 10.001; + const numberFormat = new Intl.NumberFormat(loc, opts).format(num); + assert.strictEqual(numberFormat, '10'); + } + + const collOpts = { sensitivity: 'base', ignorePunctuation: true }; + const coll = new Intl.Collator(['en'], collOpts); + + // Ignore punctuation + assert.strictEqual(coll.compare('blackbird', 'black-bird'), 0); + // Compare less + assert.strictEqual(coll.compare('blackbird', 'red-bird'), -1); + // Compare greater + assert.strictEqual(coll.compare('bluebird', 'blackbird'), 1); + // Ignore case + assert.strictEqual(coll.compare('Bluebird', 'bluebird'), 0); + // `ffi` ligature (contraction) + assert.strictEqual(coll.compare('\ufb03', 'ffi'), 0); + + { + // Regression test for https://github.com/nodejs/node/issues/27379 + const env = { ...process.env, LC_ALL: 'ja' }; + execFile( + process.execPath, ['-p', 'new Date().toLocaleString()'], + { env }, + common.mustSucceed() + ); + } + + { + // Regression test for https://github.com/nodejs/node/issues/27418 + const env = { ...process.env, LC_ALL: 'fr@EURO' }; + execFile( + process.execPath, + ['-p', 'new Intl.NumberFormat().resolvedOptions().locale'], + { env }, + common.mustSucceed() + ); + } +} diff --git a/test/js/node/test/parallel/test-kill-segfault-freebsd.js b/test/js/node/test/parallel/test-kill-segfault-freebsd.js new file mode 100644 index 0000000000..e17b00741b --- /dev/null +++ b/test/js/node/test/parallel/test-kill-segfault-freebsd.js @@ -0,0 +1,19 @@ +'use strict'; +require('../common'); + +// This test ensures Node.js doesn't crash on hitting Ctrl+C in order to +// terminate the currently running process (especially on FreeBSD). +// https://github.com/nodejs/node-v0.x-archive/issues/9326 + +const assert = require('assert'); +const child_process = require('child_process'); + +// NOTE: Was crashing on FreeBSD +const cp = child_process.spawn(process.execPath, [ + '-e', + 'process.kill(process.pid, "SIGINT")', +]); + +cp.on('exit', function(code) { + assert.notStrictEqual(code, 0); +}); diff --git a/test/js/node/test/parallel/test-listen-fd-detached-inherit.js b/test/js/node/test/parallel/test-listen-fd-detached-inherit.js new file mode 100644 index 0000000000..2a8e70f0f9 --- /dev/null +++ b/test/js/node/test/parallel/test-listen-fd-detached-inherit.js @@ -0,0 +1,118 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('This test is disabled on windows.'); + +const assert = require('assert'); +const http = require('http'); +const net = require('net'); +const spawn = require('child_process').spawn; + +switch (process.argv[2]) { + case 'child': return child(); + case 'parent': return parent(); + default: return test(); +} + +// Spawn the parent, and listen for it to tell us the pid of the child. +// WARNING: This is an example of listening on some arbitrary FD number +// that has already been bound elsewhere in advance. However, binding +// server handles to stdio fd's is NOT a good or reliable way to do +// concurrency in HTTP servers! Use the cluster module, or if you want +// a more low-level approach, use child process IPC manually. +function test() { + const parent = spawn(process.execPath, [__filename, 'parent'], { + stdio: [ 0, 'pipe', 2 ] + }); + let json = ''; + parent.stdout.on('data', function(c) { + json += c.toString(); + if (json.includes('\n')) next(); + }); + function next() { + console.error('output from parent = %s', json); + const child = JSON.parse(json); + // Now make sure that we can request to the subprocess, then kill it. + http.get({ + server: 'localhost', + port: child.port, + path: '/', + }).on('response', function(res) { + let s = ''; + res.on('data', function(c) { + s += c.toString(); + }); + res.on('end', function() { + // Kill the subprocess before we start doing asserts. + // It's really annoying when tests leave orphans! + process.kill(child.pid, 'SIGKILL'); + try { + parent.kill(); + } catch { + // Continue regardless of error. + } + + assert.strictEqual(s, 'hello from child\n'); + assert.strictEqual(res.statusCode, 200); + }); + }); + } +} + +// Listen on port, and then pass the handle to the detached child. +// Then output the child's pid, and immediately exit. +function parent() { + const server = net.createServer(function(conn) { + conn.end('HTTP/1.1 403 Forbidden\r\n\r\nI got problems.\r\n'); + throw new Error('Should not see connections on parent'); + }).listen(0, function() { + console.error('server listening on %d', this.address().port); + + const child = spawn(process.execPath, [__filename, 'child'], { + stdio: [ 0, 1, 2, server._handle ], + detached: true + }); + + console.log('%j\n', { pid: child.pid, port: this.address().port }); + + // Now close the parent, so that the child is the only thing + // referencing that handle. Note that connections will still + // be accepted, because the child has the fd open, but the parent + // will exit gracefully. + server.close(); + child.unref(); + }); +} + +// Run as a child of the parent() mode. +function child() { + // Start a server on fd=3 + http.createServer(function(req, res) { + console.error('request on child'); + console.error('%s %s', req.method, req.url, req.headers); + res.end('hello from child\n'); + }).listen({ fd: 3 }, function() { + console.error('child listening on fd=3'); + }); +} diff --git a/test/js/node/test/parallel/test-listen-fd-detached.js b/test/js/node/test/parallel/test-listen-fd-detached.js new file mode 100644 index 0000000000..fba96a112f --- /dev/null +++ b/test/js/node/test/parallel/test-listen-fd-detached.js @@ -0,0 +1,115 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('This test is disabled on windows.'); + +const assert = require('assert'); +const http = require('http'); +const net = require('net'); +const spawn = require('child_process').spawn; + +switch (process.argv[2]) { + case 'child': return child(); + case 'parent': return parent(); + default: return test(); +} + +// Spawn the parent, and listen for it to tell us the pid of the child. +// WARNING: This is an example of listening on some arbitrary FD number +// that has already been bound elsewhere in advance. However, binding +// server handles to stdio fd's is NOT a good or reliable way to do +// concurrency in HTTP servers! Use the cluster module, or if you want +// a more low-level approach, use child process IPC manually. +function test() { + const parent = spawn(process.execPath, [__filename, 'parent'], { + stdio: [ 0, 'pipe', 2 ] + }); + let json = ''; + parent.stdout.on('data', function(c) { + json += c.toString(); + if (json.includes('\n')) next(); + }); + function next() { + console.error('output from parent = %s', json); + const child = JSON.parse(json); + // Now make sure that we can request to the subprocess, then kill it. + http.get({ + server: 'localhost', + port: child.port, + path: '/', + }).on('response', function(res) { + let s = ''; + res.on('data', function(c) { + s += c.toString(); + }); + res.on('end', function() { + // Kill the subprocess before we start doing asserts. + // it's really annoying when tests leave orphans! + process.kill(child.pid, 'SIGKILL'); + try { + parent.kill(); + } catch { + // Continue regardless of error. + } + + assert.strictEqual(s, 'hello from child\n'); + assert.strictEqual(res.statusCode, 200); + }); + }); + } +} + +function parent() { + const server = net.createServer(function(conn) { + console.error('connection on parent'); + conn.end('hello from parent\n'); + }).listen(0, function() { + console.error('server listening on %d', this.address().port); + + const child = spawn(process.execPath, [__filename, 'child'], { + stdio: [ 'ignore', 'ignore', 'ignore', server._handle ], + detached: true + }); + + console.log('%j\n', { pid: child.pid, port: this.address().port }); + + // Now close the parent, so that the child is the only thing + // referencing that handle. Note that connections will still + // be accepted, because the child has the fd open, but the parent + // will exit gracefully. + server.close(); + child.unref(); + }); +} + +function child() { + // Start a server on fd=3 + http.createServer(function(req, res) { + console.error('request on child'); + console.error('%s %s', req.method, req.url, req.headers); + res.end('hello from child\n'); + }).listen({ fd: 3 }, function() { + console.error('child listening on fd=3'); + }); +} diff --git a/test/js/node/test/parallel/test-memory-usage-emfile.js b/test/js/node/test/parallel/test-memory-usage-emfile.js new file mode 100644 index 0000000000..05b112e918 --- /dev/null +++ b/test/js/node/test/parallel/test-memory-usage-emfile.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); + +// On IBMi, the rss memory always returns zero +if (common.isIBMi) + common.skip('On IBMi, the rss memory always returns zero'); + +const assert = require('assert'); + +const fs = require('fs'); + +const files = []; + +while (files.length < 256) + files.push(fs.openSync(__filename, 'r')); + +const r = process.memoryUsage.rss(); +assert.strictEqual(r > 0, true); diff --git a/test/js/node/test/parallel/test-memory-usage.js b/test/js/node/test/parallel/test-memory-usage.js new file mode 100644 index 0000000000..8e5ea4de5b --- /dev/null +++ b/test/js/node/test/parallel/test-memory-usage.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --predictable-gc-schedule +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const r = process.memoryUsage(); +// On IBMi, the rss memory always returns zero +if (!common.isIBMi) { + assert.ok(r.rss > 0); + assert.ok(process.memoryUsage.rss() > 0); +} + +assert.ok(r.heapTotal > 0); +assert.ok(r.heapUsed > 0); +assert.ok(r.external > 0); + +assert.strictEqual(typeof r.arrayBuffers, 'number'); +if (r.arrayBuffers > 0) { + const size = 10 * 1024 * 1024; + // eslint-disable-next-line no-unused-vars + const ab = new ArrayBuffer(size); + + const after = process.memoryUsage(); + assert.ok(after.external - r.external >= size, + `${after.external} - ${r.external} >= ${size}`); + assert.strictEqual(after.arrayBuffers - r.arrayBuffers, size, + `${after.arrayBuffers} - ${r.arrayBuffers} === ${size}`); +} diff --git a/test/js/node/test/parallel/test-messagechannel.js b/test/js/node/test/parallel/test-messagechannel.js new file mode 100644 index 0000000000..4f92924daa --- /dev/null +++ b/test/js/node/test/parallel/test-messagechannel.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); + +// See: https://github.com/nodejs/node/issues/49940 +(async () => { + new MessageChannel().port1.postMessage({}, { + transfer: { + *[Symbol.iterator]() {} + } + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-messageevent-brandcheck.js b/test/js/node/test/parallel/test-messageevent-brandcheck.js new file mode 100644 index 0000000000..17f2b708cc --- /dev/null +++ b/test/js/node/test/parallel/test-messageevent-brandcheck.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +[ + 'data', + 'origin', + 'lastEventId', + 'source', + 'ports', +].forEach((i) => { + assert.throws(() => Reflect.get(MessageEvent.prototype, i, {}), TypeError); +}); diff --git a/test/js/node/test/parallel/http-client-abort2.test.js b/test/js/node/test/parallel/test-microtask-queue-integration.js similarity index 62% rename from test/js/node/test/parallel/http-client-abort2.test.js rename to test/js/node/test/parallel/test-microtask-queue-integration.js index 416be11173..69d55253a2 100644 --- a/test/js/node/test/parallel/http-client-abort2.test.js +++ b/test/js/node/test/parallel/test-microtask-queue-integration.js @@ -1,6 +1,3 @@ -//#FILE: test-http-client-abort2.js -//#SHA1: 9accf5214e90cab96d06a59931e65718616b85f3 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,34 +19,45 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +require('../common'); +const assert = require('assert'); -test("http client abort", async () => { - const serverHandler = jest.fn((req, res) => { - res.end("Hello"); - }); +const implementations = [ + function(fn) { + Promise.resolve().then(fn); + }, +]; - const server = http.createServer(serverHandler); +let expected = 0; +let done = 0; - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const options = { port: server.address().port }; - - const req = http.get(options, res => { - res.on("data", data => { - req.abort(); - server.close(); - }); - }); - - await new Promise(resolve => { - server.on("close", resolve); - }); - - expect(serverHandler).toHaveBeenCalledTimes(1); +process.on('exit', function() { + assert.strictEqual(done, expected); }); -//<#END_FILE: test-http-client-abort2.js +function test(scheduleMicrotask) { + let nextTickCalled = false; + expected++; + + scheduleMicrotask(function() { + process.nextTick(function() { + nextTickCalled = true; + }); + + setTimeout(function() { + assert(nextTickCalled); + done++; + }, 0); + }); +} + +// first tick case +implementations.forEach(test); + +// tick callback case +setTimeout(function() { + implementations.forEach(function(impl) { + process.nextTick(test.bind(null, impl)); + }); +}, 0); diff --git a/test/js/node/test/parallel/dgram-listen-after-bind.test.js b/test/js/node/test/parallel/test-microtask-queue-run-immediate.js similarity index 67% rename from test/js/node/test/parallel/dgram-listen-after-bind.test.js rename to test/js/node/test/parallel/test-microtask-queue-run-immediate.js index fd8faa3fe8..577391993b 100644 --- a/test/js/node/test/parallel/dgram-listen-after-bind.test.js +++ b/test/js/node/test/parallel/test-microtask-queue-run-immediate.js @@ -1,6 +1,3 @@ -//#FILE: test-dgram-listen-after-bind.js -//#SHA1: c1a91f2b83b502dd1abc4b46f023df6677fdf465 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,29 +19,41 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const dgram = require("dgram"); +'use strict'; +require('../common'); +const assert = require('assert'); -test("dgram listen after bind", done => { - const socket = dgram.createSocket("udp4"); +function enqueueMicrotask(fn) { + Promise.resolve().then(fn); +} - socket.bind(); +let done = 0; - let fired = false; - const timer = setTimeout(() => { - socket.close(); - }, 100); +process.on('exit', function() { + assert.strictEqual(done, 2); +}); - socket.on("listening", () => { - clearTimeout(timer); - fired = true; - socket.close(); - }); - - socket.on("close", () => { - expect(fired).toBe(true); - done(); +// No nextTick, microtask +setImmediate(function() { + enqueueMicrotask(function() { + done++; }); }); -//<#END_FILE: test-dgram-listen-after-bind.js + +// No nextTick, microtask with nextTick +setImmediate(function() { + let called = false; + + enqueueMicrotask(function() { + process.nextTick(function() { + called = true; + }); + }); + + setImmediate(function() { + if (called) + done++; + }); + +}); diff --git a/test/js/node/test/parallel/dgram-bytes-length.test.js b/test/js/node/test/parallel/test-microtask-queue-run.js similarity index 66% rename from test/js/node/test/parallel/dgram-bytes-length.test.js rename to test/js/node/test/parallel/test-microtask-queue-run.js index 1c1bd1e219..5281cb4f3c 100644 --- a/test/js/node/test/parallel/dgram-bytes-length.test.js +++ b/test/js/node/test/parallel/test-microtask-queue-run.js @@ -1,6 +1,3 @@ -//#FILE: test-dgram-bytes-length.js -//#SHA1: f899cc14c13e8c913645e204819cf99b867aec5c -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,21 +19,41 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const dgram = require("dgram"); +'use strict'; +require('../common'); +const assert = require('assert'); -test("dgram bytes length", async () => { - const message = Buffer.from("Some bytes"); - const client = dgram.createSocket("udp4"); +function enqueueMicrotask(fn) { + Promise.resolve().then(fn); +} - await new Promise((resolve, reject) => { - client.send(message, 0, message.length, 41234, "localhost", function (err, bytes) { - if (err) reject(err); - expect(bytes).toBe(message.length); - client.close(); - resolve(); - }); - }); +let done = 0; + +process.on('exit', function() { + assert.strictEqual(done, 2); }); -//<#END_FILE: test-dgram-bytes-length.js +// No nextTick, microtask +setTimeout(function() { + enqueueMicrotask(function() { + done++; + }); +}, 0); + + +// No nextTick, microtask with nextTick +setTimeout(function() { + let called = false; + + enqueueMicrotask(function() { + process.nextTick(function() { + called = true; + }); + }); + + setTimeout(function() { + if (called) + done++; + }, 0); + +}, 0); diff --git a/test/js/node/test/parallel/test-module-builtin.js b/test/js/node/test/parallel/test-module-builtin.js new file mode 100644 index 0000000000..3897d71ecf --- /dev/null +++ b/test/js/node/test/parallel/test-module-builtin.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { builtinModules } = require('module'); + +// Includes modules in lib/ (even deprecated ones) +assert(builtinModules.includes('http')); +assert(builtinModules.includes('sys')); + +// Does not include internal modules +assert.deepStrictEqual( + builtinModules.filter((mod) => mod.startsWith('internal/')), + [] +); diff --git a/test/js/node/test/parallel/test-module-cache.js b/test/js/node/test/parallel/test-module-cache.js new file mode 100644 index 0000000000..87913c72cc --- /dev/null +++ b/test/js/node/test/parallel/test-module-cache.js @@ -0,0 +1,18 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filePath = tmpdir.resolve('test-module-cache.json'); +assert.throws( + () => require(filePath), + { code: 'MODULE_NOT_FOUND' } +); + +fs.writeFileSync(filePath, '[]'); + +const content = require(filePath); +assert.strictEqual(Array.isArray(content), true); +assert.strictEqual(content.length, 0); diff --git a/test/js/node/test/parallel/test-module-circular-symlinks.js b/test/js/node/test/parallel/test-module-circular-symlinks.js new file mode 100644 index 0000000000..e8d80640df --- /dev/null +++ b/test/js/node/test/parallel/test-module-circular-symlinks.js @@ -0,0 +1,68 @@ +'use strict'; + +// This tests to make sure that modules with symlinked circular dependencies +// do not blow out the module cache and recurse forever. See issue +// https://github.com/nodejs/node/pull/5950 for context. PR #5950 attempted +// to solve a problem with symlinked peer dependencies by caching using the +// symlink path. Unfortunately, that breaks the case tested in this module +// because each symlinked module, despite pointing to the same code on disk, +// is loaded and cached as a separate module instance, which blows up the +// cache and leads to a recursion bug. + +// This test should pass in Node.js v4 and v5. It should pass in Node.js v6 +// after https://github.com/nodejs/node/pull/5950 has been reverted. + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +// {tmpDir} +// ├── index.js +// └── node_modules +// ├── moduleA +// │ ├── index.js +// │ └── node_modules +// │ └── moduleB -> {tmpDir}/node_modules/moduleB +// └── moduleB +// ├── index.js +// └── node_modules +// └── moduleA -> {tmpDir}/node_modules/moduleA + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const tmpDir = tmpdir.path; + +const node_modules = path.join(tmpDir, 'node_modules'); +const moduleA = path.join(node_modules, 'moduleA'); +const moduleB = path.join(node_modules, 'moduleB'); +const moduleA_link = path.join(moduleB, 'node_modules', 'moduleA'); +const moduleB_link = path.join(moduleA, 'node_modules', 'moduleB'); + +fs.mkdirSync(node_modules); +fs.mkdirSync(moduleA); +fs.mkdirSync(moduleB); +fs.mkdirSync(path.join(moduleA, 'node_modules')); +fs.mkdirSync(path.join(moduleB, 'node_modules')); + +try { + fs.symlinkSync(moduleA, moduleA_link); + fs.symlinkSync(moduleB, moduleB_link); +} catch (err) { + if (err.code !== 'EPERM') throw err; + common.skip('insufficient privileges for symlinks'); +} + +fs.writeFileSync(path.join(tmpDir, 'index.js'), + 'module.exports = require(\'moduleA\');', 'utf8'); +fs.writeFileSync(path.join(moduleA, 'index.js'), + 'module.exports = {b: require(\'moduleB\')};', 'utf8'); +fs.writeFileSync(path.join(moduleB, 'index.js'), + 'module.exports = {a: require(\'moduleA\')};', 'utf8'); + +// Ensure that the symlinks are not followed forever... +const obj = require(path.join(tmpDir, 'index')); +assert.ok(obj); +assert.ok(obj.b); +assert.ok(obj.b.a); +assert.ok(!obj.b.a.b); diff --git a/test/js/node/test/parallel/test-module-main-extension-lookup.js b/test/js/node/test/parallel/test-module-main-extension-lookup.js new file mode 100644 index 0000000000..58d78e09b1 --- /dev/null +++ b/test/js/node/test/parallel/test-module-main-extension-lookup.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const { execFileSync } = require('child_process'); + +const node = process.argv[0]; + +execFileSync(node, [fixtures.path('es-modules', 'test-esm-ok.mjs')]); +execFileSync(node, [fixtures.path('es-modules', 'noext')]); diff --git a/test/js/node/test/parallel/test-module-readonly.js b/test/js/node/test/parallel/test-module-readonly.js new file mode 100644 index 0000000000..ad9fbf7d21 --- /dev/null +++ b/test/js/node/test/parallel/test-module-readonly.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); + +if (!common.isWindows) { + // TODO: Similar checks on *nix-like systems (e.g using chmod or the like) + common.skip('test only runs on Windows'); +} + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const cp = require('child_process'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Create readOnlyMod.js and set to read only +const readOnlyMod = tmpdir.resolve('readOnlyMod'); +const readOnlyModRelative = path.relative(__dirname, readOnlyMod); +const readOnlyModFullPath = `${readOnlyMod}.js`; + +fs.writeFileSync(readOnlyModFullPath, 'module.exports = 42;'); + +// Removed any inherited ACEs, and any explicitly granted ACEs for the +// current user +cp.execSync( + `icacls.exe "${readOnlyModFullPath}" /inheritance:r /remove "%USERNAME%"`); + +// Grant the current user read & execute only +cp.execSync(`icacls.exe "${readOnlyModFullPath}" /grant "%USERNAME%":RX`); + +let except = null; +try { + // Attempt to load the module. Will fail if write access is required + require(readOnlyModRelative); +} catch (err) { + except = err; +} + +// Remove the explicitly granted rights, and re-enable inheritance +cp.execSync( + `icacls.exe "${readOnlyModFullPath}" /remove "%USERNAME%" /inheritance:e`); + +// Delete the test module (note: tmpdir should get cleaned anyway) +fs.unlinkSync(readOnlyModFullPath); + +assert.ifError(except); diff --git a/test/js/node/test/parallel/test-module-relative-lookup.js b/test/js/node/test/parallel/test-module-relative-lookup.js new file mode 100644 index 0000000000..1bd505392c --- /dev/null +++ b/test/js/node/test/parallel/test-module-relative-lookup.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const _module = require('module'); // Avoid collision with global.module + +// Current directory gets highest priority for local modules +function testFirstInPath(moduleName, isLocalModule) { + const assertFunction = isLocalModule ? + assert.strictEqual : + assert.notStrictEqual; + + let paths = _module._resolveLookupPaths(moduleName); + + assertFunction(paths[0], '.'); + + paths = _module._resolveLookupPaths(moduleName, null); + assertFunction(paths && paths[0], '.'); +} + +testFirstInPath('./lodash', true); + +// Relative path on Windows, but a regular file name elsewhere +testFirstInPath('.\\lodash', common.isWindows); diff --git a/test/js/node/test/parallel/test-net-autoselectfamily-attempt-timeout-default-value.js b/test/js/node/test/parallel/test-net-autoselectfamily-attempt-timeout-default-value.js new file mode 100644 index 0000000000..7822769527 --- /dev/null +++ b/test/js/node/test/parallel/test-net-autoselectfamily-attempt-timeout-default-value.js @@ -0,0 +1,8 @@ +'use strict'; + +const { platformTimeout } = require('../common'); + +const assert = require('assert'); +const { getDefaultAutoSelectFamilyAttemptTimeout } = require('net'); + +assert.strictEqual(getDefaultAutoSelectFamilyAttemptTimeout(), platformTimeout(2500)); diff --git a/test/js/node/test/parallel/test-net-bind-twice.js b/test/js/node/test/parallel/test-net-bind-twice.js new file mode 100644 index 0000000000..f59818a1e8 --- /dev/null +++ b/test/js/node/test/parallel/test-net-bind-twice.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(common.mustNotCall()); +server1.listen(0, '127.0.0.1', common.mustCall(function() { + const server2 = net.createServer(common.mustNotCall()); + server2.listen(this.address().port, '127.0.0.1', common.mustNotCall()); + + server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + server1.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-buffersize.js b/test/js/node/test/parallel/test-net-buffersize.js new file mode 100644 index 0000000000..7225d70af3 --- /dev/null +++ b/test/js/node/test/parallel/test-net-buffersize.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const iter = 10; + +const server = net.createServer(function(socket) { + socket.on('readable', function() { + socket.read(); + }); + + socket.on('end', function() { + server.close(); + }); +}); + +server.listen(0, common.mustCall(function() { + const client = net.connect(this.address().port); + + client.on('finish', common.mustCall(() => { + assert.strictEqual(client.bufferSize, 0); + })); + + for (let i = 1; i < iter; i++) { + client.write('a'); + assert.strictEqual(client.bufferSize, i); + } + + client.end(); +})); diff --git a/test/js/node/test/parallel/test-net-connect-call-socket-connect.js b/test/js/node/test/parallel/test-net-connect-call-socket-connect.js new file mode 100644 index 0000000000..88551889fe --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-call-socket-connect.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); + +// This test checks that calling `net.connect` internally calls +// `Socket.prototype.connect`. +// +// This is important for people who monkey-patch `Socket.prototype.connect` +// since it's not possible to monkey-patch `net.connect` directly (as the core +// `connect` function is called internally in Node instead of calling the +// `exports.connect` function). +// +// Monkey-patching of `Socket.prototype.connect` is done by - among others - +// most APM vendors, the async-listener module and the +// continuation-local-storage module. +// +// Related: +// - https://github.com/nodejs/node/pull/12342 +// - https://github.com/nodejs/node/pull/12852 + +const net = require('net'); +const Socket = net.Socket; + +// Monkey patch Socket.prototype.connect to check that it's called. +const orig = Socket.prototype.connect; +Socket.prototype.connect = common.mustCall(function() { + return orig.apply(this, arguments); +}); + +const server = net.createServer(); + +server.listen(common.mustCall(function() { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(function() { + client.end(); + })); + client.on('end', common.mustCall(function() { + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-connect-destroy.js b/test/js/node/test/parallel/test-net-connect-destroy.js new file mode 100644 index 0000000000..73fdb988f9 --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-destroy.js @@ -0,0 +1,7 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +const socket = new net.Socket(); +socket.on('close', common.mustCall()); +socket.destroy(); diff --git a/test/js/node/test/parallel/test-net-connect-immediate-destroy.js b/test/js/node/test/parallel/test-net-connect-immediate-destroy.js new file mode 100644 index 0000000000..3ca58c356b --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-immediate-destroy.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0); +const port = server.address().port; +const socket = net.connect(port, common.localhostIPv4, common.mustNotCall()); +socket.on('error', common.mustNotCall()); +server.close(); +socket.destroy(); diff --git a/test/js/node/test/parallel/test-net-connect-options-path.js b/test/js/node/test/parallel/test-net-connect-options-path.js new file mode 100644 index 0000000000..61de8caab1 --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-options-path.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +// This file tests the option handling of net.connect, +// net.createConnect, and new Socket().connect + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const CLIENT_VARIANTS = 12; + +// Test connect(path) +{ + const prefix = `${common.PIPE}-net-connect-options-path`; + const serverPath = `${prefix}-server`; + let counter = 0; + const server = net.createServer() + .on('connection', common.mustCall(function(socket) { + socket.end('ok'); + }, CLIENT_VARIANTS)) + .listen(serverPath, common.mustCall(function() { + const getConnectCb = () => common.mustCall(function() { + this.end(); + this.on('close', common.mustCall(function() { + counter++; + if (counter === CLIENT_VARIANTS) { + server.close(); + } + })); + }); + + // CLIENT_VARIANTS depends on the following code + net.connect(serverPath, getConnectCb()).resume(); + net.connect(serverPath) + .on('connect', getConnectCb()) + .resume(); + net.createConnection(serverPath, getConnectCb()).resume(); + net.createConnection(serverPath) + .on('connect', getConnectCb()) + .resume(); + new net.Socket().connect(serverPath, getConnectCb()).resume(); + new net.Socket().connect(serverPath) + .on('connect', getConnectCb()) + .resume(); + net.connect({ path: serverPath }, getConnectCb()).resume(); + net.connect({ path: serverPath }) + .on('connect', getConnectCb()) + .resume(); + net.createConnection({ path: serverPath }, getConnectCb()).resume(); + net.createConnection({ path: serverPath }) + .on('connect', getConnectCb()) + .resume(); + new net.Socket().connect({ path: serverPath }, getConnectCb()).resume(); + new net.Socket().connect({ path: serverPath }) + .on('connect', getConnectCb()) + .resume(); + })); +} diff --git a/test/js/node/test/parallel/test-net-dns-lookup-skip.js b/test/js/node/test/parallel/test-net-dns-lookup-skip.js new file mode 100644 index 0000000000..06dbd5932b --- /dev/null +++ b/test/js/node/test/parallel/test-net-dns-lookup-skip.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +function check(addressType) { + const server = net.createServer(function(client) { + client.end(); + server.close(); + }); + + const address = addressType === 4 ? '127.0.0.1' : '::1'; + server.listen(0, address, function() { + net.connect(this.address().port, address) + .on('lookup', common.mustNotCall()); + }); +} + +check(4); +common.hasIPv6 && check(6); diff --git a/test/js/node/test/parallel/test-net-during-close.js b/test/js/node/test/parallel/test-net-during-close.js new file mode 100644 index 0000000000..3670ed9c27 --- /dev/null +++ b/test/js/node/test/parallel/test-net-during-close.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + /* eslint-disable no-unused-expressions */ + const client = net.createConnection(this.address().port); + server.close(); + // Server connection event has not yet fired client is still attempting to + // connect. Accessing properties should not throw in this case. + client.remoteAddress; + client.remoteFamily; + client.remotePort; + // Exit now, do not wait for the client error event. + process.exit(0); + /* eslint-enable no-unused-expressions */ +})); diff --git a/test/js/node/test/parallel/test-net-end-without-connect.js b/test/js/node/test/parallel/test-net-end-without-connect.js new file mode 100644 index 0000000000..45d0b5477e --- /dev/null +++ b/test/js/node/test/parallel/test-net-end-without-connect.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const sock = new net.Socket(); +sock.end(common.mustCall(() => { + assert.strictEqual(sock.writable, false); +})); diff --git a/test/js/node/test/parallel/test-net-isip.js b/test/js/node/test/parallel/test-net-isip.js new file mode 100644 index 0000000000..840ffe76af --- /dev/null +++ b/test/js/node/test/parallel/test-net-isip.js @@ -0,0 +1,96 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +assert.strictEqual(net.isIP('127.0.0.1'), 4); +assert.strictEqual(net.isIP('x127.0.0.1'), 0); +assert.strictEqual(net.isIP('example.com'), 0); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000::0000'), + 0); +assert.strictEqual(net.isIP('1050:0:0:0:5:600:300c:326b'), 6); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001::'), 6); +assert.strictEqual(net.isIP('2001:dead::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::'), 6); +assert.strictEqual(net.isIP('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 6); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6'), 0); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP('2001:252::1::2008:6'), 0); +assert.strictEqual(net.isIP('::2001:252:1:2008:6'), 6); +assert.strictEqual(net.isIP('::2001:252:1:1.1.1.1'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255.76'), 0); +assert.strictEqual(net.isIP('fe80::2008%eth0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0.0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0@1'), 0); +assert.strictEqual(net.isIP('::anything'), 0); +assert.strictEqual(net.isIP('::1'), 6); +assert.strictEqual(net.isIP('::'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:12345:0000'), 0); +assert.strictEqual(net.isIP('0'), 0); +assert.strictEqual(net.isIP(), 0); +assert.strictEqual(net.isIP(''), 0); +assert.strictEqual(net.isIP(null), 0); +assert.strictEqual(net.isIP(123), 0); +assert.strictEqual(net.isIP(true), 0); +assert.strictEqual(net.isIP({}), 0); +assert.strictEqual(net.isIP({ toString: () => '::2001:252:1:255.255.255.255' }), + 6); +assert.strictEqual(net.isIP({ toString: () => '127.0.0.1' }), 4); +assert.strictEqual(net.isIP({ toString: () => 'bla' }), 0); + +assert.strictEqual(net.isIPv4('127.0.0.1'), true); +assert.strictEqual(net.isIPv4('example.com'), false); +assert.strictEqual(net.isIPv4('2001:252:0:1::2008:6'), false); +assert.strictEqual(net.isIPv4(), false); +assert.strictEqual(net.isIPv4(''), false); +assert.strictEqual(net.isIPv4(null), false); +assert.strictEqual(net.isIPv4(123), false); +assert.strictEqual(net.isIPv4(true), false); +assert.strictEqual(net.isIPv4({}), false); +assert.strictEqual(net.isIPv4({ + toString: () => '::2001:252:1:255.255.255.255' +}), false); +assert.strictEqual(net.isIPv4({ toString: () => '127.0.0.1' }), true); +assert.strictEqual(net.isIPv4({ toString: () => 'bla' }), false); + +assert.strictEqual(net.isIPv6('127.0.0.1'), false); +assert.strictEqual(net.isIPv6('example.com'), false); +assert.strictEqual(net.isIPv6('2001:252:0:1::2008:6'), true); +assert.strictEqual(net.isIPv6(), false); +assert.strictEqual(net.isIPv6(''), false); +assert.strictEqual(net.isIPv6(null), false); +assert.strictEqual(net.isIPv6(123), false); +assert.strictEqual(net.isIPv6(true), false); +assert.strictEqual(net.isIPv6({}), false); +assert.strictEqual(net.isIPv6({ + toString: () => '::2001:252:1:255.255.255.255' +}), true); +assert.strictEqual(net.isIPv6({ toString: () => '127.0.0.1' }), false); +assert.strictEqual(net.isIPv6({ toString: () => 'bla' }), false); diff --git a/test/js/node/test/parallel/test-net-isipv4.js b/test/js/node/test/parallel/test-net-isipv4.js new file mode 100644 index 0000000000..2c478e6ac6 --- /dev/null +++ b/test/js/node/test/parallel/test-net-isipv4.js @@ -0,0 +1,46 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v4 = [ + '0.0.0.0', + '8.8.8.8', + '127.0.0.1', + '100.100.100.100', + '192.168.0.1', + '18.101.25.153', + '123.23.34.2', + '172.26.168.134', + '212.58.241.131', + '128.0.0.0', + '23.71.254.72', + '223.255.255.255', + '192.0.2.235', + '99.198.122.146', + '46.51.197.88', + '173.194.34.134', +]; + +const v4not = [ + '.100.100.100.100', + '100..100.100.100.', + '100.100.100.100.', + '999.999.999.999', + '256.256.256.256', + '256.100.100.100.100', + '123.123.123', + 'http://123.123.123', + '1000.2.3.4', + '999.2.3.4', + '0000000192.168.0.200', + '192.168.0.2000000000', +]; + +for (const ip of v4) { + assert.strictEqual(net.isIPv4(ip), true); +} + +for (const ip of v4not) { + assert.strictEqual(net.isIPv4(ip), false); +} diff --git a/test/js/node/test/parallel/test-net-isipv6.js b/test/js/node/test/parallel/test-net-isipv6.js new file mode 100644 index 0000000000..dbb8d80b7b --- /dev/null +++ b/test/js/node/test/parallel/test-net-isipv6.js @@ -0,0 +1,244 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v6 = [ + '::', + '1::', + '::1', + '1::8', + '1::7:8', + '1:2:3:4:5:6:7:8', + '1:2:3:4:5:6::8', + '1:2:3:4:5:6:7::', + '1:2:3:4:5::7:8', + '1:2:3:4:5::8', + '1:2:3::8', + '1::4:5:6:7:8', + '1::6:7:8', + '1::3:4:5:6:7:8', + '1:2:3:4::6:7:8', + '1:2::4:5:6:7:8', + '::2:3:4:5:6:7:8', + '1:2::8', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876', + '3ffe:0b00:0000:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0000', + '::ffff:192.168.1.26', + '2::10', + 'ff02::1', + 'fe80::', + '2002::', + '2001:db8::', + '2001:0db8:1234::', + '::ffff:0:0', + '::ffff:192.168.1.1', + '1:2:3:4::8', + '1::2:3:4:5:6:7', + '1::2:3:4:5:6', + '1::2:3:4:5', + '1::2:3:4', + '1::2:3', + '::2:3:4:5:6:7', + '::2:3:4:5:6', + '::2:3:4:5', + '::2:3:4', + '::2:3', + '::8', + '1:2:3:4:5:6::', + '1:2:3:4:5::', + '1:2:3:4::', + '1:2:3::', + '1:2::', + '1:2:3:4::7:8', + '1:2:3::7:8', + '1:2::7:8', + '1:2:3:4:5:6:1.2.3.4', + '1:2:3:4:5::1.2.3.4', + '1:2:3:4::1.2.3.4', + '1:2:3::1.2.3.4', + '1:2::1.2.3.4', + '1::1.2.3.4', + '1:2:3:4::5:1.2.3.4', + '1:2:3::5:1.2.3.4', + '1:2::5:1.2.3.4', + '1::5:1.2.3.4', + '1::5:11.22.33.44', + 'fe80::217:f2ff:254.7.237.98', + 'fe80::217:f2ff:fe07:ed62', + '2001:DB8:0:0:8:800:200C:417A', + 'FF01:0:0:0:0:0:0:101', + '0:0:0:0:0:0:0:1', + '0:0:0:0:0:0:0:0', + '2001:DB8::8:800:200C:417A', + 'FF01::101', + '0:0:0:0:0:0:13.1.68.3', + '0:0:0:0:0:FFFF:129.144.52.38', + '::13.1.68.3', + '::FFFF:129.144.52.38', + 'fe80:0000:0000:0000:0204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:fe9d:f156', + 'fe80::204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:254.157.241.86', + 'fe80::204:61ff:254.157.241.86', + 'fe80::1', + '2001:0db8:85a3:0000:0000:8a2e:0370:7334', + '2001:db8:85a3:0:0:8a2e:370:7334', + '2001:db8:85a3::8a2e:370:7334', + '2001:0db8:0000:0000:0000:0000:1428:57ab', + '2001:0db8:0000:0000:0000::1428:57ab', + '2001:0db8:0:0:0:0:1428:57ab', + '2001:0db8:0:0::1428:57ab', + '2001:0db8::1428:57ab', + '2001:db8::1428:57ab', + '::ffff:12.34.56.78', + '::ffff:0c22:384e', + '2001:0db8:1234:0000:0000:0000:0000:0000', + '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', + '2001:db8:a::123', + '::ffff:192.0.2.128', + '::ffff:c000:280', + 'a:b:c:d:e:f:f1:f2', + 'a:b:c::d:e:f:f1', + 'a:b:c::d:e:f', + 'a:b:c::d:e', + 'a:b:c::d', + '::a', + '::a:b:c', + '::a:b:c:d:e:f:f1', + 'a::', + 'a:b:c::', + 'a:b:c:d:e:f:f1::', + 'a:bb:ccc:dddd:000e:00f:0f::', + '0:a:0:a:0:0:0:a', + '0:a:0:0:a:0:0:a', + '2001:db8:1:1:1:1:0:0', + '2001:db8:1:1:1:0:0:0', + '2001:db8:1:1:0:0:0:0', + '2001:db8:1:0:0:0:0:0', + '2001:db8:0:0:0:0:0:0', + '2001:0:0:0:0:0:0:0', + 'A:BB:CCC:DDDD:000E:00F:0F::', + '0:0:0:0:0:0:0:a', + '0:0:0:0:a:0:0:0', + '0:0:0:a:0:0:0:0', + 'a:0:0:a:0:0:a:a', + 'a:0:0:a:0:0:0:a', + 'a:0:0:0:a:0:0:a', + 'a:0:0:0:a:0:0:0', + 'a:0:0:0:0:0:0:0', + 'fe80::7:8%eth0', + 'fe80::7:8%1', +]; + +const v6not = [ + '', + '1:', + ':1', + '11:36:12', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', + '2001:0000:1234:0000:00001:C1C0:ABCD:0876', + '2001:0000:1234: 0000:0000:C1C0:ABCD:0876', + '2001:1:1:1:1:1:255Z255X255Y255', + '3ffe:0b00:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0000:0001', + '3ffe:b00::1::a', + '::1111:2222:3333:4444:5555:6666::', + '1:2:3::4:5::7:8', + '12345::6:7:8', + '1::5:400.2.3.4', + '1::5:260.2.3.4', + '1::5:256.2.3.4', + '1::5:1.256.3.4', + '1::5:1.2.256.4', + '1::5:1.2.3.256', + '1::5:300.2.3.4', + '1::5:1.300.3.4', + '1::5:1.2.300.4', + '1::5:1.2.3.300', + '1::5:900.2.3.4', + '1::5:1.900.3.4', + '1::5:1.2.900.4', + '1::5:1.2.3.900', + '1::5:300.300.300.300', + '1::5:3000.30.30.30', + '1::400.2.3.4', + '1::260.2.3.4', + '1::256.2.3.4', + '1::1.256.3.4', + '1::1.2.256.4', + '1::1.2.3.256', + '1::300.2.3.4', + '1::1.300.3.4', + '1::1.2.300.4', + '1::1.2.3.300', + '1::900.2.3.4', + '1::1.900.3.4', + '1::1.2.900.4', + '1::1.2.3.900', + '1::300.300.300.300', + '1::3000.30.30.30', + '::400.2.3.4', + '::260.2.3.4', + '::256.2.3.4', + '::1.256.3.4', + '::1.2.256.4', + '::1.2.3.256', + '::300.2.3.4', + '::1.300.3.4', + '::1.2.300.4', + '::1.2.3.300', + '::900.2.3.4', + '::1.900.3.4', + '::1.2.900.4', + '::1.2.3.900', + '::300.300.300.300', + '::3000.30.30.30', + '2001:DB8:0:0:8:800:200C:417A:221', + 'FF01::101::2', + '1111:2222:3333:4444::5555:', + '1111:2222:3333::5555:', + '1111:2222::5555:', + '1111::5555:', + '::5555:', + ':::', + '1111:', + ':', + ':1111:2222:3333:4444::5555', + ':1111:2222:3333::5555', + ':1111:2222::5555', + ':1111::5555', + ':::5555', + '1.2.3.4:1111:2222:3333:4444::5555', + '1.2.3.4:1111:2222:3333::5555', + '1.2.3.4:1111:2222::5555', + '1.2.3.4:1111::5555', + '1.2.3.4::5555', + '1.2.3.4::', + 'fe80:0000:0000:0000:0204:61ff:254.157.241.086', + '123', + 'ldkfj', + '2001::FFD3::57ab', + '2001:db8:85a3::8a2e:37023:7334', + '2001:db8:85a3::8a2e:370k:7334', + '1:2:3:4:5:6:7:8:9', + '1::2::3', + '1:::3:4:5', + '1:2:3::4:5:6:7:8:9', + '::ffff:2.3.4', + '::ffff:257.1.2.3', + '::ffff:12345678901234567890.1.26', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', +]; + +for (const ip of v6) { + assert.strictEqual(net.isIPv6(ip), true); +} + +for (const ip of v6not) { + assert.strictEqual(net.isIPv6(ip), false); +} diff --git a/test/js/node/test/parallel/test-net-listen-after-destroying-stdin.js b/test/js/node/test/parallel/test-net-listen-after-destroying-stdin.js new file mode 100644 index 0000000000..4ffec304be --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-after-destroying-stdin.js @@ -0,0 +1,22 @@ +'use strict'; +// Just test that destroying stdin doesn't mess up listening on a server. +// This is a regression test for +// https://github.com/nodejs/node-v0.x-archive/issues/746. + +const common = require('../common'); +const net = require('net'); + +process.stdin.destroy(); + +const server = net.createServer(common.mustCall((socket) => { + console.log('accepted...'); + socket.end(common.mustCall(() => { console.log('finished...'); })); + server.close(common.mustCall(() => { console.log('closed'); })); +})); + + +server.listen(0, common.mustCall(() => { + console.log('listening...'); + + net.createConnection(server.address().port); +})); diff --git a/test/js/node/test/parallel/test-net-listen-error.js b/test/js/node/test/parallel/test-net-listen-error.js new file mode 100644 index 0000000000..05ca799d3e --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-error.js @@ -0,0 +1,29 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { +}); +server.listen(1, '1.1.1.1', common.mustNotCall()); // EACCES or EADDRNOTAVAIL +server.on('error', common.mustCall()); diff --git a/test/js/node/test/parallel/test-net-listen-exclusive-random-ports.js b/test/js/node/test/parallel/test-net-listen-exclusive-random-ports.js new file mode 100644 index 0000000000..66dfb59820 --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-exclusive-random-ports.js @@ -0,0 +1,37 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + const worker1 = cluster.fork(); + + worker1.on('message', function(port1) { + assert.strictEqual(port1, port1 | 0, + `first worker could not listen on port ${port1}`); + const worker2 = cluster.fork(); + + worker2.on('message', function(port2) { + assert.strictEqual(port2, port2 | 0, + `second worker could not listen on port ${port2}`); + assert.notStrictEqual(port1, port2, 'ports should not be equal'); + worker1.kill(); + worker2.kill(); + }); + }); +} else { + const server = net.createServer(() => {}); + + server.on('error', function(err) { + process.send(err.code); + }); + + server.listen({ + port: 0, + exclusive: true + }, function() { + process.send(server.address().port); + }); +} diff --git a/test/js/node/test/parallel/test-net-listen-handle-in-cluster-1.js b/test/js/node/test/parallel/test-net-listen-handle-in-cluster-1.js new file mode 100644 index 0000000000..07e002bf2a --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-handle-in-cluster-1.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const cluster = require('cluster'); + +// Test if the worker can listen with handle successfully +if (cluster.isPrimary) { + const worker = cluster.fork(); + const server = net.createServer(); + worker.on('online', common.mustCall(() => { + server.listen(common.mustCall(() => { + // Send the server to worker + worker.send(null, server); + })); + })); + worker.on('exit', common.mustCall(() => { + server.close(); + })); +} else { + // The `got` function of net.Server will create a TCP server by listen(handle) + // See lib/internal/child_process.js + process.on('message', common.mustCall((_, server) => { + assert.strictEqual(server instanceof net.Server, true); + process.exit(0); + })); +} diff --git a/test/js/node/test/parallel/test-net-listening.js b/test/js/node/test/parallel/test-net-listening.js new file mode 100644 index 0000000000..8f2880b0bf --- /dev/null +++ b/test/js/node/test/parallel/test-net-listening.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +assert.strictEqual(server.listening, false); + +server.listen(0, common.mustCall(() => { + assert.strictEqual(server.listening, true); + + server.close(common.mustCall(() => { + assert.strictEqual(server.listening, false); + })); +})); diff --git a/test/js/node/test/parallel/http-request-end-twice.test.js b/test/js/node/test/parallel/test-net-local-address-port.js similarity index 63% rename from test/js/node/test/parallel/http-request-end-twice.test.js rename to test/js/node/test/parallel/test-net-local-address-port.js index b1d2c8a209..cfc6f61ef3 100644 --- a/test/js/node/test/parallel/http-request-end-twice.test.js +++ b/test/js/node/test/parallel/test-net-local-address-port.js @@ -1,6 +1,3 @@ -//#FILE: test-http-request-end-twice.js -//#SHA1: c8c502b3bf8a681a7acb9afa603a13cebaf1d00e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,26 +19,25 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); -test("http request end twice", async () => { - const server = http.Server((req, res) => { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("hello world\n"); +const server = net.createServer(common.mustCall(function(socket) { + assert.strictEqual(socket.localAddress, common.localhostIPv4); + assert.strictEqual(socket.localPort, this.address().port); + assert.strictEqual(socket.localFamily, this.address().family); + socket.on('end', function() { + server.close(); }); + socket.resume(); +})); - await new Promise(resolve => { - server.listen(0, () => { - const req = http.get({ port: server.address().port }, res => { - res.on("end", () => { - expect(req.end()).toBe(req); - server.close(resolve); - }); - res.resume(); - }); - }); +server.listen(0, common.localhostIPv4, function() { + const client = net.createConnection(this.address() + .port, common.localhostIPv4); + client.on('connect', function() { + client.end(); }); }); - -//<#END_FILE: test-http-request-end-twice.js diff --git a/test/js/node/test/parallel/test-net-remote-address.js b/test/js/node/test/parallel/test-net-remote-address.js new file mode 100644 index 0000000000..a116cb99d3 --- /dev/null +++ b/test/js/node/test/parallel/test-net-remote-address.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const net = require('net'); +const { strictEqual } = require('assert'); + +const server = net.createServer(); + +server.listen(common.mustCall(function() { + const socket = net.connect({ port: server.address().port }); + + strictEqual(socket.connecting, true); + strictEqual(socket.remoteAddress, undefined); + + socket.on('connect', common.mustCall(function() { + strictEqual(socket.remoteAddress !== undefined, true); + socket.end(); + })); + + socket.on('end', common.mustCall(function() { + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-server-close-before-ipc-response.js b/test/js/node/test/parallel/test-net-server-close-before-ipc-response.js new file mode 100644 index 0000000000..e85bc96ee6 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-close-before-ipc-response.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const net = require('net'); +const cluster = require('cluster'); + +// Process should exit +if (cluster.isPrimary) { + cluster.fork(); +} else { + const send = process.send; + process.send = function(message) { + // listenOnPrimaryHandle in net.js should call handle.close() + if (message.act === 'close') { + setImmediate(() => { + process.disconnect(); + }); + } + return send.apply(this, arguments); + }; + net.createServer().listen(0, common.mustNotCall()).close(); +} diff --git a/test/js/node/test/parallel/test-net-server-close.js b/test/js/node/test/parallel/test-net-server-close.js new file mode 100644 index 0000000000..8291f70432 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-close.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const sockets = []; + +const server = net.createServer(function(c) { + c.on('close', common.mustCall()); + + sockets.push(c); + + if (sockets.length === 2) { + assert.strictEqual(server.close(), server); + sockets.forEach((c) => c.destroy()); + } +}); + +server.on('close', common.mustCall()); + +assert.strictEqual(server, server.listen(0, () => { + net.createConnection(server.address().port); + net.createConnection(server.address().port); +})); diff --git a/test/js/node/test/parallel/test-net-server-listen-remove-callback.js b/test/js/node/test/parallel/test-net-server-listen-remove-callback.js new file mode 100644 index 0000000000..a874099fb8 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-listen-remove-callback.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Server should only fire listen callback once +const server = net.createServer(); + +server.on('close', function() { + const listeners = server.listeners('listening'); + console.log('Closed, listeners:', listeners.length); + assert.strictEqual(listeners.length, 0); +}); + +server.listen(0, function() { + server.close(); +}); + +server.once('close', function() { + server.listen(0, function() { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-net-server-unref.js b/test/js/node/test/parallel/test-net-server-unref.js new file mode 100644 index 0000000000..935ba5d639 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-unref.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const s = net.createServer(); +s.listen(0); +s.unref(); + +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/test/js/node/test/parallel/test-net-socket-byteswritten.js b/test/js/node/test/parallel/test-net-socket-byteswritten.js new file mode 100644 index 0000000000..b7b7af89e2 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-byteswritten.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + const socket = net.connect(server.address().port); + + // Cork the socket, then write twice; this should cause a writev, which + // previously caused an err in the bytesWritten count. + socket.cork(); + + socket.write('one'); + socket.write(Buffer.from('twø', 'utf8')); + + socket.uncork(); + + // one = 3 bytes, twø = 4 bytes + assert.strictEqual(socket.bytesWritten, 3 + 4); + + socket.on('connect', common.mustCall(function() { + assert.strictEqual(socket.bytesWritten, 3 + 4); + })); + + socket.on('end', common.mustCall(function() { + assert.strictEqual(socket.bytesWritten, 3 + 4); + + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-socket-close-after-end.js b/test/js/node/test/parallel/test-net-socket-close-after-end.js new file mode 100644 index 0000000000..06bf55f89d --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-close-after-end.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +server.on('connection', (socket) => { + let endEmitted = false; + + socket.once('readable', () => { + setTimeout(() => { + socket.read(); + }, common.platformTimeout(100)); + }); + socket.on('end', () => { + endEmitted = true; + }); + socket.on('close', () => { + assert(endEmitted); + server.close(); + }); + socket.end('foo'); +}); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port, () => { + socket.end('foo'); + }); +})); diff --git a/test/js/node/test/parallel/test-net-socket-connect-without-cb.js b/test/js/node/test/parallel/test-net-socket-connect-without-cb.js new file mode 100644 index 0000000000..274083eb29 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-connect-without-cb.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, common.mustCall(function() { + const client = new net.Socket(); + + client.on('connect', common.mustCall(function() { + client.end(); + })); + + const address = server.address(); + if (!common.hasIPv6 && address.family === 'IPv6') { + // Necessary to pass CI running inside containers. + client.connect(address.port); + } else { + client.connect(address); + } +})); diff --git a/test/js/node/test/parallel/test-net-socket-connecting.js b/test/js/node/test/parallel/test-net-socket-connecting.js new file mode 100644 index 0000000000..21aa261192 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-connecting.js @@ -0,0 +1,21 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer((conn) => { + conn.end(); + server.close(); +}).listen(0, () => { + const client = net.connect(server.address().port, () => { + assert.strictEqual(client.connecting, false); + + // Legacy getter + assert.strictEqual(client._connecting, false); + client.end(); + }); + assert.strictEqual(client.connecting, true); + + // Legacy getter + assert.strictEqual(client._connecting, true); +}); diff --git a/test/js/node/test/parallel/test-net-socket-end-before-connect.js b/test/js/node/test/parallel/test-net-socket-end-before-connect.js new file mode 100644 index 0000000000..d40c90620e --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-end-before-connect.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../common'); + +const net = require('net'); + +const server = net.createServer(); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port); + socket.on('close', common.mustCall(() => server.close())); + socket.end(); +})); diff --git a/test/js/node/test/parallel/test-net-socket-ready-without-cb.js b/test/js/node/test/parallel/test-net-socket-ready-without-cb.js new file mode 100644 index 0000000000..29da68e173 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-ready-without-cb.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, 'localhost', common.mustCall(function() { + const client = new net.Socket(); + + client.on('ready', common.mustCall(function() { + client.end(); + })); + + client.connect(server.address()); +})); diff --git a/test/js/node/test/parallel/microtask-queue-run-immediate.test.js b/test/js/node/test/parallel/test-net-socket-timeout-unref.js similarity index 58% rename from test/js/node/test/parallel/microtask-queue-run-immediate.test.js rename to test/js/node/test/parallel/test-net-socket-timeout-unref.js index e64e7022b7..ae6bde49ab 100644 --- a/test/js/node/test/parallel/microtask-queue-run-immediate.test.js +++ b/test/js/node/test/parallel/test-net-socket-timeout-unref.js @@ -1,6 +1,3 @@ -//#FILE: test-microtask-queue-run-immediate.js -//#SHA1: 49e5d82cc3467e4e12d0e93629607cd48b3548e4 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,39 +19,38 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; -function enqueueMicrotask(fn) { - Promise.resolve().then(fn); -} +// Test that unref'ed sockets with timeouts do not prevent exit. -test("microtask queue runs in setImmediate", done => { - let microtaskExecuted = false; +const common = require('../common'); +const net = require('net'); - setImmediate(() => { - enqueueMicrotask(() => { - microtaskExecuted = true; - expect(microtaskExecuted).toBe(true); - done(); - }); - }); +const server = net.createServer(function(c) { + c.write('hello'); + c.unref(); }); +server.listen(0); +server.unref(); -test("microtask with nextTick runs before next setImmediate", done => { - let nextTickCalled = false; +let connections = 0; +const sockets = []; +const delays = [8, 5, 3, 6, 2, 4]; - setImmediate(() => { - enqueueMicrotask(() => { - process.nextTick(() => { - nextTickCalled = true; +delays.forEach(function(T) { + const socket = net.createConnection(server.address().port, 'localhost'); + socket.on('connect', common.mustCall(function() { + if (++connections === delays.length) { + sockets.forEach(function(s) { + s.socket.setTimeout(s.timeout, function() { + s.socket.destroy(); + throw new Error('socket timed out unexpectedly'); + }); + + s.socket.unref(); }); - }); + } + })); - setImmediate(() => { - expect(nextTickCalled).toBe(true); - done(); - }); - }); + sockets.push({ socket: socket, timeout: T * 1000 }); }); - -//<#END_FILE: test-microtask-queue-run-immediate.js diff --git a/test/js/node/test/parallel/test-net-socket-write-error.js b/test/js/node/test/parallel/test-net-socket-write-error.js new file mode 100644 index 0000000000..e68db68c0d --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-write-error.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer().listen(0, connectToServer); + +function connectToServer() { + const client = net.createConnection(this.address().port, () => { + client.on('error', common.mustNotCall()); + assert.throws(() => { + client.write(1337); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + + client.destroy(); + }) + .on('close', () => server.close()); +} diff --git a/test/js/node/test/parallel/test-net-sync-cork.js b/test/js/node/test/parallel/test-net-sync-cork.js new file mode 100644 index 0000000000..447f42ca91 --- /dev/null +++ b/test/js/node/test/parallel/test-net-sync-cork.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(handle); + +const N = 100; +const buf = Buffer.alloc(2, 'a'); + +server.listen(0, function() { + const conn = net.connect(this.address().port); + + conn.on('connect', () => { + let res = true; + let i = 0; + for (; i < N && res; i++) { + conn.cork(); + conn.write(buf); + res = conn.write(buf); + conn.uncork(); + } + assert.strictEqual(i, N); + conn.end(); + }); +}); + +function handle(socket) { + socket.resume(); + socket.on('error', common.mustNotCall()) + .on('close', common.mustCall(() => server.close())); +} diff --git a/test/js/node/test/parallel/test-net-writable.js b/test/js/node/test/parallel/test-net-writable.js new file mode 100644 index 0000000000..3659869efb --- /dev/null +++ b/test/js/node/test/parallel/test-net-writable.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(s) { + server.close(); + s.end(); +})).listen(0, '127.0.0.1', common.mustCall(function() { + const socket = net.connect(this.address().port, '127.0.0.1'); + socket.on('end', common.mustCall(() => { + assert.strictEqual(socket.writable, true); + socket.write('hello world'); + })); +})); diff --git a/test/js/node/test/parallel/test-net-write-cb-on-destroy-before-connect.js b/test/js/node/test/parallel/test-net-write-cb-on-destroy-before-connect.js new file mode 100644 index 0000000000..99efb66034 --- /dev/null +++ b/test/js/node/test/parallel/test-net-write-cb-on-destroy-before-connect.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0, common.mustCall(() => { + const socket = new net.Socket(); + + socket.on('connect', common.mustNotCall()); + + socket.connect({ + port: server.address().port, + }); + + assert(socket.connecting); + + socket.write('foo', common.expectsError({ + code: 'ERR_SOCKET_CLOSED_BEFORE_CONNECTION', + name: 'Error' + })); + + socket.destroy(); + server.close(); +})); diff --git a/test/js/node/test/parallel/zlib-close-after-write.test.js b/test/js/node/test/parallel/test-net-write-connect-write.js similarity index 65% rename from test/js/node/test/parallel/zlib-close-after-write.test.js rename to test/js/node/test/parallel/test-net-write-connect-write.js index 05a029b37f..1f09b04f17 100644 --- a/test/js/node/test/parallel/zlib-close-after-write.test.js +++ b/test/js/node/test/parallel/test-net-write-connect-write.js @@ -1,6 +1,3 @@ -//#FILE: test-zlib-close-after-write.js -//#SHA1: 7fad593914e2a23d73598e4366e685b9aa91cc24 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,27 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const zlib = require("zlib"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); -test("zlib close after write", async () => { - const gzipPromise = new Promise((resolve, reject) => { - zlib.gzip("hello", (err, out) => { - if (err) reject(err); - else resolve(out); - }); +const server = net.createServer(function(socket) { + socket.pipe(socket); +}).listen(0, common.mustCall(function() { + const conn = net.connect(this.address().port); + let received = ''; + + conn.setEncoding('utf8'); + conn.write('before'); + conn.on('connect', function() { + conn.write(' after'); }); - - const out = await gzipPromise; - - const unzip = zlib.createGunzip(); - unzip.write(out); - - const closePromise = new Promise(resolve => { - unzip.close(resolve); + conn.on('data', function(buf) { + received += buf; + conn.end(); }); - - await closePromise; -}); - -//<#END_FILE: test-zlib-close-after-write.js + conn.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(received, 'before after'); + })); +})); diff --git a/test/js/node/test/parallel/test-net-write-slow.js b/test/js/node/test/parallel/test-net-write-slow.js new file mode 100644 index 0000000000..cf2d5790d9 --- /dev/null +++ b/test/js/node/test/parallel/test-net-write-slow.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const SIZE = 2E5; +const N = 10; +let flushed = 0; +let received = 0; +const buf = Buffer.alloc(SIZE, 'a'); + +const server = net.createServer(function(socket) { + socket.setNoDelay(); + socket.setTimeout(9999); + socket.on('timeout', function() { + assert.fail(`flushed: ${flushed}, received: ${received}/${SIZE * N}`); + }); + + for (let i = 0; i < N; ++i) { + socket.write(buf, function() { + ++flushed; + if (flushed === N) { + socket.setTimeout(0); + } + }); + } + socket.end(); + +}).listen(0, common.mustCall(function() { + const conn = net.connect(this.address().port); + conn.on('data', function(buf) { + received += buf.length; + conn.pause(); + setTimeout(function() { + conn.resume(); + }, 20); + }); + conn.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(received, SIZE * N); + })); +})); diff --git a/test/js/node/test/parallel/next-tick-doesnt-hang.test.js b/test/js/node/test/parallel/test-next-tick-doesnt-hang.js similarity index 82% rename from test/js/node/test/parallel/next-tick-doesnt-hang.test.js rename to test/js/node/test/parallel/test-next-tick-doesnt-hang.js index 23fa936de0..36c1740bbf 100644 --- a/test/js/node/test/parallel/next-tick-doesnt-hang.test.js +++ b/test/js/node/test/parallel/test-next-tick-doesnt-hang.js @@ -1,6 +1,3 @@ -//#FILE: test-next-tick-doesnt-hang.js -//#SHA1: 6812bb4cd77cd15dd04c4409e34e0c5b605bbb88 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,16 +19,12 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // This test verifies that having a single nextTick statement and nothing else // does not hang the event loop. If this test times out it has failed. -test("nextTick does not hang", done => { - process.nextTick(() => { - // Nothing - done(); - }); +require('../common'); +process.nextTick(function() { + // Nothing }); - -//<#END_FILE: test-next-tick-doesnt-hang.js diff --git a/test/js/node/test/parallel/test-next-tick-domain.js b/test/js/node/test/parallel/test-next-tick-domain.js new file mode 100644 index 0000000000..3e55ef3225 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-domain.js @@ -0,0 +1,31 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const origNextTick = process.nextTick; + +require('domain'); + +// Requiring domain should not change nextTick. +assert.strictEqual(origNextTick, process.nextTick); diff --git a/test/js/node/test/parallel/test-next-tick-errors.js b/test/js/node/test/parallel/test-next-tick-errors.js new file mode 100644 index 0000000000..6fd079625a --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-errors.js @@ -0,0 +1,74 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const order = []; +let exceptionHandled = false; + +// This nextTick function will throw an error. It should only be called once. +// When it throws an error, it should still get removed from the queue. +process.nextTick(function() { + order.push('A'); + // cause an error + what(); // eslint-disable-line no-undef +}); + +// This nextTick function should remain in the queue when the first one +// is removed. It should be called if the error in the first one is +// caught (which we do in this test). +process.nextTick(function() { + order.push('C'); +}); + +function testNextTickWith(val) { + assert.throws(() => { + process.nextTick(val); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +testNextTickWith(false); +testNextTickWith(true); +testNextTickWith(1); +testNextTickWith('str'); +testNextTickWith({}); +testNextTickWith([]); + +process.on('uncaughtException', function(err, errorOrigin) { + assert.strictEqual(errorOrigin, 'uncaughtException'); + + if (!exceptionHandled) { + exceptionHandled = true; + order.push('B'); + } else { + // If we get here then the first process.nextTick got called twice + order.push('OOPS!'); + } +}); + +process.on('exit', function() { + assert.deepStrictEqual(order, ['A', 'B', 'C']); +}); diff --git a/test/js/node/test/parallel/test-next-tick-fixed-queue-regression.js b/test/js/node/test/parallel/test-next-tick-fixed-queue-regression.js new file mode 100644 index 0000000000..1fe82d02b1 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-fixed-queue-regression.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); + +// This tests a highly specific regression tied to the FixedQueue size, which +// was introduced in Node.js 9.7.0: https://github.com/nodejs/node/pull/18617 +// More specifically, a nextTick list could potentially end up not fully +// clearing in one run through if exactly 2048 ticks were added after +// microtasks were executed within the nextTick loop. + +process.nextTick(() => { + Promise.resolve(1).then(() => { + for (let i = 0; i < 2047; i++) + process.nextTick(common.mustCall()); + const immediate = setImmediate(common.mustNotCall()); + process.nextTick(common.mustCall(() => clearImmediate(immediate))); + }); +}); diff --git a/test/js/node/test/parallel/test-next-tick-intentional-starvation.js b/test/js/node/test/parallel/test-next-tick-intentional-starvation.js new file mode 100644 index 0000000000..ed357cb233 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-intentional-starvation.js @@ -0,0 +1,61 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This is the inverse of test-next-tick-starvation. it verifies +// that process.nextTick will *always* come before other events + +let ran = false; +let starved = false; +const start = +new Date(); +let timerRan = false; + +function spin() { + ran = true; + const now = +new Date(); + if (now - start > 100) { + console.log('The timer is starving, just as we planned.'); + starved = true; + + // now let it out. + return; + } + + process.nextTick(spin); +} + +function onTimeout() { + if (!starved) throw new Error('The timer escaped!'); + console.log('The timer ran once the ban was lifted'); + timerRan = true; +} + +spin(); +setTimeout(onTimeout, 50); + +process.on('exit', function() { + assert.ok(ran); + assert.ok(starved); + assert.ok(timerRan); +}); diff --git a/test/js/node/test/parallel/dgram-ref.test.js b/test/js/node/test/parallel/test-next-tick-ordering.js similarity index 63% rename from test/js/node/test/parallel/dgram-ref.test.js rename to test/js/node/test/parallel/test-next-tick-ordering.js index bbb6602414..8d3ee6488c 100644 --- a/test/js/node/test/parallel/dgram-ref.test.js +++ b/test/js/node/test/parallel/test-next-tick-ordering.js @@ -1,6 +1,3 @@ -//#FILE: test-dgram-ref.js -//#SHA1: b1a50859a1784815d575d8203f7da20fe8d07e50 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,23 +19,37 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const dgram = require("dgram"); +'use strict'; +require('../common'); +const assert = require('assert'); +let i; -test("Should not hang when creating UDP sockets", () => { - // Should not hang, see https://github.com/nodejs/node-v0.x-archive/issues/1282 - expect(() => dgram.createSocket("udp4")).not.toThrow(); - expect(() => dgram.createSocket("udp6")).not.toThrow(); +const N = 30; +const done = []; + +function get_printer(timeout) { + return function() { + console.log(`Running from setTimeout ${timeout}`); + done.push(timeout); + }; +} + +process.nextTick(function() { + console.log('Running from nextTick'); + done.push('nextTick'); }); -test("Test ref() on a closed socket", done => { - // Test the case of ref()'ing a socket with no handle. - const s = dgram.createSocket("udp4"); +for (i = 0; i < N; i += 1) { + setTimeout(get_printer(i), i); +} - s.close(() => { - expect(() => s.ref()).not.toThrow(); - done(); - }); +console.log('Running from main.'); + + +process.on('exit', function() { + assert.strictEqual(done[0], 'nextTick'); + // Disabling this test. I don't think we can ensure the order + // for (i = 0; i < N; i += 1) { + // assert.strictEqual(i, done[i + 1]); + // } }); - -//<#END_FILE: test-dgram-ref.js diff --git a/test/js/node/test/parallel/net-end-without-connect.test.js b/test/js/node/test/parallel/test-next-tick-ordering2.js similarity index 76% rename from test/js/node/test/parallel/net-end-without-connect.test.js rename to test/js/node/test/parallel/test-next-tick-ordering2.js index ffdd987eb0..6c42bd8e57 100644 --- a/test/js/node/test/parallel/net-end-without-connect.test.js +++ b/test/js/node/test/parallel/test-next-tick-ordering2.js @@ -1,6 +1,3 @@ -//#FILE: test-net-end-without-connect.js -//#SHA1: d13d4a7117c5625fec0c619acc024e705dfb4212 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,15 +19,21 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const net = require("net"); +'use strict'; +require('../common'); +const assert = require('assert'); -test("Socket.end() without connect", done => { - const sock = new net.Socket(); - sock.end(() => { - expect(sock.writable).toBe(false); - done(); +const order = []; +process.nextTick(function() { + setTimeout(function() { + order.push('setTimeout'); + }, 0); + + process.nextTick(function() { + order.push('nextTick'); }); }); -//<#END_FILE: test-net-end-without-connect.js +process.on('exit', function() { + assert.deepStrictEqual(order, ['nextTick', 'setTimeout']); +}); diff --git a/test/js/node/test/parallel/test-next-tick-when-exiting.js b/test/js/node/test/parallel/test-next-tick-when-exiting.js new file mode 100644 index 0000000000..36dc296646 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-when-exiting.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +process.on('exit', () => { + assert.strictEqual(process._exiting, true); + + process.nextTick( + common.mustNotCall('process is exiting, should not be called') + ); +}); + +process.exit(); diff --git a/test/js/node/test/parallel/stream2-pipe-error-once-listener.test.js b/test/js/node/test/parallel/test-next-tick.js similarity index 56% rename from test/js/node/test/parallel/stream2-pipe-error-once-listener.test.js rename to test/js/node/test/parallel/test-next-tick.js index 4879a847e0..47823f45bc 100644 --- a/test/js/node/test/parallel/stream2-pipe-error-once-listener.test.js +++ b/test/js/node/test/parallel/test-next-tick.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-pipe-error-once-listener.js -//#SHA1: a0bd981aa626f937edb6779bcf0e4dc49b82e69e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,42 +19,45 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); -const stream = require("stream"); +const assert = require('assert'); -class Read extends stream.Readable { - _read(size) { - this.push("x"); - this.push(null); - } -} +process.nextTick(common.mustCall(function() { + process.nextTick(common.mustCall(function() { + process.nextTick(common.mustCall()); + })); +})); -class Write extends stream.Writable { - _write(buffer, encoding, cb) { - this.emit("error", new Error("boom")); - this.emit("alldone"); - } -} +setTimeout(common.mustCall(function() { + process.nextTick(common.mustCall()); +}), 50); -test("stream2 pipe error once listener", done => { - const read = new Read(); - const write = new Write(); +process.nextTick(common.mustCall()); - write.once("error", () => {}); - write.once("alldone", () => { - console.log("ok"); - done(); - }); +const obj = {}; - const exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {}); +process.nextTick(function(a, b) { + assert.strictEqual(a, 42); + assert.strictEqual(b, obj); + assert.strictEqual(this, undefined); +}, 42, obj); - read.pipe(write); +process.nextTick((a, b) => { + assert.strictEqual(a, 42); + assert.strictEqual(b, obj); + assert.deepStrictEqual(this, {}); +}, 42, obj); - process.nextTick(() => { - expect(exitSpy).not.toHaveBeenCalled(); - exitSpy.mockRestore(); - }); +process.nextTick(function() { + assert.strictEqual(this, undefined); +}, 1, 2, 3, 4); + +process.nextTick(() => { + assert.deepStrictEqual(this, {}); +}, 1, 2, 3, 4); + +process.on('exit', function() { + process.nextTick(common.mustNotCall()); }); - -//<#END_FILE: test-stream2-pipe-error-once-listener.js diff --git a/test/js/node/test/parallel/test-no-node-snapshot.js b/test/js/node/test/parallel/test-no-node-snapshot.js new file mode 100644 index 0000000000..a636040a4c --- /dev/null +++ b/test/js/node/test/parallel/test-no-node-snapshot.js @@ -0,0 +1,5 @@ +'use strict'; + +// Flags: --no-node-snapshot + +require('../common'); diff --git a/test/js/node/test/parallel/test-outgoing-message-destroy.js b/test/js/node/test/parallel/test-outgoing-message-destroy.js new file mode 100644 index 0000000000..0ee7b5f40e --- /dev/null +++ b/test/js/node/test/parallel/test-outgoing-message-destroy.js @@ -0,0 +1,13 @@ +'use strict'; + +// Test that http.OutgoingMessage,prototype.destroy() returns `this`. +require('../common'); + +const assert = require('assert'); +const http = require('http'); +const outgoingMessage = new http.OutgoingMessage(); + +assert.strictEqual(outgoingMessage.destroyed, false); +assert.strictEqual(outgoingMessage.destroy(), outgoingMessage); +assert.strictEqual(outgoingMessage.destroyed, true); +assert.strictEqual(outgoingMessage.destroy(), outgoingMessage); diff --git a/test/js/node/test/parallel/test-path-basename.js b/test/js/node/test/parallel/test-path-basename.js new file mode 100644 index 0000000000..b16f9e5d63 --- /dev/null +++ b/test/js/node/test/parallel/test-path-basename.js @@ -0,0 +1,76 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.basename(__filename), 'test-path-basename.js'); +assert.strictEqual(path.basename(__filename, '.js'), 'test-path-basename'); +assert.strictEqual(path.basename('.js', '.js'), ''); +assert.strictEqual(path.basename('js', '.js'), 'js'); +assert.strictEqual(path.basename('file.js', '.ts'), 'file.js'); +assert.strictEqual(path.basename('file', '.js'), 'file'); +assert.strictEqual(path.basename('file.js.old', '.js.old'), 'file'); +assert.strictEqual(path.basename(''), ''); +assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('/basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext/'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext//'), 'basename.ext'); +assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b'); +assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb'); +assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b'); +assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb'); +assert.strictEqual(path.basename('/aaa/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/'), 'aaa'); +assert.strictEqual(path.basename('/aaa/b'), 'b'); +assert.strictEqual(path.basename('/a/b'), 'b'); +assert.strictEqual(path.basename('//a'), 'a'); +assert.strictEqual(path.basename('a', 'a'), ''); + +// On Windows a backslash acts as a path separator. +assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('foo'), 'foo'); +assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb'); +assert.strictEqual(path.win32.basename('C:'), ''); +assert.strictEqual(path.win32.basename('C:.'), '.'); +assert.strictEqual(path.win32.basename('C:\\'), ''); +assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext'); +assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:foo'), 'foo'); +assert.strictEqual(path.win32.basename('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.basename('a', 'a'), ''); + +// On unix a backslash is just treated as any other character. +assert.strictEqual(path.posix.basename('\\dir\\basename.ext'), + '\\dir\\basename.ext'); +assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext'); +assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); +assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); +assert.strictEqual(path.posix.basename('foo'), 'foo'); + +// POSIX filenames may include control characters +// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html +const controlCharFilename = `Icon${String.fromCharCode(13)}`; +assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`), + controlCharFilename); diff --git a/test/js/node/test/parallel/test-path-dirname.js b/test/js/node/test/parallel/test-path-dirname.js new file mode 100644 index 0000000000..0d4a182884 --- /dev/null +++ b/test/js/node/test/parallel/test-path-dirname.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.dirname(__filename).slice(-13), + common.isWindows ? 'test\\parallel' : 'test/parallel'); + +assert.strictEqual(path.posix.dirname('/a/b/'), '/a'); +assert.strictEqual(path.posix.dirname('/a/b'), '/a'); +assert.strictEqual(path.posix.dirname('/a'), '/'); +assert.strictEqual(path.posix.dirname(''), '.'); +assert.strictEqual(path.posix.dirname('/'), '/'); +assert.strictEqual(path.posix.dirname('////'), '/'); +assert.strictEqual(path.posix.dirname('//a'), '//'); +assert.strictEqual(path.posix.dirname('foo'), '.'); + +assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); +assert.strictEqual(path.win32.dirname('c:\\foo bar\\baz'), 'c:\\foo bar'); +assert.strictEqual(path.win32.dirname('\\'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo\\'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo'); +assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo'); +assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); +assert.strictEqual(path.win32.dirname('\\foo bar\\baz'), '\\foo bar'); +assert.strictEqual(path.win32.dirname('c:'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo'); +assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo'); +assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); +assert.strictEqual(path.win32.dirname('c:foo bar\\baz'), 'c:foo bar'); +assert.strictEqual(path.win32.dirname('file:stream'), '.'); +assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share'), + '\\\\unc\\share'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'), + '\\\\unc\\share\\'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'), + '\\\\unc\\share\\'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'), + '\\\\unc\\share\\foo'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'), + '\\\\unc\\share\\foo'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'), + '\\\\unc\\share\\foo\\bar'); +assert.strictEqual(path.win32.dirname('/a/b/'), '/a'); +assert.strictEqual(path.win32.dirname('/a/b'), '/a'); +assert.strictEqual(path.win32.dirname('/a'), '/'); +assert.strictEqual(path.win32.dirname(''), '.'); +assert.strictEqual(path.win32.dirname('/'), '/'); +assert.strictEqual(path.win32.dirname('////'), '/'); +assert.strictEqual(path.win32.dirname('foo'), '.'); diff --git a/test/js/node/test/parallel/test-path-extname.js b/test/js/node/test/parallel/test-path-extname.js new file mode 100644 index 0000000000..be5a6316b0 --- /dev/null +++ b/test/js/node/test/parallel/test-path-extname.js @@ -0,0 +1,100 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; +const slashRE = /\//g; + +const testPaths = [ + [__filename, '.js'], + ['', ''], + ['/path/to/file', ''], + ['/path/to/file.ext', '.ext'], + ['/path.to/file.ext', '.ext'], + ['/path.to/file', ''], + ['/path.to/.file', ''], + ['/path.to/.file.ext', '.ext'], + ['/path/to/f.ext', '.ext'], + ['/path/to/..ext', '.ext'], + ['/path/to/..', ''], + ['file', ''], + ['file.ext', '.ext'], + ['.file', ''], + ['.file.ext', '.ext'], + ['/file', ''], + ['/file.ext', '.ext'], + ['/.file', ''], + ['/.file.ext', '.ext'], + ['.path/file.ext', '.ext'], + ['file.ext.ext', '.ext'], + ['file.', '.'], + ['.', ''], + ['./', ''], + ['.file.ext', '.ext'], + ['.file', ''], + ['.file.', '.'], + ['.file..', '.'], + ['..', ''], + ['../', ''], + ['..file.ext', '.ext'], + ['..file', '.file'], + ['..file.', '.'], + ['..file..', '.'], + ['...', '.'], + ['...ext', '.ext'], + ['....', '.'], + ['file.ext/', '.ext'], + ['file.ext//', '.ext'], + ['file/', ''], + ['file//', ''], + ['file./', '.'], + ['file.//', '.'], +]; + +for (const testPath of testPaths) { + const expected = testPath[1]; + const extNames = [path.posix.extname, path.win32.extname]; + for (const extname of extNames) { + let input = testPath[0]; + let os; + if (extname === path.win32.extname) { + input = input.replace(slashRE, '\\'); + os = 'win32'; + } else { + os = 'posix'; + } + const actual = extname(input); + const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) + failures.push(`\n${message}`); + } + const input = `C:${testPath[0].replace(slashRE, '\\')}`; + const actual = path.win32.extname(input); + const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) + failures.push(`\n${message}`); +} +assert.strictEqual(failures.length, 0, failures.join('')); + +// On Windows, backslash is a path separator. +assert.strictEqual(path.win32.extname('.\\'), ''); +assert.strictEqual(path.win32.extname('..\\'), ''); +assert.strictEqual(path.win32.extname('file.ext\\'), '.ext'); +assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext'); +assert.strictEqual(path.win32.extname('file\\'), ''); +assert.strictEqual(path.win32.extname('file\\\\'), ''); +assert.strictEqual(path.win32.extname('file.\\'), '.'); +assert.strictEqual(path.win32.extname('file.\\\\'), '.'); + +// On *nix, backslash is a valid name component like any other character. +assert.strictEqual(path.posix.extname('.\\'), ''); +assert.strictEqual(path.posix.extname('..\\'), '.\\'); +assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\'); +assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\'); +assert.strictEqual(path.posix.extname('file\\'), ''); +assert.strictEqual(path.posix.extname('file\\\\'), ''); +assert.strictEqual(path.posix.extname('file.\\'), '.\\'); +assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); diff --git a/test/js/node/test/parallel/test-path-isabsolute.js b/test/js/node/test/parallel/test-path-isabsolute.js new file mode 100644 index 0000000000..66b4f1ee51 --- /dev/null +++ b/test/js/node/test/parallel/test-path-isabsolute.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.win32.isAbsolute('/'), true); +assert.strictEqual(path.win32.isAbsolute('//'), true); +assert.strictEqual(path.win32.isAbsolute('//server'), true); +assert.strictEqual(path.win32.isAbsolute('//server/file'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\server'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\'), true); +assert.strictEqual(path.win32.isAbsolute('c'), false); +assert.strictEqual(path.win32.isAbsolute('c:'), false); +assert.strictEqual(path.win32.isAbsolute('c:\\'), true); +assert.strictEqual(path.win32.isAbsolute('c:/'), true); +assert.strictEqual(path.win32.isAbsolute('c://'), true); +assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true); +assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true); +assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false); +assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false); +assert.strictEqual(path.win32.isAbsolute('directory/directory'), false); +assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false); + +assert.strictEqual(path.posix.isAbsolute('/home/foo'), true); +assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true); +assert.strictEqual(path.posix.isAbsolute('bar/'), false); +assert.strictEqual(path.posix.isAbsolute('./baz'), false); diff --git a/test/js/node/test/parallel/test-path-join.js b/test/js/node/test/parallel/test-path-join.js new file mode 100644 index 0000000000..d6d1839996 --- /dev/null +++ b/test/js/node/test/parallel/test-path-join.js @@ -0,0 +1,143 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; +const backslashRE = /\\/g; + +const joinTests = [ + [ [path.posix.join, path.win32.join], + // Arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [[], '.'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + [['/', 'foo'], '/foo'], + [['/', '/foo'], '/foo'], + [['/', '//foo'], '/foo'], + [['/', '', '/foo'], '/foo'], + [['', '/', 'foo'], '/foo'], + [['', '/', '/foo'], '/foo'], + ], + ], +]; + +// Windows-specific join tests +joinTests.push([ + path.win32.join, + joinTests[0][1].slice(0).concat( + [// Arguments result + // UNC path expected + [['//foo/bar'], '\\\\foo\\bar\\'], + [['\\/foo/bar'], '\\\\foo\\bar\\'], + [['\\\\foo/bar'], '\\\\foo\\bar\\'], + // UNC path expected - server and share separate + [['//foo', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', 'bar'], '\\\\foo\\bar\\'], + [['//foo', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - questionable + [['//foo', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - even more questionable + [['', '//foo', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', '/bar'], '\\\\foo\\bar\\'], + // No UNC path expected (no double slash in first component) + [['\\', 'foo/bar'], '\\foo\\bar'], + [['\\', '/foo/bar'], '\\foo\\bar'], + [['', '/', '/foo/bar'], '\\foo\\bar'], + // No UNC path expected (no non-slashes in first component - + // questionable) + [['//', 'foo/bar'], '\\foo\\bar'], + [['//', '/foo/bar'], '\\foo\\bar'], + [['\\\\', '/', '/foo/bar'], '\\foo\\bar'], + [['//'], '\\'], + // No UNC path expected (share name missing - questionable). + [['//foo'], '\\foo'], + [['//foo/'], '\\foo\\'], + [['//foo', '/'], '\\foo\\'], + [['//foo', '', '/'], '\\foo\\'], + // No UNC path expected (too many leading slashes - questionable) + [['///foo/bar'], '\\foo\\bar'], + [['////foo', 'bar'], '\\foo\\bar'], + [['\\\\\\/foo/bar'], '\\foo\\bar'], + // Drive-relative vs drive-absolute paths. This merely describes the + // status quo, rather than being obviously right + [['c:'], 'c:.'], + [['c:.'], 'c:.'], + [['c:', ''], 'c:.'], + [['', 'c:'], 'c:.'], + [['c:.', '/'], 'c:.\\'], + [['c:.', 'file'], 'c:file'], + [['c:', '/'], 'c:\\'], + [['c:', 'file'], 'c:\\file'], + ] + ), +]); +joinTests.forEach((test) => { + if (!Array.isArray(test[0])) + test[0] = [test[0]]; + test[0].forEach((join) => { + test[1].forEach((test) => { + const actual = join.apply(null, test[0]); + const expected = test[1]; + // For non-Windows specific tests with the Windows join(), we need to try + // replacing the slashes since the non-Windows specific tests' `expected` + // use forward slashes + let actualAlt; + let os; + if (join === path.win32.join) { + actualAlt = actual.replace(backslashRE, '/'); + os = 'win32'; + } else { + os = 'posix'; + } + if (actual !== expected && actualAlt !== expected) { + const delimiter = test[0].map(JSON.stringify).join(','); + const message = `path.${os}.join(${delimiter})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + failures.push(`\n${message}`); + } + }); + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); diff --git a/test/js/node/test/parallel/test-path-makelong.js b/test/js/node/test/parallel/test-path-makelong.js new file mode 100644 index 0000000000..7a4783953c --- /dev/null +++ b/test/js/node/test/parallel/test-path-makelong.js @@ -0,0 +1,88 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const path = require('path'); + +if (common.isWindows) { + const file = fixtures.path('a.js'); + const resolvedFile = path.resolve(file); + + assert.strictEqual(path.toNamespacedPath(file), + `\\\\?\\${resolvedFile}`); + assert.strictEqual(path.toNamespacedPath(`\\\\?\\${file}`), + `\\\\?\\${resolvedFile}`); + assert.strictEqual(path.toNamespacedPath( + '\\\\someserver\\someshare\\somefile'), + '\\\\?\\UNC\\someserver\\someshare\\somefile'); + assert.strictEqual(path.toNamespacedPath( + '\\\\?\\UNC\\someserver\\someshare\\somefile'), + '\\\\?\\UNC\\someserver\\someshare\\somefile'); + assert.strictEqual(path.toNamespacedPath('\\\\.\\pipe\\somepipe'), + '\\\\.\\pipe\\somepipe'); +} + +assert.strictEqual(path.toNamespacedPath(''), ''); +assert.strictEqual(path.toNamespacedPath(null), null); +assert.strictEqual(path.toNamespacedPath(100), 100); +assert.strictEqual(path.toNamespacedPath(path), path); +assert.strictEqual(path.toNamespacedPath(false), false); +assert.strictEqual(path.toNamespacedPath(true), true); + +const emptyObj = {}; +assert.strictEqual(path.posix.toNamespacedPath('/foo/bar'), '/foo/bar'); +assert.strictEqual(path.posix.toNamespacedPath('foo/bar'), 'foo/bar'); +assert.strictEqual(path.posix.toNamespacedPath(null), null); +assert.strictEqual(path.posix.toNamespacedPath(true), true); +assert.strictEqual(path.posix.toNamespacedPath(1), 1); +assert.strictEqual(path.posix.toNamespacedPath(), undefined); +assert.strictEqual(path.posix.toNamespacedPath(emptyObj), emptyObj); +if (common.isWindows) { + // These tests cause resolve() to insert the cwd, so we cannot test them from + // non-Windows platforms (easily) + assert.strictEqual(path.toNamespacedPath(''), ''); + assert.strictEqual(path.win32.toNamespacedPath('foo\\bar').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`); + assert.strictEqual(path.win32.toNamespacedPath('foo/bar').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`); + const currentDeviceLetter = path.parse(process.cwd()).root.substring(0, 2); + assert.strictEqual( + path.win32.toNamespacedPath(currentDeviceLetter).toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}`); + assert.strictEqual(path.win32.toNamespacedPath('C').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\c`); +} +assert.strictEqual(path.win32.toNamespacedPath('C:\\foo'), '\\\\?\\C:\\foo'); +assert.strictEqual(path.win32.toNamespacedPath('C:/foo'), '\\\\?\\C:\\foo'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\foo\\bar'), + '\\\\?\\UNC\\foo\\bar\\'); +assert.strictEqual(path.win32.toNamespacedPath('//foo//bar'), + '\\\\?\\UNC\\foo\\bar\\'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\?\\foo'), '\\\\?\\foo\\'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\?\\c:\\Windows/System'), '\\\\?\\c:\\Windows\\System'); +assert.strictEqual(path.win32.toNamespacedPath(null), null); +assert.strictEqual(path.win32.toNamespacedPath(true), true); +assert.strictEqual(path.win32.toNamespacedPath(1), 1); +assert.strictEqual(path.win32.toNamespacedPath(), undefined); +assert.strictEqual(path.win32.toNamespacedPath(emptyObj), emptyObj); diff --git a/test/js/node/test/parallel/test-path-normalize.js b/test/js/node/test/parallel/test-path-normalize.js new file mode 100644 index 0000000000..e1d3b9ce1e --- /dev/null +++ b/test/js/node/test/parallel/test-path-normalize.js @@ -0,0 +1,72 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'), + 'fixtures\\b\\c.js'); +assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar'); +assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b'); +assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c'); +assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b'); +assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'), + '\\\\server\\share\\dir\\file.ext'); +assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z'); +assert.strictEqual(path.win32.normalize('C:'), 'C:.'); +assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc'); +assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'), + 'C:..\\..\\def'); +assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\'); +assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); +assert.strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'), + '..\\..\\bar'); +assert.strictEqual(path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'), + '..\\..\\bar'); +assert.strictEqual(path.win32.normalize('../../../foo/../../../bar'), + '..\\..\\..\\..\\..\\bar'); +assert.strictEqual(path.win32.normalize('../../../foo/../../../bar/../../'), + '..\\..\\..\\..\\..\\..\\'); +assert.strictEqual( + path.win32.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '..\\..\\' +); +assert.strictEqual( + path.win32.normalize('../.../../foobar/../../../bar/../../baz'), + '..\\..\\..\\..\\baz' +); +assert.strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz'); + +assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), + 'fixtures/b/c.js'); +assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar'); +assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b'); +assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c'); +assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b'); +assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z'); +assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/'); +assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); +assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); +assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); +assert.strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar'); +assert.strictEqual(path.posix.normalize('../.../.././.../../../bar'), + '../../bar'); +assert.strictEqual(path.posix.normalize('../../../foo/../../../bar'), + '../../../../../bar'); +assert.strictEqual(path.posix.normalize('../../../foo/../../../bar/../../'), + '../../../../../../'); +assert.strictEqual( + path.posix.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '../../' +); +assert.strictEqual( + path.posix.normalize('../.../../foobar/../../../bar/../../baz'), + '../../../../baz' +); +assert.strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz'); diff --git a/test/js/node/test/parallel/test-path-posix-exists.js b/test/js/node/test/parallel/test-path-posix-exists.js new file mode 100644 index 0000000000..dc12ed6daf --- /dev/null +++ b/test/js/node/test/parallel/test-path-posix-exists.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('path/posix'), require('path').posix); diff --git a/test/js/node/test/parallel/test-path-posix-relative-on-windows.js b/test/js/node/test/parallel/test-path-posix-relative-on-windows.js new file mode 100644 index 0000000000..bcaaca8b18 --- /dev/null +++ b/test/js/node/test/parallel/test-path-posix-relative-on-windows.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Refs: https://github.com/nodejs/node/issues/13683 + +const relativePath = path.posix.relative('a/b/c', '../../x'); +assert.match(relativePath, /^(\.\.\/){3,5}x$/); diff --git a/test/js/node/test/parallel/test-path-relative.js b/test/js/node/test/parallel/test-path-relative.js new file mode 100644 index 0000000000..f6a9f5662a --- /dev/null +++ b/test/js/node/test/parallel/test-path-relative.js @@ -0,0 +1,69 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; + +const relativeTests = [ + [ path.win32.relative, + // Arguments result + [['c:/blah\\blah', 'd:/games', 'd:\\games'], + ['c:/aaaa/bbbb', 'c:/aaaa', '..'], + ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], + ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], + ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], + ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], + ['c:/aaaa/bbbb', 'd:\\', 'd:\\'], + ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'], + ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'], + ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'], + ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'], + ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'], + ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['C:\\baz-quux', 'C:\\baz', '..\\baz'], + ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'], + ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'], + ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'], + ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz'], + ], + ], + [ path.posix.relative, + // Arguments result + [['/var/lib', '/var', '..'], + ['/var/lib', '/bin', '../../bin'], + ['/var/lib', '/var/lib', ''], + ['/var/lib', '/var/apache', '../apache'], + ['/var/', '/var/lib', 'lib'], + ['/', '/var/lib', 'var/lib'], + ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'], + ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'], + ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'], + ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'], + ['/baz-quux', '/baz', '../baz'], + ['/baz', '/baz-quux', '../baz-quux'], + ['/page1/page2/foo', '/', '../../..'], + ], + ], +]; +relativeTests.forEach((test) => { + const relative = test[0]; + test[1].forEach((test) => { + const actual = relative(test[0], test[1]); + const expected = test[2]; + if (actual !== expected) { + const os = relative === path.win32.relative ? 'win32' : 'posix'; + const message = `path.${os}.relative(${ + test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + failures.push(`\n${message}`); + } + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); diff --git a/test/js/node/test/parallel/test-path-win32-exists.js b/test/js/node/test/parallel/test-path-win32-exists.js new file mode 100644 index 0000000000..c9efa74dbd --- /dev/null +++ b/test/js/node/test/parallel/test-path-win32-exists.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('path/win32'), require('path').win32); diff --git a/test/js/node/test/parallel/test-path-zero-length-strings.js b/test/js/node/test/parallel/test-path-zero-length-strings.js new file mode 100644 index 0000000000..f6516ffff8 --- /dev/null +++ b/test/js/node/test/parallel/test-path-zero-length-strings.js @@ -0,0 +1,39 @@ +'use strict'; + +// These testcases are specific to one uncommon behavior in path module. Few +// of the functions in path module, treat '' strings as current working +// directory. This test makes sure that the behavior is intact between commits. +// See: https://github.com/nodejs/node/pull/2106 + +require('../common'); +const assert = require('assert'); +const path = require('path'); +const pwd = process.cwd(); + +// Join will internally ignore all the zero-length strings and it will return +// '.' if the joined string is a zero-length string. +assert.strictEqual(path.posix.join(''), '.'); +assert.strictEqual(path.posix.join('', ''), '.'); +assert.strictEqual(path.win32.join(''), '.'); +assert.strictEqual(path.win32.join('', ''), '.'); +assert.strictEqual(path.join(pwd), pwd); +assert.strictEqual(path.join(pwd, ''), pwd); + +// Normalize will return '.' if the input is a zero-length string +assert.strictEqual(path.posix.normalize(''), '.'); +assert.strictEqual(path.win32.normalize(''), '.'); +assert.strictEqual(path.normalize(pwd), pwd); + +// Since '' is not a valid path in any of the common environments, return false +assert.strictEqual(path.posix.isAbsolute(''), false); +assert.strictEqual(path.win32.isAbsolute(''), false); + +// Resolve, internally ignores all the zero-length strings and returns the +// current working directory +assert.strictEqual(path.resolve(''), pwd); +assert.strictEqual(path.resolve('', ''), pwd); + +// Relative, internally calls resolve. So, '' is actually the current directory +assert.strictEqual(path.relative('', pwd), ''); +assert.strictEqual(path.relative(pwd, ''), ''); +assert.strictEqual(path.relative(pwd, pwd), ''); diff --git a/test/js/node/test/parallel/test-path.js b/test/js/node/test/parallel/test-path.js new file mode 100644 index 0000000000..0cb55d42aa --- /dev/null +++ b/test/js/node/test/parallel/test-path.js @@ -0,0 +1,73 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Test thrown TypeErrors +const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; + +function fail(fn) { + const args = Array.from(arguments).slice(1); + + assert.throws(() => { + fn.apply(null, args); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} + +for (const test of typeErrorTests) { + for (const namespace of [path.posix, path.win32]) { + fail(namespace.join, test); + fail(namespace.resolve, test); + fail(namespace.normalize, test); + fail(namespace.isAbsolute, test); + fail(namespace.relative, test, 'foo'); + fail(namespace.relative, 'foo', test); + fail(namespace.parse, test); + fail(namespace.dirname, test); + fail(namespace.basename, test); + fail(namespace.extname, test); + + // Undefined is a valid value as the second argument to basename + if (test !== undefined) { + fail(namespace.basename, 'foo', test); + } + } +} + +// path.sep tests +// windows +assert.strictEqual(path.win32.sep, '\\'); +// posix +assert.strictEqual(path.posix.sep, '/'); + +// path.delimiter tests +// windows +assert.strictEqual(path.win32.delimiter, ';'); +// posix +assert.strictEqual(path.posix.delimiter, ':'); + +if (common.isWindows) + assert.strictEqual(path, path.win32); +else + assert.strictEqual(path, path.posix); diff --git a/test/js/node/test/parallel/test-perf-gc-crash.js b/test/js/node/test/parallel/test-perf-gc-crash.js new file mode 100644 index 0000000000..d980e91a2f --- /dev/null +++ b/test/js/node/test/parallel/test-perf-gc-crash.js @@ -0,0 +1,25 @@ +'use strict'; + +require('../common'); + +// Refers to https://github.com/nodejs/node/issues/39548 + +// The test fails if this crashes. If it closes normally, +// then all is good. + +const { + PerformanceObserver, +} = require('perf_hooks'); + +// We don't actually care if the observer callback is called here. +const gcObserver = new PerformanceObserver(() => {}); + +gcObserver.observe({ entryTypes: ['gc'] }); + +gcObserver.disconnect(); + +const gcObserver2 = new PerformanceObserver(() => {}); + +gcObserver2.observe({ entryTypes: ['gc'] }); + +gcObserver2.disconnect(); diff --git a/test/js/node/test/parallel/test-performance-measure.js b/test/js/node/test/parallel/test-performance-measure.js new file mode 100644 index 0000000000..949258f96e --- /dev/null +++ b/test/js/node/test/parallel/test-performance-measure.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const { PerformanceObserver, performance } = require('perf_hooks'); +const DELAY = 1000; +const ALLOWED_MARGIN = 10; + +const expected = ['Start to Now', 'A to Now', 'A to B']; +const obs = new PerformanceObserver(common.mustCall((items) => { + items.getEntries().forEach(({ name, duration }) => { + assert.ok(duration > (DELAY - ALLOWED_MARGIN)); + assert.strictEqual(expected.shift(), name); + }); +})); +obs.observe({ entryTypes: ['measure'] }); + +performance.mark('A'); +setTimeout(common.mustCall(() => { + performance.measure('Start to Now'); + performance.measure('A to Now', 'A'); + + performance.mark('B'); + performance.measure('A to B', 'A', 'B'); +}), DELAY); diff --git a/test/js/node/test/parallel/test-performanceobserver-gc.js b/test/js/node/test/parallel/test-performanceobserver-gc.js new file mode 100644 index 0000000000..fe9397631c --- /dev/null +++ b/test/js/node/test/parallel/test-performanceobserver-gc.js @@ -0,0 +1,17 @@ +'use strict'; + +require('../common'); + +// Verifies that setting up two observers to listen +// to gc performance does not crash. + +const { + PerformanceObserver, +} = require('perf_hooks'); + +// We don't actually care if the callback is ever invoked in this test +const obs = new PerformanceObserver(() => {}); +const obs2 = new PerformanceObserver(() => {}); + +obs.observe({ type: 'gc' }); +obs2.observe({ type: 'gc' }); diff --git a/test/js/node/test/parallel/test-permission-fs-windows-path.js b/test/js/node/test/parallel/test-permission-fs-windows-path.js new file mode 100644 index 0000000000..552f8e1c21 --- /dev/null +++ b/test/js/node/test/parallel/test-permission-fs-windows-path.js @@ -0,0 +1,49 @@ +// Flags: --experimental-permission --allow-fs-read=* --allow-child-process +'use strict'; + +const common = require('../common'); +common.skipIfWorker(); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +if (!common.isWindows) { + common.skip('windows UNC path test'); +} + +{ + const { stdout, status } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write', 'C:\\\\', '-e', + 'console.log(process.permission.has("fs.write", "C:\\\\"))', + ]); + assert.strictEqual(stdout.toString(), 'true\n'); + assert.strictEqual(status, 0); +} + +{ + const { stdout, status, stderr } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write="\\\\?\\C:\\"', '-e', + 'console.log(process.permission.has("fs.write", "C:\\\\"))', + ]); + assert.strictEqual(stdout.toString(), 'false\n', stderr.toString()); + assert.strictEqual(status, 0); +} + +{ + const { stdout, status, stderr } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write', 'C:\\', '-e', + `const path = require('path'); + console.log(process.permission.has('fs.write', path.toNamespacedPath('C:\\\\')))`, + ]); + assert.strictEqual(stdout.toString(), 'true\n', stderr.toString()); + assert.strictEqual(status, 0); +} + +{ + const { stdout, status, stderr } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write', 'C:\\*', '-e', + "console.log(process.permission.has('fs.write', '\\\\\\\\A\\\\C:\\Users'))", + ]); + assert.strictEqual(stdout.toString(), 'false\n', stderr.toString()); + assert.strictEqual(status, 0); +} diff --git a/test/js/node/test/parallel/test-pipe-abstract-socket-http.js b/test/js/node/test/parallel/test-pipe-abstract-socket-http.js new file mode 100644 index 0000000000..6d3beb44d1 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-abstract-socket-http.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +if (!common.isLinux) common.skip(); + +const server = http.createServer( + common.mustCall((req, res) => { + res.end('ok'); + }) +); + +server.listen( + '\0abstract', + common.mustCall(() => { + http.get( + { + socketPath: server.address(), + }, + common.mustCall((res) => { + assert.strictEqual(res.statusCode, 200); + server.close(); + }) + ); + }) +); diff --git a/test/js/node/test/parallel/test-pipe-abstract-socket.js b/test/js/node/test/parallel/test-pipe-abstract-socket.js new file mode 100644 index 0000000000..baf76d6b82 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-abstract-socket.js @@ -0,0 +1,34 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +if (!common.isLinux) common.skip(); + +const path = '\0abstract'; +const message = /can not set readableAll or writableAllt to true when path is abstract unix socket/; + +assert.throws(() => { + const server = net.createServer(common.mustNotCall()); + server.listen({ + path, + readableAll: true + }); +}, message); + +assert.throws(() => { + const server = net.createServer(common.mustNotCall()); + server.listen({ + path, + writableAll: true + }); +}, message); + +assert.throws(() => { + const server = net.createServer(common.mustNotCall()); + server.listen({ + path, + readableAll: true, + writableAll: true + }); +}, message); diff --git a/test/js/node/test/parallel/test-pipe-address.js b/test/js/node/test/parallel/test-pipe-address.js new file mode 100644 index 0000000000..3550434932 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-address.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const server = net.createServer(common.mustNotCall()); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +server.listen(common.PIPE, common.mustCall(function() { + assert.strictEqual(server.address(), common.PIPE); + server.close(); +})); diff --git a/test/js/node/test/parallel/fs-readfile-empty.test.js b/test/js/node/test/parallel/test-pipe-file-to-http.js similarity index 52% rename from test/js/node/test/parallel/fs-readfile-empty.test.js rename to test/js/node/test/parallel/test-pipe-file-to-http.js index ae4bdb4eb5..6c1244427d 100644 --- a/test/js/node/test/parallel/fs-readfile-empty.test.js +++ b/test/js/node/test/parallel/test-pipe-file-to-http.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-readfile-empty.js -//#SHA1: a78ffc8186bc3e0a7d8d8dcf0f292ef4220817a5 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -23,45 +20,64 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; - +const common = require('../common'); +const assert = require('assert'); const fs = require('fs'); -const path = require('path'); +const http = require('http'); -const fixturesPath = path.join(__dirname, '..', 'fixtures'); -const fn = path.join(fixturesPath, 'empty.txt'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); -test('fs.readFile on an empty file', (done) => { - fs.readFile(fn, (err, data) => { - expect(err).toBeNull(); - expect(data).toBeTruthy(); - done(); +const filename = tmpdir.resolve('big'); +let count = 0; + +const server = http.createServer((req, res) => { + let timeoutId; + assert.strictEqual(req.method, 'POST'); + req.pause(); + + setTimeout(() => { + req.resume(); + }, 1000); + + req.on('data', (chunk) => { + count += chunk.length; + }); + + req.on('end', () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); }); }); +server.listen(0); -test('fs.readFile on an empty file with utf8 encoding', (done) => { - fs.readFile(fn, 'utf8', (err, data) => { - expect(err).toBeNull(); - expect(data).toBe(''); - done(); +server.on('listening', () => { + common.createZeroFilledFile(filename); + makeRequest(); +}); + +function makeRequest() { + const req = http.request({ + port: server.address().port, + path: '/', + method: 'POST' }); -}); -test('fs.readFile on an empty file with encoding option', (done) => { - fs.readFile(fn, { encoding: 'utf8' }, (err, data) => { - expect(err).toBeNull(); - expect(data).toBe(''); - done(); + const s = fs.ReadStream(filename); + s.pipe(req); + s.on('close', common.mustSucceed()); + + req.on('response', (res) => { + res.resume(); + res.on('end', () => { + server.close(); + }); }); -}); +} -test('fs.readFileSync on an empty file', () => { - const data = fs.readFileSync(fn); - expect(data).toBeTruthy(); +process.on('exit', () => { + assert.strictEqual(count, 1024 * 10240); }); - -test('fs.readFileSync on an empty file with utf8 encoding', () => { - const data = fs.readFileSync(fn, 'utf8'); - expect(data).toBe(''); -}); - -//<#END_FILE: test-fs-readfile-empty.js diff --git a/test/js/node/test/parallel/test-pipe-head.js b/test/js/node/test/parallel/test-pipe-head.js new file mode 100644 index 0000000000..1e79249c29 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-head.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +const exec = require('child_process').exec; + +const nodePath = process.argv[0]; +const script = fixtures.path('print-10-lines.js'); + +const cmd = `"${nodePath}" "${script}" | head -2`; + +exec(cmd, common.mustSucceed((stdout, stderr) => { + const lines = stdout.split('\n'); + assert.strictEqual(lines.length, 3); +})); diff --git a/test/js/node/test/parallel/test-pipe-return-val.js b/test/js/node/test/parallel/test-pipe-return-val.js new file mode 100644 index 0000000000..f2a7f573ec --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-return-val.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test ensures SourceStream.pipe(DestStream) returns DestStream + +require('../common'); +const Stream = require('stream').Stream; +const assert = require('assert'); + +const sourceStream = new Stream(); +const destStream = new Stream(); +const result = sourceStream.pipe(destStream); + +assert.strictEqual(result, destStream); diff --git a/test/js/node/test/parallel/test-pipe-writev.js b/test/js/node/test/parallel/test-pipe-writev.js new file mode 100644 index 0000000000..5e5b42e6a7 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-writev.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +if (common.isWindows) + common.skip('Unix-specific test'); + +const assert = require('assert'); +const net = require('net'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const server = net.createServer((connection) => { + connection.on('error', (err) => { + throw err; + }); + + const writev = connection._writev.bind(connection); + connection._writev = common.mustCall(writev); + + connection.cork(); + connection.write('pi'); + connection.write('ng'); + connection.end(); +}); + +server.on('error', (err) => { + throw err; +}); + +server.listen(common.PIPE, () => { + const client = net.connect(common.PIPE); + + client.on('error', (err) => { + throw err; + }); + + client.on('data', common.mustCall((data) => { + assert.strictEqual(data.toString(), 'ping'); + })); + + client.on('end', () => { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-preload-print-process-argv.js b/test/js/node/test/parallel/test-preload-print-process-argv.js new file mode 100644 index 0000000000..9d2774f8a4 --- /dev/null +++ b/test/js/node/test/parallel/test-preload-print-process-argv.js @@ -0,0 +1,34 @@ +'use strict'; + +// This tests that process.argv is the same in the preloaded module +// and the user module. + +require('../common'); + +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const fs = require('fs'); + +tmpdir.refresh(); + +fs.writeFileSync( + tmpdir.resolve('preload.js'), + 'console.log(JSON.stringify(process.argv));', + 'utf-8'); + +fs.writeFileSync( + tmpdir.resolve('main.js'), + 'console.log(JSON.stringify(process.argv));', + 'utf-8'); + +const child = spawnSync(process.execPath, ['-r', './preload.js', 'main.js'], + { cwd: tmpdir.path }); + +if (child.status !== 0) { + console.log(child.stderr.toString()); + assert.strictEqual(child.status, 0); +} + +const lines = child.stdout.toString().trim().split('\n'); +assert.deepStrictEqual(JSON.parse(lines[0]), JSON.parse(lines[1])); diff --git a/test/js/node/test/parallel/test-preload-self-referential.js b/test/js/node/test/parallel/test-preload-self-referential.js new file mode 100644 index 0000000000..2624527deb --- /dev/null +++ b/test/js/node/test/parallel/test-preload-self-referential.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { exec } = require('child_process'); + +const nodeBinary = process.argv[0]; + +if (!common.isMainThread) + common.skip('process.chdir is not available in Workers'); + +const selfRefModule = fixtures.path('self_ref_module'); +const fixtureA = fixtures.path('printA.js'); + +exec(`"${nodeBinary}" -r self_ref "${fixtureA}"`, { cwd: selfRefModule }, + (err, stdout, stderr) => { + assert.ifError(err); + assert.strictEqual(stdout, 'A\n'); + }); diff --git a/test/js/node/test/parallel/test-process-abort.js b/test/js/node/test/parallel/test-process-abort.js new file mode 100644 index 0000000000..665e1399a3 --- /dev/null +++ b/test/js/node/test/parallel/test-process-abort.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +if (!common.isMainThread) + common.skip('process.abort() is not available in Workers'); + +// Check that our built-in methods do not have a prototype/constructor behaviour +// if they don't need to. This could be tested for any of our C++ methods. +assert.strictEqual(process.abort.prototype, undefined); +assert.throws(() => new process.abort(), TypeError); diff --git a/test/js/node/test/parallel/next-tick-ordering2.test.js b/test/js/node/test/parallel/test-process-argv-0.js similarity index 68% rename from test/js/node/test/parallel/next-tick-ordering2.test.js rename to test/js/node/test/parallel/test-process-argv-0.js index 233df7aed3..21b406873f 100644 --- a/test/js/node/test/parallel/next-tick-ordering2.test.js +++ b/test/js/node/test/parallel/test-process-argv-0.js @@ -1,6 +1,3 @@ -//#FILE: test-next-tick-ordering2.js -//#SHA1: 6aa2e4c01b6ff008c2c2c844cb50a75d45af481d -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,25 +19,24 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const path = require('path'); +const assert = require('assert'); +const spawn = require('child_process').spawn; -test("nextTick and setTimeout ordering", async () => { - const order = []; - - await new Promise(resolve => { - process.nextTick(() => { - setTimeout(() => { - order.push("setTimeout"); - resolve(); - }, 0); - - process.nextTick(() => { - order.push("nextTick"); - }); - }); +if (process.argv[2] !== 'child') { + const child = spawn(process.execPath, [__filename, 'child'], { + cwd: path.dirname(process.execPath) }); - expect(order).toEqual(["nextTick", "setTimeout"]); -}); - -//<#END_FILE: test-next-tick-ordering2.js + let childArgv0 = ''; + child.stdout.on('data', function(chunk) { + childArgv0 += chunk; + }); + process.on('exit', function() { + assert.strictEqual(childArgv0, process.execPath); + }); +} else { + process.stdout.write(process.argv[0]); +} diff --git a/test/js/node/test/parallel/test-process-constants-noatime.js b/test/js/node/test/parallel/test-process-constants-noatime.js new file mode 100644 index 0000000000..bd1a848ed7 --- /dev/null +++ b/test/js/node/test/parallel/test-process-constants-noatime.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const constants = require('fs').constants; + +if (common.isLinux) { + assert('O_NOATIME' in constants); + assert.strictEqual(constants.O_NOATIME, 0x40000); +} else { + assert(!('O_NOATIME' in constants)); +} diff --git a/test/js/node/test/parallel/test-process-dlopen-undefined-exports.js b/test/js/node/test/parallel/test-process-dlopen-undefined-exports.js new file mode 100644 index 0000000000..3766a73a45 --- /dev/null +++ b/test/js/node/test/parallel/test-process-dlopen-undefined-exports.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const someBindingPath = './test/addons/hello-world/build/Release/binding.node'; + +assert.throws(() => { + process.dlopen({ exports: undefined }, someBindingPath); +}, Error); diff --git a/test/js/node/test/parallel/test-process-domain-segfault.js b/test/js/node/test/parallel/test-process-domain-segfault.js new file mode 100644 index 0000000000..78009f4687 --- /dev/null +++ b/test/js/node/test/parallel/test-process-domain-segfault.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test ensures that setting `process.domain` to `null` does not result in +// node crashing with a segfault. +// https://github.com/nodejs/node-v0.x-archive/issues/4256 + +process.domain = null; +setTimeout(function() { + console.log('this console.log statement should not make node crash'); +}, 1); diff --git a/test/js/node/test/parallel/test-process-emit.js b/test/js/node/test/parallel/test-process-emit.js new file mode 100644 index 0000000000..8c2ad675cf --- /dev/null +++ b/test/js/node/test/parallel/test-process-emit.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const sym = Symbol(); + +process.on('normal', common.mustCall((data) => { + assert.strictEqual(data, 'normalData'); +})); + +process.on(sym, common.mustCall((data) => { + assert.strictEqual(data, 'symbolData'); +})); + +process.on('SIGPIPE', common.mustCall((data) => { + assert.strictEqual(data, 'signalData'); +})); + +process.emit('normal', 'normalData'); +process.emit(sym, 'symbolData'); +process.emit('SIGPIPE', 'signalData'); + +assert.strictEqual(Number.isNaN(process._eventsCount), false); diff --git a/test/js/node/test/parallel/test-process-env-windows-error-reset.js b/test/js/node/test/parallel/test-process-env-windows-error-reset.js new file mode 100644 index 0000000000..881da06d29 --- /dev/null +++ b/test/js/node/test/parallel/test-process-env-windows-error-reset.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +// This checks that after accessing a missing env var, a subsequent +// env read will succeed even for empty variables. + +{ + process.env.FOO = ''; + process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions + const foo = process.env.FOO; + + assert.strictEqual(foo, ''); +} + +{ + process.env.FOO = ''; + process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions + const hasFoo = 'FOO' in process.env; + + assert.strictEqual(hasFoo, true); +} diff --git a/test/js/node/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js b/test/js/node/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js new file mode 100644 index 0000000000..f9e685a86e --- /dev/null +++ b/test/js/node/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js @@ -0,0 +1,12 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +throw new Error('foo'); diff --git a/test/js/node/test/parallel/test-process-exception-capture.js b/test/js/node/test/parallel/test-process-exception-capture.js new file mode 100644 index 0000000000..c84d3459e2 --- /dev/null +++ b/test/js/node/test/parallel/test-process-exception-capture.js @@ -0,0 +1,13 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +process.on('uncaughtException', common.mustNotCall()); +throw new Error('foo'); diff --git a/test/js/node/test/parallel/test-process-execpath.js b/test/js/node/test/parallel/test-process-execpath.js new file mode 100644 index 0000000000..0fce35e264 --- /dev/null +++ b/test/js/node/test/parallel/test-process-execpath.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('symlinks are weird on windows'); + +const assert = require('assert'); +const child_process = require('child_process'); +const fs = require('fs'); + +assert.strictEqual(process.execPath, fs.realpathSync(process.execPath)); + +if (process.argv[2] === 'child') { + // The console.log() output is part of the test here. + console.log(process.execPath); +} else { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + + const symlinkedNode = tmpdir.resolve('symlinked-node'); + fs.symlinkSync(process.execPath, symlinkedNode); + + const proc = child_process.spawnSync(symlinkedNode, [__filename, 'child']); + assert.strictEqual(proc.stderr.toString(), ''); + assert.strictEqual(proc.stdout.toString(), `${process.execPath}\n`); + assert.strictEqual(proc.status, 0); +} diff --git a/test/js/node/test/parallel/test-process-exit-from-before-exit.js b/test/js/node/test/parallel/test-process-exit-from-before-exit.js new file mode 100644 index 0000000000..7f20c22f0b --- /dev/null +++ b/test/js/node/test/parallel/test-process-exit-from-before-exit.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.on('beforeExit', common.mustCall(function() { + setTimeout(common.mustNotCall(), 5); + process.exit(0); // Should execute immediately even if we schedule new work. + assert.fail(); +})); diff --git a/test/js/node/test/parallel/test-process-exit-handler.js b/test/js/node/test/parallel/test-process-exit-handler.js new file mode 100644 index 0000000000..d74e320fe6 --- /dev/null +++ b/test/js/node/test/parallel/test-process-exit-handler.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); + +if (!common.isMainThread) + common.skip('execArgv does not affect Workers'); + +// This test ensures that no asynchronous operations are performed in the 'exit' +// handler. +// https://github.com/nodejs/node/issues/12322 + +process.on('exit', () => { + setTimeout(() => process.abort(), 0); // Should not run. + for (const start = Date.now(); Date.now() - start < 10;); +}); diff --git a/test/js/node/test/parallel/bad-unicode.test.js b/test/js/node/test/parallel/test-process-exit-recursive.js similarity index 75% rename from test/js/node/test/parallel/bad-unicode.test.js rename to test/js/node/test/parallel/test-process-exit-recursive.js index 26c70dbf53..727aa4abe7 100644 --- a/test/js/node/test/parallel/bad-unicode.test.js +++ b/test/js/node/test/parallel/test-process-exit-recursive.js @@ -1,6 +1,3 @@ -//#FILE: test-bad-unicode.js -//#SHA1: e9b8765f74af6588aff1bd7bfcbc6a19187d100e -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,17 +19,19 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); -test("eval with bad unicode throws SyntaxError", () => { - expect(() => { - eval('"\\uc/ef"'); - }).toThrow( - expect.objectContaining({ - name: "SyntaxError", - message: expect.any(String), - }), - ); +// Recursively calling .exit() should not overflow the call stack +let nexits = 0; + +process.on('exit', function(code) { + assert.strictEqual(nexits++, 0); + assert.strictEqual(code, 1); + + // Now override the exit code of 1 with 0 so that the test passes + process.exit(0); }); -//<#END_FILE: test-bad-unicode.js +process.exit(1); diff --git a/test/js/node/test/parallel/next-tick-domain.test.js b/test/js/node/test/parallel/test-process-exit.js similarity index 74% rename from test/js/node/test/parallel/next-tick-domain.test.js rename to test/js/node/test/parallel/test-process-exit.js index d975718da7..cd605949af 100644 --- a/test/js/node/test/parallel/next-tick-domain.test.js +++ b/test/js/node/test/parallel/test-process-exit.js @@ -1,6 +1,3 @@ -//#FILE: test-next-tick-domain.js -//#SHA1: 75db0e440cbc2eb2fc89b4486eb10e398042a88b -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,15 +19,17 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); -test("requiring domain should not change process.nextTick", () => { - const origNextTick = process.nextTick; +// Calling .exit() from within "exit" should not overflow the call stack +let nexits = 0; - require("domain"); - - // Requiring domain should not change nextTick. - expect(process.nextTick).toBe(origNextTick); +process.on('exit', function(code) { + assert.strictEqual(nexits++, 0); + assert.strictEqual(code, 0); + process.exit(); }); -//<#END_FILE: test-next-tick-domain.js +// "exit" should be emitted unprovoked diff --git a/test/js/node/test/parallel/test-process-features.js b/test/js/node/test/parallel/test-process-features.js new file mode 100644 index 0000000000..3b4677c561 --- /dev/null +++ b/test/js/node/test/parallel/test-process-features.js @@ -0,0 +1,22 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const keys = new Set(Object.keys(process.features)); + +assert.deepStrictEqual(keys, new Set([ + 'inspector', + 'debug', + 'uv', + 'ipv6', + 'tls_alpn', + 'tls_sni', + 'tls_ocsp', + 'tls', + 'cached_builtins', +])); + +for (const key of keys) { + assert.strictEqual(typeof process.features[key], 'boolean'); +} diff --git a/test/js/node/test/parallel/test-process-getgroups.js b/test/js/node/test/parallel/test-process-getgroups.js new file mode 100644 index 0000000000..28df13205f --- /dev/null +++ b/test/js/node/test/parallel/test-process-getgroups.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Check `id -G` and `process.getgroups()` return same groups. + +if (common.isMacOS) + common.skip('Output of `id -G` is unreliable on Darwin.'); + +const assert = require('assert'); +const exec = require('child_process').exec; + +if (typeof process.getgroups === 'function') { + const groups = unique(process.getgroups()); + assert(Array.isArray(groups)); + assert(groups.length > 0); + exec('id -G', function(err, stdout) { + assert.ifError(err); + const real_groups = unique(stdout.match(/\d+/g).map(Number)); + assert.deepStrictEqual(groups, real_groups); + check(groups, real_groups); + check(real_groups, groups); + }); +} + +function check(a, b) { + for (let i = 0; i < a.length; ++i) assert(b.includes(a[i])); +} + +function unique(groups) { + return [...new Set(groups)].sort(); +} diff --git a/test/js/node/test/parallel/test-process-hrtime-bigint.js b/test/js/node/test/parallel/test-process-hrtime-bigint.js new file mode 100644 index 0000000000..e5ce40a994 --- /dev/null +++ b/test/js/node/test/parallel/test-process-hrtime-bigint.js @@ -0,0 +1,14 @@ +'use strict'; + +// Tests that process.hrtime.bigint() works. + +require('../common'); +const assert = require('assert'); + +const start = process.hrtime.bigint(); +assert.strictEqual(typeof start, 'bigint'); + +const end = process.hrtime.bigint(); +assert.strictEqual(typeof end, 'bigint'); + +assert(end - start >= 0n); diff --git a/test/js/node/test/parallel/test-process-kill-null.js b/test/js/node/test/parallel/test-process-kill-null.js new file mode 100644 index 0000000000..88fc677454 --- /dev/null +++ b/test/js/node/test/parallel/test-process-kill-null.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { mustCall } = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +const cat = spawn('cat'); + +assert.ok(process.kill(cat.pid, 0)); + +cat.on('exit', mustCall(function() { + assert.throws(function() { + process.kill(cat.pid, 0); + }, Error); +})); + +cat.stdout.on('data', mustCall(function() { + process.kill(cat.pid, 'SIGKILL'); +})); + +// EPIPE when null sig fails +cat.stdin.write('test'); diff --git a/test/js/node/test/parallel/test-process-next-tick.js b/test/js/node/test/parallel/test-process-next-tick.js new file mode 100644 index 0000000000..66913beebf --- /dev/null +++ b/test/js/node/test/parallel/test-process-next-tick.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const N = 2; + +function cb() { + throw new Error(); +} + +for (let i = 0; i < N; ++i) { + process.nextTick(common.mustCall(cb)); +} + +process.on('uncaughtException', common.mustCall(N)); + +process.on('exit', function() { + process.removeAllListeners('uncaughtException'); +}); + +[null, 1, 'test', {}, [], Infinity, true].forEach((i) => { + assert.throws( + () => process.nextTick(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); +}); diff --git a/test/js/node/test/parallel/test-process-ppid.js b/test/js/node/test/parallel/test-process-ppid.js new file mode 100644 index 0000000000..d78ef3a2dd --- /dev/null +++ b/test/js/node/test/parallel/test-process-ppid.js @@ -0,0 +1,16 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (process.argv[2] === 'child') { + // The following console.log() call is part of the test's functionality. + console.log(process.ppid); +} else { + const child = cp.spawnSync(process.execPath, [__filename, 'child']); + + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(+child.stdout.toString().trim(), process.pid); + assert.strictEqual(child.stderr.toString().trim(), ''); +} diff --git a/test/js/node/test/parallel/test-process-remove-all-signal-listeners.js b/test/js/node/test/parallel/test-process-remove-all-signal-listeners.js new file mode 100644 index 0000000000..afd574daaa --- /dev/null +++ b/test/js/node/test/parallel/test-process-remove-all-signal-listeners.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (common.isWindows) + common.skip('Win32 does not support signals.'); + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] !== '--do-test') { + // We are the primary, fork a child so we can verify it exits with correct + // status. + process.env.DOTEST = 'y'; + const child = spawn(process.execPath, [__filename, '--do-test']); + + child.once('exit', common.mustCall(function(code, signal) { + assert.strictEqual(signal, 'SIGINT'); + })); + + return; +} + +process.on('SIGINT', function() { + // Remove all handlers and kill ourselves. We should terminate by SIGINT + // now that we have no handlers. + process.removeAllListeners('SIGINT'); + process.kill(process.pid, 'SIGINT'); +}); + +// Signal handlers aren't sufficient to keep node alive, so resume stdin +process.stdin.resume(); + +// Demonstrate that signals are being handled +process.kill(process.pid, 'SIGINT'); diff --git a/test/js/node/test/parallel/test-process-setuid-io-uring.js b/test/js/node/test/parallel/test-process-setuid-io-uring.js new file mode 100644 index 0000000000..93193ac2f8 --- /dev/null +++ b/test/js/node/test/parallel/test-process-setuid-io-uring.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +const assert = require('node:assert'); +const { execFileSync } = require('node:child_process'); + +if (!common.isLinux) { + common.skip('test is Linux specific'); +} + +if (process.arch !== 'x64' && process.arch !== 'arm64') { + common.skip('io_uring support on this architecture is uncertain'); +} + +const kv = /^(\d+)\.(\d+)\.(\d+)/.exec(execFileSync('uname', ['-r'])).slice(1).map((n) => parseInt(n, 10)); +if (((kv[0] << 16) | (kv[1] << 8) | kv[2]) < 0x050ABA) { + common.skip('io_uring is likely buggy due to old kernel'); +} + +const userIdentitySetters = [ + ['setuid', [1000]], + ['seteuid', [1000]], + ['setgid', [1000]], + ['setegid', [1000]], + ['setgroups', [[1000]]], + ['initgroups', ['nodeuser', 1000]], +]; + +for (const [fnName, args] of userIdentitySetters) { + const call = `process.${fnName}(${args.map((a) => JSON.stringify(a)).join(', ')})`; + const code = `try { ${call}; } catch (err) { console.log(err); }`; + + const stdout = execFileSync(process.execPath, ['-e', code], { + encoding: 'utf8', + env: { ...process.env, UV_USE_IO_URING: '1' }, + }); + + const msg = new RegExp(`^Error: ${fnName}\\(\\) disabled: io_uring may be enabled\\. See CVE-[X0-9]{4}-`); + assert.match(stdout, msg); + assert.match(stdout, /code: 'ERR_INVALID_STATE'/); + + console.log(call, stdout.slice(0, stdout.indexOf('\n'))); +} diff --git a/test/js/node/test/parallel/test-process-uptime.js b/test/js/node/test/parallel/test-process-uptime.js new file mode 100644 index 0000000000..eabb6cf266 --- /dev/null +++ b/test/js/node/test/parallel/test-process-uptime.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +console.error(process.uptime()); +// Add some wiggle room for different platforms. +// Verify that the returned value is in seconds - +// 15 seconds should be a good estimate. +assert.ok(process.uptime() <= 15); + +const original = process.uptime(); + +setTimeout(function() { + const uptime = process.uptime(); + assert.ok(original < uptime); +}, 10); diff --git a/test/js/node/test/parallel/test-promise-handled-rejection-no-warning.js b/test/js/node/test/parallel/test-promise-handled-rejection-no-warning.js new file mode 100644 index 0000000000..8878d67fab --- /dev/null +++ b/test/js/node/test/parallel/test-promise-handled-rejection-no-warning.js @@ -0,0 +1,7 @@ +'use strict'; +const common = require('../common'); + +// This test verifies that DEP0018 does not occur when rejections are handled. +process.on('warning', common.mustNotCall()); +process.on('unhandledRejection', common.mustCall()); +Promise.reject(new Error()); diff --git a/test/js/node/test/parallel/test-promise-unhandled-issue-43655.js b/test/js/node/test/parallel/test-promise-unhandled-issue-43655.js new file mode 100644 index 0000000000..4fd2c1a711 --- /dev/null +++ b/test/js/node/test/parallel/test-promise-unhandled-issue-43655.js @@ -0,0 +1,27 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +function delay(time) { + return new Promise((resolve) => { + setTimeout(resolve, time); + }); +} + +async function test() { + for (let i = 0; i < 100000; i++) { + await new Promise((resolve, reject) => { + reject('value'); + }) + .then(() => { }, () => { }); + } + + const time0 = Date.now(); + await delay(0); + + const diff = Date.now() - time0; + assert.ok(Date.now() - time0 < 500, `Expected less than 500ms, got ${diff}ms`); +} + +test(); diff --git a/test/js/node/test/parallel/test-promise-unhandled-silent.js b/test/js/node/test/parallel/test-promise-unhandled-silent.js new file mode 100644 index 0000000000..edf5111eae --- /dev/null +++ b/test/js/node/test/parallel/test-promise-unhandled-silent.js @@ -0,0 +1,21 @@ +// Flags: --unhandled-rejections=none +'use strict'; + +const common = require('../common'); + +// Verify that ignoring unhandled rejection works fine and that no warning is +// logged. + +new Promise(() => { + throw new Error('One'); +}); + +Promise.reject('test'); + +process.on('warning', common.mustNotCall('warning')); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); + +process.on('unhandledRejection', common.mustCall(2)); + +setTimeout(common.mustCall(), 2); diff --git a/test/js/node/test/parallel/test-promise-unhandled-throw-handler.js b/test/js/node/test/parallel/test-promise-unhandled-throw-handler.js new file mode 100644 index 0000000000..26a1d2f85c --- /dev/null +++ b/test/js/node/test/parallel/test-promise-unhandled-throw-handler.js @@ -0,0 +1,36 @@ +// Flags: --unhandled-rejections=throw +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); + +// Verify that the unhandledRejection handler prevents triggering +// uncaught exceptions + +const err1 = new Error('One'); + +const errors = [err1, null]; + +const ref = new Promise(() => { + throw err1; +}); +// Explicitly reject `null`. +Promise.reject(null); + +process.on('warning', common.mustNotCall('warning')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); +process.on('exit', assert.strictEqual.bind(null, 0)); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); + +const timer = setTimeout(() => console.log(ref), 1000); + +const counter = new Countdown(2, () => { + clearTimeout(timer); +}); + +process.on('unhandledRejection', common.mustCall((err) => { + counter.dec(); + const knownError = errors.shift(); + assert.deepStrictEqual(err, knownError); +}, 2)); diff --git a/test/js/node/test/parallel/test-querystring-maxKeys-non-finite.js b/test/js/node/test/parallel/test-querystring-maxKeys-non-finite.js new file mode 100644 index 0000000000..610c30c7a3 --- /dev/null +++ b/test/js/node/test/parallel/test-querystring-maxKeys-non-finite.js @@ -0,0 +1,58 @@ +'use strict'; +// This test was originally written to test a regression +// that was introduced by +// https://github.com/nodejs/node/pull/2288#issuecomment-179543894 +require('../common'); + +const assert = require('assert'); +const parse = require('querystring').parse; + +// Taken from express-js/body-parser +// https://github.com/expressjs/body-parser/blob/ed25264fb494cf0c8bc992b8257092cd4f694d5e/test/urlencoded.js#L636-L651 +function createManyParams(count) { + let str = ''; + + if (count === 0) { + return str; + } + + str += '0=0'; + + for (let i = 1; i < count; i++) { + const n = i.toString(36); + str += `&${n}=${n}`; + } + + return str; +} + +const count = 10000; +const originalMaxLength = 1000; +const params = createManyParams(count); + +// thealphanerd +// 27def4f introduced a change to parse that would cause Infinity +// to be passed to String.prototype.split as an argument for limit +// In this instance split will always return an empty array +// this test confirms that the output of parse is the expected length +// when passed Infinity as the argument for maxKeys +const resultInfinity = parse(params, undefined, undefined, { + maxKeys: Infinity +}); +const resultNaN = parse(params, undefined, undefined, { + maxKeys: NaN +}); +const resultInfinityString = parse(params, undefined, undefined, { + maxKeys: 'Infinity' +}); +const resultNaNString = parse(params, undefined, undefined, { + maxKeys: 'NaN' +}); + +// Non Finite maxKeys should return the length of input +assert.strictEqual(Object.keys(resultInfinity).length, count); +assert.strictEqual(Object.keys(resultNaN).length, count); +// Strings maxKeys should return the maxLength +// defined by parses internals +assert.strictEqual(Object.keys(resultInfinityString).length, originalMaxLength); +assert.strictEqual(Object.keys(resultNaNString).length, originalMaxLength); diff --git a/test/js/node/test/parallel/test-querystring-multichar-separator.js b/test/js/node/test/parallel/test-querystring-multichar-separator.js new file mode 100644 index 0000000000..720733b1e2 --- /dev/null +++ b/test/js/node/test/parallel/test-querystring-multichar-separator.js @@ -0,0 +1,25 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const qs = require('querystring'); + +function check(actual, expected) { + assert(!(actual instanceof Object)); + assert.deepStrictEqual(Object.keys(actual).sort(), + Object.keys(expected).sort()); + Object.keys(expected).forEach(function(key) { + assert.deepStrictEqual(actual[key], expected[key]); + }); +} + +check(qs.parse('foo=>bar&&bar=>baz', '&&', '=>'), + { foo: 'bar', bar: 'baz' }); + +check(qs.stringify({ foo: 'bar', bar: 'baz' }, '&&', '=>'), + 'foo=>bar&&bar=>baz'); + +check(qs.parse('foo==>bar, bar==>baz', ', ', '==>'), + { foo: 'bar', bar: 'baz' }); + +check(qs.stringify({ foo: 'bar', bar: 'baz' }, ', ', '==>'), + 'foo==>bar, bar==>baz'); diff --git a/test/js/node/test/parallel/test-queue-microtask-uncaught-asynchooks.js b/test/js/node/test/parallel/test-queue-microtask-uncaught-asynchooks.js new file mode 100644 index 0000000000..35b3d9fa30 --- /dev/null +++ b/test/js/node/test/parallel/test-queue-microtask-uncaught-asynchooks.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// Regression test for https://github.com/nodejs/node/issues/30080: +// An uncaught exception inside a queueMicrotask callback should not lead +// to multiple after() calls for it. + +let µtaskId; +const events = []; + +async_hooks.createHook({ + init(id, type, triggerId, resource) { + if (type === 'Microtask') { + µtaskId = id; + events.push('init'); + } + }, + before(id) { + if (id === µtaskId) events.push('before'); + }, + after(id) { + if (id === µtaskId) events.push('after'); + }, + destroy(id) { + if (id === µtaskId) events.push('destroy'); + } +}).enable(); + +queueMicrotask(() => { throw new Error(); }); + +process.on('uncaughtException', common.mustCall()); +process.on('exit', () => { + assert.deepStrictEqual(events, ['init', 'after', 'before', 'destroy']); +}); diff --git a/test/js/node/test/parallel/test-quic-internal-endpoint-listen-defaults.js b/test/js/node/test/parallel/test-quic-internal-endpoint-listen-defaults.js new file mode 100644 index 0000000000..9fb9f9461c --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-endpoint-listen-defaults.js @@ -0,0 +1,76 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { internalBinding } = require('internal/test/binding'); +const { + ok, + strictEqual, + deepStrictEqual, +} = require('node:assert'); + +const { + SocketAddress: _SocketAddress, + AF_INET, +} = internalBinding('block_list'); +const quic = internalBinding('quic'); + +quic.setCallbacks({ + onEndpointClose: common.mustCall((...args) => { + deepStrictEqual(args, [0, 0]); + }), + + // The following are unused in this test + onSessionNew() {}, + onSessionClose() {}, + onSessionDatagram() {}, + onSessionDatagramStatus() {}, + onSessionHandshake() {}, + onSessionPathValidation() {}, + onSessionTicket() {}, + onSessionVersionNegotiation() {}, + onStreamCreated() {}, + onStreamBlocked() {}, + onStreamClose() {}, + onStreamReset() {}, + onStreamHeaders() {}, + onStreamTrailers() {}, +}); + +const endpoint = new quic.Endpoint({}); + +const state = new DataView(endpoint.state); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND)); +strictEqual(endpoint.address(), undefined); + +endpoint.listen({}); + +ok(state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING)); +ok(state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING)); +ok(state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND)); +const address = endpoint.address(); +ok(address instanceof _SocketAddress); + +const detail = address.detail({ + address: undefined, + port: undefined, + family: undefined, + flowlabel: undefined, +}); + +strictEqual(detail.address, '127.0.0.1'); +strictEqual(detail.family, AF_INET); +strictEqual(detail.flowlabel, 0); +ok(detail.port !== 0); + +endpoint.closeGracefully(); + +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND)); +strictEqual(endpoint.address(), undefined); diff --git a/test/js/node/test/parallel/test-quic-internal-endpoint-options.js b/test/js/node/test/parallel/test-quic-internal-endpoint-options.js new file mode 100644 index 0000000000..672fac18b4 --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-endpoint-options.js @@ -0,0 +1,215 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); +const { + throws, +} = require('node:assert'); + +const { internalBinding } = require('internal/test/binding'); +const quic = internalBinding('quic'); + +quic.setCallbacks({ + onEndpointClose() {}, + onSessionNew() {}, + onSessionClose() {}, + onSessionDatagram() {}, + onSessionDatagramStatus() {}, + onSessionHandshake() {}, + onSessionPathValidation() {}, + onSessionTicket() {}, + onSessionVersionNegotiation() {}, + onStreamCreated() {}, + onStreamBlocked() {}, + onStreamClose() {}, + onStreamReset() {}, + onStreamHeaders() {}, + onStreamTrailers() {}, +}); + +throws(() => new quic.Endpoint(), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +throws(() => new quic.Endpoint('a'), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +throws(() => new quic.Endpoint(null), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +throws(() => new quic.Endpoint(false), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +{ + // Just Works... using all defaults + new quic.Endpoint({}); +} + +const cases = [ + { + key: 'retryTokenExpiration', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'tokenExpiration', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxConnectionsPerHost', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxConnectionsTotal', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxStatelessResetsPerHost', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'addressLRUSize', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxRetries', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxPayloadSize', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'unacknowledgedPacketThreshold', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'validateAddress', + valid: [true, false, 0, 1, 'a'], + invalid: [], + }, + { + key: 'disableStatelessReset', + valid: [true, false, 0, 1, 'a'], + invalid: [], + }, + { + key: 'ipv6Only', + valid: [true, false, 0, 1, 'a'], + invalid: [], + }, + { + key: 'cc', + valid: [ + quic.CC_ALGO_RENO, + quic.CC_ALGO_CUBIC, + quic.CC_ALGO_BBR, + quic.CC_ALGO_BBR2, + quic.CC_ALGO_RENO_STR, + quic.CC_ALGO_CUBIC_STR, + quic.CC_ALGO_BBR_STR, + quic.CC_ALGO_BBR2_STR, + ], + invalid: [-1, 4, 1n, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'udpReceiveBufferSize', + valid: [0, 1, 2, 3, 4, 1000], + invalid: [-1, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'udpSendBufferSize', + valid: [0, 1, 2, 3, 4, 1000], + invalid: [-1, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'udpTTL', + valid: [0, 1, 2, 3, 4, 255], + invalid: [-1, 256, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'resetTokenSecret', + valid: [ + new Uint8Array(16), + new Uint16Array(8), + new Uint32Array(4), + ], + invalid: [ + 'a', null, false, true, {}, [], () => {}, + new Uint8Array(15), + new Uint8Array(17), + new ArrayBuffer(16), + ], + }, + { + key: 'tokenSecret', + valid: [ + new Uint8Array(16), + new Uint16Array(8), + new Uint32Array(4), + ], + invalid: [ + 'a', null, false, true, {}, [], () => {}, + new Uint8Array(15), + new Uint8Array(17), + new ArrayBuffer(16), + ], + }, + { + // Unknown options are ignored entirely for any value type + key: 'ignored', + valid: ['a', null, false, true, {}, [], () => {}], + invalid: [], + }, +]; + +for (const { key, valid, invalid } of cases) { + for (const value of valid) { + const options = {}; + options[key] = value; + new quic.Endpoint(options); + } + + for (const value of invalid) { + const options = {}; + options[key] = value; + throws(() => new quic.Endpoint(options), { + code: 'ERR_INVALID_ARG_VALUE', + }); + } +} diff --git a/test/js/node/test/parallel/test-quic-internal-endpoint-stats-state.js b/test/js/node/test/parallel/test-quic-internal-endpoint-stats-state.js new file mode 100644 index 0000000000..566dd675d7 --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-endpoint-stats-state.js @@ -0,0 +1,79 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); +const { + strictEqual, +} = require('node:assert'); + +const { internalBinding } = require('internal/test/binding'); +const quic = internalBinding('quic'); + +const { + IDX_STATS_ENDPOINT_CREATED_AT, + IDX_STATS_ENDPOINT_DESTROYED_AT, + IDX_STATS_ENDPOINT_BYTES_RECEIVED, + IDX_STATS_ENDPOINT_BYTES_SENT, + IDX_STATS_ENDPOINT_PACKETS_RECEIVED, + IDX_STATS_ENDPOINT_PACKETS_SENT, + IDX_STATS_ENDPOINT_SERVER_SESSIONS, + IDX_STATS_ENDPOINT_CLIENT_SESSIONS, + IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT, + IDX_STATS_ENDPOINT_RETRY_COUNT, + IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT, + IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT, + IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT, + IDX_STATS_ENDPOINT_COUNT, + IDX_STATE_ENDPOINT_BOUND, + IDX_STATE_ENDPOINT_BOUND_SIZE, + IDX_STATE_ENDPOINT_RECEIVING, + IDX_STATE_ENDPOINT_RECEIVING_SIZE, + IDX_STATE_ENDPOINT_LISTENING, + IDX_STATE_ENDPOINT_LISTENING_SIZE, + IDX_STATE_ENDPOINT_CLOSING, + IDX_STATE_ENDPOINT_CLOSING_SIZE, + IDX_STATE_ENDPOINT_BUSY, + IDX_STATE_ENDPOINT_BUSY_SIZE, + IDX_STATE_ENDPOINT_PENDING_CALLBACKS, + IDX_STATE_ENDPOINT_PENDING_CALLBACKS_SIZE, +} = quic; + +const endpoint = new quic.Endpoint({}); + +const state = new DataView(endpoint.state); +strictEqual(IDX_STATE_ENDPOINT_BOUND_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_RECEIVING_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_LISTENING_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_CLOSING_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_BUSY_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_PENDING_CALLBACKS_SIZE, 8); + +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BOUND), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_RECEIVING), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_LISTENING), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_CLOSING), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BUSY), 0); +strictEqual(state.getBigUint64(IDX_STATE_ENDPOINT_PENDING_CALLBACKS), 0n); + +endpoint.markBusy(true); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BUSY), 1); +endpoint.markBusy(false); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BUSY), 0); + +const stats = new BigUint64Array(endpoint.stats); +strictEqual(stats[IDX_STATS_ENDPOINT_CREATED_AT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_DESTROYED_AT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_BYTES_RECEIVED], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_BYTES_SENT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_PACKETS_RECEIVED], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_PACKETS_SENT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_SERVER_SESSIONS], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_CLIENT_SESSIONS], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_RETRY_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT], 0n); +strictEqual(IDX_STATS_ENDPOINT_COUNT, 13); diff --git a/test/js/node/test/parallel/test-quic-internal-setcallbacks.js b/test/js/node/test/parallel/test-quic-internal-setcallbacks.js new file mode 100644 index 0000000000..881e9161ca --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-setcallbacks.js @@ -0,0 +1,38 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); +const { internalBinding } = require('internal/test/binding'); +const quic = internalBinding('quic'); + +const { throws } = require('assert'); + +const callbacks = { + onEndpointClose() {}, + onSessionNew() {}, + onSessionClose() {}, + onSessionDatagram() {}, + onSessionDatagramStatus() {}, + onSessionHandshake() {}, + onSessionPathValidation() {}, + onSessionTicket() {}, + onSessionVersionNegotiation() {}, + onStreamCreated() {}, + onStreamBlocked() {}, + onStreamClose() {}, + onStreamReset() {}, + onStreamHeaders() {}, + onStreamTrailers() {}, +}; +// Fail if any callback is missing +for (const fn of Object.keys(callbacks)) { + // eslint-disable-next-line no-unused-vars + const { [fn]: _, ...rest } = callbacks; + throws(() => quic.setCallbacks(rest), { + code: 'ERR_MISSING_ARGS', + }); +} +// If all callbacks are present it should work +quic.setCallbacks(callbacks); diff --git a/test/js/node/test/parallel/test-readable-from-iterator-closing.js b/test/js/node/test/parallel/test-readable-from-iterator-closing.js new file mode 100644 index 0000000000..02252ffe56 --- /dev/null +++ b/test/js/node/test/parallel/test-readable-from-iterator-closing.js @@ -0,0 +1,197 @@ +'use strict'; + +const { mustCall, mustNotCall } = require('../common'); +const { Readable } = require('stream'); +const { strictEqual } = require('assert'); + +async function asyncSupport() { + const finallyMustCall = mustCall(); + const bodyMustCall = mustCall(); + + async function* infiniteGenerate() { + try { + while (true) yield 'a'; + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(infiniteGenerate()); + + for await (const chunk of stream) { + bodyMustCall(); + strictEqual(chunk, 'a'); + break; + } +} + +async function syncSupport() { + const finallyMustCall = mustCall(); + const bodyMustCall = mustCall(); + + function* infiniteGenerate() { + try { + while (true) yield 'a'; + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(infiniteGenerate()); + + for await (const chunk of stream) { + bodyMustCall(); + strictEqual(chunk, 'a'); + break; + } +} + +async function syncPromiseSupport() { + const returnMustBeAwaited = mustCall(); + const bodyMustCall = mustCall(); + + function* infiniteGenerate() { + try { + while (true) yield Promise.resolve('a'); + } finally { + // eslint-disable-next-line no-unsafe-finally + return { then(cb) { + returnMustBeAwaited(); + cb(); + } }; + } + } + + const stream = Readable.from(infiniteGenerate()); + + for await (const chunk of stream) { + bodyMustCall(); + strictEqual(chunk, 'a'); + break; + } +} + +async function syncRejectedSupport() { + const returnMustBeAwaited = mustCall(); + const bodyMustNotCall = mustNotCall(); + const catchMustCall = mustCall(); + const secondNextMustNotCall = mustNotCall(); + + function* generate() { + try { + yield Promise.reject('a'); + secondNextMustNotCall(); + } finally { + // eslint-disable-next-line no-unsafe-finally + return { then(cb) { + returnMustBeAwaited(); + cb(); + } }; + } + } + + const stream = Readable.from(generate()); + + try { + for await (const chunk of stream) { + bodyMustNotCall(chunk); + } + } catch { + catchMustCall(); + } +} + +async function noReturnAfterThrow() { + const returnMustNotCall = mustNotCall(); + const bodyMustNotCall = mustNotCall(); + const catchMustCall = mustCall(); + const nextMustCall = mustCall(); + + const stream = Readable.from({ + [Symbol.asyncIterator]() { return this; }, + async next() { + nextMustCall(); + throw new Error('a'); + }, + async return() { + returnMustNotCall(); + return { done: true }; + }, + }); + + try { + for await (const chunk of stream) { + bodyMustNotCall(chunk); + } + } catch { + catchMustCall(); + } +} + +async function closeStreamWhileNextIsPending() { + const finallyMustCall = mustCall(); + const dataMustCall = mustCall(); + + let resolveDestroy; + const destroyed = + new Promise((resolve) => { resolveDestroy = mustCall(resolve); }); + let resolveYielded; + const yielded = + new Promise((resolve) => { resolveYielded = mustCall(resolve); }); + + async function* infiniteGenerate() { + try { + while (true) { + yield 'a'; + resolveYielded(); + await destroyed; + } + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(infiniteGenerate()); + + stream.on('data', (data) => { + dataMustCall(); + strictEqual(data, 'a'); + }); + + yielded.then(() => { + stream.destroy(); + resolveDestroy(); + }); +} + +async function closeAfterNullYielded() { + const finallyMustCall = mustCall(); + const dataMustCall = mustCall(3); + + function* generate() { + try { + yield 'a'; + yield 'a'; + yield 'a'; + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(generate()); + + stream.on('data', (chunk) => { + dataMustCall(); + strictEqual(chunk, 'a'); + }); +} + +Promise.all([ + asyncSupport(), + syncSupport(), + syncPromiseSupport(), + syncRejectedSupport(), + noReturnAfterThrow(), + closeStreamWhileNextIsPending(), + closeAfterNullYielded(), +]).then(mustCall()); diff --git a/test/js/node/test/parallel/test-readable-from.js b/test/js/node/test/parallel/test-readable-from.js new file mode 100644 index 0000000000..b844574dc9 --- /dev/null +++ b/test/js/node/test/parallel/test-readable-from.js @@ -0,0 +1,223 @@ +'use strict'; + +const { mustCall } = require('../common'); +const { once } = require('events'); +const { Readable } = require('stream'); +const { strictEqual, throws } = require('assert'); +const common = require('../common'); + +{ + throws(() => { + Readable.from(null); + }, /ERR_INVALID_ARG_TYPE/); +} + +async function toReadableBasicSupport() { + async function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate()); + + const expected = ['a', 'b', 'c']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadableSyncIterator() { + function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate()); + + const expected = ['a', 'b', 'c']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadablePromises() { + const promises = [ + Promise.resolve('a'), + Promise.resolve('b'), + Promise.resolve('c'), + ]; + + const stream = Readable.from(promises); + + const expected = ['a', 'b', 'c']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadableString() { + const stream = Readable.from('abc'); + + const expected = ['abc']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadableBuffer() { + const stream = Readable.from(Buffer.from('abc')); + + const expected = ['abc']; + + for await (const chunk of stream) { + strictEqual(chunk.toString(), expected.shift()); + } +} + +async function toReadableOnData() { + async function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate()); + + let iterations = 0; + const expected = ['a', 'b', 'c']; + + stream.on('data', (chunk) => { + iterations++; + strictEqual(chunk, expected.shift()); + }); + + await once(stream, 'end'); + + strictEqual(iterations, 3); +} + +async function toReadableOnDataNonObject() { + async function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate(), { objectMode: false }); + + let iterations = 0; + const expected = ['a', 'b', 'c']; + + stream.on('data', (chunk) => { + iterations++; + strictEqual(chunk instanceof Buffer, true); + strictEqual(chunk.toString(), expected.shift()); + }); + + await once(stream, 'end'); + + strictEqual(iterations, 3); +} + +async function destroysTheStreamWhenThrowing() { + async function* generate() { // eslint-disable-line require-yield + throw new Error('kaboom'); + } + + const stream = Readable.from(generate()); + + stream.read(); + + const [err] = await once(stream, 'error'); + strictEqual(err.message, 'kaboom'); + strictEqual(stream.destroyed, true); + +} + +async function asTransformStream() { + async function* generate(stream) { + for await (const chunk of stream) { + yield chunk.toUpperCase(); + } + } + + const source = new Readable({ + objectMode: true, + read() { + this.push('a'); + this.push('b'); + this.push('c'); + this.push(null); + } + }); + + const stream = Readable.from(generate(source)); + + const expected = ['A', 'B', 'C']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function endWithError() { + async function* generate() { + yield 1; + yield 2; + yield Promise.reject('Boum'); + } + + const stream = Readable.from(generate()); + + const expected = [1, 2]; + + try { + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } + throw new Error(); + } catch (err) { + strictEqual(expected.length, 0); + strictEqual(err, 'Boum'); + } +} + +async function destroyingStreamWithErrorThrowsInGenerator() { + const validateError = common.mustCall((e) => { + strictEqual(e, 'Boum'); + }); + async function* generate() { + try { + yield 1; + yield 2; + yield 3; + throw new Error(); + } catch (e) { + validateError(e); + } + } + const stream = Readable.from(generate()); + stream.read(); + stream.once('error', common.mustCall()); + stream.destroy('Boum'); +} + +Promise.all([ + toReadableBasicSupport(), + toReadableSyncIterator(), + toReadablePromises(), + toReadableString(), + toReadableBuffer(), + toReadableOnData(), + toReadableOnDataNonObject(), + destroysTheStreamWhenThrowing(), + asTransformStream(), + endWithError(), + destroyingStreamWithErrorThrowsInGenerator(), +]).then(mustCall()); diff --git a/test/js/node/test/parallel/test-readable-large-hwm.js b/test/js/node/test/parallel/test-readable-large-hwm.js new file mode 100644 index 0000000000..d5bf25bc0e --- /dev/null +++ b/test/js/node/test/parallel/test-readable-large-hwm.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +// Make sure that readable completes +// even when reading larger buffer. +const bufferSize = 10 * 1024 * 1024; +let n = 0; +const r = new Readable({ + read() { + // Try to fill readable buffer piece by piece. + r.push(Buffer.alloc(bufferSize / 10)); + + if (n++ > 10) { + r.push(null); + } + } +}); + +r.on('readable', () => { + while (true) { + const ret = r.read(bufferSize); + if (ret === null) + break; + } +}); +r.on('end', common.mustCall()); diff --git a/test/js/node/test/parallel/test-readable-single-end.js b/test/js/node/test/parallel/test-readable-single-end.js new file mode 100644 index 0000000000..0969d49aa4 --- /dev/null +++ b/test/js/node/test/parallel/test-readable-single-end.js @@ -0,0 +1,16 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +// This test ensures that there will not be an additional empty 'readable' +// event when stream has ended (only 1 event signalling about end) + +const r = new Readable({ + read: () => {}, +}); + +r.push(null); + +r.on('readable', common.mustCall()); +r.on('end', common.mustCall()); diff --git a/test/js/node/test/parallel/test-readline-async-iterators-destroy.js b/test/js/node/test/parallel/test-readline-async-iterators-destroy.js new file mode 100644 index 0000000000..0a3fb01890 --- /dev/null +++ b/test/js/node/test/parallel/test-readline-async-iterators-destroy.js @@ -0,0 +1,89 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const { once } = require('events'); +const readline = require('readline'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filename = tmpdir.resolve('test.txt'); + +const testContents = [ + '', + '\n', + 'line 1', + 'line 1\nline 2 南越国是前203年至前111年存在于岭南地区的一个国家\nline 3\ntrailing', + 'line 1\nline 2\nline 3 ends with newline\n', +]; + +async function testSimpleDestroy() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const iteratedLines = []; + for await (const k of rli) { + iteratedLines.push(k); + break; + } + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + expectedLines.splice(1); + + assert.deepStrictEqual(iteratedLines, expectedLines); + + rli.close(); + readable.destroy(); + + await once(readable, 'close'); + } +} + +async function testMutualDestroy() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + expectedLines.splice(2); + + const iteratedLines = []; + for await (const k of rli) { + iteratedLines.push(k); + for await (const l of rli) { + iteratedLines.push(l); + break; + } + assert.deepStrictEqual(iteratedLines, expectedLines); + break; + } + + assert.deepStrictEqual(iteratedLines, expectedLines); + + rli.close(); + readable.destroy(); + + await once(readable, 'close'); + } +} + +testSimpleDestroy().then(testMutualDestroy).then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-readline-async-iterators.js b/test/js/node/test/parallel/test-readline-async-iterators.js new file mode 100644 index 0000000000..32fa32a128 --- /dev/null +++ b/test/js/node/test/parallel/test-readline-async-iterators.js @@ -0,0 +1,120 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const readline = require('readline'); +const { Readable } = require('stream'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filename = tmpdir.resolve('test.txt'); + +const testContents = [ + '', + '\n', + 'line 1', + 'line 1\nline 2 南越国是前203年至前111年存在于岭南地区的一个国家\nline 3\ntrailing', + 'line 1\nline 2\nline 3 ends with newline\n', +]; + +async function testSimple() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const iteratedLines = []; + for await (const k of rli) { + iteratedLines.push(k); + } + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + assert.deepStrictEqual(iteratedLines, expectedLines); + assert.strictEqual(iteratedLines.join(''), fileContent.replace(/\n/g, '')); + } +} + +async function testMutual() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + const iteratedLines = []; + let iterated = false; + for await (const k of rli) { + // This outer loop should only iterate once. + assert.strictEqual(iterated, false); + iterated = true; + iteratedLines.push(k); + for await (const l of rli) { + iteratedLines.push(l); + } + assert.deepStrictEqual(iteratedLines, expectedLines); + } + assert.deepStrictEqual(iteratedLines, expectedLines); + } +} + +async function testSlowStreamForLeaks() { + const message = 'a\nb\nc\n'; + const DELAY = 1; + const REPETITIONS = 100; + const warningCallback = common.mustNotCall(); + process.on('warning', warningCallback); + + function getStream() { + const readable = Readable({ + objectMode: true, + }); + readable._read = () => {}; + let i = REPETITIONS; + function schedule() { + setTimeout(() => { + i--; + if (i < 0) { + readable.push(null); + } else { + readable.push(message); + schedule(); + } + }, DELAY); + } + schedule(); + return readable; + } + const iterable = readline.createInterface({ + input: getStream(), + }); + + let lines = 0; + // eslint-disable-next-line no-unused-vars + for await (const _ of iterable) { + lines++; + } + + assert.strictEqual(lines, 3 * REPETITIONS); + process.off('warning', warningCallback); +} + +testSimple() + .then(testMutual) + .then(testSlowStreamForLeaks) + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-readline-emit-keypress-events.js b/test/js/node/test/parallel/test-readline-emit-keypress-events.js new file mode 100644 index 0000000000..a9ffd3276c --- /dev/null +++ b/test/js/node/test/parallel/test-readline-emit-keypress-events.js @@ -0,0 +1,72 @@ +'use strict'; +// emitKeypressEvents is thoroughly tested in test-readline-keys.js. +// However, that test calls it implicitly. This is just a quick sanity check +// to verify that it works when called explicitly. + +require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const PassThrough = require('stream').PassThrough; + +const expectedSequence = ['f', 'o', 'o']; +const expectedKeys = [ + { sequence: 'f', name: 'f', ctrl: false, meta: false, shift: false }, + { sequence: 'o', name: 'o', ctrl: false, meta: false, shift: false }, + { sequence: 'o', name: 'o', ctrl: false, meta: false, shift: false }, +]; + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + + readline.emitKeypressEvents(stream); + stream.on('keypress', (s, k) => { + sequence.push(s); + keys.push(k); + }); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + + stream.on('keypress', (s, k) => { + sequence.push(s); + keys.push(k); + }); + readline.emitKeypressEvents(stream); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + const keypressListener = (s, k) => { + sequence.push(s); + keys.push(k); + }; + + stream.on('keypress', keypressListener); + readline.emitKeypressEvents(stream); + stream.removeListener('keypress', keypressListener); + stream.write('foo'); + + assert.deepStrictEqual(sequence, []); + assert.deepStrictEqual(keys, []); + + stream.on('keypress', keypressListener); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} diff --git a/test/js/node/test/parallel/test-readline-interface-escapecodetimeout.js b/test/js/node/test/parallel/test-readline-interface-escapecodetimeout.js new file mode 100644 index 0000000000..a0c0e77cb8 --- /dev/null +++ b/test/js/node/test/parallel/test-readline-interface-escapecodetimeout.js @@ -0,0 +1,46 @@ +'use strict'; +require('../common'); + +// This test ensures that the escapeCodeTimeout option set correctly + +const assert = require('assert'); +const readline = require('readline'); +const EventEmitter = require('events').EventEmitter; + +class FakeInput extends EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: 50 + }); + assert.strictEqual(rli.escapeCodeTimeout, 50); + rli.close(); +} + +[ + null, + {}, + NaN, + '50', +].forEach((invalidInput) => { + assert.throws(() => { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: invalidInput + }); + rli.close(); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); +}); diff --git a/test/js/node/test/parallel/test-readline-position.js b/test/js/node/test/parallel/test-readline-position.js new file mode 100644 index 0000000000..3603a42ece --- /dev/null +++ b/test/js/node/test/parallel/test-readline-position.js @@ -0,0 +1,36 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); +const readline = require('readline'); +const assert = require('assert'); + +const ctrlU = { ctrl: true, name: 'u' }; + +common.skipIfDumbTerminal(); + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input, + prompt: '' + }); + + const tests = [ + [1, 'a'], + [2, 'ab'], + [2, '丁'], + [0, '\u0301'], // COMBINING ACUTE ACCENT + [1, 'a\u0301'], // á + [0, '\u20DD'], // COMBINING ENCLOSING CIRCLE + [2, 'a\u20DDb'], // a⃝b + [0, '\u200E'], // LEFT-TO-RIGHT MARK + ]; + + for (const [cursor, string] of tests) { + rl.write(string); + assert.strictEqual(rl.getCursorPos().cols, cursor); + rl.write(null, ctrlU); + } +} diff --git a/test/js/node/test/parallel/test-readline-reopen.js b/test/js/node/test/parallel/test-readline-reopen.js new file mode 100644 index 0000000000..fd305fee3e --- /dev/null +++ b/test/js/node/test/parallel/test-readline-reopen.js @@ -0,0 +1,44 @@ +'use strict'; + +// Regression test for https://github.com/nodejs/node/issues/13557 +// Tests that multiple subsequent readline instances can re-use an input stream. + +const common = require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const { PassThrough } = require('stream'); + +const input = new PassThrough(); +const output = new PassThrough(); + +const rl1 = readline.createInterface({ + input, + output, + terminal: true +}); + +rl1.on('line', common.mustCall(rl1OnLine)); + +// Write a line plus the first byte of a UTF-8 multibyte character to make sure +// that it doesn’t get lost when closing the readline instance. +input.write(Buffer.concat([ + Buffer.from('foo\n'), + Buffer.from([ 0xe2 ]), // Exactly one third of a ☃ snowman. +])); + +function rl1OnLine(line) { + assert.strictEqual(line, 'foo'); + rl1.close(); + const rl2 = readline.createInterface({ + input, + output, + terminal: true + }); + + rl2.on('line', common.mustCall((line) => { + assert.strictEqual(line, '☃bar'); + rl2.close(); + })); + input.write(Buffer.from([0x98, 0x83])); // The rest of the ☃ snowman. + input.write('bar\n'); +} diff --git a/test/js/node/test/parallel/test-readline-undefined-columns.js b/test/js/node/test/parallel/test-readline-undefined-columns.js new file mode 100644 index 0000000000..25bafe957f --- /dev/null +++ b/test/js/node/test/parallel/test-readline-undefined-columns.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const PassThrough = require('stream').PassThrough; +const readline = require('readline'); + +common.skipIfDumbTerminal(); + +// Checks that tab completion still works +// when output column size is undefined + +const iStream = new PassThrough(); +const oStream = new PassThrough(); + +readline.createInterface({ + terminal: true, + input: iStream, + output: oStream, + completer: function(line, cb) { + cb(null, [['process.stdout', 'process.stdin', 'process.stderr'], line]); + } +}); + +let output = ''; + +oStream.on('data', function(data) { + output += data; +}); + +oStream.on('end', common.mustCall(() => { + const expect = 'process.stdout\r\n' + + 'process.stdin\r\n' + + 'process.stderr'; + assert.match(output, new RegExp(expect)); +})); + +iStream.write('process.s\t'); + +// Completion works. +assert.match(output, /process\.std\b/); +// Completion doesn’t show all results yet. +assert.doesNotMatch(output, /stdout/); + +iStream.write('\t'); +oStream.end(); diff --git a/test/js/node/test/parallel/test-readline.js b/test/js/node/test/parallel/test-readline.js new file mode 100644 index 0000000000..77799fc14c --- /dev/null +++ b/test/js/node/test/parallel/test-readline.js @@ -0,0 +1,151 @@ +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); +const readline = require('readline'); +const assert = require('assert'); + +common.skipIfDumbTerminal(); + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustCall((data) => { + assert.strictEqual(data, 'abc'); + })); + + input.end('abc'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustNotCall('must not be called before newline')); + + input.write('abc'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustCall((data) => { + assert.strictEqual(data, 'abc'); + })); + + input.write('abc\n'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.write('foo'); + assert.strictEqual(rl.cursor, 3); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + end: ['\x1b[F', { ctrl: true, name: 'e' }], + }, + gnome: { + home: ['\x1bOH', { ctrl: true, name: 'a' }], + end: ['\x1bOF', { ctrl: true, name: 'e' }] + }, + rxvt: { + home: ['\x1b[7', { ctrl: true, name: 'a' }], + end: ['\x1b[8', { ctrl: true, name: 'e' }] + }, + putty: { + home: ['\x1b[1~', { ctrl: true, name: 'a' }], + end: ['\x1b[>~', { ctrl: true, name: 'e' }] + } + }; + + [key.xterm, key.gnome, key.rxvt, key.putty].forEach(function(key) { + rl.write.apply(rl, key.home); + assert.strictEqual(rl.cursor, 0); + rl.write.apply(rl, key.end); + assert.strictEqual(rl.cursor, 3); + }); + +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + metab: ['\x1bb', { meta: true, name: 'b' }], + metaf: ['\x1bf', { meta: true, name: 'f' }], + } + }; + + rl.write('foo bar.hop/zoo'); + rl.write.apply(rl, key.xterm.home); + [ + { cursor: 4, key: key.xterm.metaf }, + { cursor: 7, key: key.xterm.metaf }, + { cursor: 8, key: key.xterm.metaf }, + { cursor: 11, key: key.xterm.metaf }, + { cursor: 12, key: key.xterm.metaf }, + { cursor: 15, key: key.xterm.metaf }, + { cursor: 12, key: key.xterm.metab }, + { cursor: 11, key: key.xterm.metab }, + { cursor: 8, key: key.xterm.metab }, + { cursor: 7, key: key.xterm.metab }, + { cursor: 4, key: key.xterm.metab }, + { cursor: 0, key: key.xterm.metab }, + ].forEach(function(action) { + rl.write.apply(rl, action.key); + assert.strictEqual(rl.cursor, action.cursor); + }); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + metad: ['\x1bd', { meta: true, name: 'd' }] + } + }; + + rl.write('foo bar.hop/zoo'); + rl.write.apply(rl, key.xterm.home); + [ + 'bar.hop/zoo', + '.hop/zoo', + 'hop/zoo', + '/zoo', + 'zoo', + '', + ].forEach(function(expectedLine) { + rl.write.apply(rl, key.xterm.metad); + assert.strictEqual(rl.cursor, 0); + assert.strictEqual(rl.line, expectedLine); + }); +} diff --git a/test/js/node/test/parallel/test-ref-unref-return.js b/test/js/node/test/parallel/test-ref-unref-return.js new file mode 100644 index 0000000000..aec2fff5ce --- /dev/null +++ b/test/js/node/test/parallel/test-ref-unref-return.js @@ -0,0 +1,12 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); +const dgram = require('dgram'); + +assert.ok((new net.Server()).ref() instanceof net.Server); +assert.ok((new net.Server()).unref() instanceof net.Server); +assert.ok((new net.Socket()).ref() instanceof net.Socket); +assert.ok((new net.Socket()).unref() instanceof net.Socket); +assert.ok((new dgram.Socket('udp4')).ref() instanceof dgram.Socket); +assert.ok((new dgram.Socket('udp6')).unref() instanceof dgram.Socket); diff --git a/test/js/node/test/parallel/test-regression-object-prototype.js b/test/js/node/test/parallel/test-regression-object-prototype.js new file mode 100644 index 0000000000..2ea1ba858a --- /dev/null +++ b/test/js/node/test/parallel/test-regression-object-prototype.js @@ -0,0 +1,28 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-disable node-core/require-common-first, node-core/required-modules */ +'use strict'; + +Object.prototype.xadsadsdasasdxx = function() { +}; + +console.log('puts after'); diff --git a/test/js/node/test/parallel/test-repl-clear-immediate-crash.js b/test/js/node/test/parallel/test-repl-clear-immediate-crash.js new file mode 100644 index 0000000000..ce8aaf48e7 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-clear-immediate-crash.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const child_process = require('child_process'); +const assert = require('assert'); + +// Regression test for https://github.com/nodejs/node/issues/37806: +const proc = child_process.spawn(process.execPath, ['-i']); +proc.on('error', common.mustNotCall()); +proc.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); +proc.stdin.write('clearImmediate({});\n.exit\n'); diff --git a/test/js/node/test/parallel/test-repl-dynamic-import.js b/test/js/node/test/parallel/test-repl-dynamic-import.js new file mode 100644 index 0000000000..a043e31bf5 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-dynamic-import.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const child = child_process.spawn(process.execPath, [ + '--interactive', + '--expose-gc', +], { + stdio: 'pipe' +}); +child.stdin.write('\nimport("fs");\n_.then(gc);\n'); +// Wait for concurrent GC to finish +setTimeout(() => { + child.stdin.write('\nimport("fs");\n'); + child.stdin.write('\nprocess.exit(0);\n'); +}, common.platformTimeout(50)); +child.on('exit', (code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); +}); diff --git a/test/js/node/test/parallel/test-repl-preview-without-inspector.js b/test/js/node/test/parallel/test-repl-preview-without-inspector.js new file mode 100644 index 0000000000..8905d21483 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-preview-without-inspector.js @@ -0,0 +1,161 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { REPLServer } = require('repl'); +const { Stream } = require('stream'); + +if (process.features.inspector) + common.skip('test is for node compiled with --without-inspector only'); + +// Ignore terminal settings. This is so the test can be run intact if TERM=dumb. +process.env.TERM = ''; +const PROMPT = 'repl > '; + +class REPLStream extends Stream { + readable = true; + writable = true; + + constructor() { + super(); + this.lines = ['']; + } + run(data) { + for (const entry of data) { + this.emit('data', entry); + } + this.emit('data', '\n'); + } + write(chunk) { + const chunkLines = chunk.toString('utf8').split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line'); + return true; + } + wait() { + this.lines = ['']; + return new Promise((resolve, reject) => { + const onError = (err) => { + this.removeListener('line', onLine); + reject(err); + }; + const onLine = () => { + if (this.lines[this.lines.length - 1].includes(PROMPT)) { + this.removeListener('error', onError); + this.removeListener('line', onLine); + resolve(this.lines); + } + }; + this.once('error', onError); + this.on('line', onLine); + }); + } + pause() { } + resume() { } +} + +function runAndWait(cmds, repl) { + const promise = repl.inputStream.wait(); + for (const cmd of cmds) { + repl.inputStream.run(cmd); + } + return promise; +} + +const repl = REPLServer({ + prompt: PROMPT, + stream: new REPLStream(), + ignoreUndefined: true, + useColors: true, + terminal: true, +}); + +repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', + 'a = undefined;', + 'const r = 5;', +]); + +const testCases = [{ + input: 'foo', + preview: [ + 'foo\r', + '\x1B[36m[Function: foo]\x1B[39m', + ] +}, { + input: 'r', + preview: [ + 'r\r', + '\x1B[33m5\x1B[39m', + ] +}, { + input: 'koo', + preview: [ + 'koo\r', + '\x1B[36m[Function: koo]\x1B[39m', + ] +}, { + input: 'a', + preview: ['a\r'] // No "undefined" preview. +}, { + input: " { b: 1 }['b'] === 1", + preview: [ + " { b: 1 }['b'] === 1\r", + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: "{ b: 1 }['b'] === 1;", + preview: [ + "{ b: 1 }['b'] === 1;\r", + '\x1B[33mfalse\x1B[39m', + ] +}, { + input: '{ a: true }', + preview: [ + '{ a: true }\r', + '{ a: \x1B[33mtrue\x1B[39m }', + ] +}, { + input: '{ a: true };', + preview: [ + '{ a: true };\r', + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: ' \t { a: true};', + preview: [ + ' { a: true};\r', + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: '1n + 2n', + preview: [ + '1n + 2n\r', + '\x1B[33m3n\x1B[39m', + ] +}, { + input: '{};1', + preview: [ + '{};1\r', + '\x1B[33m1\x1B[39m', + ], +}]; + +async function runTest() { + for (const { input, preview } of testCases) { + const toBeRun = input.split('\n'); + let lines = await runAndWait(toBeRun, repl); + // Remove error messages. That allows the code to run in different + // engines. + // eslint-disable-next-line no-control-regex + lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); + assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); + assert.deepStrictEqual(lines, preview); + } +} + +runTest().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-syntax-error-handling.js b/test/js/node/test/parallel/test-repl-syntax-error-handling.js new file mode 100644 index 0000000000..91a8614d1d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-syntax-error-handling.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +switch (process.argv[2]) { + case 'child': + return child(); + case undefined: + return parent(); + default: + throw new Error('invalid'); +} + +function parent() { + const spawn = require('child_process').spawn; + const child = spawn(process.execPath, [__filename, 'child']); + + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function(c) { + console.error(`${c}`); + throw new Error('should not get stderr data'); + }); + + child.stdout.setEncoding('utf8'); + let out = ''; + child.stdout.on('data', function(c) { + out += c; + }); + child.stdout.on('end', function() { + assert.strictEqual(out, '10\n'); + console.log('ok - got expected output'); + }); + + child.on('exit', function(c) { + assert(!c); + console.log('ok - exit success'); + }); +} + +function child() { + const vm = require('vm'); + let caught; + try { + vm.runInThisContext('haf!@##&$!@$*!@', { displayErrors: false }); + } catch { + caught = true; + } + assert(caught); + vm.runInThisContext('console.log(10)', { displayErrors: false }); +} diff --git a/test/js/node/test/parallel/pipe-return-val.test.js b/test/js/node/test/parallel/test-require-cache.js similarity index 69% rename from test/js/node/test/parallel/pipe-return-val.test.js rename to test/js/node/test/parallel/test-require-cache.js index 5027349c9c..7b62ab5764 100644 --- a/test/js/node/test/parallel/pipe-return-val.test.js +++ b/test/js/node/test/parallel/test-require-cache.js @@ -1,6 +1,3 @@ -//#FILE: test-pipe-return-val.js -//#SHA1: 63e55c62d40a6ea0c652d66b609d89f9f5324ea1 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,17 +19,26 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -// This test ensures SourceStream.pipe(DestStream) returns DestStream +'use strict'; +require('../common'); +const assert = require('assert'); -const Stream = require("stream").Stream; +{ + const relativePath = '../fixtures/semicolon'; + const absolutePath = require.resolve(relativePath); + const fakeModule = {}; -test("SourceStream.pipe(DestStream) returns DestStream", () => { - const sourceStream = new Stream(); - const destStream = new Stream(); - const result = sourceStream.pipe(destStream); + require.cache[absolutePath] = { exports: fakeModule }; - expect(result).toBe(destStream); -}); + assert.strictEqual(require(relativePath), fakeModule); +} -//<#END_FILE: test-pipe-return-val.js + +{ + const relativePath = 'fs'; + const fakeModule = {}; + + require.cache[relativePath] = { exports: fakeModule }; + + assert.strictEqual(require(relativePath), fakeModule); +} diff --git a/test/js/node/test/parallel/test-require-dot.js b/test/js/node/test/parallel/test-require-dot.js new file mode 100644 index 0000000000..7145e688d4 --- /dev/null +++ b/test/js/node/test/parallel/test-require-dot.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const m = require('module'); +const fixtures = require('../common/fixtures'); + +const a = require(fixtures.path('module-require', 'relative', 'dot.js')); +const b = require(fixtures.path('module-require', 'relative', 'dot-slash.js')); + +assert.strictEqual(a.value, 42); +// require(".") should resolve like require("./") +assert.strictEqual(a, b); + +process.env.NODE_PATH = fixtures.path('module-require', 'relative'); +m._initPaths(); + +assert.throws( + () => require('.'), + { + message: /Cannot find module '\.'/, + code: 'MODULE_NOT_FOUND' + } +); diff --git a/test/js/node/test/parallel/test-require-empty-main.js b/test/js/node/test/parallel/test-require-empty-main.js new file mode 100644 index 0000000000..73f141d1f9 --- /dev/null +++ b/test/js/node/test/parallel/test-require-empty-main.js @@ -0,0 +1,25 @@ +'use strict'; +require('../common'); + +// A package.json with an empty "main" property should use index.js if present. +// require.resolve() should resolve to index.js for the same reason. +// +// In fact, any "main" property that doesn't resolve to a file should result +// in index.js being used, but that's already checked for by other tests. +// This test only concerns itself with the empty string. + +const assert = require('assert'); +const path = require('path'); +const fixtures = require('../common/fixtures'); + +const where = fixtures.path('require-empty-main'); +const expected = path.join(where, 'index.js'); + +test(); +setImmediate(test); + +function test() { + assert.strictEqual(require.resolve(where), expected); + assert.strictEqual(require(where), 42); + assert.strictEqual(require.resolve(where), expected); +} diff --git a/test/js/node/test/parallel/test-require-extensions-main.js b/test/js/node/test/parallel/test-require-extensions-main.js new file mode 100644 index 0000000000..16fbad6cf7 --- /dev/null +++ b/test/js/node/test/parallel/test-require-extensions-main.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const fixturesRequire = require(fixtures.path('require-bin', 'bin', 'req.js')); + +assert.strictEqual( + fixturesRequire, + '', + 'test-require-extensions-main failed to import fixture requirements: ' + + fixturesRequire +); diff --git a/test/js/node/test/parallel/child-process-fork3.test.js b/test/js/node/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js similarity index 71% rename from test/js/node/test/parallel/child-process-fork3.test.js rename to test/js/node/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js index ab5558264a..2461ece860 100644 --- a/test/js/node/test/parallel/child-process-fork3.test.js +++ b/test/js/node/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js @@ -1,6 +1,3 @@ -//#FILE: test-child-process-fork3.js -//#SHA1: afa7c58bf22c64585426b2b3ec4358a415f4ff47 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,15 +19,17 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const child_process = require("child_process"); -const path = require("path"); +'use strict'; +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); -test("child_process.fork should not hang", () => { - const emptyScriptPath = path.join(__dirname, "fixtures", "empty.js"); - expect(() => { - child_process.fork(emptyScriptPath); - }).not.toThrow(); -}); +const content = + require(fixtures.path('json-with-directory-name-module', + 'module-stub', + 'one-trailing-slash', + 'two', + 'three.js')); -//<#END_FILE: test-child-process-fork3.js +assert.notStrictEqual(content.rocko, 'artischocko'); +assert.strictEqual(content, 'hello from module-stub!'); diff --git a/test/js/node/test/parallel/test-require-long-path.js b/test/js/node/test/parallel/test-require-long-path.js new file mode 100644 index 0000000000..abc75176bc --- /dev/null +++ b/test/js/node/test/parallel/test-require-long-path.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +if (!common.isWindows) + common.skip('this test is Windows-specific.'); + +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +// Make a path that is more than 260 chars long. +const dirNameLen = Math.max(260 - tmpdir.path.length, 1); +const dirName = tmpdir.resolve('x'.repeat(dirNameLen)); +const fullDirPath = path.resolve(dirName); + +const indexFile = path.join(fullDirPath, 'index.js'); +const otherFile = path.join(fullDirPath, 'other.js'); + +tmpdir.refresh(); + +fs.mkdirSync(fullDirPath); +fs.writeFileSync(indexFile, 'require("./other");'); +fs.writeFileSync(otherFile, ''); + +require(indexFile); +require(otherFile); + +tmpdir.refresh(); diff --git a/test/js/node/test/parallel/test-require-process.js b/test/js/node/test/parallel/test-require-process.js new file mode 100644 index 0000000000..57af1508f0 --- /dev/null +++ b/test/js/node/test/parallel/test-require-process.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const nativeProcess = require('process'); +// require('process') should return global process reference +assert.strictEqual(nativeProcess, process); diff --git a/test/js/node/test/parallel/test-require-unicode.js b/test/js/node/test/parallel/test-require-unicode.js new file mode 100644 index 0000000000..362ec6487a --- /dev/null +++ b/test/js/node/test/parallel/test-require-unicode.js @@ -0,0 +1,17 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const dirname = tmpdir.resolve('\u4e2d\u6587\u76ee\u5f55'); +fs.mkdirSync(dirname); +fs.writeFileSync(path.join(dirname, 'file.js'), 'module.exports = 42;'); +fs.writeFileSync(path.join(dirname, 'package.json'), + JSON.stringify({ name: 'test', main: 'file.js' })); +assert.strictEqual(require(dirname), 42); +assert.strictEqual(require(path.join(dirname, 'file.js')), 42); diff --git a/test/js/node/test/parallel/test-sigint-infinite-loop.js b/test/js/node/test/parallel/test-sigint-infinite-loop.js new file mode 100644 index 0000000000..30eb98ecb8 --- /dev/null +++ b/test/js/node/test/parallel/test-sigint-infinite-loop.js @@ -0,0 +1,34 @@ +'use strict'; +// This test is to assert that we can SIGINT a script which loops forever. +// Ref(http): +// groups.google.com/group/nodejs-dev/browse_thread/thread/e20f2f8df0296d3f +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +console.log('start'); + +const c = spawn(process.execPath, ['-e', 'while(true) { console.log("hi"); }']); + +let sentKill = false; + +c.stdout.on('data', function(s) { + // Prevent race condition: + // Wait for the first bit of output from the child process + // so that we're sure that it's in the V8 event loop and not + // just in the startup phase of execution. + if (!sentKill) { + c.kill('SIGINT'); + console.log('SIGINT infinite-loop.js'); + sentKill = true; + } +}); + +c.on('exit', common.mustCall(function(code) { + assert.ok(code !== 0); + console.log('killed infinite-loop.js'); +})); + +process.on('exit', function() { + assert.ok(sentKill); +}); diff --git a/test/js/node/test/parallel/test-signal-args.js b/test/js/node/test/parallel/test-signal-args.js new file mode 100644 index 0000000000..7b72ed6dcb --- /dev/null +++ b/test/js/node/test/parallel/test-signal-args.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +if (common.isWindows) + common.skip('Sending signals with process.kill is not supported on Windows'); +if (!common.isMainThread) + common.skip('No signal handling available in Workers'); + +process.once('SIGINT', common.mustCall((signal) => { + assert.strictEqual(signal, 'SIGINT'); +})); + +process.kill(process.pid, 'SIGINT'); + +process.once('SIGTERM', common.mustCall((signal) => { + assert.strictEqual(signal, 'SIGTERM'); +})); + +process.kill(process.pid, 'SIGTERM'); + +// Prevent Node.js from exiting due to empty event loop before signal handlers +// are fired +setImmediate(() => {}); diff --git a/test/js/node/test/parallel/test-signal-handler-remove-on-exit.js b/test/js/node/test/parallel/test-signal-handler-remove-on-exit.js new file mode 100644 index 0000000000..1c87497172 --- /dev/null +++ b/test/js/node/test/parallel/test-signal-handler-remove-on-exit.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); + +// Regression test for https://github.com/nodejs/node/issues/30581 +// This script should not crash. + +function dummy() {} +process.on('SIGINT', dummy); +process.on('exit', () => process.removeListener('SIGINT', dummy)); diff --git a/test/js/node/test/parallel/timers-ordering.test.js b/test/js/node/test/parallel/test-signal-handler.js similarity index 56% rename from test/js/node/test/parallel/timers-ordering.test.js rename to test/js/node/test/parallel/test-signal-handler.js index bd3769d4fc..05ec4e7f73 100644 --- a/test/js/node/test/parallel/timers-ordering.test.js +++ b/test/js/node/test/parallel/test-signal-handler.js @@ -1,6 +1,3 @@ -//#FILE: test-timers-ordering.js -//#SHA1: b2be662b4e86125b4dcd87ced4316d84acf57912 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,35 +19,38 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; -const N = 30; +const common = require('../common'); -test("Timer ordering and timing", async () => { - let last_i = 0; - let last_ts = 0; +if (common.isWindows) + common.skip('SIGUSR1 and SIGHUP signals are not supported'); +if (!common.isMainThread) + common.skip('Signal handling in Workers is not supported'); - function f(i) { - return new Promise(resolve => { - if (i <= N) { - // check order - expect(i).toBe(last_i + 1); - last_i = i; +console.log(`process.pid: ${process.pid}`); - // Check that this iteration is fired at least 1ms later than the previous - const now = Date.now(); - expect(now).toBeGreaterThanOrEqual(last_ts + 1); - last_ts = now; +process.on('SIGUSR1', common.mustCall()); - // Schedule next iteration - setTimeout(() => resolve(f(i + 1)), 1); - } else { - resolve(); - } - }); +process.on('SIGUSR1', common.mustCall(function() { + setTimeout(function() { + console.log('End.'); + process.exit(0); + }, 5); +})); + +let i = 0; +setInterval(function() { + console.log(`running process...${++i}`); + + if (i === 5) { + process.kill(process.pid, 'SIGUSR1'); } +}, 1); - await f(1); -}, 10000); // Increased timeout to ensure all iterations complete - -//<#END_FILE: test-timers-ordering.js +// Test on condition where a watcher for SIGNAL +// has been previously registered, and `process.listeners(SIGNAL).length === 1` +process.on('SIGHUP', common.mustNotCall()); +process.removeAllListeners('SIGHUP'); +process.on('SIGHUP', common.mustCall()); +process.kill(process.pid, 'SIGHUP'); diff --git a/test/js/node/test/parallel/test-signal-unregister.js b/test/js/node/test/parallel/test-signal-unregister.js new file mode 100644 index 0000000000..2c4d312942 --- /dev/null +++ b/test/js/node/test/parallel/test-signal-unregister.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const child = spawn(process.argv[0], [fixtures.path('should_exit.js')]); +child.stdout.once('data', function() { + child.kill('SIGINT'); +}); +child.on('exit', common.mustCall(function(exitCode, signalCode) { + assert.strictEqual(exitCode, null); + assert.strictEqual(signalCode, 'SIGINT'); +})); diff --git a/test/js/node/test/parallel/test-spawn-cmd-named-pipe.js b/test/js/node/test/parallel/test-spawn-cmd-named-pipe.js new file mode 100644 index 0000000000..4e7ad185a5 --- /dev/null +++ b/test/js/node/test/parallel/test-spawn-cmd-named-pipe.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common'); +// This test is intended for Windows only +if (!common.isWindows) + common.skip('this test is Windows-specific.'); + +const assert = require('assert'); + +if (!process.argv[2]) { + // parent + const net = require('net'); + const spawn = require('child_process').spawn; + const path = require('path'); + + const pipeNamePrefix = `${path.basename(__filename)}.${process.pid}`; + const stdinPipeName = `\\\\.\\pipe\\${pipeNamePrefix}.stdin`; + const stdoutPipeName = `\\\\.\\pipe\\${pipeNamePrefix}.stdout`; + + const stdinPipeServer = net.createServer(function(c) { + c.on('end', common.mustCall()); + c.end('hello'); + }); + stdinPipeServer.listen(stdinPipeName); + + const output = []; + + const stdoutPipeServer = net.createServer(function(c) { + c.on('data', function(x) { + output.push(x); + }); + c.on('end', common.mustCall(function() { + assert.strictEqual(output.join(''), 'hello'); + })); + }); + stdoutPipeServer.listen(stdoutPipeName); + + const args = + [`"${__filename}"`, 'child', '<', stdinPipeName, '>', stdoutPipeName]; + + const child = spawn(`"${process.execPath}"`, args, { shell: true }); + + child.on('exit', common.mustCall(function(exitCode) { + stdinPipeServer.close(); + stdoutPipeServer.close(); + assert.strictEqual(exitCode, 0); + })); +} else { + // child + process.stdin.pipe(process.stdout); +} diff --git a/test/js/node/test/parallel/test-stdin-child-proc.js b/test/js/node/test/parallel/test-stdin-child-proc.js new file mode 100644 index 0000000000..bbb6a29cd7 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-child-proc.js @@ -0,0 +1,15 @@ +'use strict'; +// This tests that pausing and resuming stdin does not hang and timeout +// when done in a child process. See test/parallel/test-stdin-pause-resume.js +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const path = require('path'); +const cp = child_process.spawn( + process.execPath, + [path.resolve(__dirname, 'test-stdin-pause-resume.js')] +); + +cp.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-stdin-from-file-spawn.js b/test/js/node/test/parallel/test-stdin-from-file-spawn.js new file mode 100644 index 0000000000..3830ac124a --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-from-file-spawn.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); +const process = require('process'); + +let defaultShell; +if (process.platform === 'linux' || process.platform === 'darwin') { + defaultShell = '/bin/sh'; +} else if (process.platform === 'win32') { + defaultShell = 'cmd.exe'; +} else { + common.skip('This is test exists only on Linux/Win32/macOS'); +} + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const tmpdir = require('../common/tmpdir'); + +const tmpDir = tmpdir.path; +tmpdir.refresh(); +const tmpCmdFile = path.join(tmpDir, 'test-stdin-from-file-spawn-cmd'); +const tmpJsFile = path.join(tmpDir, 'test-stdin-from-file-spawn.js'); +fs.writeFileSync(tmpCmdFile, 'echo hello'); +fs.writeFileSync(tmpJsFile, ` +'use strict'; +const { spawn } = require('child_process'); +// Reference the object to invoke the getter +process.stdin; +setTimeout(() => { + let ok = false; + const child = spawn(process.env.SHELL || '${defaultShell}', + [], { stdio: ['inherit', 'pipe'] }); + child.stdout.on('data', () => { + ok = true; + }); + child.on('close', () => { + process.exit(ok ? 0 : -1); + }); +}, 100); +`); + +execSync(`${process.argv[0]} ${tmpJsFile} < ${tmpCmdFile}`); diff --git a/test/js/node/test/parallel/test-stdin-hang.js b/test/js/node/test/parallel/test-stdin-hang.js new file mode 100644 index 0000000000..887f31fdb7 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-hang.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test *only* verifies that invoking the stdin getter does not +// cause node to hang indefinitely. +// If it does, then the test-runner will nuke it. + +// invoke the getter. +process.stdin; // eslint-disable-line no-unused-expressions + +console.error('Should exit normally now.'); diff --git a/test/js/node/test/parallel/test-stdin-pause-resume.js b/test/js/node/test/parallel/test-stdin-pause-resume.js new file mode 100644 index 0000000000..459256099e --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-pause-resume.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +console.error('before opening stdin'); +process.stdin.resume(); +console.error('stdin opened'); +setTimeout(function() { + console.error('pausing stdin'); + process.stdin.pause(); + setTimeout(function() { + console.error('opening again'); + process.stdin.resume(); + setTimeout(function() { + console.error('pausing again'); + process.stdin.pause(); + console.error('should exit now'); + }, 1); + }, 1); +}, 1); diff --git a/test/js/node/test/parallel/test-stdin-pipe-large.js b/test/js/node/test/parallel/test-stdin-pipe-large.js new file mode 100644 index 0000000000..5f4a2f10c8 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-pipe-large.js @@ -0,0 +1,23 @@ +'use strict'; +// See https://github.com/nodejs/node/issues/5927 + +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'child') { + process.stdin.pipe(process.stdout); + return; +} + +const child = spawn(process.execPath, [__filename, 'child'], { stdio: 'pipe' }); + +const expectedBytes = 1024 * 1024; +let readBytes = 0; + +child.stdin.end(Buffer.alloc(expectedBytes)); + +child.stdout.on('data', (chunk) => readBytes += chunk.length); +child.stdout.on('end', common.mustCall(() => { + assert.strictEqual(readBytes, expectedBytes); +})); diff --git a/test/js/node/test/parallel/test-stdin-pipe-resume.js b/test/js/node/test/parallel/test-stdin-pipe-resume.js new file mode 100644 index 0000000000..e9000933a3 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-pipe-resume.js @@ -0,0 +1,27 @@ +'use strict'; +// This tests that piping stdin will cause it to resume() as well. +require('../common'); +const assert = require('assert'); + +if (process.argv[2] === 'child') { + process.stdin.pipe(process.stdout); +} else { + const spawn = require('child_process').spawn; + const buffers = []; + const child = spawn(process.execPath, [__filename, 'child']); + child.stdout.on('data', function(c) { + buffers.push(c); + }); + child.stdout.on('close', function() { + const b = Buffer.concat(buffers).toString(); + assert.strictEqual(b, 'Hello, world\n'); + console.log('ok'); + }); + child.stdin.write('Hel'); + child.stdin.write('lo,'); + child.stdin.write(' wo'); + setTimeout(function() { + child.stdin.write('rld\n'); + child.stdin.end(); + }, 10); +} diff --git a/test/js/node/test/parallel/test-stdin-script-child-option.js b/test/js/node/test/parallel/test-stdin-script-child-option.js new file mode 100644 index 0000000000..5526d66f3f --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-script-child-option.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const expected = '--option-to-be-seen-on-child'; + +const { spawn } = require('child_process'); +const child = spawn(process.execPath, ['-', expected], { stdio: 'pipe' }); + +child.stdin.end('console.log(process.argv[2])'); + +let actual = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (chunk) => actual += chunk); +child.stdout.on('end', common.mustCall(() => { + assert.strictEqual(actual.trim(), expected); +})); diff --git a/test/js/node/test/parallel/test-stdio-closed.js b/test/js/node/test/parallel/test-stdio-closed.js new file mode 100644 index 0000000000..cc9f1e86cc --- /dev/null +++ b/test/js/node/test/parallel/test-stdio-closed.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +if (common.isWindows) { + if (process.argv[2] === 'child') { + /* eslint-disable no-unused-expressions */ + process.stdin; + process.stdout; + process.stderr; + return; + /* eslint-enable no-unused-expressions */ + } + const python = process.env.PYTHON || 'python'; + const script = fixtures.path('spawn_closed_stdio.py'); + const proc = spawn(python, [script, process.execPath, __filename, 'child']); + proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + })); + return; +} + +if (process.argv[2] === 'child') { + [0, 1, 2].forEach((i) => fs.fstatSync(i)); + return; +} + +// Run the script in a shell but close stdout and stderr. +const cmd = `"${process.execPath}" "${__filename}" child 1>&- 2>&-`; +const proc = spawn('/bin/sh', ['-c', cmd], { stdio: 'inherit' }); + +proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); +})); diff --git a/test/js/node/test/parallel/test-stdio-pipe-stderr.js b/test/js/node/test/parallel/test-stdio-pipe-stderr.js new file mode 100644 index 0000000000..c914877062 --- /dev/null +++ b/test/js/node/test/parallel/test-stdio-pipe-stderr.js @@ -0,0 +1,36 @@ +'use strict'; +require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const { spawnSync } = require('child_process'); + +// Test that invoking node with require, and piping stderr to file, +// does not result in exception, +// see: https://github.com/nodejs/node/issues/11257 + +tmpdir.refresh(); +const fakeModulePath = tmpdir.resolve('batman.js'); +const stderrOutputPath = tmpdir.resolve('stderr-output.txt'); +// We need to redirect stderr to a file to produce #11257 +const stream = fs.createWriteStream(stderrOutputPath); + +// The error described in #11257 only happens when we require a +// non-built-in module. +fs.writeFileSync(fakeModulePath, '', 'utf8'); + +stream.on('open', () => { + spawnSync(process.execPath, { + input: `require(${JSON.stringify(fakeModulePath)})`, + stdio: ['pipe', 'pipe', stream] + }); + const stderr = fs.readFileSync(stderrOutputPath, 'utf8').trim(); + assert.strictEqual( + stderr, + '', + `piping stderr to file should not result in exception: ${stderr}` + ); + stream.end(); + fs.unlinkSync(stderrOutputPath); + fs.unlinkSync(fakeModulePath); +}); diff --git a/test/js/node/test/parallel/test-stdio-undestroy.js b/test/js/node/test/parallel/test-stdio-undestroy.js new file mode 100644 index 0000000000..b525672db2 --- /dev/null +++ b/test/js/node/test/parallel/test-stdio-undestroy.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'child') { + process.stdout.destroy(); + process.stderr.destroy(); + console.log('stdout'); + process.stdout.write('rocks\n'); + console.error('stderr'); + setTimeout(function() { + process.stderr.write('rocks too\n'); + }, 10); + return; +} + +const proc = spawn(process.execPath, [__filename, 'child'], { stdio: 'pipe' }); + +let stdout = ''; +proc.stdout.setEncoding('utf8'); +proc.stdout.on('data', common.mustCallAtLeast(function(chunk) { + stdout += chunk; +}, 1)); + +let stderr = ''; +proc.stderr.setEncoding('utf8'); +proc.stderr.on('data', common.mustCallAtLeast(function(chunk) { + stderr += chunk; +}, 1)); + +proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + assert.strictEqual(stdout, 'stdout\nrocks\n'); + assert.strictEqual(stderr, 'stderr\nrocks too\n'); +})); diff --git a/test/js/node/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js b/test/js/node/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js new file mode 100644 index 0000000000..7cd4b90c00 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js @@ -0,0 +1,32 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +if (process.argv[2] === 'child') + process.stdout.end('foo'); +else + parent(); + +function parent() { + const spawn = require('child_process').spawn; + const child = spawn(process.execPath, [__filename, 'child']); + let out = ''; + let err = ''; + + child.stdout.setEncoding('utf8'); + child.stderr.setEncoding('utf8'); + + child.stdout.on('data', function(c) { + out += c; + }); + child.stderr.on('data', function(c) { + err += c; + }); + + child.on('close', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(err, ''); + assert.strictEqual(out, 'foo'); + console.log('ok'); + }); +} diff --git a/test/js/node/test/parallel/test-stdout-pipeline-destroy.js b/test/js/node/test/parallel/test-stdout-pipeline-destroy.js new file mode 100644 index 0000000000..291579cf69 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-pipeline-destroy.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const { Transform, Readable, pipeline } = require('stream'); +const assert = require('assert'); + +const reader = new Readable({ + read(size) { this.push('foo'); } +}); + +let count = 0; + +const err = new Error('this-error-gets-hidden'); + +const transform = new Transform({ + transform(chunk, enc, cb) { + if (count++ >= 5) + this.emit('error', err); + else + cb(null, count.toString() + '\n'); + } +}); + +pipeline( + reader, + transform, + process.stdout, + common.mustCall((e) => { + assert.strictEqual(e, err); + }) +); diff --git a/test/js/node/test/parallel/test-stdout-stderr-reading.js b/test/js/node/test/parallel/test-stdout-stderr-reading.js new file mode 100644 index 0000000000..57bfffa272 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-stderr-reading.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Verify that stdout is never read from. +const net = require('net'); +const read = net.Socket.prototype.read; + +net.Socket.prototype.read = function() { + if (this.fd === 1) + throw new Error('reading from stdout!'); + if (this.fd === 2) + throw new Error('reading from stderr!'); + return read.apply(this, arguments); +}; + +if (process.argv[2] === 'child') + child(); +else + parent(); + +function parent() { + const spawn = require('child_process').spawn; + const node = process.execPath; + + const c1 = spawn(node, [__filename, 'child']); + let c1out = ''; + c1.stdout.setEncoding('utf8'); + c1.stdout.on('data', function(chunk) { + c1out += chunk; + }); + c1.stderr.setEncoding('utf8'); + c1.stderr.on('data', function(chunk) { + console.error(`c1err: ${chunk.split('\n').join('\nc1err: ')}`); + }); + c1.on('close', common.mustCall(function(code, signal) { + assert(!code); + assert(!signal); + assert.strictEqual(c1out, 'ok\n'); + console.log('ok'); + })); + + const c2 = spawn(node, ['-e', 'console.log("ok")']); + let c2out = ''; + c2.stdout.setEncoding('utf8'); + c2.stdout.on('data', function(chunk) { + c2out += chunk; + }); + c1.stderr.setEncoding('utf8'); + c1.stderr.on('data', function(chunk) { + console.error(`c1err: ${chunk.split('\n').join('\nc1err: ')}`); + }); + c2.on('close', common.mustCall(function(code, signal) { + assert(!code); + assert(!signal); + assert.strictEqual(c2out, 'ok\n'); + console.log('ok'); + })); +} + +function child() { + // Should not be reading *ever* in here. + net.Socket.prototype.read = function() { + throw new Error('no reading allowed in child'); + }; + console.log('ok'); +} diff --git a/test/js/node/test/parallel/test-stdout-stderr-write.js b/test/js/node/test/parallel/test-stdout-stderr-write.js new file mode 100644 index 0000000000..803fc70536 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-stderr-write.js @@ -0,0 +1,8 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// https://github.com/nodejs/node/pull/39246 +assert.strictEqual(process.stderr.write('asd'), true); +assert.strictEqual(process.stdout.write('asd'), true); diff --git a/test/js/node/test/parallel/test-stdout-to-file.js b/test/js/node/test/parallel/test-stdout-to-file.js new file mode 100644 index 0000000000..9114f22443 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-to-file.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const childProcess = require('child_process'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); + +const scriptString = fixtures.path('print-chars.js'); +const scriptBuffer = fixtures.path('print-chars-from-buffer.js'); +const tmpFile = tmpdir.resolve('stdout.txt'); + +tmpdir.refresh(); + +function test(size, useBuffer, cb) { + const cmd = `"${process.argv[0]}" "${ + useBuffer ? scriptBuffer : scriptString}" ${size} > "${tmpFile}"`; + + try { + fs.unlinkSync(tmpFile); + } catch { + // Continue regardless of error. + } + + console.log(`${size} chars to ${tmpFile}...`); + + childProcess.exec(cmd, common.mustSucceed(() => { + console.log('done!'); + + const stat = fs.statSync(tmpFile); + + console.log(`${tmpFile} has ${stat.size} bytes`); + + assert.strictEqual(size, stat.size); + fs.unlinkSync(tmpFile); + + cb(); + })); +} + +test(1024 * 1024, false, common.mustCall(function() { + console.log('Done printing with string'); + test(1024 * 1024, true, common.mustCall(function() { + console.log('Done printing with buffer'); + })); +})); diff --git a/test/js/node/test/parallel/test-strace-openat-openssl.js b/test/js/node/test/parallel/test-strace-openat-openssl.js new file mode 100644 index 0000000000..13882e67ae --- /dev/null +++ b/test/js/node/test/parallel/test-strace-openat-openssl.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../common'); +const { spawn, spawnSync } = require('node:child_process'); +const { createInterface } = require('node:readline'); +const assert = require('node:assert'); + +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.isLinux) + common.skip('linux only'); +if (common.isASan) + common.skip('strace does not work well with address sanitizer builds'); +if (spawnSync('strace').error !== undefined) { + common.skip('missing strace'); +} + +{ + const allowedOpenCalls = new Set([ + '/etc/ssl/openssl.cnf', + ]); + const strace = spawn('strace', [ + '-f', '-ff', + '-e', 'trace=open,openat', + '-s', '512', + '-D', process.execPath, '-e', 'require("crypto")', + ]); + + // stderr is the default for strace + const rl = createInterface({ input: strace.stderr }); + rl.on('line', (line) => { + if (!line.startsWith('open')) { + return; + } + + const file = line.match(/"(.*?)"/)[1]; + // skip .so reading attempt + if (file.match(/.+\.so(\.?)/) !== null) { + return; + } + // skip /proc/* + if (file.match(/\/proc\/.+/) !== null) { + return; + } + + assert(allowedOpenCalls.delete(file), `${file} is not in the list of allowed openat calls`); + }); + const debugOutput = []; + strace.stderr.setEncoding('utf8'); + strace.stderr.on('data', (chunk) => { + debugOutput.push(chunk.toString()); + }); + strace.on('error', common.mustNotCall()); + strace.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0, debugOutput); + const missingKeys = Array.from(allowedOpenCalls.keys()); + if (missingKeys.length) { + assert.fail(`The following openat call are missing: ${missingKeys.join(',')}`); + } + })); +} diff --git a/test/js/node/test/parallel/test-stream-auto-destroy.js b/test/js/node/test/parallel/test-stream-auto-destroy.js new file mode 100644 index 0000000000..2a1a5190de --- /dev/null +++ b/test/js/node/test/parallel/test-stream-auto-destroy.js @@ -0,0 +1,112 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + autoDestroy: true, + read() { + this.push('hello'); + this.push('world'); + this.push(null); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let ended = false; + + r.resume(); + + r.on('end', common.mustCall(() => { + ended = true; + })); + + r.on('close', common.mustCall(() => { + assert(ended); + })); +} + +{ + const w = new stream.Writable({ + autoDestroy: true, + write(data, enc, cb) { + cb(null); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let finished = false; + + w.write('hello'); + w.write('world'); + w.end(); + + w.on('finish', common.mustCall(() => { + finished = true; + })); + + w.on('close', common.mustCall(() => { + assert(finished); + })); +} + +{ + const t = new stream.Transform({ + autoDestroy: true, + transform(data, enc, cb) { + cb(null, data); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let ended = false; + let finished = false; + + t.write('hello'); + t.write('world'); + t.end(); + + t.resume(); + + t.on('end', common.mustCall(() => { + ended = true; + })); + + t.on('finish', common.mustCall(() => { + finished = true; + })); + + t.on('close', common.mustCall(() => { + assert(ended); + assert(finished); + })); +} + +{ + const r = new stream.Readable({ + read() { + r2.emit('error', new Error('fail')); + } + }); + const r2 = new stream.Readable({ + autoDestroy: true, + destroy: common.mustCall((err, cb) => cb()) + }); + + r.pipe(r2); +} + +{ + const r = new stream.Readable({ + read() { + w.emit('error', new Error('fail')); + } + }); + const w = new stream.Writable({ + autoDestroy: true, + destroy: common.mustCall((err, cb) => cb()) + }); + + r.pipe(w); +} diff --git a/test/js/node/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js b/test/js/node/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js new file mode 100644 index 0000000000..110d46bb9f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); + +const encode = new PassThrough({ + highWaterMark: 1 +}); + +const decode = new PassThrough({ + highWaterMark: 1 +}); + +const send = common.mustCall((buf) => { + encode.write(buf); +}, 4); + +let i = 0; +const onData = common.mustCall(() => { + if (++i === 2) { + send(Buffer.from([0x3])); + send(Buffer.from([0x4])); + } +}, 4); + +encode.pipe(decode).on('data', onData); + +send(Buffer.from([0x1])); +send(Buffer.from([0x2])); diff --git a/test/js/node/test/parallel/test-stream-backpressure.js b/test/js/node/test/parallel/test-stream-backpressure.js new file mode 100644 index 0000000000..03bcc233c8 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-backpressure.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let pushes = 0; +const total = 65500 + 40 * 1024; +const rs = new stream.Readable({ + read: common.mustCall(function() { + if (pushes++ === 10) { + this.push(null); + return; + } + + const length = this._readableState.length; + + // We are at most doing two full runs of _reads + // before stopping, because Readable is greedy + // to keep its buffer full + assert(length <= total); + + this.push(Buffer.alloc(65500)); + for (let i = 0; i < 40; i++) { + this.push(Buffer.alloc(1024)); + } + + // We will be over highWaterMark at this point + // but a new call to _read is scheduled anyway. + }, 11) +}); + +const ws = stream.Writable({ + write: common.mustCall(function(data, enc, cb) { + setImmediate(cb); + }, 41 * 10) +}); + +rs.pipe(ws); diff --git a/test/js/node/test/parallel/stream-big-packet.test.js b/test/js/node/test/parallel/test-stream-big-packet.js similarity index 50% rename from test/js/node/test/parallel/stream-big-packet.test.js rename to test/js/node/test/parallel/test-stream-big-packet.js index 01b34975c9..fdbe3cd211 100644 --- a/test/js/node/test/parallel/stream-big-packet.test.js +++ b/test/js/node/test/parallel/test-stream-big-packet.js @@ -1,6 +1,3 @@ -//#FILE: test-stream-big-packet.js -//#SHA1: ce0f56afc4946041321028a7128eca47765fd53d -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,8 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const stream = require("stream"); +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); let passed = false; @@ -31,43 +30,36 @@ class TestStream extends stream.Transform { _transform(chunk, encoding, done) { if (!passed) { // Char 'a' only exists in the last write - passed = chunk.toString().includes("a"); + passed = chunk.toString().includes('a'); } done(); } } -test("Large buffer is handled properly by Writable Stream", done => { - const s1 = new stream.Transform({ - transform(chunk, encoding, cb) { - process.nextTick(cb, null, chunk); - }, - }); - const s2 = new stream.PassThrough(); - const s3 = new TestStream(); - s1.pipe(s3); - // Don't let s2 auto close which may close s3 - s2.pipe(s3, { end: false }); - - // We must write a buffer larger than highWaterMark - const big = Buffer.alloc(s1.writableHighWaterMark + 1, "x"); - - // Since big is larger than highWaterMark, it will be buffered internally. - expect(s1.write(big)).toBe(false); - // 'tiny' is small enough to pass through internal buffer. - expect(s2.write("tiny")).toBe(true); - - // Write some small data in next IO loop, which will never be written to s3 - // Because 'drain' event is not emitted from s1 and s1 is still paused - setImmediate(s1.write.bind(s1), "later"); - - // Assert after two IO loops when all operations have been done. - setImmediate(() => { - setImmediate(() => { - expect(passed).toBe(true); - done(); - }); - }); +const s1 = new stream.Transform({ + transform(chunk, encoding, cb) { + process.nextTick(cb, null, chunk); + } }); +const s2 = new stream.PassThrough(); +const s3 = new TestStream(); +s1.pipe(s3); +// Don't let s2 auto close which may close s3 +s2.pipe(s3, { end: false }); -//<#END_FILE: test-stream-big-packet.js +// We must write a buffer larger than highWaterMark +const big = Buffer.alloc(s1.writableHighWaterMark + 1, 'x'); + +// Since big is larger than highWaterMark, it will be buffered internally. +assert(!s1.write(big)); +// 'tiny' is small enough to pass through internal buffer. +assert(s2.write('tiny')); + +// Write some small data in next IO loop, which will never be written to s3 +// Because 'drain' event is not emitted from s1 and s1 is still paused +setImmediate(s1.write.bind(s1), 'later'); + +// Assert after two IO loops when all operations have been done. +process.on('exit', function() { + assert(passed, 'Large buffer is not handled properly by Writable Stream'); +}); diff --git a/test/js/node/test/parallel/tls-zero-clear-in.test.js b/test/js/node/test/parallel/test-stream-big-push.js similarity index 50% rename from test/js/node/test/parallel/tls-zero-clear-in.test.js rename to test/js/node/test/parallel/test-stream-big-push.js index 4b254bdf3c..f9e75edd3f 100644 --- a/test/js/node/test/parallel/tls-zero-clear-in.test.js +++ b/test/js/node/test/parallel/test-stream-big-push.js @@ -1,6 +1,3 @@ -//#FILE: test-tls-zero-clear-in.js -//#SHA1: 6014fd3aa5a294b4e8594a32f0eb8e7b3c206213 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,60 +19,56 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const str = 'asdfasdfasdfasdfasdf'; -const tls = require("tls"); -const { readKey } = require("../common/fixtures"); - -if (!process.versions.openssl) { - test.skip("missing crypto"); -} - -const cert = readKey("rsa_cert.crt"); -const key = readKey("rsa_private.pem"); - -test("SSL_write() call with 0 bytes should not be treated as error", done => { - const server = tls.createServer( - { - cert, - key, - }, - c => { - // Nop - setTimeout(() => { - c.end(); - server.close(); - }, 20); - }, - ); - - server.listen(0, () => { - const conn = tls.connect( - { - cert: cert, - key: key, - rejectUnauthorized: false, - port: server.address().port, - }, - () => { - setTimeout(() => { - conn.destroy(); - }, 20); - }, - ); - - // SSL_write() call's return value, when called 0 bytes, should not be - // treated as error. - conn.end(""); - - conn.on("error", err => { - done(new Error("Unexpected error event")); - }); - - setTimeout(() => { - done(); - }, 100); - }); +const r = new stream.Readable({ + highWaterMark: 5, + encoding: 'utf8' }); -//<#END_FILE: test-tls-zero-clear-in.js +let reads = 0; + +function _read() { + if (reads === 0) { + setTimeout(() => { + r.push(str); + }, 1); + reads++; + } else if (reads === 1) { + const ret = r.push(str); + assert.strictEqual(ret, false); + reads++; + } else { + r.push(null); + } +} + +r._read = common.mustCall(_read, 3); + +r.on('end', common.mustCall()); + +// Push some data in to start. +// We've never gotten any read event at this point. +const ret = r.push(str); +// Should be false. > hwm +assert(!ret); +let chunk = r.read(); +assert.strictEqual(chunk, str); +chunk = r.read(); +assert.strictEqual(chunk, null); + +r.once('readable', () => { + // This time, we'll get *all* the remaining data, because + // it's been added synchronously, as the read WOULD take + // us below the hwm, and so it triggered a _read() again, + // which synchronously added more, which we then return. + chunk = r.read(); + assert.strictEqual(chunk, str + str); + + chunk = r.read(); + assert.strictEqual(chunk, null); +}); diff --git a/test/js/node/test/parallel/test-stream-catch-rejections.js b/test/js/node/test/parallel/test-stream-catch-rejections.js new file mode 100644 index 0000000000..81427c3575 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-catch-rejections.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + captureRejections: true, + read() { + } + }); + r.push('hello'); + r.push('world'); + + const err = new Error('kaboom'); + + r.on('error', common.mustCall((_err) => { + assert.strictEqual(err, _err); + assert.strictEqual(r.destroyed, true); + })); + + r.on('data', async () => { + throw err; + }); +} + +{ + const w = new stream.Writable({ + captureRejections: true, + highWaterMark: 1, + write(chunk, enc, cb) { + process.nextTick(cb); + } + }); + + const err = new Error('kaboom'); + + w.write('hello', () => { + w.write('world'); + }); + + w.on('error', common.mustCall((_err) => { + assert.strictEqual(err, _err); + assert.strictEqual(w.destroyed, true); + })); + + w.on('drain', common.mustCall(async () => { + throw err; + }, 2)); +} diff --git a/test/js/node/test/parallel/test-stream-construct.js b/test/js/node/test/parallel/test-stream-construct.js new file mode 100644 index 0000000000..907b9aa0e3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-construct.js @@ -0,0 +1,280 @@ +'use strict'; + +const common = require('../common'); +const { Writable, Readable, Duplex } = require('stream'); +const assert = require('assert'); + +{ + // Multiple callback. + new Writable({ + construct: common.mustCall((callback) => { + callback(); + callback(); + }) + }).on('error', common.expectsError({ + name: 'Error', + code: 'ERR_MULTIPLE_CALLBACK' + })); +} + +{ + // Multiple callback. + new Readable({ + construct: common.mustCall((callback) => { + callback(); + callback(); + }) + }).on('error', common.expectsError({ + name: 'Error', + code: 'ERR_MULTIPLE_CALLBACK' + })); +} + +{ + // Synchronous error. + + new Writable({ + construct: common.mustCall((callback) => { + callback(new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Synchronous error. + + new Readable({ + construct: common.mustCall((callback) => { + callback(new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Asynchronous error. + + new Writable({ + construct: common.mustCall((callback) => { + process.nextTick(callback, new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Asynchronous error. + + new Readable({ + construct: common.mustCall((callback) => { + process.nextTick(callback, new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +function testDestroy(factory) { + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(null, () => { + assert.strictEqual(constructed, true); + }); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(); + } + + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'kaboom'); + })); + s.destroy(new Error('kaboom'), (err) => { + assert.strictEqual(err.message, 'kaboom'); + assert.strictEqual(constructed, true); + }); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('error', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(new Error()); + } +} +testDestroy((opts) => new Readable({ + read: common.mustNotCall(), + ...opts +})); +testDestroy((opts) => new Writable({ + write: common.mustNotCall(), + final: common.mustNotCall(), + ...opts +})); + +{ + let constructed = false; + const r = new Readable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + read: common.mustCall(() => { + assert.strictEqual(constructed, true); + r.push(null); + }) + }); + r.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + r.on('data', common.mustNotCall()); +} + +{ + let constructed = false; + const w = new Writable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }), + final: common.mustCall((cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }) + }); + w.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + w.end('data'); +} + +{ + let constructed = false; + const w = new Writable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + write: common.mustNotCall(), + final: common.mustCall((cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }) + }); + w.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + w.end(); +} + +{ + new Duplex({ + construct: common.mustCall() + }); +} + +{ + // https://github.com/nodejs/node/issues/34448 + + let constructed = false; + const d = new Duplex({ + readable: false, + construct: common.mustCall((callback) => { + setImmediate(common.mustCall(() => { + constructed = true; + callback(); + })); + }), + write(chunk, encoding, callback) { + callback(); + }, + read() { + this.push(null); + } + }); + d.resume(); + d.end('foo'); + d.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); +} + +{ + // Construct should not cause stream to read. + new Readable({ + construct: common.mustCall((callback) => { + callback(); + }), + read: common.mustNotCall() + }); +} diff --git a/test/js/node/test/parallel/test-stream-decoder-objectmode.js b/test/js/node/test/parallel/test-stream-decoder-objectmode.js new file mode 100644 index 0000000000..4c572fed6b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-decoder-objectmode.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const readable = new stream.Readable({ + read: () => {}, + encoding: 'utf16le', + objectMode: true +}); + +readable.push(Buffer.from('abc', 'utf16le')); +readable.push(Buffer.from('def', 'utf16le')); +readable.push(null); + +// Without object mode, these would be concatenated into a single chunk. +assert.strictEqual(readable.read(), 'abc'); +assert.strictEqual(readable.read(), 'def'); +assert.strictEqual(readable.read(), null); diff --git a/test/js/node/test/parallel/test-stream-destroy-event-order.js b/test/js/node/test/parallel/test-stream-destroy-event-order.js new file mode 100644 index 0000000000..a88fff820d --- /dev/null +++ b/test/js/node/test/parallel/test-stream-destroy-event-order.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const rs = new Readable({ + read() {} +}); + +let closed = false; +let errored = false; + +rs.on('close', common.mustCall(() => { + closed = true; + assert(errored); +})); + +rs.on('error', common.mustCall((err) => { + errored = true; + assert(!closed); +})); + +rs.destroy(new Error('kaboom')); diff --git a/test/js/node/test/parallel/test-stream-duplex-end.js b/test/js/node/test/parallel/test-stream-duplex-end.js new file mode 100644 index 0000000000..2c7706146e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-end.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Duplex = require('stream').Duplex; + +{ + const stream = new Duplex({ + read() {} + }); + assert.strictEqual(stream.allowHalfOpen, true); + stream.on('finish', common.mustNotCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} + +{ + const stream = new Duplex({ + read() {}, + allowHalfOpen: false + }); + assert.strictEqual(stream.allowHalfOpen, false); + stream.on('finish', common.mustCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} + +{ + const stream = new Duplex({ + read() {}, + allowHalfOpen: false + }); + assert.strictEqual(stream.allowHalfOpen, false); + stream._writableState.ended = true; + stream.on('finish', common.mustNotCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} diff --git a/test/js/node/test/parallel/test-stream-duplex-props.js b/test/js/node/test/parallel/test-stream-duplex-props.js new file mode 100644 index 0000000000..aa6b23125a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-props.js @@ -0,0 +1,31 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Duplex } = require('stream'); + +{ + const d = new Duplex({ + objectMode: true, + highWaterMark: 100 + }); + + assert.strictEqual(d.writableObjectMode, true); + assert.strictEqual(d.writableHighWaterMark, 100); + assert.strictEqual(d.readableObjectMode, true); + assert.strictEqual(d.readableHighWaterMark, 100); +} + +{ + const d = new Duplex({ + readableObjectMode: false, + readableHighWaterMark: 10, + writableObjectMode: true, + writableHighWaterMark: 100 + }); + + assert.strictEqual(d.writableObjectMode, true); + assert.strictEqual(d.writableHighWaterMark, 100); + assert.strictEqual(d.readableObjectMode, false); + assert.strictEqual(d.readableHighWaterMark, 10); +} diff --git a/test/js/node/test/parallel/test-stream-duplex-readable-end.js b/test/js/node/test/parallel/test-stream-duplex-readable-end.js new file mode 100644 index 0000000000..3b1d4d21ce --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-readable-end.js @@ -0,0 +1,31 @@ +'use strict'; +// https://github.com/nodejs/node/issues/35926 +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let loops = 5; + +const src = new stream.Readable({ + highWaterMark: 16 * 1024, + read() { + if (loops--) + this.push(Buffer.alloc(20000)); + } +}); + +const dst = new stream.Transform({ + highWaterMark: 16 * 1024, + transform(chunk, output, fn) { + this.push(null); + fn(); + } +}); + +src.pipe(dst); + +dst.on('data', () => { }); +dst.on('end', common.mustCall(() => { + assert.strictEqual(loops, 3); + assert.ok(src.isPaused()); +})); diff --git a/test/js/node/test/parallel/test-stream-duplex-readable-writable.js b/test/js/node/test/parallel/test-stream-duplex-readable-writable.js new file mode 100644 index 0000000000..aec88fc120 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-readable-writable.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const { Duplex } = require('stream'); +const assert = require('assert'); + +{ + const duplex = new Duplex({ + readable: false + }); + assert.strictEqual(duplex.readable, false); + duplex.push('asd'); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_PUSH_AFTER_EOF'); + })); + duplex.on('data', common.mustNotCall()); + duplex.on('end', common.mustNotCall()); +} + +{ + const duplex = new Duplex({ + writable: false, + write: common.mustNotCall() + }); + assert.strictEqual(duplex.writable, false); + duplex.write('asd'); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + duplex.on('finish', common.mustNotCall()); +} + +{ + const duplex = new Duplex({ + readable: false + }); + assert.strictEqual(duplex.readable, false); + duplex.on('data', common.mustNotCall()); + duplex.on('end', common.mustNotCall()); + async function run() { + for await (const chunk of duplex) { + assert(false, chunk); + } + } + run().then(common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream-duplex-writable-finished.js b/test/js/node/test/parallel/test-stream-duplex-writable-finished.js new file mode 100644 index 0000000000..20c0781a22 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-writable-finished.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); +const { Duplex } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Duplex.prototype + assert(Object.hasOwn(Duplex.prototype, 'writableFinished')); +} + +// event +{ + const duplex = new Duplex(); + + duplex._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(duplex.writableFinished, false); + cb(); + }; + + duplex.on('finish', common.mustCall(() => { + assert.strictEqual(duplex.writableFinished, true); + })); + + duplex.end('testing finished state', common.mustCall(() => { + assert.strictEqual(duplex.writableFinished, true); + })); +} diff --git a/test/js/node/test/parallel/test-stream-end-of-streams.js b/test/js/node/test/parallel/test-stream-end-of-streams.js new file mode 100644 index 0000000000..80a39d052b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-end-of-streams.js @@ -0,0 +1,20 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const { Duplex, finished } = require('stream'); + +assert.throws( + () => { + // Passing empty object to mock invalid stream + // should throw error + finished({}, () => {}); + }, + { code: 'ERR_INVALID_ARG_TYPE' } +); + +const streamObj = new Duplex(); +streamObj.end(); +// Below code should not throw any errors as the +// streamObj is `Stream` +finished(streamObj, () => {}); diff --git a/test/js/node/test/parallel/test-stream-end-paused.js b/test/js/node/test/parallel/test-stream-end-paused.js new file mode 100644 index 0000000000..f29c82f532 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-end-paused.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Make sure we don't miss the end event for paused 0-length streams + +const Readable = require('stream').Readable; +const stream = new Readable(); +let calledRead = false; +stream._read = function() { + assert(!calledRead); + calledRead = true; + this.push(null); +}; + +stream.on('data', function() { + throw new Error('should not ever get data'); +}); +stream.pause(); + +setTimeout(common.mustCall(function() { + stream.on('end', common.mustCall()); + stream.resume(); +}), 1); + +process.on('exit', function() { + assert(calledRead); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-err-multiple-callback-construction.js b/test/js/node/test/parallel/test-stream-err-multiple-callback-construction.js new file mode 100644 index 0000000000..829af3ffe7 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-err-multiple-callback-construction.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +class TestWritable extends stream.Writable { + _write(_chunk, _encoding, callback) { + callback(); + } + + _final(callback) { + process.nextTick(callback); + process.nextTick(callback); + } +} + +const writable = new TestWritable(); + +writable.on('finish', common.mustCall()); +writable.on('error', common.mustCall((error) => { + assert.strictEqual(error.message, 'Callback called multiple times'); +})); + +writable.write('some data'); +writable.end(); diff --git a/test/js/node/test/parallel/test-stream-error-once.js b/test/js/node/test/parallel/test-stream-error-once.js new file mode 100644 index 0000000000..71f268cfa4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-error-once.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const { Writable, Readable } = require('stream'); + +{ + const writable = new Writable(); + writable.on('error', common.mustCall()); + writable.end(); + writable.write('h'); + writable.write('h'); +} + +{ + const readable = new Readable(); + readable.on('error', common.mustCall()); + readable.push(null); + readable.push('h'); + readable.push('h'); +} diff --git a/test/js/node/test/parallel/test-stream-events-prepend.js b/test/js/node/test/parallel/test-stream-events-prepend.js new file mode 100644 index 0000000000..80fedf8fae --- /dev/null +++ b/test/js/node/test/parallel/test-stream-events-prepend.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +class Writable extends stream.Writable { + constructor() { + super(); + this.prependListener = undefined; + } + + _write(chunk, end, cb) { + cb(); + } +} + +class Readable extends stream.Readable { + _read() { + this.push(null); + } +} + +const w = new Writable(); +w.on('pipe', common.mustCall()); + +const r = new Readable(); +r.pipe(w); diff --git a/test/js/node/test/parallel/test-stream-inheritance.js b/test/js/node/test/parallel/test-stream-inheritance.js new file mode 100644 index 0000000000..658bd2be33 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-inheritance.js @@ -0,0 +1,63 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Readable, Writable, Duplex, Transform } = require('stream'); + +const readable = new Readable({ read() {} }); +const writable = new Writable({ write() {} }); +const duplex = new Duplex({ read() {}, write() {} }); +const transform = new Transform({ transform() {} }); + +assert.ok(readable instanceof Readable); +assert.ok(!(writable instanceof Readable)); +assert.ok(duplex instanceof Readable); +assert.ok(transform instanceof Readable); + +assert.ok(!(readable instanceof Writable)); +assert.ok(writable instanceof Writable); +assert.ok(duplex instanceof Writable); +assert.ok(transform instanceof Writable); + +assert.ok(!(readable instanceof Duplex)); +assert.ok(!(writable instanceof Duplex)); +assert.ok(duplex instanceof Duplex); +assert.ok(transform instanceof Duplex); + +assert.ok(!(readable instanceof Transform)); +assert.ok(!(writable instanceof Transform)); +assert.ok(!(duplex instanceof Transform)); +assert.ok(transform instanceof Transform); + +assert.ok(!(null instanceof Writable)); +assert.ok(!(undefined instanceof Writable)); + +// Simple inheritance check for `Writable` works fine in a subclass constructor. +function CustomWritable() { + assert.ok( + this instanceof CustomWritable, + `${this} does not inherit from CustomWritable` + ); + assert.ok( + this instanceof Writable, + `${this} does not inherit from Writable` + ); +} + +Object.setPrototypeOf(CustomWritable, Writable); +Object.setPrototypeOf(CustomWritable.prototype, Writable.prototype); + +new CustomWritable(); + +assert.throws( + CustomWritable, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + message: 'undefined does not inherit from CustomWritable' + } +); + +class OtherCustomWritable extends Writable {} + +assert(!(new OtherCustomWritable() instanceof CustomWritable)); +assert(!(new CustomWritable() instanceof OtherCustomWritable)); diff --git a/test/js/node/test/parallel/test-stream-iterator-helpers-test262-tests.mjs b/test/js/node/test/parallel/test-stream-iterator-helpers-test262-tests.mjs new file mode 100644 index 0000000000..59bcfdc565 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-iterator-helpers-test262-tests.mjs @@ -0,0 +1,146 @@ +import { mustCall } from '../common/index.mjs'; +import { Readable } from 'stream'; +import assert from 'assert'; + +// These tests are manually ported from the draft PR for the test262 test suite +// Authored by Rick Waldron in https://github.com/tc39/test262/pull/2818/files + +// test262 license: +// The << Software identified by reference to the Ecma Standard* ("Software)">> +// is protected by copyright and is being made available under the +// "BSD License", included below. This Software may be subject to third party +// rights (rights from parties other than Ecma International), including patent +// rights, and no licenses under such third party rights are granted under this +// license even if the third party concerned is a member of Ecma International. +// SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT +// http://www.ecma-international.org/memento/codeofconduct.htm FOR INFORMATION +// REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA +// INTERNATIONAL STANDARDS* + +// Copyright (C) 2012-2013 Ecma International +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the authors nor Ecma International may be used to +// endorse or promote products derived from this software without specific +// prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS +// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +// NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// * Ecma International Standards hereafter means Ecma International Standards +// as well as Ecma Technical Reports + + +// Note all the tests that check AsyncIterator's prototype itself and things +// that happen before stream conversion were not ported. +{ + // drop/length + assert.strictEqual(Readable.prototype.drop.length, 1); + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'drop' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, true); + // drop/limit-equals-total + const iterator = Readable.from([1, 2]).drop(2); + const result = await iterator[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result, { done: true, value: undefined }); + // drop/limit-greater-than-total.js + const iterator2 = Readable.from([1, 2]).drop(3); + const result2 = await iterator2[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result2, { done: true, value: undefined }); + // drop/limit-less-than-total.js + const iterator3 = Readable.from([1, 2]).drop(1); + const result3 = await iterator3[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result3, { done: false, value: 2 }); + // drop/limit-rangeerror + assert.throws(() => Readable.from([1]).drop(-1), RangeError); + assert.throws(() => { + Readable.from([1]).drop({ + valueOf() { + throw new Error('boom'); + } + }); + }, /boom/); + // drop/limit-tointeger + const two = await Readable.from([1, 2]).drop({ valueOf: () => 1 }).toArray(); + assert.deepStrictEqual(two, [2]); + // drop/name + assert.strictEqual(Readable.prototype.drop.name, 'drop'); + // drop/non-constructible + assert.throws(() => new Readable.prototype.drop(1), TypeError); + // drop/proto + const proto = Object.getPrototypeOf(Readable.prototype.drop); + assert.strictEqual(proto, Function.prototype); +} +{ + // every/abrupt-iterator-close + const stream = Readable.from([1, 2, 3]); + const e = new Error(); + await assert.rejects(stream.every(mustCall(() => { + throw e; + }, 1)), e); +} +{ + // every/callable-fn + await assert.rejects(Readable.from([1, 2]).every({}), TypeError); +} +{ + // every/callable + Readable.prototype.every.call(Readable.from([]), () => {}); + // eslint-disable-next-line array-callback-return + Readable.from([]).every(() => {}); + assert.throws(() => { + const r = Readable.from([]); + new r.every(() => {}); + }, TypeError); +} + +{ + // every/false + const iterator = Readable.from([1, 2, 3]); + const result = await iterator.every((v) => v === 1); + assert.strictEqual(result, false); +} +{ + // every/every + const iterator = Readable.from([1, 2, 3]); + const result = await iterator.every((v) => true); + assert.strictEqual(result, true); +} + +{ + // every/is-function + assert.strictEqual(typeof Readable.prototype.every, 'function'); +} +{ + // every/length + assert.strictEqual(Readable.prototype.every.length, 1); + // every/name + assert.strictEqual(Readable.prototype.every.name, 'every'); + // every/propdesc + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'every' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, true); +} diff --git a/test/js/node/test/parallel/test-stream-once-readable-pipe.js b/test/js/node/test/parallel/test-stream-once-readable-pipe.js new file mode 100644 index 0000000000..e8f4e9422d --- /dev/null +++ b/test/js/node/test/parallel/test-stream-once-readable-pipe.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +// This test ensures that if have 'readable' listener +// on Readable instance it will not disrupt the pipe. + +{ + let receivedData = ''; + const w = new Writable({ + write: (chunk, env, callback) => { + receivedData += chunk; + callback(); + }, + }); + + const data = ['foo', 'bar', 'baz']; + const r = new Readable({ + read: () => {}, + }); + + r.once('readable', common.mustCall()); + + r.pipe(w); + r.push(data[0]); + r.push(data[1]); + r.push(data[2]); + r.push(null); + + w.on('finish', common.mustCall(() => { + assert.strictEqual(receivedData, data.join('')); + })); +} + +{ + let receivedData = ''; + const w = new Writable({ + write: (chunk, env, callback) => { + receivedData += chunk; + callback(); + }, + }); + + const data = ['foo', 'bar', 'baz']; + const r = new Readable({ + read: () => {}, + }); + + r.pipe(w); + r.push(data[0]); + r.push(data[1]); + r.push(data[2]); + r.push(null); + r.once('readable', common.mustCall()); + + w.on('finish', common.mustCall(() => { + assert.strictEqual(receivedData, data.join('')); + })); +} diff --git a/test/js/node/test/parallel/test-stream-passthrough-drain.js b/test/js/node/test/parallel/test-stream-passthrough-drain.js new file mode 100644 index 0000000000..244bf87407 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-passthrough-drain.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { PassThrough } = require('stream'); + +const pt = new PassThrough({ highWaterMark: 0 }); +pt.on('drain', common.mustCall()); +assert(!pt.write('hello1')); +pt.read(); +pt.read(); diff --git a/test/js/node/test/parallel/stream2-base64-single-char-read-end.test.js b/test/js/node/test/parallel/test-stream-pipe-after-end.js similarity index 52% rename from test/js/node/test/parallel/stream2-base64-single-char-read-end.test.js rename to test/js/node/test/parallel/test-stream-pipe-after-end.js index bcaae8e2bc..045d27e085 100644 --- a/test/js/node/test/parallel/stream2-base64-single-char-read-end.test.js +++ b/test/js/node/test/parallel/test-stream-pipe-after-end.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-base64-single-char-read-end.js -//#SHA1: 7d3b8e9ad3f47e915bc265658c0b5639fa4a68cd -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,44 +19,51 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { Readable: R, Writable: W } = require("stream"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); -test("stream2 base64 single char read end", async () => { - const src = new R({ encoding: "base64" }); - const dst = new W(); - let hasRead = false; - const accum = []; +class TestReadable extends Readable { + constructor(opt) { + super(opt); + this._ended = false; + } - src._read = function (n) { - if (!hasRead) { - hasRead = true; - process.nextTick(function () { - src.push(Buffer.from("1")); - src.push(null); - }); - } - }; + _read() { + if (this._ended) + this.emit('error', new Error('_read called twice')); + this._ended = true; + this.push(null); + } +} - dst._write = function (chunk, enc, cb) { - accum.push(chunk); +class TestWritable extends Writable { + constructor(opt) { + super(opt); + this._written = []; + } + + _write(chunk, encoding, cb) { + this._written.push(chunk); cb(); - }; + } +} - const endPromise = new Promise(resolve => { - src.on("end", resolve); - }); +// This one should not emit 'end' until we read() from it later. +const ender = new TestReadable(); - src.pipe(dst); +// What happens when you pipe() a Readable that's already ended? +const piper = new TestReadable(); +// pushes EOF null, and length=0, so this will trigger 'end' +piper.read(); - await Promise.race([ - endPromise, - new Promise((_, reject) => { - setTimeout(() => reject(new Error("timed out waiting for _write")), 100); - }), - ]); +setTimeout(common.mustCall(function() { + ender.on('end', common.mustCall()); + const c = ender.read(); + assert.strictEqual(c, null); - expect(String(Buffer.concat(accum))).toBe("MQ=="); -}); - -//<#END_FILE: test-stream2-base64-single-char-read-end.js + const w = new TestWritable(); + w.on('finish', common.mustCall()); + piper.pipe(w); +}), 1); diff --git a/test/js/node/test/parallel/test-stream-pipe-await-drain-manual-resume.js b/test/js/node/test/parallel/test-stream-pipe-await-drain-manual-resume.js new file mode 100644 index 0000000000..a95a5e05ae --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-await-drain-manual-resume.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +// A consumer stream with a very low highWaterMark, which starts in a state +// where it buffers the chunk it receives rather than indicating that they +// have been consumed. +const writable = new stream.Writable({ + highWaterMark: 5 +}); + +let isCurrentlyBufferingWrites = true; +const queue = []; + +writable._write = (chunk, encoding, cb) => { + if (isCurrentlyBufferingWrites) + queue.push({ chunk, cb }); + else + cb(); +}; + +const readable = new stream.Readable({ + read() {} +}); + +readable.pipe(writable); + +readable.once('pause', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + writable, + 'Expected awaitDrainWriters to be a Writable but instead got ' + + `${readable._readableState.awaitDrainWriters}` + ); + // First pause, resume manually. The next write() to writable will still + // return false, because chunks are still being buffered, so it will increase + // the awaitDrain counter again. + + process.nextTick(common.mustCall(() => { + readable.resume(); + })); + + readable.once('pause', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + writable, + '.resume() should not reset the awaitDrainWriters, but instead got ' + + `${readable._readableState.awaitDrainWriters}` + ); + // Second pause, handle all chunks from now on. Once all callbacks that + // are currently queued up are handled, the awaitDrain drain counter should + // fall back to 0 and all chunks that are pending on the readable side + // should be flushed. + isCurrentlyBufferingWrites = false; + for (const queued of queue) + queued.cb(); + })); +})); + +readable.push(Buffer.alloc(100)); // Fill the writable HWM, first 'pause'. +readable.push(Buffer.alloc(100)); // Second 'pause'. +readable.push(Buffer.alloc(100)); // Should get through to the writable. +readable.push(null); + +writable.on('finish', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + null, + `awaitDrainWriters should be reset to null + after all chunks are written but instead got + ${readable._readableState.awaitDrainWriters}` + ); + // Everything okay, all chunks were written. +})); diff --git a/test/js/node/test/parallel/test-stream-pipe-await-drain-push-while-write.js b/test/js/node/test/parallel/test-stream-pipe-await-drain-push-while-write.js new file mode 100644 index 0000000000..089767166c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-await-drain-push-while-write.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const writable = new stream.Writable({ + highWaterMark: 16 * 1024, + write: common.mustCall(function(chunk, encoding, cb) { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + null, + ); + + if (chunk.length === 32 * 1024) { // first chunk + readable.push(Buffer.alloc(34 * 1024)); // above hwm + // We should check if awaitDrain counter is increased in the next + // tick, because awaitDrain is incremented after this method finished + process.nextTick(() => { + assert.strictEqual(readable._readableState.awaitDrainWriters, writable); + }); + } + + process.nextTick(cb); + }, 3) +}); + +// A readable stream which produces two buffers. +const bufs = [Buffer.alloc(32 * 1024), Buffer.alloc(33 * 1024)]; // above hwm +const readable = new stream.Readable({ + highWaterMark: 16 * 1024, + read: function() { + while (bufs.length > 0) { + this.push(bufs.shift()); + } + } +}); + +readable.pipe(writable); diff --git a/test/js/node/test/parallel/test-stream-pipe-await-drain.js b/test/js/node/test/parallel/test-stream-pipe-await-drain.js new file mode 100644 index 0000000000..35b86f67f9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-await-drain.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +// This is very similar to test-stream-pipe-cleanup-pause.js. + +const reader = new stream.Readable(); +const writer1 = new stream.Writable(); +const writer2 = new stream.Writable(); +const writer3 = new stream.Writable(); + +// 560000 is chosen here because it is larger than the (default) highWaterMark +// and will cause `.write()` to return false +// See: https://github.com/nodejs/node/issues/5820 +const buffer = Buffer.allocUnsafe(560000); + +reader._read = () => {}; + +writer1._write = common.mustCall(function(chunk, encoding, cb) { + this.emit('chunk-received'); + process.nextTick(cb); +}, 1); + +writer1.once('chunk-received', () => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 0, + 'awaitDrain initial value should be 0, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + setImmediate(() => { + // This one should *not* get through to writer1 because writer2 is not + // "done" processing. + reader.push(buffer); + }); +}); + +// A "slow" consumer: +writer2._write = common.mustCall((chunk, encoding, cb) => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 1, + 'awaitDrain should be 1 after first push, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + // Not calling cb here to "simulate" slow stream. + // This should be called exactly once, since the first .write() call + // will return false. +}, 1); + +writer3._write = common.mustCall((chunk, encoding, cb) => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 2, + 'awaitDrain should be 2 after second push, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + // Not calling cb here to "simulate" slow stream. + // This should be called exactly once, since the first .write() call + // will return false. +}, 1); + +reader.pipe(writer1); +reader.pipe(writer2); +reader.pipe(writer3); +reader.push(buffer); diff --git a/test/js/node/test/parallel/test-stream-pipe-cleanup-pause.js b/test/js/node/test/parallel/test-stream-pipe-cleanup-pause.js new file mode 100644 index 0000000000..3cdab94648 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-cleanup-pause.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +const reader = new stream.Readable(); +const writer1 = new stream.Writable(); +const writer2 = new stream.Writable(); + +// 560000 is chosen here because it is larger than the (default) highWaterMark +// and will cause `.write()` to return false +// See: https://github.com/nodejs/node/issues/2323 +const buffer = Buffer.allocUnsafe(560000); + +reader._read = () => {}; + +writer1._write = common.mustCall(function(chunk, encoding, cb) { + this.emit('chunk-received'); + cb(); +}, 1); +writer1.once('chunk-received', function() { + reader.unpipe(writer1); + reader.pipe(writer2); + reader.push(buffer); + setImmediate(function() { + reader.push(buffer); + setImmediate(function() { + reader.push(buffer); + }); + }); +}); + +writer2._write = common.mustCall(function(chunk, encoding, cb) { + cb(); +}, 3); + +reader.pipe(writer1); +reader.push(buffer); diff --git a/test/js/node/test/parallel/test-stream-pipe-cleanup.js b/test/js/node/test/parallel/test-stream-pipe-cleanup.js new file mode 100644 index 0000000000..cdb4d503d6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-cleanup.js @@ -0,0 +1,125 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test asserts that Stream.prototype.pipe does not leave listeners +// hanging on the source or dest. +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function Writable() { + this.writable = true; + this.endCalls = 0; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); +Writable.prototype.end = function() { + this.endCalls++; +}; + +Writable.prototype.destroy = function() { + this.endCalls++; +}; + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +function Duplex() { + this.readable = true; + Writable.call(this); +} +Object.setPrototypeOf(Duplex.prototype, Writable.prototype); +Object.setPrototypeOf(Duplex, Writable); + +let i = 0; +const limit = 100; + +let w = new Writable(); + +let r; + +for (i = 0; i < limit; i++) { + r = new Readable(); + r.pipe(w); + r.emit('end'); +} +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(w.endCalls, limit); + +w.endCalls = 0; + +for (i = 0; i < limit; i++) { + r = new Readable(); + r.pipe(w); + r.emit('close'); +} +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(w.endCalls, limit); + +w.endCalls = 0; + +r = new Readable(); + +for (i = 0; i < limit; i++) { + w = new Writable(); + r.pipe(w); + w.emit('close'); +} +assert.strictEqual(w.listeners('close').length, 0); + +r = new Readable(); +w = new Writable(); +const d = new Duplex(); +r.pipe(d); // pipeline A +d.pipe(w); // pipeline B +assert.strictEqual(r.listeners('end').length, 2); // A.onend, A.cleanup +assert.strictEqual(r.listeners('close').length, 2); // A.onclose, A.cleanup +assert.strictEqual(d.listeners('end').length, 2); // B.onend, B.cleanup +// A.cleanup, B.onclose, B.cleanup +assert.strictEqual(d.listeners('close').length, 3); +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 1); // B.cleanup + +r.emit('end'); +assert.strictEqual(d.endCalls, 1); +assert.strictEqual(w.endCalls, 0); +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(d.listeners('end').length, 2); // B.onend, B.cleanup +assert.strictEqual(d.listeners('close').length, 2); // B.onclose, B.cleanup +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 1); // B.cleanup + +d.emit('end'); +assert.strictEqual(d.endCalls, 1); +assert.strictEqual(w.endCalls, 1); +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(d.listeners('end').length, 0); +assert.strictEqual(d.listeners('close').length, 0); +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 0); diff --git a/test/js/node/test/parallel/test-stream-pipe-error-handling.js b/test/js/node/test/parallel/test-stream-pipe-error-handling.js new file mode 100644 index 0000000000..cf3a3699d0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-error-handling.js @@ -0,0 +1,124 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Stream, PassThrough } = require('stream'); + +{ + const source = new Stream(); + const dest = new Stream(); + + source.pipe(dest); + + let gotErr = null; + source.on('error', function(err) { + gotErr = err; + }); + + const err = new Error('This stream turned into bacon.'); + source.emit('error', err); + assert.strictEqual(gotErr, err); +} + +{ + const source = new Stream(); + const dest = new Stream(); + + source.pipe(dest); + + const err = new Error('This stream turned into bacon.'); + + let gotErr = null; + try { + source.emit('error', err); + } catch (e) { + gotErr = e; + } + + assert.strictEqual(gotErr, err); +} + +{ + const R = Stream.Readable; + const W = Stream.Writable; + + const r = new R({ autoDestroy: false }); + const w = new W({ autoDestroy: false }); + let removed = false; + + r._read = common.mustCall(function() { + setTimeout(common.mustCall(function() { + assert(removed); + assert.throws(function() { + w.emit('error', new Error('fail')); + }, /^Error: fail$/); + }), 1); + }); + + w.on('error', myOnError); + r.pipe(w); + w.removeListener('error', myOnError); + removed = true; + + function myOnError() { + throw new Error('this should not happen'); + } +} + +{ + const R = Stream.Readable; + const W = Stream.Writable; + + const r = new R(); + const w = new W(); + let removed = false; + + r._read = common.mustCall(function() { + setTimeout(common.mustCall(function() { + assert(removed); + w.emit('error', new Error('fail')); + }), 1); + }); + + w.on('error', common.mustCall()); + w._write = () => {}; + + r.pipe(w); + // Removing some OTHER random listener should not do anything + w.removeListener('error', () => {}); + removed = true; +} + +{ + const _err = new Error('this should be handled'); + const destination = new PassThrough(); + destination.once('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + + const stream = new Stream(); + stream + .pipe(destination); + + destination.destroy(_err); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-error-unhandled.js b/test/js/node/test/parallel/test-stream-pipe-error-unhandled.js new file mode 100644 index 0000000000..42c1ce77fe --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-error-unhandled.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'asd'); +})); + +const r = new Readable({ + read() { + this.push('asd'); + } +}); +const w = new Writable({ + autoDestroy: true, + write() {} +}); + +r.pipe(w); +w.destroy(new Error('asd')); diff --git a/test/js/node/test/parallel/stream-pipe-event.test.js b/test/js/node/test/parallel/test-stream-pipe-event.js similarity index 78% rename from test/js/node/test/parallel/stream-pipe-event.test.js rename to test/js/node/test/parallel/test-stream-pipe-event.js index 5c7c0626f9..d7772df6a1 100644 --- a/test/js/node/test/parallel/stream-pipe-event.test.js +++ b/test/js/node/test/parallel/test-stream-pipe-event.js @@ -1,6 +1,3 @@ -//#FILE: test-stream-pipe-event.js -//#SHA1: 63887b8cce85a4c7cfa27c8111edd14330a2078f -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,8 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const stream = require("stream"); +'use strict'; +require('../common'); +const stream = require('stream'); +const assert = require('assert'); function Writable() { this.writable = true; @@ -39,18 +38,14 @@ function Readable() { Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); Object.setPrototypeOf(Readable, stream.Stream); -test("pipe event is emitted", () => { - let passed = false; +let passed = false; - const w = new Writable(); - w.on("pipe", function (src) { - passed = true; - }); - - const r = new Readable(); - r.pipe(w); - - expect(passed).toBe(true); +const w = new Writable(); +w.on('pipe', function(src) { + passed = true; }); -//<#END_FILE: test-stream-pipe-event.js +const r = new Readable(); +r.pipe(w); + +assert.ok(passed); diff --git a/test/js/node/test/parallel/test-stream-pipe-flow-after-unpipe.js b/test/js/node/test/parallel/test-stream-pipe-flow-after-unpipe.js new file mode 100644 index 0000000000..048b7ea5e5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-flow-after-unpipe.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const { Readable, Writable } = require('stream'); + +// Tests that calling .unpipe() un-blocks a stream that is paused because +// it is waiting on the writable side to finish a write(). + +const rs = new Readable({ + highWaterMark: 1, + // That this gets called at least 20 times is the real test here. + read: common.mustCallAtLeast(() => rs.push('foo'), 20) +}); + +const ws = new Writable({ + highWaterMark: 1, + write: common.mustCall(() => { + // Ignore the callback, this write() simply never finishes. + setImmediate(() => rs.unpipe(ws)); + }) +}); + +let chunks = 0; +rs.on('data', common.mustCallAtLeast(() => { + chunks++; + if (chunks >= 20) + rs.pause(); // Finish this test. +})); + +rs.pipe(ws); diff --git a/test/js/node/test/parallel/test-stream-pipe-flow.js b/test/js/node/test/parallel/test-stream-pipe-flow.js new file mode 100644 index 0000000000..1f2e8f54ce --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-flow.js @@ -0,0 +1,90 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable, PassThrough } = require('stream'); + +{ + let ticks = 17; + + const rs = new Readable({ + objectMode: true, + read: () => { + if (ticks-- > 0) + return process.nextTick(() => rs.push({})); + rs.push({}); + rs.push(null); + } + }); + + const ws = new Writable({ + highWaterMark: 0, + objectMode: true, + write: (data, end, cb) => setImmediate(cb) + }); + + rs.on('end', common.mustCall()); + ws.on('finish', common.mustCall()); + rs.pipe(ws); +} + +{ + let missing = 8; + + const rs = new Readable({ + objectMode: true, + read: () => { + if (missing--) rs.push({}); + else rs.push(null); + } + }); + + const pt = rs + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })) + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })); + + pt.on('end', () => { + wrapper.push(null); + }); + + const wrapper = new Readable({ + objectMode: true, + read: () => { + process.nextTick(() => { + let data = pt.read(); + if (data === null) { + pt.once('readable', () => { + data = pt.read(); + if (data !== null) wrapper.push(data); + }); + } else { + wrapper.push(data); + } + }); + } + }); + + wrapper.resume(); + wrapper.on('end', common.mustCall()); +} + +{ + // Only register drain if there is backpressure. + const rs = new Readable({ read() {} }); + + const pt = rs + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })); + assert.strictEqual(pt.listenerCount('drain'), 0); + pt.on('finish', () => { + assert.strictEqual(pt.listenerCount('drain'), 0); + }); + + rs.push('asd'); + assert.strictEqual(pt.listenerCount('drain'), 0); + + process.nextTick(() => { + rs.push('asd'); + assert.strictEqual(pt.listenerCount('drain'), 0); + rs.push(null); + assert.strictEqual(pt.listenerCount('drain'), 0); + }); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-manual-resume.js b/test/js/node/test/parallel/test-stream-pipe-manual-resume.js new file mode 100644 index 0000000000..08269acfd3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-manual-resume.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +function test(throwCodeInbetween) { + // Check that a pipe does not stall if .read() is called unexpectedly + // (i.e. the stream is not resumed by the pipe). + + const n = 1000; + let counter = n; + const rs = stream.Readable({ + objectMode: true, + read: common.mustCallAtLeast(() => { + if (--counter >= 0) + rs.push({ counter }); + else + rs.push(null); + }, n) + }); + + const ws = stream.Writable({ + objectMode: true, + write: common.mustCall((data, enc, cb) => { + setImmediate(cb); + }, n) + }); + + setImmediate(() => throwCodeInbetween(rs, ws)); + + rs.pipe(ws); +} + +test((rs) => rs.read()); +test((rs) => rs.resume()); +test(() => 0); diff --git a/test/js/node/test/parallel/test-stream-pipe-multiple-pipes.js b/test/js/node/test/parallel/test-stream-pipe-multiple-pipes.js new file mode 100644 index 0000000000..890c274b9d --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-multiple-pipes.js @@ -0,0 +1,51 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const readable = new stream.Readable({ + read: () => {} +}); + +const writables = []; + +for (let i = 0; i < 5; i++) { + const target = new stream.Writable({ + write: common.mustCall((chunk, encoding, callback) => { + target.output.push(chunk); + callback(); + }, 1) + }); + target.output = []; + + target.on('pipe', common.mustCall()); + readable.pipe(target); + + + writables.push(target); +} + +const input = Buffer.from([1, 2, 3, 4, 5]); + +readable.push(input); + +// The pipe() calls will postpone emission of the 'resume' event using nextTick, +// so no data will be available to the writable streams until then. +process.nextTick(common.mustCall(() => { + for (const target of writables) { + assert.deepStrictEqual(target.output, [input]); + + target.on('unpipe', common.mustCall()); + readable.unpipe(target); + } + + readable.push('something else'); // This does not get through. + readable.push(null); + readable.resume(); // Make sure the 'end' event gets emitted. +})); + +readable.on('end', common.mustCall(() => { + for (const target of writables) { + assert.deepStrictEqual(target.output, [input]); + } +})); diff --git a/test/js/node/test/parallel/test-stream-pipe-needDrain.js b/test/js/node/test/parallel/test-stream-pipe-needDrain.js new file mode 100644 index 0000000000..7faf45417a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-needDrain.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +// Pipe should pause temporarily if writable needs drain. +{ + const w = new Writable({ + write(buf, encoding, callback) { + process.nextTick(callback); + }, + highWaterMark: 1 + }); + + while (w.write('asd')); + + assert.strictEqual(w.writableNeedDrain, true); + + const r = new Readable({ + read() { + this.push('asd'); + this.push(null); + } + }); + + r.on('pause', common.mustCall(2)); + r.on('end', common.mustCall()); + + r.pipe(w); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-same-destination-twice.js b/test/js/node/test/parallel/test-stream-pipe-same-destination-twice.js new file mode 100644 index 0000000000..ff71639588 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-same-destination-twice.js @@ -0,0 +1,78 @@ +'use strict'; +const common = require('../common'); + +// Regression test for https://github.com/nodejs/node/issues/12718. +// Tests that piping a source stream twice to the same destination stream +// works, and that a subsequent unpipe() call only removes the pipe *once*. +const assert = require('assert'); +const { PassThrough, Writable } = require('stream'); + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(`${chunk}`, 'foobar'); + cb(); + }) + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.unpipe(dest); + + assert.strictEqual(passThrough._events.data.length, 1); + assert.strictEqual(passThrough._readableState.pipes.length, 1); + assert.deepStrictEqual(passThrough._readableState.pipes, [dest]); + + passThrough.write('foobar'); + passThrough.pipe(dest); +} + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(`${chunk}`, 'foobar'); + cb(); + }, 2) + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.write('foobar'); +} + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustNotCall() + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.unpipe(dest); + passThrough.unpipe(dest); + + assert.strictEqual(passThrough._events.data, undefined); + assert.strictEqual(passThrough._readableState.pipes.length, 0); + + passThrough.write('foobar'); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-unpipe-streams.js b/test/js/node/test/parallel/test-stream-pipe-unpipe-streams.js new file mode 100644 index 0000000000..74c4353993 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-unpipe-streams.js @@ -0,0 +1,95 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable, Writable } = require('stream'); + +const source = Readable({ read: () => {} }); +const dest1 = Writable({ write: () => {} }); +const dest2 = Writable({ write: () => {} }); + +source.pipe(dest1); +source.pipe(dest2); + +dest1.on('unpipe', common.mustCall()); +dest2.on('unpipe', common.mustCall()); + +assert.strictEqual(source._readableState.pipes[0], dest1); +assert.strictEqual(source._readableState.pipes[1], dest2); +assert.strictEqual(source._readableState.pipes.length, 2); + +// Should be able to unpipe them in the reverse order that they were piped. + +source.unpipe(dest2); + +assert.deepStrictEqual(source._readableState.pipes, [dest1]); +assert.notStrictEqual(source._readableState.pipes, dest2); + +dest2.on('unpipe', common.mustNotCall()); +source.unpipe(dest2); + +source.unpipe(dest1); + +assert.strictEqual(source._readableState.pipes.length, 0); + +{ + // Test `cleanup()` if we unpipe all streams. + const source = Readable({ read: () => {} }); + const dest1 = Writable({ write: () => {} }); + const dest2 = Writable({ write: () => {} }); + + let destCount = 0; + const srcCheckEventNames = ['end', 'data']; + const destCheckEventNames = ['close', 'finish', 'drain', 'error', 'unpipe']; + + const checkSrcCleanup = common.mustCall(() => { + assert.strictEqual(source._readableState.pipes.length, 0); + assert.strictEqual(source._readableState.flowing, false); + for (const eventName of srcCheckEventNames) { + assert.strictEqual( + source.listenerCount(eventName), 0, + `source's '${eventName}' event listeners not removed` + ); + } + }); + + function checkDestCleanup(dest) { + const currentDestId = ++destCount; + source.pipe(dest); + + const unpipeChecker = common.mustCall(() => { + assert.deepStrictEqual( + dest.listeners('unpipe'), [unpipeChecker], + `destination{${currentDestId}} should have a 'unpipe' event ` + + 'listener which is `unpipeChecker`' + ); + dest.removeListener('unpipe', unpipeChecker); + for (const eventName of destCheckEventNames) { + assert.strictEqual( + dest.listenerCount(eventName), 0, + `destination{${currentDestId}}'s '${eventName}' event ` + + 'listeners not removed' + ); + } + + if (--destCount === 0) + checkSrcCleanup(); + }); + + dest.on('unpipe', unpipeChecker); + } + + checkDestCleanup(dest1); + checkDestCleanup(dest2); + source.unpipe(); +} + +{ + const src = Readable({ read: () => {} }); + const dst = Writable({ write: () => {} }); + src.pipe(dst); + src.on('resume', common.mustCall(() => { + src.on('pause', common.mustCall()); + src.unpipe(dst); + })); +} diff --git a/test/js/node/test/parallel/test-stream-pipeline-async-iterator.js b/test/js/node/test/parallel/test-stream-pipeline-async-iterator.js new file mode 100644 index 0000000000..06a2ed6ca8 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-async-iterator.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const { Readable, PassThrough, pipeline } = require('stream'); +const assert = require('assert'); + +const _err = new Error('kaboom'); + +async function run() { + const source = new Readable({ + read() { + } + }); + source.push('hello'); + source.push('world'); + + setImmediate(() => { source.destroy(_err); }); + + const iterator = pipeline( + source, + new PassThrough(), + () => {}); + + iterator.setEncoding('utf8'); + + for await (const k of iterator) { + assert.strictEqual(k, 'helloworld'); + } +} + +run().catch(common.mustCall((err) => assert.strictEqual(err, _err))); diff --git a/test/js/node/test/parallel/test-stream-pipeline-listeners.js b/test/js/node/test/parallel/test-stream-pipeline-listeners.js new file mode 100644 index 0000000000..81e287b77c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-listeners.js @@ -0,0 +1,76 @@ +'use strict'; + +const common = require('../common'); +const { pipeline, Duplex, PassThrough, Writable } = require('stream'); +const assert = require('assert'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'no way'); +}, 2)); + +// Ensure that listeners is removed if last stream is readable +// And other stream's listeners unchanged +const a = new PassThrough(); +a.end('foobar'); +const b = new Duplex({ + write(chunk, encoding, callback) { + callback(); + } +}); +pipeline(a, b, common.mustCall((error) => { + if (error) { + assert.ifError(error); + } + + assert(a.listenerCount('error') > 0); + assert.strictEqual(b.listenerCount('error'), 0); + setTimeout(() => { + assert.strictEqual(b.listenerCount('error'), 0); + b.destroy(new Error('no way')); + }, 100); +})); + +// Async generators +const c = new PassThrough(); +c.end('foobar'); +const d = pipeline( + c, + async function* (source) { + for await (const chunk of source) { + yield String(chunk).toUpperCase(); + } + }, + common.mustCall((error) => { + if (error) { + assert.ifError(error); + } + + assert(c.listenerCount('error') > 0); + assert.strictEqual(d.listenerCount('error'), 0); + setTimeout(() => { + assert.strictEqual(b.listenerCount('error'), 0); + d.destroy(new Error('no way')); + }, 100); + }) +); + +// If last stream is not readable, will not throw and remove listeners +const e = new PassThrough(); +e.end('foobar'); +const f = new Writable({ + write(chunk, encoding, callback) { + callback(); + } +}); +pipeline(e, f, common.mustCall((error) => { + if (error) { + assert.ifError(error); + } + + assert(e.listenerCount('error') > 0); + assert(f.listenerCount('error') > 0); + setTimeout(() => { + assert(f.listenerCount('error') > 0); + f.destroy(new Error('no way')); + }, 100); +})); diff --git a/test/js/node/test/parallel/test-stream-pipeline-process.js b/test/js/node/test/parallel/test-stream-pipeline-process.js new file mode 100644 index 0000000000..a535e7263e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-process.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const os = require('os'); + +if (process.argv[2] === 'child') { + const { pipeline } = require('stream'); + pipeline( + process.stdin, + process.stdout, + common.mustSucceed() + ); +} else { + const cp = require('child_process'); + cp.exec([ + 'echo', + 'hello', + '|', + `"${process.execPath}"`, + `"${__filename}"`, + 'child', + ].join(' '), common.mustSucceed((stdout) => { + assert.strictEqual(stdout.split(os.EOL).shift().trim(), 'hello'); + })); +} diff --git a/test/js/node/test/parallel/test-stream-pipeline-queued-end-in-destroy.js b/test/js/node/test/parallel/test-stream-pipeline-queued-end-in-destroy.js new file mode 100644 index 0000000000..480e5b7f72 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-queued-end-in-destroy.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Duplex, pipeline } = require('stream'); + +// Test that the callback for pipeline() is called even when the ._destroy() +// method of the stream places an .end() request to itself that does not +// get processed before the destruction of the stream (i.e. the 'close' event). +// Refs: https://github.com/nodejs/node/issues/24456 + +const readable = new Readable({ + read: common.mustCall() +}); + +const duplex = new Duplex({ + write(chunk, enc, cb) { + // Simulate messages queueing up. + }, + read() {}, + destroy(err, cb) { + // Call end() from inside the destroy() method, like HTTP/2 streams + // do at the time of writing. + this.end(); + cb(err); + } +}); + +duplex.on('finished', common.mustNotCall()); + +pipeline(readable, duplex, common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_PREMATURE_CLOSE'); +})); + +// Write one chunk of data, and destroy the stream later. +// That should trigger the pipeline destruction. +readable.push('foo'); +setImmediate(() => { + readable.destroy(); +}); diff --git a/test/js/node/test/parallel/test-stream-pipeline-uncaught.js b/test/js/node/test/parallel/test-stream-pipeline-uncaught.js new file mode 100644 index 0000000000..8aa1c47b7f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-uncaught.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const { + pipeline, + PassThrough +} = require('stream'); +const assert = require('assert'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'error'); +})); + +// Ensure that pipeline that ends with Promise +// still propagates error to uncaughtException. +const s = new PassThrough(); +s.end('data'); +pipeline(s, async function(source) { + for await (const chunk of source) { } // eslint-disable-line no-unused-vars, no-empty +}, common.mustSucceed(() => { + throw new Error('error'); +})); diff --git a/test/js/node/test/parallel/test-stream-pipeline-with-empty-string.js b/test/js/node/test/parallel/test-stream-pipeline-with-empty-string.js new file mode 100644 index 0000000000..5df1ff9edf --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-with-empty-string.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); +const { + pipeline, + PassThrough +} = require('stream'); + + +async function runTest() { + await pipeline( + '', + new PassThrough({ objectMode: true }), + common.mustCall(), + ); +} + +runTest().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-stream-preprocess.js b/test/js/node/test/parallel/test-stream-preprocess.js new file mode 100644 index 0000000000..d42c2fd63e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-preprocess.js @@ -0,0 +1,60 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const fs = require('fs'); +const rl = require('readline'); +const fixtures = require('../common/fixtures'); + +const BOM = '\uFEFF'; + +// Get the data using a non-stream way to compare with the streamed data. +const modelData = fixtures.readSync('file-to-read-without-bom.txt', 'utf8'); +const modelDataFirstCharacter = modelData[0]; + +// Detect the number of forthcoming 'line' events for mustCall() 'expected' arg. +const lineCount = modelData.match(/\n/g).length; + +// Ensure both without-bom and with-bom test files are textwise equal. +assert.strictEqual(fixtures.readSync('file-to-read-with-bom.txt', 'utf8'), + `${BOM}${modelData}` +); + +// An unjustified BOM stripping with a non-BOM character unshifted to a stream. +const inputWithoutBOM = + fs.createReadStream(fixtures.path('file-to-read-without-bom.txt'), 'utf8'); + +inputWithoutBOM.once('readable', common.mustCall(() => { + const maybeBOM = inputWithoutBOM.read(1); + assert.strictEqual(maybeBOM, modelDataFirstCharacter); + assert.notStrictEqual(maybeBOM, BOM); + + inputWithoutBOM.unshift(maybeBOM); + + let streamedData = ''; + rl.createInterface({ + input: inputWithoutBOM, + }).on('line', common.mustCall((line) => { + streamedData += `${line}\n`; + }, lineCount)).on('close', common.mustCall(() => { + assert.strictEqual(streamedData, modelData); + })); +})); + +// A justified BOM stripping. +const inputWithBOM = + fs.createReadStream(fixtures.path('file-to-read-with-bom.txt'), 'utf8'); + +inputWithBOM.once('readable', common.mustCall(() => { + const maybeBOM = inputWithBOM.read(1); + assert.strictEqual(maybeBOM, BOM); + + let streamedData = ''; + rl.createInterface({ + input: inputWithBOM, + }).on('line', common.mustCall((line) => { + streamedData += `${line}\n`; + }, lineCount)).on('close', common.mustCall(() => { + assert.strictEqual(streamedData, modelData); + })); +})); diff --git a/test/js/node/test/parallel/process-exit.test.js b/test/js/node/test/parallel/test-stream-push-order.js similarity index 67% rename from test/js/node/test/parallel/process-exit.test.js rename to test/js/node/test/parallel/test-stream-push-order.js index 53ad68de05..f026cb5b8a 100644 --- a/test/js/node/test/parallel/process-exit.test.js +++ b/test/js/node/test/parallel/test-stream-push-order.js @@ -1,6 +1,3 @@ -//#FILE: test-process-exit.js -//#SHA1: 6b66cdd7fd70fedb0fab288294724b6ffe7df8c9 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,25 +19,34 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const Readable = require('stream').Readable; +const assert = require('assert'); -test('Calling .exit() from within "exit" should not overflow the call stack', () => { - let nexits = 0; - - const exitHandler = jest.fn(code => { - expect(nexits++).toBe(0); - expect(code).toBe(0); - process.exit(); - }); - - process.on("exit", exitHandler); - - // Simulate process exit - process.emit("exit", 0); - - expect(exitHandler).toHaveBeenCalledTimes(1); +const s = new Readable({ + highWaterMark: 20, + encoding: 'ascii' }); -// "exit" should be emitted unprovoked +const list = ['1', '2', '3', '4', '5', '6']; -//<#END_FILE: test-process-exit.js +s._read = function(n) { + const one = list.shift(); + if (!one) { + s.push(null); + } else { + const two = list.shift(); + s.push(one); + s.push(two); + } +}; + +s.read(0); + +// ACTUALLY [1, 3, 5, 6, 4, 2] + +process.on('exit', function() { + assert.strictEqual(s.readableBuffer.join(','), '1,2,3,4,5,6'); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/stream-push-strings.test.js b/test/js/node/test/parallel/test-stream-push-strings.js similarity index 66% rename from test/js/node/test/parallel/stream-push-strings.test.js rename to test/js/node/test/parallel/test-stream-push-strings.js index fecd87ceaa..d582c8add0 100644 --- a/test/js/node/test/parallel/stream-push-strings.test.js +++ b/test/js/node/test/parallel/test-stream-push-strings.js @@ -1,6 +1,3 @@ -//#FILE: test-stream-push-strings.js -//#SHA1: d2da34fc74795ea8cc46460ce443d0b30b0e98d8 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,9 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); -const { Readable } = require("stream"); +const Readable = require('stream').Readable; class MyStream extends Readable { constructor(options) { @@ -38,35 +37,31 @@ class MyStream extends Readable { return this.push(null); case 1: return setTimeout(() => { - this.push("last chunk"); + this.push('last chunk'); }, 100); case 2: - return this.push("second to last chunk"); + return this.push('second to last chunk'); case 3: return process.nextTick(() => { - this.push("first chunk"); + this.push('first chunk'); }); default: - throw new Error("?"); + throw new Error('?'); } } } -test("MyStream pushes strings correctly", done => { - const ms = new MyStream(); - const results = []; - ms.on("readable", function () { - let chunk; - while (null !== (chunk = ms.read())) results.push(String(chunk)); - }); - - const expected = ["first chunksecond to last chunk", "last chunk"]; - - ms.on("end", () => { - expect(ms._chunks).toBe(-1); - expect(results).toEqual(expected); - done(); - }); +const ms = new MyStream(); +const results = []; +ms.on('readable', function() { + let chunk; + while (null !== (chunk = ms.read())) + results.push(String(chunk)); }); -//<#END_FILE: test-stream-push-strings.js +const expect = [ 'first chunksecond to last chunk', 'last chunk' ]; +process.on('exit', function() { + assert.strictEqual(ms._chunks, -1); + assert.deepStrictEqual(results, expect); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-aborted.js b/test/js/node/test/parallel/test-stream-readable-aborted.js new file mode 100644 index 0000000000..9badffc51f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-aborted.js @@ -0,0 +1,66 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Duplex } = require('stream'); + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push(null); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push('asd'); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push('asd'); + readable.push(null); + assert.strictEqual(readable.readableAborted, false); + readable.on('end', common.mustCall(() => { + assert.strictEqual(readable.readableAborted, false); + readable.destroy(); + assert.strictEqual(readable.readableAborted, false); + queueMicrotask(() => { + assert.strictEqual(readable.readableAborted, false); + }); + })); + readable.resume(); +} + +{ + const duplex = new Duplex({ + readable: false, + write() {} + }); + duplex.destroy(); + assert.strictEqual(duplex.readableAborted, false); +} diff --git a/test/js/node/test/parallel/test-stream-readable-add-chunk-during-data.js b/test/js/node/test/parallel/test-stream-readable-add-chunk-during-data.js new file mode 100644 index 0000000000..6c36359790 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-add-chunk-during-data.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +// Verify that .push() and .unshift() can be called from 'data' listeners. + +for (const method of ['push', 'unshift']) { + const r = new Readable({ read() {} }); + r.once('data', common.mustCall((chunk) => { + assert.strictEqual(r.readableLength, 0); + r[method](chunk); + assert.strictEqual(r.readableLength, chunk.length); + + r.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(), 'Hello, world'); + })); + })); + + r.push('Hello, world'); +} diff --git a/test/js/node/test/parallel/test-stream-readable-constructor-set-methods.js b/test/js/node/test/parallel/test-stream-readable-constructor-set-methods.js new file mode 100644 index 0000000000..1b9f0496b9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-constructor-set-methods.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); + +const Readable = require('stream').Readable; + +const _read = common.mustCall(function _read(n) { + this.push(null); +}); + +const r = new Readable({ read: _read }); +r.resume(); diff --git a/test/js/node/test/parallel/test-stream-readable-data.js b/test/js/node/test/parallel/test-stream-readable-data.js new file mode 100644 index 0000000000..277adddde6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-data.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); + +const readable = new Readable({ + read() {} +}); + +function read() {} + +readable.setEncoding('utf8'); +readable.on('readable', read); +readable.removeListener('readable', read); + +process.nextTick(function() { + readable.on('data', common.mustCall()); + readable.push('hello'); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-destroy.js b/test/js/node/test/parallel/test-stream-readable-destroy.js new file mode 100644 index 0000000000..fb7da632f7 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-destroy.js @@ -0,0 +1,405 @@ +'use strict'; + +const common = require('../common'); +const { Readable, addAbortSignal } = require('stream'); +const assert = require('assert'); + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read.on('close', common.mustCall()); + + read.destroy(); + assert.strictEqual(read.errored, null); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + read.destroy(expected); + assert.strictEqual(read.errored, expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(err); + }); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + read.destroy(expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {}, + destroy: common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(); + }) + }); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + + // Error is swallowed by the custom _destroy + read.on('error', common.mustNotCall('no error event')); + read.on('close', common.mustCall()); + + read.destroy(expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(); + }); + + read.destroy(); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + process.nextTick(() => { + this.push(null); + cb(); + }); + }); + + const fail = common.mustNotCall('no end event'); + + read.on('end', fail); + read.on('close', common.mustCall()); + + read.destroy(); + + read.removeListener('end', fail); + read.on('end', common.mustNotCall()); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + const expected = new Error('kaboom'); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(expected); + }); + + let ticked = false; + read.on('end', common.mustNotCall('no end event')); + read.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(read._readableState.errorEmitted, true); + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(err, expected); + })); + + read.destroy(); + assert.strictEqual(read._readableState.errorEmitted, false); + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(read.destroyed, true); + ticked = true; +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read.destroyed = true; + assert.strictEqual(read.destroyed, true); + + // The internal destroy() mechanism should not be triggered + read.on('end', common.mustNotCall()); + read.destroy(); +} + +{ + function MyReadable() { + assert.strictEqual(this.destroyed, false); + this.destroyed = false; + Readable.call(this); + } + + Object.setPrototypeOf(MyReadable.prototype, Readable.prototype); + Object.setPrototypeOf(MyReadable, Readable); + + new MyReadable(); +} + +{ + // Destroy and destroy callback + const read = new Readable({ + read() {} + }); + read.resume(); + + const expected = new Error('kaboom'); + + let ticked = false; + read.on('close', common.mustCall(() => { + assert.strictEqual(read._readableState.errorEmitted, true); + assert.strictEqual(ticked, true); + })); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + assert.strictEqual(read._readableState.errored, null); + assert.strictEqual(read._readableState.errorEmitted, false); + + read.destroy(expected, common.mustCall(function(err) { + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(err, expected); + })); + assert.strictEqual(read._readableState.errorEmitted, false); + assert.strictEqual(read._readableState.errored, expected); + ticked = true; +} + +{ + const readable = new Readable({ + destroy: common.mustCall(function(err, cb) { + process.nextTick(cb, new Error('kaboom 1')); + }), + read() {} + }); + + let ticked = false; + readable.on('close', common.mustCall(() => { + assert.strictEqual(ticked, true); + assert.strictEqual(readable._readableState.errorEmitted, true); + })); + readable.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.message, 'kaboom 1'); + assert.strictEqual(readable._readableState.errorEmitted, true); + })); + + readable.destroy(); + assert.strictEqual(readable.destroyed, true); + assert.strictEqual(readable._readableState.errored, null); + assert.strictEqual(readable._readableState.errorEmitted, false); + + // Test case where `readable.destroy()` is called again with an error before + // the `_destroy()` callback is called. + readable.destroy(new Error('kaboom 2')); + assert.strictEqual(readable._readableState.errorEmitted, false); + assert.strictEqual(readable._readableState.errored, null); + + ticked = true; +} + +{ + const read = new Readable({ + read() {} + }); + + read.destroy(); + read.push('hi'); + read.on('data', common.mustNotCall()); +} + +{ + const read = new Readable({ + read: common.mustNotCall() + }); + read.destroy(); + assert.strictEqual(read.destroyed, true); + read.read(); +} + +{ + const read = new Readable({ + autoDestroy: false, + read() { + this.push(null); + this.push('asd'); + } + }); + + read.on('error', common.mustCall(() => { + assert(read._readableState.errored); + })); + read.resume(); +} + +{ + const controller = new AbortController(); + const read = addAbortSignal(controller.signal, new Readable({ + read() { + this.push('asd'); + }, + })); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + controller.abort(); + read.on('data', common.mustNotCall()); +} + +{ + const controller = new AbortController(); + const read = new Readable({ + signal: controller.signal, + read() { + this.push('asd'); + }, + }); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + controller.abort(); + read.on('data', common.mustNotCall()); +} + +{ + const controller = new AbortController(); + const read = addAbortSignal(controller.signal, new Readable({ + objectMode: true, + read() { + return false; + } + })); + read.push('asd'); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + assert.rejects((async () => { + // eslint-disable-next-line no-unused-vars, no-empty + for await (const chunk of read) { } + })(), /AbortError/).then(common.mustCall()); + setTimeout(() => controller.abort(), 0); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('error', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(new Error('asd')); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.unshift('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.unshift('asd'); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.resume(); + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.push('asd'); +} diff --git a/test/js/node/test/parallel/test-stream-readable-didRead.js b/test/js/node/test/parallel/test-stream-readable-didRead.js new file mode 100644 index 0000000000..878340ba19 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-didRead.js @@ -0,0 +1,111 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { isDisturbed, isErrored, Readable } = require('stream'); + +function noop() {} + +function check(readable, data, fn) { + assert.strictEqual(readable.readableDidRead, false); + assert.strictEqual(isDisturbed(readable), false); + assert.strictEqual(isErrored(readable), false); + if (data === -1) { + readable.on('error', common.mustCall(() => { + assert.strictEqual(isErrored(readable), true); + })); + readable.on('data', common.mustNotCall()); + readable.on('end', common.mustNotCall()); + } else { + readable.on('error', common.mustNotCall()); + if (data === -2) { + readable.on('end', common.mustNotCall()); + } else { + readable.on('end', common.mustCall()); + } + if (data > 0) { + readable.on('data', common.mustCallAtLeast(data)); + } else { + readable.on('data', common.mustNotCall()); + } + } + readable.on('close', common.mustCall()); + fn(); + setImmediate(() => { + assert.strictEqual(readable.readableDidRead, data > 0); + if (data > 0) { + assert.strictEqual(isDisturbed(readable), true); + } + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, 0, () => { + readable.read(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, 0, () => { + readable.resume(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, -2, () => { + readable.destroy(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + + check(readable, -1, () => { + readable.destroy(new Error()); + }); +} + +{ + const readable = new Readable({ + read() { + this.push('data'); + this.push(null); + } + }); + + check(readable, 1, () => { + readable.on('data', noop); + }); +} + +{ + const readable = new Readable({ + read() { + this.push('data'); + this.push(null); + } + }); + + check(readable, 1, () => { + readable.on('data', noop); + readable.off('data', noop); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-emit-readable-short-stream.js b/test/js/node/test/parallel/test-stream-readable-emit-readable-short-stream.js new file mode 100644 index 0000000000..d8b84bfbe7 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-emit-readable-short-stream.js @@ -0,0 +1,146 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + read: common.mustCall(function() { + this.push('content'); + this.push(null); + }) + }); + + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + r.pipe(t); + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.end('content'); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.write('content'); + t.end(); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); +} + +{ + const t = new stream.Readable({ + read() { + } + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); + + t.push('content'); + t.push(null); +} + +{ + const t = new stream.Readable({ + read() { + } + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); + + process.nextTick(() => { + t.push('content'); + t.push(null); + }); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); + + t.write('content'); + t.end(); +} diff --git a/test/js/node/test/parallel/test-stream-readable-emittedReadable.js b/test/js/node/test/parallel/test-stream-readable-emittedReadable.js new file mode 100644 index 0000000000..ba613f9e9f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-emittedReadable.js @@ -0,0 +1,73 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +const readable = new Readable({ + read: () => {} +}); + +// Initialized to false. +assert.strictEqual(readable._readableState.emittedReadable, false); + +const expected = [Buffer.from('foobar'), Buffer.from('quo'), null]; +readable.on('readable', common.mustCall(() => { + // emittedReadable should be true when the readable event is emitted + assert.strictEqual(readable._readableState.emittedReadable, true); + assert.deepStrictEqual(readable.read(), expected.shift()); + // emittedReadable is reset to false during read() + assert.strictEqual(readable._readableState.emittedReadable, false); +}, 3)); + +// When the first readable listener is just attached, +// emittedReadable should be false +assert.strictEqual(readable._readableState.emittedReadable, false); + +// These trigger a single 'readable', as things are batched up +process.nextTick(common.mustCall(() => { + readable.push('foo'); +})); +process.nextTick(common.mustCall(() => { + readable.push('bar'); +})); + +// These triggers two readable events +setImmediate(common.mustCall(() => { + readable.push('quo'); + process.nextTick(common.mustCall(() => { + readable.push(null); + })); +})); + +const noRead = new Readable({ + read: () => {} +}); + +noRead.on('readable', common.mustCall(() => { + // emittedReadable should be true when the readable event is emitted + assert.strictEqual(noRead._readableState.emittedReadable, true); + noRead.read(0); + // emittedReadable is not reset during read(0) + assert.strictEqual(noRead._readableState.emittedReadable, true); +})); + +noRead.push('foo'); +noRead.push(null); + +const flowing = new Readable({ + read: () => {} +}); + +flowing.on('data', common.mustCall(() => { + // When in flowing mode, emittedReadable is always false. + assert.strictEqual(flowing._readableState.emittedReadable, false); + flowing.read(); + assert.strictEqual(flowing._readableState.emittedReadable, false); +}, 3)); + +flowing.push('foooo'); +flowing.push('bar'); +flowing.push('quo'); +process.nextTick(common.mustCall(() => { + flowing.push(null); +})); diff --git a/test/js/node/test/parallel/test-stream-readable-end-destroyed.js b/test/js/node/test/parallel/test-stream-readable-end-destroyed.js new file mode 100644 index 0000000000..4b60bf4614 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-end-destroyed.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +{ + // Don't emit 'end' after 'close'. + + const r = new Readable(); + + r.on('end', common.mustNotCall()); + r.resume(); + r.destroy(); + r.on('close', common.mustCall(() => { + r.push(null); + })); +} diff --git a/test/js/node/test/parallel/test-stream-readable-ended.js b/test/js/node/test/parallel/test-stream-readable-ended.js new file mode 100644 index 0000000000..bdd714c955 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-ended.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Readable.prototype + assert(Object.hasOwn(Readable.prototype, 'readableEnded')); +} + +// event +{ + const readable = new Readable(); + + readable._read = () => { + // The state ended should start in false. + assert.strictEqual(readable.readableEnded, false); + readable.push('asd'); + assert.strictEqual(readable.readableEnded, false); + readable.push(null); + assert.strictEqual(readable.readableEnded, false); + }; + + readable.on('end', common.mustCall(() => { + assert.strictEqual(readable.readableEnded, true); + })); + + readable.on('data', common.mustCall(() => { + assert.strictEqual(readable.readableEnded, false); + })); +} + +// Verifies no `error` triggered on multiple .push(null) invocations +{ + const readable = new Readable(); + + readable.on('readable', () => { readable.read(); }); + readable.on('error', common.mustNotCall()); + readable.on('end', common.mustCall()); + + readable.push('a'); + readable.push(null); + readable.push(null); +} diff --git a/test/js/node/test/parallel/test-stream-readable-error-end.js b/test/js/node/test/parallel/test-stream-readable-error-end.js new file mode 100644 index 0000000000..b46fd7f32c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-error-end.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +{ + const r = new Readable({ read() {} }); + + r.on('end', common.mustNotCall()); + r.on('data', common.mustCall()); + r.on('error', common.mustCall()); + r.push('asd'); + r.push(null); + r.destroy(new Error('kaboom')); +} diff --git a/test/js/node/test/parallel/test-stream-readable-event.js b/test/js/node/test/parallel/test-stream-readable-event.js new file mode 100644 index 0000000000..4f2383508a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-event.js @@ -0,0 +1,128 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +{ + // First test, not reading when the readable is added. + // make sure that on('readable', ...) triggers a readable event. + const r = new Readable({ + highWaterMark: 3 + }); + + r._read = common.mustNotCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('blerg')); + + setTimeout(function() { + // We're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Second test, make sure that readable is re-emitted if there's + // already a length, while it IS reading. + + const r = new Readable({ + highWaterMark: 3 + }); + + r._read = common.mustCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('bl')); + + setTimeout(function() { + // Assert we're testing what we think we are + assert(r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Third test, not reading when the stream has not passed + // the highWaterMark but *has* reached EOF. + const r = new Readable({ + highWaterMark: 30 + }); + + r._read = common.mustNotCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('blerg')); + r.push(null); + + setTimeout(function() { + // Assert we're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Pushing an empty string in non-objectMode should + // trigger next `read()`. + const underlyingData = ['', 'x', 'y', '', 'z']; + const expected = underlyingData.filter((data) => data); + const result = []; + + const r = new Readable({ + encoding: 'utf8', + }); + r._read = function() { + process.nextTick(() => { + if (!underlyingData.length) { + this.push(null); + } else { + this.push(underlyingData.shift()); + } + }); + }; + + r.on('readable', () => { + const data = r.read(); + if (data !== null) result.push(data); + }); + + r.on('end', common.mustCall(() => { + assert.deepStrictEqual(result, expected); + })); +} + +{ + // #20923 + const r = new Readable(); + r._read = function() { + // Actually doing thing here + }; + r.on('data', function() {}); + + r.removeAllListeners(); + + assert.strictEqual(r.eventNames().length, 0); +} diff --git a/test/js/node/test/parallel/test-stream-readable-flow-recursion.js b/test/js/node/test/parallel/test-stream-readable-flow-recursion.js new file mode 100644 index 0000000000..bac6427fb0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-flow-recursion.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This test verifies that passing a huge number to read(size) +// will push up the highWaterMark, and cause the stream to read +// more data continuously, but without triggering a nextTick +// warning or RangeError. + +const Readable = require('stream').Readable; + +// Throw an error if we trigger a nextTick warning. +process.throwDeprecation = true; + +const stream = new Readable({ highWaterMark: 2 }); +let reads = 0; +let total = 5000; +stream._read = function(size) { + reads++; + size = Math.min(size, total); + total -= size; + if (size === 0) + stream.push(null); + else + stream.push(Buffer.allocUnsafe(size)); +}; + +let depth = 0; + +function flow(stream, size, callback) { + depth += 1; + const chunk = stream.read(size); + + if (!chunk) + stream.once('readable', flow.bind(null, stream, size, callback)); + else + callback(chunk); + + depth -= 1; + console.log(`flow(${depth}): exit`); +} + +flow(stream, 5000, function() { + console.log(`complete (${depth})`); +}); + +process.on('exit', function(code) { + assert.strictEqual(reads, 2); + // We pushed up the high water mark + assert.strictEqual(stream.readableHighWaterMark, 8192); + // Length is 0 right now, because we pulled it all out. + assert.strictEqual(stream.readableLength, 0); + assert(!code); + assert.strictEqual(depth, 0); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-hwm-0-async.js b/test/js/node/test/parallel/test-stream-readable-hwm-0-async.js new file mode 100644 index 0000000000..866b524893 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-hwm-0-async.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); + +// This test ensures that Readable stream will continue to call _read +// for streams with highWaterMark === 0 once the stream returns data +// by calling push() asynchronously. + +const { Readable } = require('stream'); + +let count = 5; + +const r = new Readable({ + // Called 6 times: First 5 return data, last one signals end of stream. + read: common.mustCall(() => { + process.nextTick(common.mustCall(() => { + if (count--) + r.push('a'); + else + r.push(null); + })); + }, 6), + highWaterMark: 0, +}); + +r.on('end', common.mustCall()); +r.on('data', common.mustCall(5)); diff --git a/test/js/node/test/parallel/test-stream-readable-hwm-0-no-flow-data.js b/test/js/node/test/parallel/test-stream-readable-hwm-0-no-flow-data.js new file mode 100644 index 0000000000..5f0186d720 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-hwm-0-no-flow-data.js @@ -0,0 +1,104 @@ +'use strict'; + +const common = require('../common'); + +// Ensure that subscribing the 'data' event will not make the stream flow. +// The 'data' event will require calling read() by hand. +// +// The test is written for the (somewhat rare) highWaterMark: 0 streams to +// specifically catch any regressions that might occur with these streams. + +const assert = require('assert'); +const { Readable } = require('stream'); + +const streamData = [ 'a', null ]; + +// Track the calls so we can assert their order later. +const calls = []; +const r = new Readable({ + read: common.mustCall(() => { + calls.push('_read:' + streamData[0]); + process.nextTick(() => { + calls.push('push:' + streamData[0]); + r.push(streamData.shift()); + }); + }, streamData.length), + highWaterMark: 0, + + // Object mode is used here just for testing convenience. It really + // shouldn't affect the order of events. Just the data and its format. + objectMode: true, +}); + +assert.strictEqual(r.readableFlowing, null); +r.on('readable', common.mustCall(() => { + calls.push('readable'); +}, 2)); +assert.strictEqual(r.readableFlowing, false); +r.on('data', common.mustCall((data) => { + calls.push('data:' + data); +}, 1)); +r.on('end', common.mustCall(() => { + calls.push('end'); +})); +assert.strictEqual(r.readableFlowing, false); + +// The stream emits the events asynchronously but that's not guaranteed to +// happen on the next tick (especially since the _read implementation above +// uses process.nextTick). +// +// We use setImmediate here to give the stream enough time to emit all the +// events it's about to emit. +setImmediate(() => { + + // Only the _read, push, readable calls have happened. No data must be + // emitted yet. + assert.deepStrictEqual(calls, ['_read:a', 'push:a', 'readable']); + + // Calling 'r.read()' should trigger the data event. + assert.strictEqual(r.read(), 'a'); + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a']); + + // The next 'read()' will return null because hwm: 0 does not buffer any + // data and the _read implementation above does the push() asynchronously. + // + // Note: This 'null' signals "no data available". It isn't the end-of-stream + // null value as the stream doesn't know yet that it is about to reach the + // end. + // + // Using setImmediate again to give the stream enough time to emit all the + // events it wants to emit. + assert.strictEqual(r.read(), null); + setImmediate(() => { + + // There's a new 'readable' event after the data has been pushed. + // The 'end' event will be emitted only after a 'read()'. + // + // This is somewhat special for the case where the '_read' implementation + // calls 'push' asynchronously. If 'push' was synchronous, the 'end' event + // would be emitted here _before_ we call read(). + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable']); + + assert.strictEqual(r.read(), null); + + // While it isn't really specified whether the 'end' event should happen + // synchronously with read() or not, we'll assert the current behavior + // ('end' event happening on the next tick after read()) so any changes + // to it are noted and acknowledged in the future. + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable']); + process.nextTick(() => { + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable', 'end']); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-hwm-0.js b/test/js/node/test/parallel/test-stream-readable-hwm-0.js new file mode 100644 index 0000000000..5bb17882db --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-hwm-0.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); + +// This test ensures that Readable stream will call _read() for streams +// with highWaterMark === 0 upon .read(0) instead of just trying to +// emit 'readable' event. + +const assert = require('assert'); +const { Readable } = require('stream'); + +const r = new Readable({ + // Must be called only once upon setting 'readable' listener + read: common.mustCall(), + highWaterMark: 0, +}); + +let pushedNull = false; +// This will trigger read(0) but must only be called after push(null) +// because the we haven't pushed any data +r.on('readable', common.mustCall(() => { + assert.strictEqual(r.read(), null); + assert.strictEqual(pushedNull, true); +})); +r.on('end', common.mustCall()); +process.nextTick(() => { + assert.strictEqual(r.read(), null); + pushedNull = true; + r.push(null); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-infinite-read.js b/test/js/node/test/parallel/test-stream-readable-infinite-read.js new file mode 100644 index 0000000000..df88d78b74 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-infinite-read.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const buf = Buffer.alloc(8192); + +const readable = new Readable({ + highWaterMark: 16 * 1024, + read: common.mustCall(function() { + this.push(buf); + }, 31) +}); + +let i = 0; + +readable.on('readable', common.mustCall(function() { + if (i++ === 10) { + // We will just terminate now. + process.removeAllListeners('readable'); + return; + } + + const data = readable.read(); + // TODO(mcollina): there is something odd in the highWaterMark logic + // investigate. + if (i === 1) { + assert.strictEqual(data.length, 8192 * 2); + } else { + assert.strictEqual(data.length, 8192 * 3); + } +}, 11)); diff --git a/test/js/node/test/parallel/test-stream-readable-invalid-chunk.js b/test/js/node/test/parallel/test-stream-readable-invalid-chunk.js new file mode 100644 index 0000000000..0fcc76ae73 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-invalid-chunk.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); + +function testPushArg(val) { + const readable = new stream.Readable({ + read: () => {} + }); + readable.on('error', common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + })); + readable.push(val); +} + +testPushArg([]); +testPushArg({}); +testPushArg(0); + +function testUnshiftArg(val) { + const readable = new stream.Readable({ + read: () => {} + }); + readable.on('error', common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + })); + readable.unshift(val); +} + +testUnshiftArg([]); +testUnshiftArg({}); +testUnshiftArg(0); diff --git a/test/js/node/test/parallel/test-stream-readable-needReadable.js b/test/js/node/test/parallel/test-stream-readable-needReadable.js new file mode 100644 index 0000000000..c4bc90bb19 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-needReadable.js @@ -0,0 +1,99 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +const readable = new Readable({ + read: () => {} +}); + +// Initialized to false. +assert.strictEqual(readable._readableState.needReadable, false); + +readable.on('readable', common.mustCall(() => { + // When the readable event fires, needReadable is reset. + assert.strictEqual(readable._readableState.needReadable, false); + readable.read(); +})); + +// If a readable listener is attached, then a readable event is needed. +assert.strictEqual(readable._readableState.needReadable, true); + +readable.push('foo'); +readable.push(null); + +readable.on('end', common.mustCall(() => { + // No need to emit readable anymore when the stream ends. + assert.strictEqual(readable._readableState.needReadable, false); +})); + +const asyncReadable = new Readable({ + read: () => {} +}); + +asyncReadable.on('readable', common.mustCall(() => { + if (asyncReadable.read() !== null) { + // After each read(), the buffer is empty. + // If the stream doesn't end now, + // then we need to notify the reader on future changes. + assert.strictEqual(asyncReadable._readableState.needReadable, true); + } +}, 2)); + +process.nextTick(common.mustCall(() => { + asyncReadable.push('foooo'); +})); +process.nextTick(common.mustCall(() => { + asyncReadable.push('bar'); +})); +setImmediate(common.mustCall(() => { + asyncReadable.push(null); + assert.strictEqual(asyncReadable._readableState.needReadable, false); +})); + +const flowing = new Readable({ + read: () => {} +}); + +// Notice this must be above the on('data') call. +flowing.push('foooo'); +flowing.push('bar'); +flowing.push('quo'); +process.nextTick(common.mustCall(() => { + flowing.push(null); +})); + +// When the buffer already has enough data, and the stream is +// in flowing mode, there is no need for the readable event. +flowing.on('data', common.mustCall(function(data) { + assert.strictEqual(flowing._readableState.needReadable, false); +}, 3)); + +const slowProducer = new Readable({ + read: () => {} +}); + +slowProducer.on('readable', common.mustCall(() => { + const chunk = slowProducer.read(8); + const state = slowProducer._readableState; + if (chunk === null) { + // The buffer doesn't have enough data, and the stream is not need, + // we need to notify the reader when data arrives. + assert.strictEqual(state.needReadable, true); + } else { + assert.strictEqual(state.needReadable, false); + } +}, 4)); + +process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push(null); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-stream-readable-next-no-null.js b/test/js/node/test/parallel/test-stream-readable-next-no-null.js new file mode 100644 index 0000000000..7599e386ca --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-next-no-null.js @@ -0,0 +1,19 @@ +'use strict'; +const { mustNotCall, expectsError } = require('../common'); +const { Readable } = require('stream'); + +async function* generate() { + yield null; +} + +const stream = Readable.from(generate()); + +stream.on('error', expectsError({ + code: 'ERR_STREAM_NULL_VALUES', + name: 'TypeError', + message: 'May not write null values to stream' +})); + +stream.on('data', mustNotCall()); + +stream.on('end', mustNotCall()); diff --git a/test/js/node/test/parallel/test-stream-readable-no-unneeded-readable.js b/test/js/node/test/parallel/test-stream-readable-no-unneeded-readable.js new file mode 100644 index 0000000000..20092b57d9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-no-unneeded-readable.js @@ -0,0 +1,62 @@ +'use strict'; +const common = require('../common'); +const { Readable, PassThrough } = require('stream'); + +function test(r) { + const wrapper = new Readable({ + read: () => { + let data = r.read(); + + if (data) { + wrapper.push(data); + return; + } + + r.once('readable', function() { + data = r.read(); + if (data) { + wrapper.push(data); + } + // else: the end event should fire + }); + }, + }); + + r.once('end', function() { + wrapper.push(null); + }); + + wrapper.resume(); + wrapper.once('end', common.mustCall()); +} + +{ + const source = new Readable({ + read: () => {} + }); + source.push('foo'); + source.push('bar'); + source.push(null); + + const pt = source.pipe(new PassThrough()); + test(pt); +} + +{ + // This is the underlying cause of the above test case. + const pushChunks = ['foo', 'bar']; + const r = new Readable({ + read: () => { + const chunk = pushChunks.shift(); + if (chunk) { + // synchronous call + r.push(chunk); + } else { + // asynchronous call + process.nextTick(() => r.push(null)); + } + }, + }); + + test(r); +} diff --git a/test/js/node/test/parallel/test-stream-readable-object-multi-push-async.js b/test/js/node/test/parallel/test-stream-readable-object-multi-push-async.js new file mode 100644 index 0000000000..3bdbe4d351 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-object-multi-push-async.js @@ -0,0 +1,183 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const MAX = 42; +const BATCH = 10; + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + if (data.length === 0) { + console.log('pushing null'); + this.push(null); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + }); + }, Math.floor(MAX / BATCH) + 2) + }); + + let i = 0; + function fetchData(cb) { + if (i > MAX) { + setTimeout(cb, 10, null, []); + } else { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + } + + readable.on('readable', () => { + let data; + console.log('readable emitted'); + while ((data = readable.read()) !== null) { + console.log(data); + } + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + if (data.length === 0) { + console.log('pushing null'); + this.push(null); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + }); + }, Math.floor(MAX / BATCH) + 2) + }); + + let i = 0; + function fetchData(cb) { + if (i > MAX) { + setTimeout(cb, 10, null, []); + } else { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + } + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + + if (data[BATCH - 1] >= MAX) { + console.log('pushing null'); + this.push(null); + } + }); + }, Math.floor(MAX / BATCH) + 1) + }); + + let i = 0; + function fetchData(cb) { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustNotCall() + }); + + readable.on('data', common.mustNotCall()); + + readable.push(null); + + let nextTickPassed = false; + process.nextTick(() => { + nextTickPassed = true; + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(nextTickPassed, true); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall() + }); + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall()); + + setImmediate(() => { + readable.push('aaa'); + readable.push(null); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-readable.js b/test/js/node/test/parallel/test-stream-readable-readable.js new file mode 100644 index 0000000000..6e1a7fb32e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-readable.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable } = require('stream'); + +{ + const r = new Readable({ + read() {} + }); + assert.strictEqual(r.readable, true); + r.destroy(); + assert.strictEqual(r.readable, false); +} + +{ + const mustNotCall = common.mustNotCall(); + const r = new Readable({ + read() {} + }); + assert.strictEqual(r.readable, true); + r.on('end', mustNotCall); + r.resume(); + r.push(null); + assert.strictEqual(r.readable, true); + r.off('end', mustNotCall); + r.on('end', common.mustCall(() => { + assert.strictEqual(r.readable, false); + })); +} + +{ + const r = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + r.destroy(new Error()); + assert.strictEqual(r.readable, false); + }); + }) + }); + r.resume(); + r.on('error', common.mustCall(() => { + assert.strictEqual(r.readable, false); + })); +} diff --git a/test/js/node/test/parallel/test-stream-readable-reading-readingMore.js b/test/js/node/test/parallel/test-stream-readable-reading-readingMore.js new file mode 100644 index 0000000000..5e39c86dcb --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-reading-readingMore.js @@ -0,0 +1,171 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + readable.on('data', common.mustCall((data) => { + // While in a flowing state with a 'readable' listener + // we should not be reading more + if (readable.readableFlowing) + assert.strictEqual(state.readingMore, true); + + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + const expectedReadingMore = [true, true, false]; + readable.on('readable', common.mustCall(() => { + // There is only one readingMore scheduled from on('data'), + // after which everything is governed by the .read() call + assert.strictEqual(state.readingMore, expectedReadingMore.shift()); + + // If the stream has ended, we shouldn't be reading + assert.strictEqual(state.ended, !state.reading); + + // Consume all the data + while (readable.read() !== null); + + if (expectedReadingMore.length === 0) // Reached end of stream + process.nextTick(common.mustCall(onStreamEnd, 1)); + }, 3)); + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + readable.read(6); + + // reading + assert.strictEqual(state.reading, true); + assert.strictEqual(state.readingMore, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); +} + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + readable.on('data', common.mustCall((data) => { + // While in a flowing state without a 'readable' listener + // we should be reading more + if (readable.readableFlowing) + assert.strictEqual(state.readingMore, true); + + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + // Stop emitting 'data' events + assert.strictEqual(state.flowing, true); + readable.pause(); + + // paused + assert.strictEqual(state.reading, false); + assert.strictEqual(state.flowing, false); + + readable.resume(); + assert.strictEqual(state.reading, false); + assert.strictEqual(state.flowing, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); +} + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + const onReadable = common.mustNotCall(); + + readable.on('readable', onReadable); + + readable.on('data', common.mustCall((data) => { + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + readable.removeListener('readable', onReadable); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + // We are still not flowing, we will be resuming in the next tick + assert.strictEqual(state.flowing, false); + + // Wait for nextTick, so the readableListener flag resets + process.nextTick(function() { + readable.resume(); + + // Stop emitting 'data' events + assert.strictEqual(state.flowing, true); + readable.pause(); + + // paused + assert.strictEqual(state.flowing, false); + + readable.resume(); + assert.strictEqual(state.flowing, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-resume-hwm.js b/test/js/node/test/parallel/test-stream-readable-resume-hwm.js new file mode 100644 index 0000000000..3f0bbad243 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-resume-hwm.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +// readable.resume() should not lead to a ._read() call being scheduled +// when we exceed the high water mark already. + +const readable = new Readable({ + read: common.mustNotCall(), + highWaterMark: 100 +}); + +// Fill up the internal buffer so that we definitely exceed the HWM: +for (let i = 0; i < 10; i++) + readable.push('a'.repeat(200)); + +// Call resume, and pause after one chunk. +// The .pause() is just so that we don’t empty the buffer fully, which would +// be a valid reason to call ._read(). +readable.resume(); +readable.once('data', common.mustCall(() => readable.pause())); diff --git a/test/js/node/test/parallel/test-stream-readable-resumeScheduled.js b/test/js/node/test/parallel/test-stream-readable-resumeScheduled.js new file mode 100644 index 0000000000..aa521629b6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-resumeScheduled.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); + +// Testing Readable Stream resumeScheduled state + +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +{ + // pipe() test case + const r = new Readable({ read() {} }); + const w = new Writable(); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + // Calling pipe() should change the state value = true. + r.pipe(w); + assert.strictEqual(r._readableState.resumeScheduled, true); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} + +{ + // 'data' listener test case + const r = new Readable({ read() {} }); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + r.push(Buffer.from([1, 2, 3])); + + // Adding 'data' listener should change the state value + r.on('data', common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); + assert.strictEqual(r._readableState.resumeScheduled, true); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} + +{ + // resume() test case + const r = new Readable({ read() {} }); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + // Calling resume() should change the state value. + r.resume(); + assert.strictEqual(r._readableState.resumeScheduled, true); + + r.on('resume', common.mustCall(() => { + // The state value should be `false` again + assert.strictEqual(r._readableState.resumeScheduled, false); + })); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} diff --git a/test/js/node/test/parallel/test-stream-readable-setEncoding-existing-buffers.js b/test/js/node/test/parallel/test-stream-readable-setEncoding-existing-buffers.js new file mode 100644 index 0000000000..eb75260bac --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-setEncoding-existing-buffers.js @@ -0,0 +1,60 @@ +'use strict'; +require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +{ + // Call .setEncoding() while there are bytes already in the buffer. + const r = new Readable({ read() {} }); + + r.push(Buffer.from('a')); + r.push(Buffer.from('b')); + + r.setEncoding('utf8'); + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['ab']); + }); +} + +{ + // Call .setEncoding() while the buffer contains a complete, + // but chunked character. + const r = new Readable({ read() {} }); + + r.push(Buffer.from([0xf0])); + r.push(Buffer.from([0x9f])); + r.push(Buffer.from([0x8e])); + r.push(Buffer.from([0x89])); + + r.setEncoding('utf8'); + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['🎉']); + }); +} + +{ + // Call .setEncoding() while the buffer contains an incomplete character, + // and finish the character later. + const r = new Readable({ read() {} }); + + r.push(Buffer.from([0xf0])); + r.push(Buffer.from([0x9f])); + + r.setEncoding('utf8'); + + r.push(Buffer.from([0x8e])); + r.push(Buffer.from([0x89])); + + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['🎉']); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-strategy-option.js b/test/js/node/test/parallel/test-stream-readable-strategy-option.js new file mode 100644 index 0000000000..a32e70ef21 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-strategy-option.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); +const { strictEqual } = require('assert'); + +{ + // Strategy 2 + const streamData = ['a', 'b', 'c', null]; + + // Fulfill a Readable object + const readable = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + readable.push(streamData.shift()); + }); + }, streamData.length), + }); + + // Use helper to convert it to a Web ReadableStream using ByteLength strategy + const readableStream = Readable.toWeb(readable, { + strategy: new ByteLengthQueuingStrategy({ highWaterMark: 1 }), + }); + + assert(!readableStream.locked); + readableStream.getReader().read().then(common.mustCall()); +} + +{ + // Strategy 2 + const streamData = ['a', 'b', 'c', null]; + + // Fulfill a Readable object + const readable = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + readable.push(streamData.shift()); + }); + }, streamData.length), + }); + + // Use helper to convert it to a Web ReadableStream using Count strategy + const readableStream = Readable.toWeb(readable, { + strategy: new CountQueuingStrategy({ highWaterMark: 1 }), + }); + + assert(!readableStream.locked); + readableStream.getReader().read().then(common.mustCall()); +} + +{ + const desireSizeExpected = 2; + + const stringStream = new ReadableStream( + { + start(controller) { + // Check if the strategy is being assigned on the init of the ReadableStream + strictEqual(controller.desiredSize, desireSizeExpected); + controller.enqueue('a'); + controller.enqueue('b'); + controller.close(); + }, + }, + new CountQueuingStrategy({ highWaterMark: desireSizeExpected }) + ); + + const reader = stringStream.getReader(); + + reader.read().then(common.mustCall()); + reader.read().then(common.mustCall()); + reader.read().then(({ value, done }) => { + strictEqual(value, undefined); + strictEqual(done, true); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-with-unimplemented-_read.js b/test/js/node/test/parallel/test-stream-readable-with-unimplemented-_read.js new file mode 100644 index 0000000000..85e83aa3b6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-with-unimplemented-_read.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +const readable = new Readable(); + +readable.read(); +readable.on('error', common.expectsError({ + code: 'ERR_METHOD_NOT_IMPLEMENTED', + name: 'Error', + message: 'The _read() method is not implemented' +})); +readable.on('close', common.mustCall()); diff --git a/test/js/node/test/parallel/test-stream-readableListening-state.js b/test/js/node/test/parallel/test-stream-readableListening-state.js new file mode 100644 index 0000000000..5e3071faf3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readableListening-state.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const r = new stream.Readable({ + read: () => {} +}); + +// readableListening state should start in `false`. +assert.strictEqual(r._readableState.readableListening, false); + +r.on('readable', common.mustCall(() => { + // Inside the readable event this state should be true. + assert.strictEqual(r._readableState.readableListening, true); +})); + +r.push(Buffer.from('Testing readableListening state')); + +const r2 = new stream.Readable({ + read: () => {} +}); + +// readableListening state should start in `false`. +assert.strictEqual(r2._readableState.readableListening, false); + +r2.on('data', common.mustCall((chunk) => { + // readableListening should be false because we don't have + // a `readable` listener + assert.strictEqual(r2._readableState.readableListening, false); +})); + +r2.push(Buffer.from('Testing readableListening state')); diff --git a/test/js/node/test/parallel/test-stream-set-default-hwm.js b/test/js/node/test/parallel/test-stream-set-default-hwm.js new file mode 100644 index 0000000000..3d78907b74 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-set-default-hwm.js @@ -0,0 +1,36 @@ +'use strict'; + +require('../common'); + +const assert = require('node:assert'); +const { + setDefaultHighWaterMark, + getDefaultHighWaterMark, + Writable, + Readable, + Transform +} = require('stream'); + +assert.notStrictEqual(getDefaultHighWaterMark(false), 32 * 1000); +setDefaultHighWaterMark(false, 32 * 1000); +assert.strictEqual(getDefaultHighWaterMark(false), 32 * 1000); + +assert.notStrictEqual(getDefaultHighWaterMark(true), 32); +setDefaultHighWaterMark(true, 32); +assert.strictEqual(getDefaultHighWaterMark(true), 32); + +const w = new Writable({ + write() {} +}); +assert.strictEqual(w.writableHighWaterMark, 32 * 1000); + +const r = new Readable({ + read() {} +}); +assert.strictEqual(r.readableHighWaterMark, 32 * 1000); + +const t = new Transform({ + transform() {} +}); +assert.strictEqual(t.writableHighWaterMark, 32 * 1000); +assert.strictEqual(t.readableHighWaterMark, 32 * 1000); diff --git a/test/js/node/test/parallel/test-stream-transform-callback-twice.js b/test/js/node/test/parallel/test-stream-transform-callback-twice.js new file mode 100644 index 0000000000..bf2ccdcde4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-callback-twice.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const { Transform } = require('stream'); +const stream = new Transform({ + transform(chunk, enc, cb) { cb(); cb(); } +}); + +stream.on('error', common.expectsError({ + name: 'Error', + message: 'Callback called multiple times', + code: 'ERR_MULTIPLE_CALLBACK' +})); + +stream.write('foo'); diff --git a/test/js/node/test/parallel/test-stream-transform-constructor-set-methods.js b/test/js/node/test/parallel/test-stream-transform-constructor-set-methods.js new file mode 100644 index 0000000000..a20a1a07cf --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-constructor-set-methods.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const { Transform } = require('stream'); + +const t = new Transform(); + +assert.throws( + () => { + t.end(Buffer.from('blerg')); + }, + { + name: 'Error', + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _transform() method is not implemented' + } +); + +const _transform = common.mustCall((chunk, _, next) => { + next(); +}); + +const _final = common.mustCall((next) => { + next(); +}); + +const _flush = common.mustCall((next) => { + next(); +}); + +const t2 = new Transform({ + transform: _transform, + flush: _flush, + final: _final +}); + +assert.strictEqual(t2._transform, _transform); +assert.strictEqual(t2._flush, _flush); +assert.strictEqual(t2._final, _final); + +t2.end(Buffer.from('blerg')); +t2.resume(); diff --git a/test/js/node/test/parallel/test-stream-transform-final-sync.js b/test/js/node/test/parallel/test-stream-transform-final-sync.js new file mode 100644 index 0000000000..5cc80703ee --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-final-sync.js @@ -0,0 +1,110 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let state = 0; + + +// What you do +// +// const stream = new stream.Transform({ +// transform: function transformCallback(chunk, _, next) { +// // part 1 +// this.push(chunk); +// //part 2 +// next(); +// }, +// final: function endCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// }, +// flush: function flushCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// } +// }); +// t.on('data', dataListener); +// t.on('end', endListener); +// t.on('finish', finishListener); +// t.write(1); +// t.write(4); +// t.end(7, endMethodCallback); +// +// The order things are called +// +// 1. transformCallback part 1 +// 2. dataListener +// 3. transformCallback part 2 +// 4. transformCallback part 1 +// 5. dataListener +// 6. transformCallback part 2 +// 7. transformCallback part 1 +// 8. dataListener +// 9. transformCallback part 2 +// 10. finalCallback part 1 +// 11. finalCallback part 2 +// 12. flushCallback part 1 +// 13. finishListener +// 14. endMethodCallback +// 15. flushCallback part 2 +// 16. endListener + +const t = new stream.Transform({ + objectMode: true, + transform: common.mustCall(function(chunk, _, next) { + // transformCallback part 1 + assert.strictEqual(++state, chunk); + this.push(state); + // transformCallback part 2 + assert.strictEqual(++state, chunk + 2); + process.nextTick(next); + }, 3), + final: common.mustCall(function(done) { + state++; + // finalCallback part 1 + assert.strictEqual(state, 10); + state++; + // finalCallback part 2 + assert.strictEqual(state, 11); + done(); + }, 1), + flush: common.mustCall(function(done) { + state++; + // fluchCallback part 1 + assert.strictEqual(state, 12); + process.nextTick(function() { + state++; + // fluchCallback part 2 + assert.strictEqual(state, 13); + done(); + }); + }, 1) +}); +t.on('finish', common.mustCall(function() { + state++; + // finishListener + assert.strictEqual(state, 15); +}, 1)); +t.on('end', common.mustCall(function() { + state++; + // endEvent + assert.strictEqual(state, 16); +}, 1)); +t.on('data', common.mustCall(function(d) { + // dataListener + assert.strictEqual(++state, d + 1); +}, 3)); +t.write(1); +t.write(4); +t.end(7, common.mustCall(function() { + state++; + // endMethodCallback + assert.strictEqual(state, 14); +}, 1)); diff --git a/test/js/node/test/parallel/test-stream-transform-final.js b/test/js/node/test/parallel/test-stream-transform-final.js new file mode 100644 index 0000000000..e0b2b7e40f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-final.js @@ -0,0 +1,112 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let state = 0; + + +// What you do: +// +// const stream = new stream.Transform({ +// transform: function transformCallback(chunk, _, next) { +// // part 1 +// this.push(chunk); +// //part 2 +// next(); +// }, +// final: function endCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// }, +// flush: function flushCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// } +// }); +// t.on('data', dataListener); +// t.on('end', endListener); +// t.on('finish', finishListener); +// t.write(1); +// t.write(4); +// t.end(7, endMethodCallback); +// +// The order things are called + +// 1. transformCallback part 1 +// 2. dataListener +// 3. transformCallback part 2 +// 4. transformCallback part 1 +// 5. dataListener +// 6. transformCallback part 2 +// 7. transformCallback part 1 +// 8. dataListener +// 9. transformCallback part 2 +// 10. finalCallback part 1 +// 11. finalCallback part 2 +// 12. flushCallback part 1 +// 13. finishListener +// 14. endMethodCallback +// 15. flushCallback part 2 +// 16. endListener + +const t = new stream.Transform({ + objectMode: true, + transform: common.mustCall(function(chunk, _, next) { + // transformCallback part 1 + assert.strictEqual(++state, chunk); + this.push(state); + // transformCallback part 2 + assert.strictEqual(++state, chunk + 2); + process.nextTick(next); + }, 3), + final: common.mustCall(function(done) { + state++; + // finalCallback part 1 + assert.strictEqual(state, 10); + setTimeout(function() { + state++; + // finalCallback part 2 + assert.strictEqual(state, 11); + done(); + }, 100); + }, 1), + flush: common.mustCall(function(done) { + state++; + // flushCallback part 1 + assert.strictEqual(state, 12); + process.nextTick(function() { + state++; + // flushCallback part 2 + assert.strictEqual(state, 13); + done(); + }); + }, 1) +}); +t.on('finish', common.mustCall(function() { + state++; + // finishListener + assert.strictEqual(state, 15); +}, 1)); +t.on('end', common.mustCall(function() { + state++; + // end event + assert.strictEqual(state, 16); +}, 1)); +t.on('data', common.mustCall(function(d) { + // dataListener + assert.strictEqual(++state, d + 1); +}, 3)); +t.write(1); +t.write(4); +t.end(7, common.mustCall(function() { + state++; + // endMethodCallback + assert.strictEqual(state, 14); +}, 1)); diff --git a/test/js/node/test/parallel/test-stream-transform-flush-data.js b/test/js/node/test/parallel/test-stream-transform-flush-data.js new file mode 100644 index 0000000000..51e2c8bc52 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-flush-data.js @@ -0,0 +1,28 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const Transform = require('stream').Transform; + + +const expected = 'asdf'; + + +function _transform(d, e, n) { + n(); +} + +function _flush(n) { + n(null, expected); +} + +const t = new Transform({ + transform: _transform, + flush: _flush +}); + +t.end(Buffer.from('blerg')); +t.on('data', (data) => { + assert.strictEqual(data.toString(), expected); +}); diff --git a/test/js/node/test/parallel/child-process-exec-env.test.js b/test/js/node/test/parallel/test-stream-transform-objectmode-falsey-value.js similarity index 60% rename from test/js/node/test/parallel/child-process-exec-env.test.js rename to test/js/node/test/parallel/test-stream-transform-objectmode-falsey-value.js index 1649fad830..78ede5d100 100644 --- a/test/js/node/test/parallel/child-process-exec-env.test.js +++ b/test/js/node/test/parallel/test-stream-transform-objectmode-falsey-value.js @@ -1,6 +1,3 @@ -//#FILE: test-child-process-exec-env.js -//#SHA1: cba9b5f7a9d1ab7cd674bde00c1c4184470e4899 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,29 +19,33 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { isWindows } = require("../common"); -const { exec } = require("child_process"); -const util = require("util"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); -const execPromise = util.promisify(exec); +const stream = require('stream'); +const PassThrough = stream.PassThrough; -test("exec with custom environment", async () => { - let command, options; +const src = new PassThrough({ objectMode: true }); +const tx = new PassThrough({ objectMode: true }); +const dest = new PassThrough({ objectMode: true }); - if (!isWindows) { - command = "/usr/bin/env"; - options = { env: { HELLO: "WORLD" } }; +const expect = [ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; +const results = []; + +dest.on('data', common.mustCall(function(x) { + results.push(x); +}, expect.length)); + +src.pipe(tx).pipe(dest); + +let i = -1; +const int = setInterval(common.mustCall(function() { + if (results.length === expect.length) { + src.end(); + clearInterval(int); + assert.deepStrictEqual(results, expect); } else { - command = "set"; - options = { env: { ...process.env, HELLO: "WORLD" } }; + src.write(i++); } - - const { stdout, stderr } = await execPromise(command, options); - - expect(stdout).not.toBe(""); - expect(stdout).toContain("HELLO=WORLD"); - expect(stderr).toBe(""); -}); - -//<#END_FILE: test-child-process-exec-env.js +}, expect.length + 1), 1); diff --git a/test/js/node/test/parallel/test-stream-unpipe-event.js b/test/js/node/test/parallel/test-stream-unpipe-event.js new file mode 100644 index 0000000000..46cc8e8cb0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-unpipe-event.js @@ -0,0 +1,85 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Writable, Readable } = require('stream'); +class NullWriteable extends Writable { + _write(chunk, encoding, callback) { + return callback(); + } +} +class QuickEndReadable extends Readable { + _read() { + this.push(null); + } +} +class NeverEndReadable extends Readable { + _read() {} +} + +{ + const dest = new NullWriteable(); + const src = new QuickEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustNotCall('unpipe should not have been emitted')); + src.pipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 1); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest); + src.unpipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new QuickEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest, { end: false }); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustNotCall('unpipe should not have been emitted')); + src.pipe(dest, { end: false }); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 1); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest, { end: false }); + src.unpipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} diff --git a/test/js/node/test/parallel/test-stream-unshift-empty-chunk.js b/test/js/node/test/parallel/test-stream-unshift-empty-chunk.js new file mode 100644 index 0000000000..e8136a68e9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-unshift-empty-chunk.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This test verifies that stream.unshift(Buffer.alloc(0)) or +// stream.unshift('') does not set state.reading=false. +const Readable = require('stream').Readable; + +const r = new Readable(); +let nChunks = 10; +const chunk = Buffer.alloc(10, 'x'); + +r._read = function(n) { + setImmediate(() => { + r.push(--nChunks === 0 ? null : chunk); + }); +}; + +let readAll = false; +const seen = []; +r.on('readable', () => { + let chunk; + while ((chunk = r.read()) !== null) { + seen.push(chunk.toString()); + // Simulate only reading a certain amount of the data, + // and then putting the rest of the chunk back into the + // stream, like a parser might do. We just fill it with + // 'y' so that it's easy to see which bits were touched, + // and which were not. + const putBack = Buffer.alloc(readAll ? 0 : 5, 'y'); + readAll = !readAll; + r.unshift(putBack); + } +}); + +const expect = + [ 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy' ]; + +r.on('end', () => { + assert.deepStrictEqual(seen, expect); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-unshift-read-race.js b/test/js/node/test/parallel/test-stream-unshift-read-race.js new file mode 100644 index 0000000000..fe110ea285 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-unshift-read-race.js @@ -0,0 +1,128 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// This test verifies that: +// 1. unshift() does not cause colliding _read() calls. +// 2. unshift() after the 'end' event is an error, but after the EOF +// signalling null, it is ok, and just creates a new readable chunk. +// 3. push() after the EOF signaling null is an error. +// 4. _read() is not called after pushing the EOF null chunk. + +const stream = require('stream'); +const hwm = 10; +const r = stream.Readable({ highWaterMark: hwm, autoDestroy: false }); +const chunks = 10; + +const data = Buffer.allocUnsafe(chunks * hwm + Math.ceil(hwm / 2)); +for (let i = 0; i < data.length; i++) { + const c = 'asdf'.charCodeAt(i % 4); + data[i] = c; +} + +let pos = 0; +let pushedNull = false; +r._read = function(n) { + assert(!pushedNull, '_read after null push'); + + // Every third chunk is fast + push(!(chunks % 3)); + + function push(fast) { + assert(!pushedNull, 'push() after null push'); + const c = pos >= data.length ? null : data.slice(pos, pos + n); + pushedNull = c === null; + if (fast) { + pos += n; + r.push(c); + if (c === null) pushError(); + } else { + setTimeout(function() { + pos += n; + r.push(c); + if (c === null) pushError(); + }, 1); + } + } +}; + +function pushError() { + r.unshift(Buffer.allocUnsafe(1)); + w.end(); + + assert.throws(() => { + r.push(Buffer.allocUnsafe(1)); + }, { + code: 'ERR_STREAM_PUSH_AFTER_EOF', + name: 'Error', + message: 'stream.push() after EOF' + }); +} + + +const w = stream.Writable(); +const written = []; +w._write = function(chunk, encoding, cb) { + written.push(chunk.toString()); + cb(); +}; + +r.on('end', common.mustNotCall()); + +r.on('readable', function() { + let chunk; + while (null !== (chunk = r.read(10))) { + w.write(chunk); + if (chunk.length > 4) + r.unshift(Buffer.from('1234')); + } +}); + +w.on('finish', common.mustCall(function() { + // Each chunk should start with 1234, and then be asfdasdfasdf... + // The first got pulled out before the first unshift('1234'), so it's + // lacking that piece. + assert.strictEqual(written[0], 'asdfasdfas'); + let asdf = 'd'; + console.error(`0: ${written[0]}`); + for (let i = 1; i < written.length; i++) { + console.error(`${i.toString(32)}: ${written[i]}`); + assert.strictEqual(written[i].slice(0, 4), '1234'); + for (let j = 4; j < written[i].length; j++) { + const c = written[i].charAt(j); + assert.strictEqual(c, asdf); + switch (asdf) { + case 'a': asdf = 's'; break; + case 's': asdf = 'd'; break; + case 'd': asdf = 'f'; break; + case 'f': asdf = 'a'; break; + } + } + } +})); + +process.on('exit', function() { + assert.strictEqual(written.length, 18); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-writable-aborted.js b/test/js/node/test/parallel/test-stream-writable-aborted.js new file mode 100644 index 0000000000..01d638115b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-aborted.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Writable } = require('stream'); + +{ + const writable = new Writable({ + write() { + } + }); + assert.strictEqual(writable.writableAborted, false); + writable.destroy(); + assert.strictEqual(writable.writableAborted, true); +} + +{ + const writable = new Writable({ + write() { + } + }); + assert.strictEqual(writable.writableAborted, false); + writable.end(); + writable.destroy(); + assert.strictEqual(writable.writableAborted, true); +} diff --git a/test/js/node/test/parallel/stream-writable-change-default-encoding.test.js b/test/js/node/test/parallel/test-stream-writable-change-default-encoding.js similarity index 53% rename from test/js/node/test/parallel/stream-writable-change-default-encoding.test.js rename to test/js/node/test/parallel/test-stream-writable-change-default-encoding.js index 0ccbd29ac5..94a892567c 100644 --- a/test/js/node/test/parallel/stream-writable-change-default-encoding.test.js +++ b/test/js/node/test/parallel/test-stream-writable-change-default-encoding.js @@ -1,6 +1,3 @@ -//#FILE: test-stream-writable-change-default-encoding.js -//#SHA1: 7a4e7c22888d4901899fe5a0ed4604c319f73ff9 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,8 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const stream = require("stream"); +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); class MyWritable extends stream.Writable { constructor(fn, options) { @@ -37,55 +37,42 @@ class MyWritable extends stream.Writable { } } -test("default encoding is utf8", () => { - const m = new MyWritable( - (isBuffer, type, enc) => { - expect(enc).toBe("utf8"); - }, - { decodeStrings: false }, - ); - m.write("foo"); +(function defaultCondingIsUtf8() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'utf8'); + }, { decodeStrings: false }); + m.write('foo'); m.end(); -}); +}()); -test("change default encoding to ascii", () => { - const m = new MyWritable( - (isBuffer, type, enc) => { - expect(enc).toBe("ascii"); - }, - { decodeStrings: false }, - ); - m.setDefaultEncoding("ascii"); - m.write("bar"); +(function changeDefaultEncodingToAscii() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'ascii'); + }, { decodeStrings: false }); + m.setDefaultEncoding('ascii'); + m.write('bar'); m.end(); -}); +}()); -test("change default encoding to invalid value", () => { - const m = new MyWritable((isBuffer, type, enc) => {}, { decodeStrings: false }); - - expect(() => { - m.setDefaultEncoding({}); - m.write("bar"); - m.end(); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_UNKNOWN_ENCODING", - message: expect.any(String), - }), - ); -}); - -test("check variable case encoding", () => { +// Change default encoding to invalid value. +assert.throws(() => { const m = new MyWritable( - (isBuffer, type, enc) => { - expect(enc).toBe("ascii"); - }, - { decodeStrings: false }, - ); - m.setDefaultEncoding("AsCii"); - m.write("bar"); + (isBuffer, type, enc) => {}, + { decodeStrings: false }); + m.setDefaultEncoding({}); + m.write('bar'); m.end(); +}, { + name: 'TypeError', + code: 'ERR_UNKNOWN_ENCODING', + message: 'Unknown encoding: {}' }); -//<#END_FILE: test-stream-writable-change-default-encoding.js +(function checkVariableCaseEncoding() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'ascii'); + }, { decodeStrings: false }); + m.setDefaultEncoding('AsCii'); + m.write('bar'); + m.end(); +}()); diff --git a/test/js/node/test/parallel/test-stream-writable-clear-buffer.js b/test/js/node/test/parallel/test-stream-writable-clear-buffer.js new file mode 100644 index 0000000000..c4d7ae151a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-clear-buffer.js @@ -0,0 +1,35 @@ +'use strict'; + +// This test ensures that the _writeableState.bufferedRequestCount and +// the actual buffered request count are the same. + +const common = require('../common'); +const Stream = require('stream'); +const assert = require('assert'); + +class StreamWritable extends Stream.Writable { + constructor() { + super({ objectMode: true }); + } + + // Refs: https://github.com/nodejs/node/issues/6758 + // We need a timer like on the original issue thread. + // Otherwise the code will never reach our test case. + _write(chunk, encoding, cb) { + setImmediate(cb); + } +} + +const testStream = new StreamWritable(); +testStream.cork(); + +for (let i = 1; i <= 5; i++) { + testStream.write(i, common.mustCall(() => { + assert.strictEqual( + testStream._writableState.bufferedRequestCount, + testStream._writableState.getBuffer().length + ); + })); +} + +testStream.end(); diff --git a/test/js/node/test/parallel/test-stream-writable-constructor-set-methods.js b/test/js/node/test/parallel/test-stream-writable-constructor-set-methods.js new file mode 100644 index 0000000000..34fda8edda --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-constructor-set-methods.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const { Writable } = require('stream'); + +const bufferBlerg = Buffer.from('blerg'); +const w = new Writable(); + +assert.throws( + () => { + w.end(bufferBlerg); + }, + { + name: 'Error', + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _write() method is not implemented' + } +); + +const _write = common.mustCall((chunk, _, next) => { + next(); +}); + +const _writev = common.mustCall((chunks, next) => { + assert.strictEqual(chunks.length, 2); + next(); +}); + +const w2 = new Writable({ write: _write, writev: _writev }); + +assert.strictEqual(w2._write, _write); +assert.strictEqual(w2._writev, _writev); + +w2.write(bufferBlerg); + +w2.cork(); +w2.write(bufferBlerg); +w2.write(bufferBlerg); + +w2.end(); diff --git a/test/js/node/test/parallel/test-stream-writable-end-cb-uncaught.js b/test/js/node/test/parallel/test-stream-writable-end-cb-uncaught.js new file mode 100644 index 0000000000..02586b45d9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-end-cb-uncaught.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'kaboom'); +})); + +const writable = new stream.Writable(); +const _err = new Error('kaboom'); + +writable._write = (chunk, encoding, cb) => { + cb(); +}; +writable._final = (cb) => { + cb(_err); +}; + +writable.write('asd'); +writable.end(common.mustCall((err) => { + assert.strictEqual(err, _err); +})); diff --git a/test/js/node/test/parallel/test-stream-writable-end-multiple.js b/test/js/node/test/parallel/test-stream-writable-end-multiple.js new file mode 100644 index 0000000000..000f5b07f5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-end-multiple.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); +writable._write = (chunk, encoding, cb) => { + setTimeout(() => cb(), 10); +}; + +writable.end('testing ended state', common.mustCall()); +writable.end(common.mustCall()); +writable.on('finish', common.mustCall(() => { + let ticked = false; + writable.end(common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED'); + })); + ticked = true; +})); diff --git a/test/js/node/test/parallel/test-stream-writable-ended-state.js b/test/js/node/test/parallel/test-stream-writable-ended-state.js new file mode 100644 index 0000000000..2c40c62a9e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-ended-state.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._write = (chunk, encoding, cb) => { + assert.strictEqual(writable._writableState.ended, false); + assert.strictEqual(writable._writableState.writable, undefined); + assert.strictEqual(writable.writableEnded, false); + cb(); +}; + +assert.strictEqual(writable._writableState.ended, false); +assert.strictEqual(writable._writableState.writable, undefined); +assert.strictEqual(writable.writable, true); +assert.strictEqual(writable.writableEnded, false); + +writable.end('testing ended state', common.mustCall(() => { + assert.strictEqual(writable._writableState.ended, true); + assert.strictEqual(writable._writableState.writable, undefined); + assert.strictEqual(writable.writable, false); + assert.strictEqual(writable.writableEnded, true); +})); + +assert.strictEqual(writable._writableState.ended, true); +assert.strictEqual(writable._writableState.writable, undefined); +assert.strictEqual(writable.writable, false); +assert.strictEqual(writable.writableEnded, true); diff --git a/test/js/node/test/parallel/test-stream-writable-final-async.js b/test/js/node/test/parallel/test-stream-writable-final-async.js new file mode 100644 index 0000000000..c17b843322 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-final-async.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const { + Duplex, +} = require('stream'); +const { setTimeout } = require('timers/promises'); + +{ + class Foo extends Duplex { + async _final(callback) { + await setTimeout(common.platformTimeout(1)); + callback(); + } + + _read() {} + } + + const foo = new Foo(); + foo._write = common.mustCall((chunk, encoding, cb) => { + cb(); + }); + foo.end('test', common.mustCall()); + foo.on('error', common.mustNotCall()); +} diff --git a/test/js/node/test/parallel/test-stream-writable-final-destroy.js b/test/js/node/test/parallel/test-stream-writable-final-destroy.js new file mode 100644 index 0000000000..8d3bf72c89 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-final-destroy.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); + +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write(chunk, encoding, callback) { + callback(null); + }, + final(callback) { + queueMicrotask(callback); + } + }); + w.end(); + w.destroy(); + + w.on('prefinish', common.mustNotCall()); + w.on('finish', common.mustNotCall()); + w.on('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream-writable-final-throw.js b/test/js/node/test/parallel/test-stream-writable-final-throw.js new file mode 100644 index 0000000000..e7dd21abc3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-final-throw.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const { + Duplex, +} = require('stream'); + +{ + class Foo extends Duplex { + _final(callback) { + throw new Error('fhqwhgads'); + } + + _read() {} + } + + const foo = new Foo(); + foo._write = common.mustCall((chunk, encoding, cb) => { + cb(); + }); + foo.end('test', common.expectsError({ message: 'fhqwhgads' })); + foo.on('error', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream-writable-finish-destroyed.js b/test/js/node/test/parallel/test-stream-writable-finish-destroyed.js new file mode 100644 index 0000000000..22657a170f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-finish-destroyed.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.end('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + w.end(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.write('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write() { + } + }); + w.on('finish', common.mustNotCall()); + w.end(); + w.destroy(); +} diff --git a/test/js/node/test/parallel/test-stream-writable-finished-state.js b/test/js/node/test/parallel/test-stream-writable-finished-state.js new file mode 100644 index 0000000000..b42137ed0b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-finished-state.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(writable._writableState.finished, false); + cb(); +}; + +writable.on('finish', common.mustCall(() => { + assert.strictEqual(writable._writableState.finished, true); +})); + +writable.end('testing finished state', common.mustCall(() => { + assert.strictEqual(writable._writableState.finished, true); +})); diff --git a/test/js/node/test/parallel/test-stream-writable-finished.js b/test/js/node/test/parallel/test-stream-writable-finished.js new file mode 100644 index 0000000000..933a80a2f9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-finished.js @@ -0,0 +1,99 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Writable.prototype + assert(Object.hasOwn(Writable.prototype, 'writableFinished')); +} + +// event +{ + const writable = new Writable(); + + writable._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(writable.writableFinished, false); + cb(); + }; + + writable.on('finish', common.mustCall(() => { + assert.strictEqual(writable.writableFinished, true); + })); + + writable.end('testing finished state', common.mustCall(() => { + assert.strictEqual(writable.writableFinished, true); + })); +} + +{ + // Emit finish asynchronously. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + } + }); + + w.end(); + w.on('finish', common.mustCall()); +} + +{ + // Emit prefinish synchronously. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + } + }); + + let sync = true; + w.on('prefinish', common.mustCall(() => { + assert.strictEqual(sync, true); + })); + w.end(); + sync = false; +} + +{ + // Emit prefinish synchronously w/ final. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + }, + final(cb) { + cb(); + } + }); + + let sync = true; + w.on('prefinish', common.mustCall(() => { + assert.strictEqual(sync, true); + })); + w.end(); + sync = false; +} + + +{ + // Call _final synchronously. + + let sync = true; + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + }, + final: common.mustCall((cb) => { + assert.strictEqual(sync, true); + cb(); + }) + }); + + w.end(); + sync = false; +} diff --git a/test/js/node/test/parallel/test-stream-writable-invalid-chunk.js b/test/js/node/test/parallel/test-stream-writable-invalid-chunk.js new file mode 100644 index 0000000000..09032c07c5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-invalid-chunk.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function testWriteType(val, objectMode, code) { + const writable = new stream.Writable({ + objectMode, + write: () => {} + }); + writable.on('error', common.mustNotCall()); + if (code) { + assert.throws(() => { + writable.write(val); + }, { code }); + } else { + writable.write(val); + } +} + +testWriteType([], false, 'ERR_INVALID_ARG_TYPE'); +testWriteType({}, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(0, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(true, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(0.0, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(undefined, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(null, false, 'ERR_STREAM_NULL_VALUES'); + +testWriteType([], true); +testWriteType({}, true); +testWriteType(0, true); +testWriteType(true, true); +testWriteType(0.0, true); +testWriteType(undefined, true); +testWriteType(null, true, 'ERR_STREAM_NULL_VALUES'); diff --git a/test/js/node/test/parallel/test-stream-writable-needdrain-state.js b/test/js/node/test/parallel/test-stream-writable-needdrain-state.js new file mode 100644 index 0000000000..0e72d832bc --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-needdrain-state.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const transform = new stream.Transform({ + transform: _transform, + highWaterMark: 1 +}); + +function _transform(chunk, encoding, cb) { + process.nextTick(() => { + assert.strictEqual(transform._writableState.needDrain, true); + cb(); + }); +} + +assert.strictEqual(transform._writableState.needDrain, false); + +transform.write('asdasd', common.mustCall(() => { + assert.strictEqual(transform._writableState.needDrain, false); +})); + +assert.strictEqual(transform._writableState.needDrain, true); diff --git a/test/js/node/test/parallel/test-stream-writable-null.js b/test/js/node/test/parallel/test-stream-writable-null.js new file mode 100644 index 0000000000..99419f1cf9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-null.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class MyWritable extends stream.Writable { + constructor(options) { + super({ autoDestroy: false, ...options }); + } + _write(chunk, encoding, callback) { + assert.notStrictEqual(chunk, null); + callback(); + } +} + +{ + const m = new MyWritable({ objectMode: true }); + m.on('error', common.mustNotCall()); + assert.throws(() => { + m.write(null); + }, { + code: 'ERR_STREAM_NULL_VALUES' + }); +} + +{ + const m = new MyWritable(); + m.on('error', common.mustNotCall()); + assert.throws(() => { + m.write(false); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); +} + +{ // Should not throw. + const m = new MyWritable({ objectMode: true }); + m.write(false, assert.ifError); +} + +{ // Should not throw. + const m = new MyWritable({ objectMode: true }).on('error', (e) => { + assert.ifError(e || new Error('should not get here')); + }); + m.write(false, assert.ifError); +} diff --git a/test/js/node/test/parallel/test-stream-writable-properties.js b/test/js/node/test/parallel/test-stream-writable-properties.js new file mode 100644 index 0000000000..424bb58710 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-properties.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +{ + const w = new Writable(); + assert.strictEqual(w.writableCorked, 0); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); + w.cork(); + assert.strictEqual(w.writableCorked, 1); + w.cork(); + assert.strictEqual(w.writableCorked, 2); + w.uncork(); + assert.strictEqual(w.writableCorked, 1); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); +} diff --git a/test/js/node/test/parallel/test-stream-writable-writable.js b/test/js/node/test/parallel/test-stream-writable-writable.js new file mode 100644 index 0000000000..ef5454dc52 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-writable.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write() {} + }); + assert.strictEqual(w.writable, true); + w.destroy(); + assert.strictEqual(w.writable, false); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + callback(new Error()); + }) + }); + assert.strictEqual(w.writable, true); + w.write('asd'); + assert.strictEqual(w.writable, false); + w.on('error', common.mustCall()); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + process.nextTick(() => { + callback(new Error()); + assert.strictEqual(w.writable, false); + }); + }) + }); + w.write('asd'); + w.on('error', common.mustCall()); +} + +{ + const w = new Writable({ + write: common.mustNotCall() + }); + assert.strictEqual(w.writable, true); + w.end(); + assert.strictEqual(w.writable, false); +} diff --git a/test/js/node/test/parallel/test-stream-writable-write-cb-error.js b/test/js/node/test/parallel/test-stream-writable-write-cb-error.js new file mode 100644 index 0000000000..72db1b7e3f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-cb-error.js @@ -0,0 +1,58 @@ +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// Ensure callback is always invoked before +// error is emitted. Regardless if error was +// sync or async. + +{ + let callbackCalled = false; + // Sync Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(new Error()); + }) + }); + writable.on('error', common.mustCall(() => { + assert.strictEqual(callbackCalled, true); + })); + writable.write('hi', common.mustCall(() => { + callbackCalled = true; + })); +} + +{ + let callbackCalled = false; + // Async Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + process.nextTick(cb, new Error()); + }) + }); + writable.on('error', common.mustCall(() => { + assert.strictEqual(callbackCalled, true); + })); + writable.write('hi', common.mustCall(() => { + callbackCalled = true; + })); +} + +{ + // Sync Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(new Error()); + }) + }); + + writable.on('error', common.mustCall()); + + let cnt = 0; + // Ensure we don't live lock on sync error + while (writable.write('a')) + cnt++; + + assert.strictEqual(cnt, 0); +} diff --git a/test/js/node/test/parallel/test-stream-writable-write-cb-twice.js b/test/js/node/test/parallel/test-stream-writable-write-cb-twice.js new file mode 100644 index 0000000000..244698c522 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-cb-twice.js @@ -0,0 +1,52 @@ +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); + +{ + // Sync + Sync + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(); + cb(); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} + +{ + // Sync + Async + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(); + process.nextTick(() => { + cb(); + }); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} + +{ + // Async + Async + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + process.nextTick(cb); + process.nextTick(() => { + cb(); + }); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} diff --git a/test/js/node/test/parallel/test-stream-writable-write-error.js b/test/js/node/test/parallel/test-stream-writable-write-error.js new file mode 100644 index 0000000000..069e32e1be --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-error.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +function expectError(w, args, code, sync) { + if (sync) { + if (code) { + assert.throws(() => w.write(...args), { code }); + } else { + w.write(...args); + } + } else { + let errorCalled = false; + let ticked = false; + w.write(...args, common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(errorCalled, false); + assert.strictEqual(err.code, code); + })); + ticked = true; + w.on('error', common.mustCall((err) => { + errorCalled = true; + assert.strictEqual(err.code, code); + })); + } +} + +function test(autoDestroy) { + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + w.end(); + expectError(w, ['asd'], 'ERR_STREAM_WRITE_AFTER_END'); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + w.destroy(); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + expectError(w, [null], 'ERR_STREAM_NULL_VALUES', true); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + expectError(w, [{}], 'ERR_INVALID_ARG_TYPE', true); + } + + { + const w = new Writable({ + decodeStrings: false, + autoDestroy, + _write() {} + }); + expectError(w, ['asd', 'noencoding'], 'ERR_UNKNOWN_ENCODING', true); + } +} + +test(false); +test(true); diff --git a/test/js/node/test/parallel/test-stream-writable-write-writev-finish.js b/test/js/node/test/parallel/test-stream-writable-write-writev-finish.js new file mode 100644 index 0000000000..9fce315f8b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-writev-finish.js @@ -0,0 +1,152 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +// Ensure consistency between the finish event when using cork() +// and writev and when not using them + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + cb(new Error('write test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'write test error'); + })); + + writable.end('test'); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + setImmediate(cb, new Error('write test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'write test error'); + })); + + writable.end('test'); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + cb(new Error('write test error')); + }; + + writable._writev = (chunks, cb) => { + cb(new Error('writev test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'writev test error'); + })); + + writable.cork(); + writable.write('test'); + + setImmediate(function() { + writable.end('test'); + }); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + setImmediate(cb, new Error('write test error')); + }; + + writable._writev = (chunks, cb) => { + setImmediate(cb, new Error('writev test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'writev test error'); + })); + + writable.cork(); + writable.write('test'); + + setImmediate(function() { + writable.end('test'); + }); +} + +// Regression test for +// https://github.com/nodejs/node/issues/13812 + +{ + const rs = new stream.Readable(); + rs.push('ok'); + rs.push(null); + rs._read = () => {}; + + const ws = new stream.Writable(); + + ws.on('finish', common.mustNotCall()); + ws.on('error', common.mustCall()); + + ws._write = (chunk, encoding, done) => { + setImmediate(done, new Error()); + }; + rs.pipe(ws); +} + +{ + const rs = new stream.Readable(); + rs.push('ok'); + rs.push(null); + rs._read = () => {}; + + const ws = new stream.Writable(); + + ws.on('finish', common.mustNotCall()); + ws.on('error', common.mustCall()); + + ws._write = (chunk, encoding, done) => { + done(new Error()); + }; + rs.pipe(ws); +} + +{ + const w = new stream.Writable(); + w._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('finish', common.mustNotCall()); + w.on('prefinish', () => { + w.write("shouldn't write in prefinish listener"); + }); + w.end(); +} + +{ + const w = new stream.Writable(); + w._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('finish', () => { + w.write("shouldn't write in finish listener"); + }); + w.end(); +} diff --git a/test/js/node/test/parallel/test-stream-writableState-ending.js b/test/js/node/test/parallel/test-stream-writableState-ending.js new file mode 100644 index 0000000000..d301d355cc --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writableState-ending.js @@ -0,0 +1,37 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +function testStates(ending, finished, ended) { + assert.strictEqual(writable._writableState.ending, ending); + assert.strictEqual(writable._writableState.finished, finished); + assert.strictEqual(writable._writableState.ended, ended); +} + +writable._write = (chunk, encoding, cb) => { + // Ending, finished, ended start in false. + testStates(false, false, false); + cb(); +}; + +writable.on('finish', () => { + // Ending, finished, ended = true. + testStates(true, true, true); +}); + +const result = writable.end('testing function end()', () => { + // Ending, finished, ended = true. + testStates(true, true, true); +}); + +// End returns the writable instance +assert.strictEqual(result, writable); + +// Ending, ended = true. +// finished = false. +testStates(true, false, true); diff --git a/test/js/node/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js b/test/js/node/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js new file mode 100644 index 0000000000..b7375b9fa2 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._writev = common.mustCall((chunks, cb) => { + assert.strictEqual(chunks.length, 2); + cb(); +}, 1); + +writable._write = common.mustCall((chunk, encoding, cb) => { + cb(); +}, 1); + +// first cork +writable.cork(); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 0); + +// cork again +writable.cork(); +assert.strictEqual(writable._writableState.corked, 2); + +// The first chunk is buffered +writable.write('first chunk'); +assert.strictEqual(writable._writableState.bufferedRequestCount, 1); + +// First uncork does nothing +writable.uncork(); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 1); + +process.nextTick(uncork); + +// The second chunk is buffered, because we uncork at the end of tick +writable.write('second chunk'); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 2); + +function uncork() { + // Second uncork flushes the buffer + writable.uncork(); + assert.strictEqual(writable._writableState.corked, 0); + assert.strictEqual(writable._writableState.bufferedRequestCount, 0); + + // Verify that end() uncorks correctly + writable.cork(); + writable.write('third chunk'); + writable.end(); + + // End causes an uncork() as well + assert.strictEqual(writable._writableState.corked, 0); + assert.strictEqual(writable._writableState.bufferedRequestCount, 0); +} diff --git a/test/js/node/test/parallel/test-stream-write-destroy.js b/test/js/node/test/parallel/test-stream-write-destroy.js new file mode 100644 index 0000000000..d436b98f84 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-write-destroy.js @@ -0,0 +1,62 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Writable } = require('stream'); + +// Test interaction between calling .destroy() on a writable and pending +// writes. + +for (const withPendingData of [ false, true ]) { + for (const useEnd of [ false, true ]) { + const callbacks = []; + + const w = new Writable({ + write(data, enc, cb) { + callbacks.push(cb); + }, + // Effectively disable the HWM to observe 'drain' events more easily. + highWaterMark: 1 + }); + + let chunksWritten = 0; + let drains = 0; + w.on('drain', () => drains++); + + function onWrite(err) { + if (err) { + assert.strictEqual(w.destroyed, true); + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + } else { + chunksWritten++; + } + } + + w.write('abc', onWrite); + assert.strictEqual(chunksWritten, 0); + assert.strictEqual(drains, 0); + callbacks.shift()(); + assert.strictEqual(chunksWritten, 1); + assert.strictEqual(drains, 1); + + if (withPendingData) { + // Test 2 cases: There either is or is not data still in the write queue. + // (The second write will never actually get executed either way.) + w.write('def', onWrite); + } + if (useEnd) { + // Again, test 2 cases: Either we indicate that we want to end the + // writable or not. + w.end('ghi', onWrite); + } else { + w.write('ghi', onWrite); + } + + assert.strictEqual(chunksWritten, 1); + w.destroy(); + assert.strictEqual(chunksWritten, 1); + callbacks.shift()(); + assert.strictEqual(chunksWritten, useEnd && !withPendingData ? 1 : 2); + assert.strictEqual(callbacks.length, 0); + assert.strictEqual(drains, 1); + } +} diff --git a/test/js/node/test/parallel/test-stream-write-drain.js b/test/js/node/test/parallel/test-stream-write-drain.js new file mode 100644 index 0000000000..bd65c1fdbb --- /dev/null +++ b/test/js/node/test/parallel/test-stream-write-drain.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); + +// Don't emit 'drain' if ended + +const w = new Writable({ + write(data, enc, cb) { + process.nextTick(cb); + }, + highWaterMark: 1 +}); + +w.on('drain', common.mustNotCall()); +w.write('asd'); +w.end(); diff --git a/test/js/node/test/parallel/test-stream-write-final.js b/test/js/node/test/parallel/test-stream-write-final.js new file mode 100644 index 0000000000..56537bd7fa --- /dev/null +++ b/test/js/node/test/parallel/test-stream-write-final.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let shutdown = false; + +const w = new stream.Writable({ + final: common.mustCall(function(cb) { + assert.strictEqual(this, w); + setTimeout(function() { + shutdown = true; + cb(); + }, 100); + }), + write: function(chunk, e, cb) { + process.nextTick(cb); + } +}); +w.on('finish', common.mustCall(function() { + assert(shutdown); +})); +w.write(Buffer.allocUnsafe(1)); +w.end(Buffer.allocUnsafe(0)); diff --git a/test/js/node/test/parallel/test-stream-writev.js b/test/js/node/test/parallel/test-stream-writev.js new file mode 100644 index 0000000000..5a42411c6f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writev.js @@ -0,0 +1,130 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +const queue = []; +for (let decode = 0; decode < 2; decode++) { + for (let uncork = 0; uncork < 2; uncork++) { + for (let multi = 0; multi < 2; multi++) { + queue.push([!!decode, !!uncork, !!multi]); + } + } +} + +run(); + +function run() { + const t = queue.pop(); + if (t) + test(t[0], t[1], t[2], run); + else + console.log('ok'); +} + +function test(decode, uncork, multi, next) { + console.log(`# decode=${decode} uncork=${uncork} multi=${multi}`); + let counter = 0; + let expectCount = 0; + function cnt(msg) { + expectCount++; + const expect = expectCount; + return function(er) { + assert.ifError(er); + counter++; + assert.strictEqual(counter, expect); + }; + } + + const w = new stream.Writable({ decodeStrings: decode }); + w._write = common.mustNotCall('Should not call _write'); + + const expectChunks = decode ? [ + { encoding: 'buffer', + chunk: [104, 101, 108, 108, 111, 44, 32] }, + { encoding: 'buffer', + chunk: [119, 111, 114, 108, 100] }, + { encoding: 'buffer', + chunk: [33] }, + { encoding: 'buffer', + chunk: [10, 97, 110, 100, 32, 116, 104, 101, 110, 46, 46, 46] }, + { encoding: 'buffer', + chunk: [250, 206, 190, 167, 222, 173, 190, 239, 222, 202, 251, 173] }, + ] : [ + { encoding: 'ascii', chunk: 'hello, ' }, + { encoding: 'utf8', chunk: 'world' }, + { encoding: 'buffer', chunk: [33] }, + { encoding: 'latin1', chunk: '\nand then...' }, + { encoding: 'hex', chunk: 'facebea7deadbeefdecafbad' }, + ]; + + let actualChunks; + w._writev = function(chunks, cb) { + actualChunks = chunks.map(function(chunk) { + return { + encoding: chunk.encoding, + chunk: Buffer.isBuffer(chunk.chunk) ? + Array.prototype.slice.call(chunk.chunk) : chunk.chunk + }; + }); + cb(); + }; + + w.cork(); + w.write('hello, ', 'ascii', cnt('hello')); + w.write('world', 'utf8', cnt('world')); + + if (multi) + w.cork(); + + w.write(Buffer.from('!'), 'buffer', cnt('!')); + w.write('\nand then...', 'latin1', cnt('and then')); + + if (multi) + w.uncork(); + + w.write('facebea7deadbeefdecafbad', 'hex', cnt('hex')); + + if (uncork) + w.uncork(); + + w.end(cnt('end')); + + w.on('finish', function() { + // Make sure finish comes after all the write cb + cnt('finish')(); + assert.deepStrictEqual(actualChunks, expectChunks); + next(); + }); +} + +{ + const w = new stream.Writable({ + writev: common.mustCall(function(chunks, cb) { + cb(); + }) + }); + w.write('asd', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream2-base64-single-char-read-end.js b/test/js/node/test/parallel/test-stream2-base64-single-char-read-end.js new file mode 100644 index 0000000000..2e1eb15f9f --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-base64-single-char-read-end.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +const src = new R({ encoding: 'base64' }); +const dst = new W(); +let hasRead = false; +const accum = []; + +src._read = function(n) { + if (!hasRead) { + hasRead = true; + process.nextTick(function() { + src.push(Buffer.from('1')); + src.push(null); + }); + } +}; + +dst._write = function(chunk, enc, cb) { + accum.push(chunk); + cb(); +}; + +src.on('end', function() { + assert.strictEqual(String(Buffer.concat(accum)), 'MQ=='); + clearTimeout(timeout); +}); + +src.pipe(dst); + +const timeout = setTimeout(function() { + assert.fail('timed out waiting for _write'); +}, 100); diff --git a/test/js/node/test/parallel/test-stream2-basic.js b/test/js/node/test/parallel/test-stream2-basic.js new file mode 100644 index 0000000000..2670deda53 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-basic.js @@ -0,0 +1,445 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +const EE = require('events').EventEmitter; + +class TestReader extends R { + constructor(n) { + super(); + this._buffer = Buffer.alloc(n || 100, 'x'); + this._pos = 0; + this._bufs = 10; + } + + _read(n) { + const max = this._buffer.length - this._pos; + n = Math.max(n, 0); + const toRead = Math.min(n, max); + if (toRead === 0) { + // Simulate the read buffer filling up with some more bytes some time + // in the future. + setTimeout(() => { + this._pos = 0; + this._bufs -= 1; + if (this._bufs <= 0) { + // read them all! + if (!this.ended) + this.push(null); + } else { + // now we have more. + // kinda cheating by calling _read, but whatever, + // it's just fake anyway. + this._read(n); + } + }, 10); + return; + } + + const ret = this._buffer.slice(this._pos, this._pos + toRead); + this._pos += toRead; + this.push(ret); + } +} + +class TestWriter extends EE { + constructor() { + super(); + this.received = []; + this.flush = false; + } + + write(c) { + this.received.push(c.toString()); + this.emit('write', c); + return true; + } + + end(c) { + if (c) this.write(c); + this.emit('end', this.received); + } +} + +{ + // Test basic functionality + const r = new TestReader(20); + + const reads = []; + const expect = [ 'x', + 'xx', + 'xxx', + 'xxxx', + 'xxxxx', + 'xxxxxxxxx', + 'xxxxxxxxxx', + 'xxxxxxxxxxxx', + 'xxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx' ]; + + r.on('end', common.mustCall(function() { + assert.deepStrictEqual(reads, expect); + })); + + let readSize = 1; + function flow() { + let res; + while (null !== (res = r.read(readSize++))) { + reads.push(res.toString()); + } + r.once('readable', flow); + } + + flow(); +} + +{ + // Verify pipe + const r = new TestReader(5); + + const expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + const w = new TestWriter(); + + w.on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + + r.pipe(w); +} + + +[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function(SPLIT) { + // Verify unpipe + const r = new TestReader(5); + + // Unpipe after 3 writes, then write to another stream instead. + let expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + const w = [ new TestWriter(), new TestWriter() ]; + + let writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + assert.deepStrictEqual(r._readableState.pipes, []); + w[0].end(); + r.pipe(w[1]); + assert.deepStrictEqual(r._readableState.pipes, [w[1]]); + } + }); + + let ended = 0; + + w[0].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 1); + assert.deepStrictEqual(results, expect[0]); + })); + + w[1].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 2); + assert.deepStrictEqual(results, expect[1]); + })); + + r.pipe(w[0]); +}); + + +{ + // Verify both writers get the same data when piping to destinations + const r = new TestReader(5); + const w = [ new TestWriter(), new TestWriter() ]; + + const expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + w[0].on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + w[1].on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + + r.pipe(w[0]); + r.pipe(w[1]); +} + + +[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function(SPLIT) { + // Verify multi-unpipe + const r = new TestReader(5); + + // Unpipe after 3 writes, then write to another stream instead. + let expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + const w = [ new TestWriter(), new TestWriter(), new TestWriter() ]; + + let writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + w[0].end(); + r.pipe(w[1]); + } + }); + + let ended = 0; + + w[0].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 1); + assert.deepStrictEqual(results, expect[0]); + })); + + w[1].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 2); + assert.deepStrictEqual(results, expect[1]); + })); + + r.pipe(w[0]); + r.pipe(w[2]); +}); + +{ + // Verify that back pressure is respected + const r = new R({ objectMode: true }); + r._read = common.mustNotCall(); + let counter = 0; + r.push(['one']); + r.push(['two']); + r.push(['three']); + r.push(['four']); + r.push(null); + + const w1 = new R(); + w1.write = function(chunk) { + assert.strictEqual(chunk[0], 'one'); + w1.emit('close'); + process.nextTick(function() { + r.pipe(w2); + r.pipe(w3); + }); + }; + w1.end = common.mustNotCall(); + + r.pipe(w1); + + const expected = ['two', 'two', 'three', 'three', 'four', 'four']; + + const w2 = new R(); + w2.write = function(chunk) { + assert.strictEqual(chunk[0], expected.shift()); + assert.strictEqual(counter, 0); + + counter++; + + if (chunk[0] === 'four') { + return true; + } + + setTimeout(function() { + counter--; + w2.emit('drain'); + }, 10); + + return false; + }; + w2.end = common.mustCall(); + + const w3 = new R(); + w3.write = function(chunk) { + assert.strictEqual(chunk[0], expected.shift()); + assert.strictEqual(counter, 1); + + counter++; + + if (chunk[0] === 'four') { + return true; + } + + setTimeout(function() { + counter--; + w3.emit('drain'); + }, 50); + + return false; + }; + w3.end = common.mustCall(function() { + assert.strictEqual(counter, 2); + assert.strictEqual(expected.length, 0); + }); +} + +{ + // Verify read(0) behavior for ended streams + const r = new R(); + let written = false; + let ended = false; + r._read = common.mustNotCall(); + + r.push(Buffer.from('foo')); + r.push(null); + + const v = r.read(0); + + assert.strictEqual(v, null); + + const w = new R(); + w.write = function(buffer) { + written = true; + assert.strictEqual(ended, false); + assert.strictEqual(buffer.toString(), 'foo'); + }; + + w.end = common.mustCall(function() { + ended = true; + assert.strictEqual(written, true); + }); + + r.pipe(w); +} + +{ + // Verify synchronous _read ending + const r = new R(); + let called = false; + r._read = function(n) { + r.push(null); + }; + + r.once('end', function() { + // Verify that this is called before the next tick + called = true; + }); + + r.read(); + + process.nextTick(function() { + assert.strictEqual(called, true); + }); +} + +{ + // Verify that adding readable listeners trigger data flow + const r = new R({ highWaterMark: 5 }); + let onReadable = false; + let readCalled = 0; + + r._read = function(n) { + if (readCalled++ === 2) + r.push(null); + else + r.push(Buffer.from('asdf')); + }; + + r.on('readable', function() { + onReadable = true; + r.read(); + }); + + r.on('end', common.mustCall(function() { + assert.strictEqual(readCalled, 3); + assert.ok(onReadable); + })); +} + +{ + // Verify that streams are chainable + const r = new R(); + r._read = common.mustCall(); + const r2 = r.setEncoding('utf8').pause().resume().pause(); + assert.strictEqual(r, r2); +} + +{ + // Verify readableEncoding property + assert(Object.hasOwn(R.prototype, 'readableEncoding')); + + const r = new R({ encoding: 'utf8' }); + assert.strictEqual(r.readableEncoding, 'utf8'); +} + +{ + // Verify readableObjectMode property + assert(Object.hasOwn(R.prototype, 'readableObjectMode')); + + const r = new R({ objectMode: true }); + assert.strictEqual(r.readableObjectMode, true); +} + +{ + // Verify writableObjectMode property + assert(Object.hasOwn(W.prototype, 'writableObjectMode')); + + const w = new W({ objectMode: true }); + assert.strictEqual(w.writableObjectMode, true); +} diff --git a/test/js/node/test/parallel/stream2-compatibility.test.js b/test/js/node/test/parallel/test-stream2-compatibility.js similarity index 65% rename from test/js/node/test/parallel/stream2-compatibility.test.js rename to test/js/node/test/parallel/test-stream2-compatibility.js index a06395efa9..d760db8b32 100644 --- a/test/js/node/test/parallel/stream2-compatibility.test.js +++ b/test/js/node/test/parallel/test-stream2-compatibility.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-compatibility.js -//#SHA1: 4767fa4655235101bee847c06850d6db3b71d1cd -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,17 +19,19 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { Readable: R, Writable: W } = require("stream"); +'use strict'; +require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); let ondataCalled = 0; class TestReader extends R { constructor() { super(); - this._buffer = Buffer.alloc(100, "x"); + this._buffer = Buffer.alloc(100, 'x'); - this.on("data", () => { + this.on('data', () => { ondataCalled++; }); } @@ -43,10 +42,17 @@ class TestReader extends R { } } +const reader = new TestReader(); +setImmediate(function() { + assert.strictEqual(ondataCalled, 1); + console.log('ok'); + reader.push(null); +}); + class TestWriter extends W { constructor() { super(); - this.write("foo"); + this.write('foo'); this.end(); } @@ -55,27 +61,10 @@ class TestWriter extends W { } } -test("TestReader emits data once", done => { - const reader = new TestReader(); - setImmediate(() => { - expect(ondataCalled).toBe(1); - reader.push(null); - done(); - }); -}); +const writer = new TestWriter(); -test("TestWriter ends correctly", () => { - const writer = new TestWriter(); - expect(writer.writable).toBe(false); +process.on('exit', function() { + assert.strictEqual(reader.readable, false); + assert.strictEqual(writer.writable, false); + console.log('ok'); }); - -test("TestReader becomes unreadable", done => { - const reader = new TestReader(); - reader.on("end", () => { - expect(reader.readable).toBe(false); - done(); - }); - reader.push(null); -}); - -//<#END_FILE: test-stream2-compatibility.js diff --git a/test/js/node/test/parallel/test-stream2-decode-partial.js b/test/js/node/test/parallel/test-stream2-decode-partial.js new file mode 100644 index 0000000000..9d9ae21bfe --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-decode-partial.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +let buf = ''; +const euro = Buffer.from([0xE2, 0x82, 0xAC]); +const cent = Buffer.from([0xC2, 0xA2]); +const source = Buffer.concat([euro, cent]); + +const readable = Readable({ encoding: 'utf8' }); +readable.push(source.slice(0, 2)); +readable.push(source.slice(2, 4)); +readable.push(source.slice(4, 6)); +readable.push(null); + +readable.on('data', function(data) { + buf += data; +}); + +process.on('exit', function() { + assert.strictEqual(buf, '€¢'); +}); diff --git a/test/js/node/test/parallel/test-stream2-finish-pipe-error.js b/test/js/node/test/parallel/test-stream2-finish-pipe-error.js new file mode 100644 index 0000000000..a603e154b9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-finish-pipe-error.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +process.on('uncaughtException', common.mustCall()); + +const r = new stream.Readable(); +r._read = function(size) { + r.push(Buffer.allocUnsafe(size)); +}; + +const w = new stream.Writable(); +w._write = function(data, encoding, cb) { + cb(null); +}; + +r.pipe(w); + +// end() after pipe should cause unhandled exception +w.end(); diff --git a/test/js/node/test/parallel/test-stream2-finish-pipe.js b/test/js/node/test/parallel/test-stream2-finish-pipe.js new file mode 100644 index 0000000000..5e2969aad4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-finish-pipe.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const stream = require('stream'); + +const r = new stream.Readable(); +r._read = function(size) { + r.push(Buffer.allocUnsafe(size)); +}; + +const w = new stream.Writable(); +w._write = function(data, encoding, cb) { + process.nextTick(cb, null); +}; + +r.pipe(w); + +// end() must be called in nextTick or a WRITE_AFTER_END error occurs. +process.nextTick(() => { + // This might sound unrealistic, but it happens in net.js. When + // socket.allowHalfOpen === false, EOF will cause .destroySoon() call which + // ends the writable side of net.Socket. + w.end(); +}); diff --git a/test/js/node/test/parallel/stream2-large-read-stall.test.js b/test/js/node/test/parallel/test-stream2-large-read-stall.js similarity index 54% rename from test/js/node/test/parallel/stream2-large-read-stall.test.js rename to test/js/node/test/parallel/test-stream2-large-read-stall.js index 9754a67b0a..2d44bb7f78 100644 --- a/test/js/node/test/parallel/stream2-large-read-stall.test.js +++ b/test/js/node/test/parallel/test-stream2-large-read-stall.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-large-read-stall.js -//#SHA1: c83f78a1c91b12eedd6262c4691bb2e9118c90ae -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,7 +19,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const assert = require('assert'); // If everything aligns so that you do a read(n) of exactly the // remaining buffer, then make sure that 'end' still emits. @@ -32,47 +31,44 @@ const PUSHSIZE = 20; const PUSHCOUNT = 1000; const HWM = 50; -const { Readable } = require("stream"); +const Readable = require('stream').Readable; +const r = new Readable({ + highWaterMark: HWM +}); +const rs = r._readableState; -test("large read stall", async () => { - const r = new Readable({ - highWaterMark: HWM, - }); - const rs = r._readableState; +r._read = push; - r._read = push; +r.on('readable', function() { + console.error('>> readable'); + let ret; + do { + console.error(` > read(${READSIZE})`); + ret = r.read(READSIZE); + console.error(` < ${ret && ret.length} (${rs.length} remain)`); + } while (ret && ret.length === READSIZE); - let pushes = 0; - function push() { - if (pushes > PUSHCOUNT) return; - - if (pushes++ === PUSHCOUNT) { - console.error(" push(EOF)"); - return r.push(null); - } - - console.error(` push #${pushes}`); - if (r.push(Buffer.allocUnsafe(PUSHSIZE))) setTimeout(push, 1); - } - - r.on("readable", function () { - console.error(">> readable"); - let ret; - do { - console.error(` > read(${READSIZE})`); - ret = r.read(READSIZE); - console.error(` < ${ret && ret.length} (${rs.length} remain)`); - } while (ret && ret.length === READSIZE); - - console.error("<< after read()", ret && ret.length, rs.needReadable, rs.length); - }); - - await new Promise(resolve => { - r.on("end", () => { - expect(pushes).toBe(PUSHCOUNT + 1); - resolve(); - }); - }); + console.error('<< after read()', + ret && ret.length, + rs.needReadable, + rs.length); }); -//<#END_FILE: test-stream2-large-read-stall.js +r.on('end', common.mustCall(function() { + assert.strictEqual(pushes, PUSHCOUNT + 1); +})); + +let pushes = 0; +function push() { + if (pushes > PUSHCOUNT) + return; + + if (pushes++ === PUSHCOUNT) { + console.error(' push(EOF)'); + return r.push(null); + } + + console.error(` push #${pushes}`); + if (r.push(Buffer.allocUnsafe(PUSHSIZE))) + setTimeout(push, 1); +} diff --git a/test/js/node/test/parallel/test-stream2-objects.js b/test/js/node/test/parallel/test-stream2-objects.js new file mode 100644 index 0000000000..b7ad074628 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-objects.js @@ -0,0 +1,297 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Readable, Writable } = require('stream'); +const assert = require('assert'); + +function toArray(callback) { + const stream = new Writable({ objectMode: true }); + const list = []; + stream.write = function(chunk) { + list.push(chunk); + }; + + stream.end = common.mustCall(function() { + callback(list); + }); + + return stream; +} + +function fromArray(list) { + const r = new Readable({ objectMode: true }); + r._read = common.mustNotCall(); + list.forEach(function(chunk) { + r.push(chunk); + }); + r.push(null); + + return r; +} + +{ + // Verify that objects can be read from the stream + const r = fromArray([{ one: '1' }, { two: '2' }]); + + const v1 = r.read(); + const v2 = r.read(); + const v3 = r.read(); + + assert.deepStrictEqual(v1, { one: '1' }); + assert.deepStrictEqual(v2, { two: '2' }); + assert.strictEqual(v3, null); +} + +{ + // Verify that objects can be piped into the stream + const r = fromArray([{ one: '1' }, { two: '2' }]); + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that read(n) is ignored + const r = fromArray([{ one: '1' }, { two: '2' }]); + const value = r.read(2); + + assert.deepStrictEqual(value, { one: '1' }); +} + +{ + // Verify that objects can be synchronously read + const r = new Readable({ objectMode: true }); + const list = [{ one: '1' }, { two: '2' }]; + r._read = function(n) { + const item = list.shift(); + r.push(item || null); + }; + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that objects can be asynchronously read + const r = new Readable({ objectMode: true }); + const list = [{ one: '1' }, { two: '2' }]; + r._read = function(n) { + const item = list.shift(); + process.nextTick(function() { + r.push(item || null); + }); + }; + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that strings can be read as objects + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + const list = ['one', 'two', 'three']; + list.forEach(function(str) { + r.push(str); + }); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, list); + }))); +} + +{ + // Verify read(0) behavior for object streams + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + + r.push('foobar'); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, ['foobar']); + }))); +} + +{ + // Verify the behavior of pushing falsey values + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + + r.push(false); + r.push(0); + r.push(''); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, [false, 0, '']); + }))); +} + +{ + // Verify high watermark _read() behavior + const r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + let calls = 0; + const list = ['1', '2', '3', '4', '5', '6', '7', '8']; + + r._read = function(n) { + calls++; + }; + + list.forEach(function(c) { + r.push(c); + }); + + const v = r.read(); + + assert.strictEqual(calls, 0); + assert.strictEqual(v, '1'); + + const v2 = r.read(); + assert.strictEqual(v2, '2'); + + const v3 = r.read(); + assert.strictEqual(v3, '3'); + + assert.strictEqual(calls, 1); +} + +{ + // Verify high watermark push behavior + const r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + r._read = common.mustNotCall(); + for (let i = 0; i < 6; i++) { + const bool = r.push(i); + assert.strictEqual(bool, i !== 5); + } +} + +{ + // Verify that objects can be written to stream + const w = new Writable({ objectMode: true }); + + w._write = function(chunk, encoding, cb) { + assert.deepStrictEqual(chunk, { foo: 'bar' }); + cb(); + }; + + w.on('finish', common.mustCall()); + w.write({ foo: 'bar' }); + w.end(); +} + +{ + // Verify that multiple objects can be written to stream + const w = new Writable({ objectMode: true }); + const list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + cb(); + }; + + w.on('finish', common.mustCall(function() { + assert.deepStrictEqual(list, [0, 1, 2, 3, 4]); + })); + + w.write(0); + w.write(1); + w.write(2); + w.write(3); + w.write(4); + w.end(); +} + +{ + // Verify that strings can be written as objects + const w = new Writable({ + objectMode: true + }); + const list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + process.nextTick(cb); + }; + + w.on('finish', common.mustCall(function() { + assert.deepStrictEqual(list, ['0', '1', '2', '3', '4']); + })); + + w.write('0'); + w.write('1'); + w.write('2'); + w.write('3'); + w.write('4'); + w.end(); +} + +{ + // Verify that stream buffers finish until callback is called + const w = new Writable({ + objectMode: true + }); + let called = false; + + w._write = function(chunk, encoding, cb) { + assert.strictEqual(chunk, 'foo'); + + process.nextTick(function() { + called = true; + cb(); + }); + }; + + w.on('finish', common.mustCall(function() { + assert.strictEqual(called, true); + })); + + w.write('foo'); + w.end(); +} diff --git a/test/js/node/test/parallel/stream2-pipe-error-handling.test.js b/test/js/node/test/parallel/test-stream2-pipe-error-handling.js similarity index 63% rename from test/js/node/test/parallel/stream2-pipe-error-handling.test.js rename to test/js/node/test/parallel/test-stream2-pipe-error-handling.js index 4a482b234a..d3f4838105 100644 --- a/test/js/node/test/parallel/stream2-pipe-error-handling.test.js +++ b/test/js/node/test/parallel/test-stream2-pipe-error-handling.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-pipe-error-handling.js -//#SHA1: c5e7ad139c64f22b16e8fff8a62f6f91067087c8 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,89 +19,88 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const stream = require("stream"); +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); -test("stream pipe error handling with autoDestroy true", () => { +{ let count = 1000; const source = new stream.Readable(); - source._read = function (n) { + source._read = function(n) { n = Math.min(count, n); count -= n; source.push(Buffer.allocUnsafe(n)); }; let unpipedDest; - source.unpipe = function (dest) { + source.unpipe = function(dest) { unpipedDest = dest; stream.Readable.prototype.unpipe.call(this, dest); }; const dest = new stream.Writable(); - dest._write = function (chunk, encoding, cb) { + dest._write = function(chunk, encoding, cb) { cb(); }; source.pipe(dest); let gotErr = null; - dest.on("error", function (err) { + dest.on('error', function(err) { gotErr = err; }); let unpipedSource; - dest.on("unpipe", function (src) { + dest.on('unpipe', function(src) { unpipedSource = src; }); - const err = new Error("This stream turned into bacon."); - dest.emit("error", err); - expect(gotErr).toBe(err); - expect(unpipedSource).toBe(source); - expect(unpipedDest).toBe(dest); -}); + const err = new Error('This stream turned into bacon.'); + dest.emit('error', err); + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +} -test("stream pipe error handling with autoDestroy false", () => { +{ let count = 1000; const source = new stream.Readable(); - source._read = function (n) { + source._read = function(n) { n = Math.min(count, n); count -= n; source.push(Buffer.allocUnsafe(n)); }; let unpipedDest; - source.unpipe = function (dest) { + source.unpipe = function(dest) { unpipedDest = dest; stream.Readable.prototype.unpipe.call(this, dest); }; const dest = new stream.Writable({ autoDestroy: false }); - dest._write = function (chunk, encoding, cb) { + dest._write = function(chunk, encoding, cb) { cb(); }; source.pipe(dest); let unpipedSource; - dest.on("unpipe", function (src) { + dest.on('unpipe', function(src) { unpipedSource = src; }); - const err = new Error("This stream turned into bacon."); + const err = new Error('This stream turned into bacon.'); let gotErr = null; - expect(() => { - dest.emit("error", err); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); - expect(unpipedSource).toBe(source); - expect(unpipedDest).toBe(dest); -}); - -//<#END_FILE: test-stream2-pipe-error-handling.js + try { + dest.emit('error', err); + } catch (e) { + gotErr = e; + } + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +} diff --git a/test/js/node/test/parallel/test-stream2-pipe-error-once-listener.js b/test/js/node/test/parallel/test-stream2-pipe-error-once-listener.js new file mode 100644 index 0000000000..003e78e64f --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-pipe-error-once-listener.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const stream = require('stream'); + +class Read extends stream.Readable { + _read(size) { + this.push('x'); + this.push(null); + } +} + +class Write extends stream.Writable { + _write(buffer, encoding, cb) { + this.emit('error', new Error('boom')); + this.emit('alldone'); + } +} + +const read = new Read(); +const write = new Write(); + +write.once('error', () => {}); +write.once('alldone', function(err) { + console.log('ok'); +}); + +process.on('exit', function(c) { + console.error('error thrown even with listener'); +}); + +read.pipe(write); diff --git a/test/js/node/test/parallel/test-stream2-push.js b/test/js/node/test/parallel/test-stream2-push.js new file mode 100644 index 0000000000..748a77b9c4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-push.js @@ -0,0 +1,136 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +const EE = require('events').EventEmitter; + + +// A mock thing a bit like the net.Socket/tcp_wrap.handle interaction + +const stream = new Readable({ + highWaterMark: 16, + encoding: 'utf8' +}); + +const source = new EE(); + +stream._read = function() { + console.error('stream._read'); + readStart(); +}; + +let ended = false; +stream.on('end', function() { + ended = true; +}); + +source.on('data', function(chunk) { + const ret = stream.push(chunk); + console.error('data', stream.readableLength); + if (!ret) + readStop(); +}); + +source.on('end', function() { + stream.push(null); +}); + +let reading = false; + +function readStart() { + console.error('readStart'); + reading = true; +} + +function readStop() { + console.error('readStop'); + reading = false; + process.nextTick(function() { + const r = stream.read(); + if (r !== null) + writer.write(r); + }); +} + +const writer = new Writable({ + decodeStrings: false +}); + +const written = []; + +const expectWritten = + [ 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg' ]; + +writer._write = function(chunk, encoding, cb) { + console.error(`WRITE ${chunk}`); + written.push(chunk); + process.nextTick(cb); +}; + +writer.on('finish', finish); + + +// Now emit some chunks. + +const chunk = 'asdfg'; + +let set = 0; +readStart(); +data(); +function data() { + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(!reading); + if (set++ < 5) + setTimeout(data, 10); + else + end(); +} + +function finish() { + console.error('finish'); + assert.deepStrictEqual(written, expectWritten); + console.log('ok'); +} + +function end() { + source.emit('end'); + assert(!reading); + writer.end(stream.read()); + setImmediate(function() { + assert(ended); + }); +} diff --git a/test/js/node/test/parallel/stream2-readable-wrap-empty.test.js b/test/js/node/test/parallel/test-stream2-read-sync-stack.js similarity index 66% rename from test/js/node/test/parallel/stream2-readable-wrap-empty.test.js rename to test/js/node/test/parallel/test-stream2-read-sync-stack.js index 9735960e5b..e6a5ea7e52 100644 --- a/test/js/node/test/parallel/stream2-readable-wrap-empty.test.js +++ b/test/js/node/test/parallel/test-stream2-read-sync-stack.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-readable-wrap-empty.js -//#SHA1: aaac82ec7df0743321f2aaacd9512ecf1b932ad6 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,25 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const Readable = require('stream').Readable; -const { Readable } = require("stream"); -const EventEmitter = require("events"); +// This tests synchronous read callbacks and verifies that even if they nest +// heavily the process handles it without an error -test("Readable.wrap with empty stream", done => { - const oldStream = new EventEmitter(); - oldStream.pause = jest.fn(); - oldStream.resume = jest.fn(); +const r = new Readable(); +const N = 256 * 1024; - const newStream = new Readable().wrap(oldStream); +let reads = 0; +r._read = function(n) { + const chunk = reads++ === N ? null : Buffer.allocUnsafe(1); + r.push(chunk); +}; - newStream - .on("readable", () => {}) - .on("end", () => { - done(); - }); - - oldStream.emit("end"); +r.on('readable', function onReadable() { + if (!(r.readableLength % 256)) + console.error('readable', r.readableLength); + r.read(N * 2); }); -//<#END_FILE: test-stream2-readable-wrap-empty.js +r.on('end', common.mustCall()); + +r.read(0); diff --git a/test/js/node/test/parallel/stream2-readable-empty-buffer-no-eof.test.js b/test/js/node/test/parallel/test-stream2-readable-empty-buffer-no-eof.js similarity index 71% rename from test/js/node/test/parallel/stream2-readable-empty-buffer-no-eof.test.js rename to test/js/node/test/parallel/test-stream2-readable-empty-buffer-no-eof.js index 5aad62d568..7be2c358ee 100644 --- a/test/js/node/test/parallel/stream2-readable-empty-buffer-no-eof.test.js +++ b/test/js/node/test/parallel/test-stream2-readable-empty-buffer-no-eof.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-readable-empty-buffer-no-eof.js -//#SHA1: 70ef48637116477747867b03a60462fb64331087 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,10 +19,16 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { Readable } = require("stream"); +'use strict'; +require('../common'); +const assert = require('assert'); -test("test1", done => { +const Readable = require('stream').Readable; + +test1(); +test2(); + +function test1() { const r = new Readable(); // Should not end when we get a Buffer.alloc(0) or '' as the _read @@ -38,9 +41,9 @@ test("test1", done => { // r.read(0) again later, otherwise there is no more work being done // and the process just exits. - const buf = Buffer.alloc(5, "x"); + const buf = Buffer.alloc(5, 'x'); let reads = 5; - r._read = function (n) { + r._read = function(n) { switch (reads--) { case 5: return setImmediate(() => { @@ -64,44 +67,51 @@ test("test1", done => { case 0: return r.push(null); // EOF default: - throw new Error("unreachable"); + throw new Error('unreachable'); } }; const results = []; function flow() { let chunk; - while (null !== (chunk = r.read())) results.push(String(chunk)); + while (null !== (chunk = r.read())) + results.push(String(chunk)); } - r.on("readable", flow); - r.on("end", () => { - results.push("EOF"); - expect(results).toEqual(["xxxxx", "xxxxx", "EOF"]); - done(); + r.on('readable', flow); + r.on('end', () => { + results.push('EOF'); }); flow(); -}); -test("test2", done => { - const r = new Readable({ encoding: "base64" }); + process.on('exit', () => { + assert.deepStrictEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]); + console.log('ok'); + }); +} + +function test2() { + const r = new Readable({ encoding: 'base64' }); let reads = 5; - r._read = function (n) { - if (!reads--) return r.push(null); // EOF - return r.push(Buffer.from("x")); + r._read = function(n) { + if (!reads--) + return r.push(null); // EOF + return r.push(Buffer.from('x')); }; const results = []; function flow() { let chunk; - while (null !== (chunk = r.read())) results.push(String(chunk)); + while (null !== (chunk = r.read())) + results.push(String(chunk)); } - r.on("readable", flow); - r.on("end", () => { - results.push("EOF"); - expect(results).toEqual(["eHh4", "eHg=", "EOF"]); - done(); + r.on('readable', flow); + r.on('end', () => { + results.push('EOF'); }); flow(); -}); -//<#END_FILE: test-stream2-readable-empty-buffer-no-eof.js + process.on('exit', () => { + assert.deepStrictEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]); + console.log('ok'); + }); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-legacy-drain.js b/test/js/node/test/parallel/test-stream2-readable-legacy-drain.js new file mode 100644 index 0000000000..beb3657776 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-legacy-drain.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const Stream = require('stream'); +const Readable = Stream.Readable; + +const r = new Readable(); +const N = 256; +let reads = 0; +r._read = function(n) { + return r.push(++reads === N ? null : Buffer.allocUnsafe(1)); +}; + +r.on('end', common.mustCall()); + +const w = new Stream(); +w.writable = true; +let buffered = 0; +w.write = function(c) { + buffered += c.length; + process.nextTick(drain); + return false; +}; + +function drain() { + assert(buffered <= 3); + buffered = 0; + w.emit('drain'); +} + +w.end = common.mustCall(); + +r.pipe(w); diff --git a/test/js/node/test/parallel/test-stream2-readable-non-empty-end.js b/test/js/node/test/parallel/test-stream2-readable-non-empty-end.js new file mode 100644 index 0000000000..417f2c3b0e --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-non-empty-end.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +let len = 0; +const chunks = new Array(10); +for (let i = 1; i <= 10; i++) { + chunks[i - 1] = Buffer.allocUnsafe(i); + len += i; +} + +const test = new Readable(); +let n = 0; +test._read = function(size) { + const chunk = chunks[n++]; + setTimeout(function() { + test.push(chunk === undefined ? null : chunk); + }, 1); +}; + +test.on('end', thrower); +function thrower() { + throw new Error('this should not happen!'); +} + +let bytesread = 0; +test.on('readable', function() { + const b = len - bytesread - 1; + const res = test.read(b); + if (res) { + bytesread += res.length; + console.error(`br=${bytesread} len=${len}`); + setTimeout(next, 1); + } + test.read(0); +}); +test.read(0); + +function next() { + // Now let's make 'end' happen + test.removeListener('end', thrower); + test.on('end', common.mustCall()); + + // One to get the last byte + let r = test.read(); + assert(r); + assert.strictEqual(r.length, 1); + r = test.read(); + assert.strictEqual(r, null); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap-destroy.js b/test/js/node/test/parallel/test-stream2-readable-wrap-destroy.js new file mode 100644 index 0000000000..e310ae09e6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap-destroy.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +const oldStream = new EE(); +oldStream.pause = () => {}; +oldStream.resume = () => {}; + +{ + new Readable({ + autoDestroy: false, + destroy: common.mustCall() + }) + .wrap(oldStream); + oldStream.emit('destroy'); +} + +{ + new Readable({ + autoDestroy: false, + destroy: common.mustCall() + }) + .wrap(oldStream); + oldStream.emit('close'); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap-empty.js b/test/js/node/test/parallel/test-stream2-readable-wrap-empty.js new file mode 100644 index 0000000000..3dbbdaa9b5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap-empty.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +const oldStream = new EE(); +oldStream.pause = () => {}; +oldStream.resume = () => {}; + +const newStream = new Readable().wrap(oldStream); + +newStream + .on('readable', () => {}) + .on('end', common.mustCall()); + +oldStream.emit('end'); diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap-error.js b/test/js/node/test/parallel/test-stream2-readable-wrap-error.js new file mode 100644 index 0000000000..2d2c26e2ca --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap-error.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +class LegacyStream extends EE { + pause() {} + resume() {} +} + +{ + const err = new Error(); + const oldStream = new LegacyStream(); + const r = new Readable({ autoDestroy: true }) + .wrap(oldStream) + .on('error', common.mustCall(() => { + assert.strictEqual(r._readableState.errorEmitted, true); + assert.strictEqual(r._readableState.errored, err); + assert.strictEqual(r.destroyed, true); + })); + oldStream.emit('error', err); +} + +{ + const err = new Error(); + const oldStream = new LegacyStream(); + const r = new Readable({ autoDestroy: false }) + .wrap(oldStream) + .on('error', common.mustCall(() => { + assert.strictEqual(r._readableState.errorEmitted, true); + assert.strictEqual(r._readableState.errored, err); + assert.strictEqual(r.destroyed, false); + })); + oldStream.emit('error', err); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap.js b/test/js/node/test/parallel/test-stream2-readable-wrap.js new file mode 100644 index 0000000000..eebe72bc0d --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap.js @@ -0,0 +1,100 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); +const EE = require('events').EventEmitter; + +function runTest(highWaterMark, objectMode, produce) { + + const old = new EE(); + const r = new Readable({ highWaterMark, objectMode }); + assert.strictEqual(r, r.wrap(old)); + + r.on('end', common.mustCall()); + + old.pause = function() { + old.emit('pause'); + flowing = false; + }; + + old.resume = function() { + old.emit('resume'); + flow(); + }; + + // Make sure pause is only emitted once. + let pausing = false; + r.on('pause', () => { + assert.strictEqual(pausing, false); + pausing = true; + process.nextTick(() => { + pausing = false; + }); + }); + + let flowing; + let chunks = 10; + let oldEnded = false; + const expected = []; + function flow() { + flowing = true; + while (flowing && chunks-- > 0) { + const item = produce(); + expected.push(item); + old.emit('data', item); + } + if (chunks <= 0) { + oldEnded = true; + old.emit('end'); + } + } + + const w = new Writable({ highWaterMark: highWaterMark * 2, + objectMode }); + const written = []; + w._write = function(chunk, encoding, cb) { + written.push(chunk); + setTimeout(cb, 1); + }; + + w.on('finish', common.mustCall(function() { + performAsserts(); + })); + + r.pipe(w); + + flow(); + + function performAsserts() { + assert(oldEnded); + assert.deepStrictEqual(written, expected); + } +} + +runTest(100, false, function() { return Buffer.allocUnsafe(100); }); +runTest(10, false, function() { return Buffer.from('xxxxxxxxxx'); }); +runTest(1, true, function() { return { foo: 'bar' }; }); + +const objectChunks = [ 5, 'a', false, 0, '', 'xyz', { x: 4 }, 7, [], 555 ]; +runTest(1, true, function() { return objectChunks.shift(); }); diff --git a/test/js/node/test/parallel/test-stream2-set-encoding.js b/test/js/node/test/parallel/test-stream2-set-encoding.js new file mode 100644 index 0000000000..2d35b161bf --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-set-encoding.js @@ -0,0 +1,323 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable: R } = require('stream'); + +class TestReader extends R { + constructor(n, opts) { + super(opts); + this.pos = 0; + this.len = n || 100; + } + + _read(n) { + setTimeout(() => { + if (this.pos >= this.len) { + // Double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + n = Math.min(n, this.len - this.pos); + if (n <= 0) { + // Double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + this.pos += n; + const ret = Buffer.alloc(n, 'a'); + + return this.push(ret); + }, 1); + } +} + +{ + // Verify utf8 encoding + const tr = new TestReader(100); + tr.setEncoding('utf8'); + const out = []; + const expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + + +{ + // Verify hex encoding + const tr = new TestReader(100); + tr.setEncoding('hex'); + const out = []; + const expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify hex encoding with read(13) + const tr = new TestReader(100); + tr.setEncoding('hex'); + const out = []; + const expect = + [ '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '16161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify base64 encoding + const tr = new TestReader(100); + tr.setEncoding('base64'); + const out = []; + const expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify utf8 encoding + const tr = new TestReader(100, { encoding: 'utf8' }); + const out = []; + const expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + + +{ + // Verify hex encoding + const tr = new TestReader(100, { encoding: 'hex' }); + const out = []; + const expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify hex encoding with read(13) + const tr = new TestReader(100, { encoding: 'hex' }); + const out = []; + const expect = + [ '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '16161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify base64 encoding + const tr = new TestReader(100, { encoding: 'base64' }); + const out = []; + const expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify chaining behavior + const tr = new TestReader(100); + assert.deepStrictEqual(tr.setEncoding('utf8'), tr); +} diff --git a/test/js/node/test/parallel/stream2-unpipe-drain.test.js b/test/js/node/test/parallel/test-stream2-unpipe-drain.js similarity index 64% rename from test/js/node/test/parallel/stream2-unpipe-drain.test.js rename to test/js/node/test/parallel/test-stream2-unpipe-drain.js index a2ea536ef6..4c283df680 100644 --- a/test/js/node/test/parallel/stream2-unpipe-drain.test.js +++ b/test/js/node/test/parallel/test-stream2-unpipe-drain.js @@ -1,6 +1,3 @@ -//#FILE: test-stream2-unpipe-drain.js -//#SHA1: b04d9c383281786f45989d8d7f85f6f1a620bde2 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,17 +19,21 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); -const stream = require("stream"); +const stream = require('stream'); class TestWriter extends stream.Writable { _write(buffer, encoding, callback) { - console.log("write called"); + console.log('write called'); // Super slow write stream (callback never called) } } +const dest = new TestWriter(); + class TestReader extends stream.Readable { constructor() { super(); @@ -45,31 +46,27 @@ class TestReader extends stream.Readable { } } -test("stream2 unpipe drain", done => { - const dest = new TestWriter(); - const src1 = new TestReader(); - const src2 = new TestReader(); +const src1 = new TestReader(); +const src2 = new TestReader(); - src1.pipe(dest); +src1.pipe(dest); - src1.once("readable", () => { - process.nextTick(() => { - src2.pipe(dest); +src1.once('readable', () => { + process.nextTick(() => { - src2.once("readable", () => { - process.nextTick(() => { - src1.unpipe(dest); + src2.pipe(dest); - // Use setImmediate to ensure all microtasks have been processed - setImmediate(() => { - expect(src1.reads).toBe(2); - expect(src2.reads).toBe(2); - done(); - }); - }); + src2.once('readable', () => { + process.nextTick(() => { + + src1.unpipe(dest); }); }); }); }); -//<#END_FILE: test-stream2-unpipe-drain.js + +process.on('exit', () => { + assert.strictEqual(src1.reads, 2); + assert.strictEqual(src2.reads, 2); +}); diff --git a/test/js/node/test/parallel/test-stream3-cork-end.js b/test/js/node/test/parallel/test-stream3-cork-end.js new file mode 100644 index 0000000000..0cbc033a2e --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-cork-end.js @@ -0,0 +1,91 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const Writable = stream.Writable; + +// Test the buffering behavior of Writable streams. +// +// The call to cork() triggers storing chunks which are flushed +// on calling end() and the stream subsequently ended. +// +// node version target: 0.12 + +const expectedChunks = ['please', 'buffer', 'me', 'kindly']; +const inputChunks = expectedChunks.slice(0); +let seenChunks = []; +let seenEnd = false; + +const w = new Writable(); +// Let's arrange to store the chunks. +w._write = function(chunk, encoding, cb) { + // Stream end event is not seen before the last write. + assert.ok(!seenEnd); + // Default encoding given none was specified. + assert.strictEqual(encoding, 'buffer'); + + seenChunks.push(chunk); + cb(); +}; +// Let's record the stream end event. +w.on('finish', () => { + seenEnd = true; +}); + +function writeChunks(remainingChunks, callback) { + const writeChunk = remainingChunks.shift(); + let writeState; + + if (writeChunk) { + setImmediate(() => { + writeState = w.write(writeChunk); + // We were not told to stop writing. + assert.ok(writeState); + + writeChunks(remainingChunks, callback); + }); + } else { + callback(); + } +} + +// Do an initial write. +w.write('stuff'); +// The write was immediate. +assert.strictEqual(seenChunks.length, 1); +// Reset the seen chunks. +seenChunks = []; + +// Trigger stream buffering. +w.cork(); + +// Write the bufferedChunks. +writeChunks(inputChunks, () => { + // Should not have seen anything yet. + assert.strictEqual(seenChunks.length, 0); + + // Trigger flush and ending the stream. + w.end(); + + // Stream should not ended in current tick. + assert.ok(!seenEnd); + + // Buffered bytes should be seen in current tick. + assert.strictEqual(seenChunks.length, 4); + + // Did the chunks match. + for (let i = 0, l = expectedChunks.length; i < l; i++) { + const seen = seenChunks[i]; + // There was a chunk. + assert.ok(seen); + + const expected = Buffer.from(expectedChunks[i]); + // It was what we expected. + assert.ok(seen.equals(expected)); + } + + setImmediate(() => { + // Stream should have ended in next tick. + assert.ok(seenEnd); + }); +}); diff --git a/test/js/node/test/parallel/test-stream3-cork-uncork.js b/test/js/node/test/parallel/test-stream3-cork-uncork.js new file mode 100644 index 0000000000..dfb901af41 --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-cork-uncork.js @@ -0,0 +1,86 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const Writable = stream.Writable; + +// Test the buffering behavior of Writable streams. +// +// The call to cork() triggers storing chunks which are flushed +// on calling uncork() in the same tick. +// +// node version target: 0.12 + +const expectedChunks = ['please', 'buffer', 'me', 'kindly']; +const inputChunks = expectedChunks.slice(0); +let seenChunks = []; +let seenEnd = false; + +const w = new Writable(); +// Let's arrange to store the chunks. +w._write = function(chunk, encoding, cb) { + // Default encoding given none was specified. + assert.strictEqual(encoding, 'buffer'); + + seenChunks.push(chunk); + cb(); +}; +// Let's record the stream end event. +w.on('finish', () => { + seenEnd = true; +}); + +function writeChunks(remainingChunks, callback) { + const writeChunk = remainingChunks.shift(); + let writeState; + + if (writeChunk) { + setImmediate(() => { + writeState = w.write(writeChunk); + // We were not told to stop writing. + assert.ok(writeState); + + writeChunks(remainingChunks, callback); + }); + } else { + callback(); + } +} + +// Do an initial write. +w.write('stuff'); +// The write was immediate. +assert.strictEqual(seenChunks.length, 1); +// Reset the chunks seen so far. +seenChunks = []; + +// Trigger stream buffering. +w.cork(); + +// Write the bufferedChunks. +writeChunks(inputChunks, () => { + // Should not have seen anything yet. + assert.strictEqual(seenChunks.length, 0); + + // Trigger writing out the buffer. + w.uncork(); + + // Buffered bytes should be seen in current tick. + assert.strictEqual(seenChunks.length, 4); + + // Did the chunks match. + for (let i = 0, l = expectedChunks.length; i < l; i++) { + const seen = seenChunks[i]; + // There was a chunk. + assert.ok(seen); + + const expected = Buffer.from(expectedChunks[i]); + // It was what we expected. + assert.ok(seen.equals(expected)); + } + + setImmediate(() => { + // The stream should not have been ended. + assert.ok(!seenEnd); + }); +}); diff --git a/test/js/node/test/parallel/test-stream3-pause-then-read.js b/test/js/node/test/parallel/test-stream3-pause-then-read.js new file mode 100644 index 0000000000..1a38547220 --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-pause-then-read.js @@ -0,0 +1,170 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +const Readable = stream.Readable; +const Writable = stream.Writable; + +const totalChunks = 100; +const chunkSize = 99; +const expectTotalData = totalChunks * chunkSize; +let expectEndingData = expectTotalData; + +const r = new Readable({ highWaterMark: 1000 }); +let chunks = totalChunks; +r._read = function(n) { + console.log('_read called', chunks); + if (!(chunks % 2)) + setImmediate(push); + else if (!(chunks % 3)) + process.nextTick(push); + else + push(); +}; + +let totalPushed = 0; +function push() { + const chunk = chunks-- > 0 ? Buffer.alloc(chunkSize, 'x') : null; + if (chunk) { + totalPushed += chunk.length; + } + console.log('chunks', chunks); + r.push(chunk); +} + +read100(); + +// First we read 100 bytes. +function read100() { + readn(100, onData); +} + +function readn(n, then) { + console.error(`read ${n}`); + expectEndingData -= n; + (function read() { + const c = r.read(n); + console.error('c', c); + if (!c) + r.once('readable', read); + else { + assert.strictEqual(c.length, n); + assert(!r.readableFlowing); + then(); + } + })(); +} + +// Then we listen to some data events. +function onData() { + expectEndingData -= 100; + console.error('onData'); + let seen = 0; + r.on('data', function od(c) { + seen += c.length; + if (seen >= 100) { + // Seen enough + r.removeListener('data', od); + r.pause(); + if (seen > 100) { + // Oh no, seen too much! + // Put the extra back. + const diff = seen - 100; + r.unshift(c.slice(c.length - diff)); + console.error('seen too much', seen, diff); + } + + // Nothing should be lost in-between. + setImmediate(pipeLittle); + } + }); +} + +// Just pipe 200 bytes, then unshift the extra and unpipe. +function pipeLittle() { + expectEndingData -= 200; + console.error('pipe a little'); + const w = new Writable(); + let written = 0; + w.on('finish', () => { + assert.strictEqual(written, 200); + setImmediate(read1234); + }); + w._write = function(chunk, encoding, cb) { + written += chunk.length; + if (written >= 200) { + r.unpipe(w); + w.end(); + cb(); + if (written > 200) { + const diff = written - 200; + written -= diff; + r.unshift(chunk.slice(chunk.length - diff)); + } + } else { + setImmediate(cb); + } + }; + r.pipe(w); +} + +// Now read 1234 more bytes. +function read1234() { + readn(1234, resumePause); +} + +function resumePause() { + console.error('resumePause'); + // Don't read anything, just resume and re-pause a whole bunch. + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + setImmediate(pipe); +} + + +function pipe() { + console.error('pipe the rest'); + const w = new Writable(); + let written = 0; + w._write = function(chunk, encoding, cb) { + written += chunk.length; + cb(); + }; + w.on('finish', () => { + console.error('written', written, totalPushed); + assert.strictEqual(written, expectEndingData); + assert.strictEqual(totalPushed, expectTotalData); + console.log('ok'); + }); + r.pipe(w); +} diff --git a/test/js/node/test/parallel/test-stream3-pipeline-async-iterator.js b/test/js/node/test/parallel/test-stream3-pipeline-async-iterator.js new file mode 100644 index 0000000000..ad1e464777 --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-pipeline-async-iterator.js @@ -0,0 +1,27 @@ +/* eslint-disable node-core/require-common-first, require-yield */ +'use strict'; +const { pipeline } = require('node:stream/promises'); +{ + // Ensure that async iterators can act as readable and writable streams + async function* myCustomReadable() { + yield 'Hello'; + yield 'World'; + } + + const messages = []; + async function* myCustomWritable(stream) { + for await (const chunk of stream) { + messages.push(chunk); + } + } + + (async () => { + await pipeline( + myCustomReadable, + myCustomWritable, + ); + // Importing here to avoid initializing streams + require('assert').deepStrictEqual(messages, ['Hello', 'World']); + })() + .then(require('../common').mustCall()); +} diff --git a/test/js/node/test/parallel/test-string-decoder-end.js b/test/js/node/test/parallel/test-string-decoder-end.js new file mode 100644 index 0000000000..5a3c5cc720 --- /dev/null +++ b/test/js/node/test/parallel/test-string-decoder-end.js @@ -0,0 +1,128 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Verify that the string decoder works getting 1 byte at a time, +// the whole buffer at once, and that both match the .toString(enc) +// result of the entire buffer. + +require('../common'); +const assert = require('assert'); +const SD = require('string_decoder').StringDecoder; +const encodings = ['base64', 'base64url', 'hex', 'utf8', 'utf16le', 'ucs2']; + +const bufs = [ '☃💩', 'asdf' ].map((b) => Buffer.from(b)); + +// Also test just arbitrary bytes from 0-15. +for (let i = 1; i <= 16; i++) { + const bytes = '.'.repeat(i - 1).split('.').map((_, j) => j + 0x78); + bufs.push(Buffer.from(bytes)); +} + +encodings.forEach(testEncoding); + +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x82), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xAC), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82, 0xAC), Buffer.of(0x61), '€a'); + +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0x61, 0x00), 'a'); +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0xD8, 0x4D, 0xDC), '\u4DD8'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(), '\uD83D'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(0x61, 0x00), '\uD83Da'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8), + Buffer.of(0x4D, 0xDC), + '\uD83D\uDC4D' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D), + Buffer.of(0x61, 0x00), + '\uD83Da' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(0xDC), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D, 0xDC), + Buffer.of(0x61, 0x00), + '👍a' +); + +testEnd('base64', Buffer.of(0x61), Buffer.of(), 'YQ=='); +testEnd('base64', Buffer.of(0x61), Buffer.of(0x61), 'YQ==YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWE=YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh'); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ=='); + +testEnd('base64url', Buffer.of(0x61), Buffer.of(), 'YQ'); +testEnd('base64url', Buffer.of(0x61), Buffer.of(0x61), 'YQYQ'); +testEnd('base64url', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE'); +testEnd('base64url', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWEYQ'); +testEnd('base64url', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh'); +testEnd('base64url', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ'); + +function testEncoding(encoding) { + bufs.forEach((buf) => { + testBuf(encoding, buf); + }); +} + +function testBuf(encoding, buf) { + // Write one byte at a time. + let s = new SD(encoding); + let res1 = ''; + for (let i = 0; i < buf.length; i++) { + res1 += s.write(buf.slice(i, i + 1)); + } + res1 += s.end(); + + // Write the whole buffer at once. + let res2 = ''; + s = new SD(encoding); + res2 += s.write(buf); + res2 += s.end(); + + // .toString() on the buffer + const res3 = buf.toString(encoding); + + // One byte at a time should match toString + assert.strictEqual(res1, res3); + // All bytes at once should match toString + assert.strictEqual(res2, res3); +} + +function testEnd(encoding, incomplete, next, expected) { + let res = ''; + const s = new SD(encoding); + res += s.write(incomplete); + res += s.end(); + res += s.write(next); + res += s.end(); + + assert.strictEqual(res, expected); +} diff --git a/test/js/node/test/parallel/test-stringbytes-external.js b/test/js/node/test/parallel/test-stringbytes-external.js new file mode 100644 index 0000000000..d64312f525 --- /dev/null +++ b/test/js/node/test/parallel/test-stringbytes-external.js @@ -0,0 +1,143 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +// Minimum string size to overflow into external string space +const EXTERN_APEX = 0xFBEE9; + +// Manually controlled string for checking binary output +let ucs2_control = 'a\u0000'; +let write_str = 'a'; + + +// First do basic checks +let b = Buffer.from(write_str, 'ucs2'); +// first check latin1 +let c = b.toString('latin1'); +assert.strictEqual(b[0], 0x61); +assert.strictEqual(b[1], 0); +assert.strictEqual(ucs2_control, c); +// now check binary +c = b.toString('binary'); +assert.strictEqual(b[0], 0x61); +assert.strictEqual(b[1], 0); +assert.strictEqual(ucs2_control, c); + +// Now create big strings +const size = 1 << 20; +write_str = write_str.repeat(size); +ucs2_control = ucs2_control.repeat(size); + +// Check resultant buffer and output string +b = Buffer.from(write_str, 'ucs2'); +// Check fist Buffer created from write string +for (let i = 0; i < b.length; i += 2) { + assert.strictEqual(b[i], 0x61); + assert.strictEqual(b[i + 1], 0); +} + +// Create another string to create an external string +const b_ucs = b.toString('ucs2'); + +// Check control against external binary string +const l_bin = b.toString('latin1'); +assert.strictEqual(ucs2_control, l_bin); + +// Check control against external binary string +const b_bin = b.toString('binary'); +assert.strictEqual(ucs2_control, b_bin); + +// Create buffer copy from external +const c_bin = Buffer.from(l_bin, 'latin1'); +const c_ucs = Buffer.from(b_ucs, 'ucs2'); +// Make sure they're the same length +assert.strictEqual(c_bin.length, c_ucs.length); +// Make sure Buffers from externals are the same +for (let i = 0; i < c_bin.length; i++) { + assert.strictEqual(c_bin[i], c_ucs[i]); +} +// Check resultant strings +assert.strictEqual(c_bin.toString('ucs2'), c_ucs.toString('ucs2')); +assert.strictEqual(c_bin.toString('latin1'), ucs2_control); +assert.strictEqual(c_ucs.toString('latin1'), ucs2_control); + + +// Now let's test BASE64 and HEX encoding/decoding +const RADIOS = 2; +const PRE_HALF_APEX = Math.ceil(EXTERN_APEX / 2) - RADIOS; +const PRE_3OF4_APEX = Math.ceil((EXTERN_APEX / 4) * 3) - RADIOS; + +{ + for (let j = 0; j < RADIOS * 2; j += 1) { + const datum = b; + const slice = datum.slice(0, PRE_HALF_APEX + j); + const slice2 = datum.slice(0, PRE_HALF_APEX + j + 2); + const pumped_string = slice.toString('hex'); + const pumped_string2 = slice2.toString('hex'); + const decoded = Buffer.from(pumped_string, 'hex'); + + // The string are the same? + for (let k = 0; k < pumped_string.length; ++k) { + assert.strictEqual(pumped_string[k], pumped_string2[k]); + } + + // The recoded buffer is the same? + for (let i = 0; i < decoded.length; ++i) { + assert.strictEqual(datum[i], decoded[i]); + } + } +} + +{ + for (let j = 0; j < RADIOS * 2; j += 1) { + const datum = b; + const slice = datum.slice(0, PRE_3OF4_APEX + j); + const slice2 = datum.slice(0, PRE_3OF4_APEX + j + 2); + const pumped_string = slice.toString('base64'); + const pumped_string2 = slice2.toString('base64'); + const decoded = Buffer.from(pumped_string, 'base64'); + + // The string are the same? + for (let k = 0; k < pumped_string.length - 3; ++k) { + assert.strictEqual(pumped_string[k], pumped_string2[k]); + } + + // The recoded buffer is the same? + for (let i = 0; i < decoded.length; ++i) { + assert.strictEqual(datum[i], decoded[i]); + } + } +} + +// https://github.com/nodejs/node/issues/1024 +{ + const a = 'x'.repeat(1 << 20 - 1); + const b = Buffer.from(a, 'ucs2').toString('ucs2'); + const c = Buffer.from(b, 'utf8').toString('utf8'); + + assert.strictEqual(a.length, b.length); + assert.strictEqual(b.length, c.length); + + assert.strictEqual(a, b); + assert.strictEqual(b, c); +} diff --git a/test/js/node/test/parallel/test-sync-fileread.js b/test/js/node/test/parallel/test-sync-fileread.js new file mode 100644 index 0000000000..826f62d220 --- /dev/null +++ b/test/js/node/test/parallel/test-sync-fileread.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +assert.strictEqual(fs.readFileSync(fixtures.path('x.txt')).toString(), 'xyz\n'); diff --git a/test/js/node/test/parallel/sys.test.js b/test/js/node/test/parallel/test-sys.js similarity index 78% rename from test/js/node/test/parallel/sys.test.js rename to test/js/node/test/parallel/test-sys.js index eb25bc0689..a7a77b8e1c 100644 --- a/test/js/node/test/parallel/sys.test.js +++ b/test/js/node/test/parallel/test-sys.js @@ -1,6 +1,3 @@ -//#FILE: test-sys.js -//#SHA1: a7732f65863d5e2856179378dc44f09f2b315650 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,13 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); +const sys = require('sys'); // eslint-disable-line no-restricted-modules +const util = require('util'); -const sys = require("sys"); // eslint-disable-line no-restricted-modules -const util = require("util"); - -test("sys module is identical to util module", () => { - expect(sys).toBe(util); -}); - -//<#END_FILE: test-sys.js +assert.strictEqual(sys, util); diff --git a/test/js/node/test/parallel/test-timers-api-refs.js b/test/js/node/test/parallel/test-timers-api-refs.js new file mode 100644 index 0000000000..3c55a05ac4 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-api-refs.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const timers = require('timers'); + +// Delete global APIs to make sure they're not relied on by the internal timers +// code +delete global.setTimeout; +delete global.clearTimeout; +delete global.setInterval; +delete global.clearInterval; +delete global.setImmediate; +delete global.clearImmediate; + +const timeoutCallback = () => { timers.clearTimeout(timeout); }; +const timeout = timers.setTimeout(common.mustCall(timeoutCallback), 1); + +const intervalCallback = () => { timers.clearInterval(interval); }; +const interval = timers.setInterval(common.mustCall(intervalCallback), 1); + +const immediateCallback = () => { timers.clearImmediate(immediate); }; +const immediate = timers.setImmediate(immediateCallback); diff --git a/test/js/node/test/parallel/test-timers-args.js b/test/js/node/test/parallel/test-timers-args.js new file mode 100644 index 0000000000..1ba44d8bcf --- /dev/null +++ b/test/js/node/test/parallel/test-timers-args.js @@ -0,0 +1,31 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +function range(n) { + return 'x'.repeat(n + 1).split('').map(function(_, i) { return i; }); +} + +function timeout(nargs) { + const args = range(nargs); + setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) timeout(nargs + 1); + } +} + +function interval(nargs) { + const args = range(nargs); + const timer = setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + clearInterval(timer); + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) interval(nargs + 1); + } +} + +timeout(0); +interval(0); diff --git a/test/js/node/test/parallel/test-timers-clear-object-does-not-throw-error.js b/test/js/node/test/parallel/test-timers-clear-object-does-not-throw-error.js new file mode 100644 index 0000000000..9752f53abd --- /dev/null +++ b/test/js/node/test/parallel/test-timers-clear-object-does-not-throw-error.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); + +// This test makes sure clearing timers with +// objects doesn't throw +clearImmediate({}); +clearTimeout({}); +clearInterval({}); diff --git a/test/js/node/test/parallel/test-timers-clear-timeout-interval-equivalent.js b/test/js/node/test/parallel/test-timers-clear-timeout-interval-equivalent.js new file mode 100644 index 0000000000..94611b7070 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-clear-timeout-interval-equivalent.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); + +// This test makes sure that timers created with setTimeout can be disarmed by +// clearInterval and that timers created with setInterval can be disarmed by +// clearTimeout. +// +// This behavior is documented in the HTML Living Standard: +// +// * Refs: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval + +// Disarm interval with clearTimeout. +const interval = setInterval(common.mustNotCall(), 1); +clearTimeout(interval); + +// Disarm timeout with clearInterval. +const timeout = setTimeout(common.mustNotCall(), 1); +clearInterval(timeout); diff --git a/test/js/node/test/parallel/test-timers-clearImmediate.js b/test/js/node/test/parallel/test-timers-clearImmediate.js new file mode 100644 index 0000000000..ccd9826bb0 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-clearImmediate.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); + +const N = 3; + +function next() { + const fn = common.mustCall(() => clearImmediate(immediate)); + const immediate = setImmediate(fn); +} + +for (let i = 0; i < N; i++) { + next(); +} diff --git a/test/js/node/test/parallel/timers-immediate-queue.test.js b/test/js/node/test/parallel/test-timers-immediate-queue.js similarity index 67% rename from test/js/node/test/parallel/timers-immediate-queue.test.js rename to test/js/node/test/parallel/test-timers-immediate-queue.js index 92aefa2084..8b433ddedb 100644 --- a/test/js/node/test/parallel/timers-immediate-queue.test.js +++ b/test/js/node/test/parallel/test-timers-immediate-queue.js @@ -1,6 +1,3 @@ -//#FILE: test-timers-immediate-queue.js -//#SHA1: 04dace9bcd1a7634224efafb4fdd5a953bfc28f6 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,7 +19,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); +const assert = require('assert'); // setImmediate should run clear its queued cbs once per event loop turn // but immediates queued while processing the current queue should happen @@ -31,33 +30,27 @@ // hit should be the exact same size of QUEUE, if we're letting things // recursively add to the immediate QUEUE hit will be > QUEUE -test("setImmediate queue processing", done => { - let ticked = false; - let hit = 0; - const QUEUE = 10; +let ticked = false; - function run() { - if (hit === 0) { - setTimeout(() => { - ticked = true; - }, 1); - const now = Date.now(); - while (Date.now() - now < 2); - } +let hit = 0; +const QUEUE = 10; - if (ticked) return; - - hit += 1; - setImmediate(run); +function run() { + if (hit === 0) { + setTimeout(() => { ticked = true; }, 1); + const now = Date.now(); + while (Date.now() - now < 2); } - for (let i = 0; i < QUEUE; i++) setImmediate(run); + if (ticked) return; - // Use setImmediate to ensure all other immediates have run - setImmediate(() => { - expect(hit).toBe(QUEUE); - done(); - }); + hit += 1; + setImmediate(run); +} + +for (let i = 0; i < QUEUE; i++) + setImmediate(run); + +process.on('exit', function() { + assert.strictEqual(hit, QUEUE); }); - -//<#END_FILE: test-timers-immediate-queue.js diff --git a/test/js/node/test/parallel/test-timers-immediate.js b/test/js/node/test/parallel/test-timers-immediate.js new file mode 100644 index 0000000000..0227e38efa --- /dev/null +++ b/test/js/node/test/parallel/test-timers-immediate.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +let mainFinished = false; + +setImmediate(common.mustCall(function() { + assert.strictEqual(mainFinished, true); + clearImmediate(immediateB); +})); + +const immediateB = setImmediate(common.mustNotCall()); + +setImmediate(common.mustCall((...args) => { + assert.deepStrictEqual(args, [1, 2, 3]); +}), 1, 2, 3); + +setImmediate(common.mustCall((...args) => { + assert.deepStrictEqual(args, [1, 2, 3, 4, 5]); +}), 1, 2, 3, 4, 5); + +mainFinished = true; diff --git a/test/js/node/test/parallel/test-timers-interval-throw.js b/test/js/node/test/parallel/test-timers-interval-throw.js new file mode 100644 index 0000000000..876f0de55a --- /dev/null +++ b/test/js/node/test/parallel/test-timers-interval-throw.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// To match browser behaviour, interval should continue +// being rescheduled even if it throws. + +let count = 2; +const interval = setInterval(() => { throw new Error('IntervalError'); }, 1); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'IntervalError'); + if (--count === 0) { + clearInterval(interval); + } +}, 2)); diff --git a/test/js/node/test/parallel/test-timers-non-integer-delay.js b/test/js/node/test/parallel/test-timers-non-integer-delay.js new file mode 100644 index 0000000000..089c1fee8b --- /dev/null +++ b/test/js/node/test/parallel/test-timers-non-integer-delay.js @@ -0,0 +1,81 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// This test makes sure that non-integer timer delays do not make the process +// hang. See https://github.com/joyent/node/issues/8065 and +// https://github.com/joyent/node/issues/8068 which have been fixed by +// https://github.com/joyent/node/pull/8073. +// +// If the process hangs, this test will make the tests suite timeout, +// otherwise it will exit very quickly (after 50 timers with a short delay +// fire). +// +// We have to set at least several timers with a non-integer delay to +// reproduce the issue. Sometimes, a timer with a non-integer delay will +// expire correctly. 50 timers has always been more than enough to reproduce +// it 100%. + +const TIMEOUT_DELAY = 1.1; +let N = 50; + +const interval = setInterval(common.mustCall(() => { + if (--N === 0) { + clearInterval(interval); + } +}, N), TIMEOUT_DELAY); + +// Test non-integer delay ordering +{ + const ordering = []; + + setTimeout(common.mustCall(() => { + ordering.push(1); + }), 1); + + setTimeout(common.mustCall(() => { + ordering.push(2); + }), 1.8); + + setTimeout(common.mustCall(() => { + ordering.push(3); + }), 1.1); + + setTimeout(common.mustCall(() => { + ordering.push(4); + }), 1); + + setTimeout(common.mustCall(() => { + const expected = [1, 2, 3, 4]; + + assert.deepStrictEqual( + ordering, + expected, + `Non-integer delay ordering should be ${expected}, but got ${ordering}` + ); + + // 2 should always be last of these delays due to ordering guarantees by + // the implementation. + }), 2); +} diff --git a/test/js/node/test/parallel/test-timers-process-tampering.js b/test/js/node/test/parallel/test-timers-process-tampering.js new file mode 100644 index 0000000000..766cc9f356 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-process-tampering.js @@ -0,0 +1,8 @@ +// Check that setImmediate works even if process is tampered with. +// This is a regression test for https://github.com/nodejs/node/issues/17681. + +'use strict'; +const common = require('../common'); +global.process = {}; // Boom! +common.allowGlobals(global.process); +setImmediate(common.mustCall()); diff --git a/test/js/node/test/parallel/test-timers-promises-scheduler.js b/test/js/node/test/parallel/test-timers-promises-scheduler.js new file mode 100644 index 0000000000..7caf92fdf6 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-promises-scheduler.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); + +const { scheduler } = require('timers/promises'); +const { setTimeout } = require('timers'); +const { + strictEqual, + rejects, +} = require('assert'); + +async function testYield() { + await scheduler.yield(); + process.emit('foo'); +} +testYield().then(common.mustCall()); +queueMicrotask(common.mustCall(() => { + process.addListener('foo', common.mustCall()); +})); + +async function testWait() { + let value = 0; + setTimeout(() => value++, 10); + await scheduler.wait(15); + strictEqual(value, 1); +} + +testWait().then(common.mustCall()); + +async function testCancelableWait1() { + const ac = new AbortController(); + const wait = scheduler.wait(1e6, { signal: ac.signal }); + ac.abort(); + await rejects(wait, { + code: 'ABORT_ERR', + message: 'The operation was aborted', + }); +} + +testCancelableWait1().then(common.mustCall()); + +async function testCancelableWait2() { + const wait = scheduler.wait(10000, { signal: AbortSignal.abort() }); + await rejects(wait, { + code: 'ABORT_ERR', + message: 'The operation was aborted', + }); +} + +testCancelableWait2().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-timers-refresh-in-callback.js b/test/js/node/test/parallel/test-timers-refresh-in-callback.js new file mode 100644 index 0000000000..df62512acd --- /dev/null +++ b/test/js/node/test/parallel/test-timers-refresh-in-callback.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); + +// This test checks whether a refresh called inside the callback will keep +// the event loop alive to run the timer again. + +let didCall = false; +const timer = setTimeout(common.mustCall(() => { + if (!didCall) { + didCall = true; + timer.refresh(); + } +}, 2), 1); diff --git a/test/js/node/test/parallel/test-timers-same-timeout-wrong-list-deleted.js b/test/js/node/test/parallel/test-timers-same-timeout-wrong-list-deleted.js new file mode 100644 index 0000000000..f02603ad88 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-same-timeout-wrong-list-deleted.js @@ -0,0 +1,34 @@ +'use strict'; + +// This is a regression test for https://github.com/nodejs/node/issues/7722. +// +// When nested timers have the same timeout, calling clearTimeout on the +// older timer after it has fired causes the list the newer timer is in +// to be deleted. Since the newer timer was not cleared, it still blocks +// the event loop completing for the duration of its timeout, however, since +// no reference exists to it in its list, it cannot be canceled and its +// callback is not called when the timeout elapses. + +const common = require('../common'); + +const TIMEOUT = common.platformTimeout(100); + +const handle1 = setTimeout(common.mustCall(function() { + // Cause the old TIMEOUT list to be deleted + clearTimeout(handle1); + + // Cause a new list with the same key (TIMEOUT) to be created for this timer + const handle2 = setTimeout(common.mustNotCall(), TIMEOUT); + + setTimeout(common.mustCall(function() { + // Attempt to cancel the second timer. Fix for this bug will keep the + // newer timer from being dereferenced by keeping its list from being + // erroneously deleted. If we are able to cancel the timer successfully, + // the bug is fixed. + clearTimeout(handle2); + }), 1); + + // When this callback completes, `listOnTimeout` should now look at the + // correct list and refrain from removing the new TIMEOUT list which + // contains the reference to the newer timer. +}), TIMEOUT); diff --git a/test/js/node/test/parallel/test-timers-setimmediate-infinite-loop.js b/test/js/node/test/parallel/test-timers-setimmediate-infinite-loop.js new file mode 100644 index 0000000000..49adc390fa --- /dev/null +++ b/test/js/node/test/parallel/test-timers-setimmediate-infinite-loop.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that if an Immediate callback clears subsequent +// immediates we don't get stuck in an infinite loop. +// +// If the process does get stuck, it will be timed out by the test +// runner. +// +// Ref: https://github.com/nodejs/node/issues/9756 + +setImmediate(common.mustCall(function() { + clearImmediate(i2); + clearImmediate(i3); +})); + +const i2 = setImmediate(common.mustNotCall()); + +const i3 = setImmediate(common.mustNotCall()); diff --git a/test/js/node/test/parallel/test-timers-this.js b/test/js/node/test/parallel/test-timers-this.js new file mode 100644 index 0000000000..a2a028fb06 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-this.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const immediateHandler = setImmediate(common.mustCall(function() { + assert.strictEqual(this, immediateHandler); +})); + +const immediateArgsHandler = setImmediate(common.mustCall(function() { + assert.strictEqual(this, immediateArgsHandler); +}), 'args ...'); + +const intervalHandler = setInterval(common.mustCall(function() { + clearInterval(intervalHandler); + assert.strictEqual(this, intervalHandler); +}), 1); + +const intervalArgsHandler = setInterval(common.mustCall(function() { + clearInterval(intervalArgsHandler); + assert.strictEqual(this, intervalArgsHandler); +}), 1, 'args ...'); + +const timeoutHandler = setTimeout(common.mustCall(function() { + assert.strictEqual(this, timeoutHandler); +}), 1); + +const timeoutArgsHandler = setTimeout(common.mustCall(function() { + assert.strictEqual(this, timeoutArgsHandler); +}), 1, 'args ...'); diff --git a/test/js/node/test/parallel/test-timers-timeout-with-non-integer.js b/test/js/node/test/parallel/test-timers-timeout-with-non-integer.js new file mode 100644 index 0000000000..96efc69e50 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-timeout-with-non-integer.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); + +/** + * This test is for https://github.com/nodejs/node/issues/24203 + */ +let count = 50; +const time = 1.00000000000001; +const exec = common.mustCall(() => { + if (--count === 0) { + return; + } + setTimeout(exec, time); +}, count); +exec(); diff --git a/test/js/node/test/parallel/vm-create-context-circular-reference.test.js b/test/js/node/test/parallel/test-timers-uncaught-exception.js similarity index 72% rename from test/js/node/test/parallel/vm-create-context-circular-reference.test.js rename to test/js/node/test/parallel/test-timers-uncaught-exception.js index a088f700bf..8bcf72e36c 100644 --- a/test/js/node/test/parallel/vm-create-context-circular-reference.test.js +++ b/test/js/node/test/parallel/test-timers-uncaught-exception.js @@ -1,6 +1,3 @@ -//#FILE: test-vm-create-context-circular-reference.js -//#SHA1: 30740bd812c7365e1f7b72a3d98c5ca91d8adbb5 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,18 +19,21 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const vm = require("vm"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const errorMsg = 'BAM!'; -test("vm.createContext with circular reference", () => { - let sbx = {}; - sbx.window = sbx; +// The first timer throws... +setTimeout(common.mustCall(function() { + throw new Error(errorMsg); +}), 1); - sbx = vm.createContext(sbx); +// ...but the second one should still run +setTimeout(common.mustCall(), 1); - sbx.test = 123; +function uncaughtException(err) { + assert.strictEqual(err.message, errorMsg); +} - expect(sbx.window.window.window.window.window.test).toBe(123); -}); - -//<#END_FILE: test-vm-create-context-circular-reference.js +process.on('uncaughtException', common.mustCall(uncaughtException)); diff --git a/test/js/node/test/parallel/test-timers-unref-throw-then-ref.js b/test/js/node/test/parallel/test-timers-unref-throw-then-ref.js new file mode 100644 index 0000000000..1dd5fdd0ad --- /dev/null +++ b/test/js/node/test/parallel/test-timers-unref-throw-then-ref.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.once('uncaughtException', common.mustCall((err) => { + common.expectsError({ + message: 'Timeout Error' + })(err); +})); + +let called = false; +const t = setTimeout(() => { + assert(!called); + called = true; + t.ref(); + throw new Error('Timeout Error'); +}, 1).unref(); + +setTimeout(common.mustCall(), 1); diff --git a/test/js/node/test/parallel/test-timers-unrefd-interval-still-fires.js b/test/js/node/test/parallel/test-timers-unrefd-interval-still-fires.js new file mode 100644 index 0000000000..98172d18bc --- /dev/null +++ b/test/js/node/test/parallel/test-timers-unrefd-interval-still-fires.js @@ -0,0 +1,22 @@ +'use strict'; +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/8900. +const common = require('../common'); + +const TEST_DURATION = common.platformTimeout(1000); +let N = 3; + +const keepOpen = + setTimeout( + common.mustNotCall('Test timed out. keepOpen was not canceled.'), + TEST_DURATION); + +const timer = setInterval(common.mustCall(() => { + if (--N === 0) { + clearInterval(timer); + timer._onTimeout = + common.mustNotCall('Unrefd interval fired after being cleared'); + clearTimeout(keepOpen); + } +}, N), 1); + +timer.unref(); diff --git a/test/js/node/test/parallel/test-timers-unrefed-in-beforeexit.js b/test/js/node/test/parallel/test-timers-unrefed-in-beforeexit.js new file mode 100644 index 0000000000..a38b55bf45 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-unrefed-in-beforeexit.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); + +process.on('beforeExit', common.mustCall(() => { + setTimeout(common.mustNotCall(), 1).unref(); +})); diff --git a/test/js/node/test/parallel/test-timers-user-call.js b/test/js/node/test/parallel/test-timers-user-call.js new file mode 100644 index 0000000000..4ff24e688b --- /dev/null +++ b/test/js/node/test/parallel/test-timers-user-call.js @@ -0,0 +1,40 @@ +// Make sure `setTimeout()` and friends don't throw if the user-supplied +// function has .call() and .apply() monkey-patched to undesirable values. + +// Refs: https://github.com/nodejs/node/issues/12956 + +'use strict'; + +const common = require('../common'); + +{ + const fn = common.mustCall(10); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + setTimeout(fn, 1); + setTimeout(fn, 1, 'oneArg'); + setTimeout(fn, 1, 'two', 'args'); + setTimeout(fn, 1, 'three', '(3)', 'args'); + setTimeout(fn, 1, 'more', 'than', 'three', 'args'); + + setImmediate(fn, 1); + setImmediate(fn, 1, 'oneArg'); + setImmediate(fn, 1, 'two', 'args'); + setImmediate(fn, 1, 'three', '(3)', 'args'); + setImmediate(fn, 1, 'more', 'than', 'three', 'args'); +} + +{ + const testInterval = (...args) => { + const fn = common.mustCall(() => { clearInterval(interval); }); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + const interval = setInterval(fn, 1, ...args); + }; + + testInterval(); + testInterval('oneArg'); + testInterval('two', 'args'); + testInterval('three', '(3)', 'args'); + testInterval('more', 'than', 'three', 'args'); +} diff --git a/test/js/node/test/parallel/process-argv-0.test.js b/test/js/node/test/parallel/test-timers-zero-timeout.js similarity index 63% rename from test/js/node/test/parallel/process-argv-0.test.js rename to test/js/node/test/parallel/test-timers-zero-timeout.js index 7dee3674eb..61a5b2131b 100644 --- a/test/js/node/test/parallel/process-argv-0.test.js +++ b/test/js/node/test/parallel/test-timers-zero-timeout.js @@ -1,6 +1,3 @@ -//#FILE: test-process-argv-0.js -//#SHA1: 849d017b5c8496f1927b0618a55a688f4a7aa982 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,25 +19,31 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const path = require("path"); -const { spawn } = require("child_process"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); -if (process.argv[2] !== "child") { - test("process.argv[0] in child process", async () => { - const child = spawn(process.execPath, [__filename, "child"], { - cwd: path.dirname(process.execPath), - }); +// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args +{ + setTimeout(common.mustCall(f), 0, 'foo', 'bar', 'baz'); + setTimeout(() => {}, 0); - let childArgv0 = ""; - for await (const chunk of child.stdout) { - childArgv0 += chunk; - } - - expect(childArgv0).toBe(process.execPath); - }); -} else { - process.stdout.write(process.argv[0]); + function f(a, b, c) { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + } } -//<#END_FILE: test-process-argv-0.js +{ + let ncalled = 3; + + const f = common.mustCall((a, b, c) => { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + if (--ncalled === 0) clearTimeout(iv); + }, ncalled); + + const iv = setInterval(f, 0, 'foo', 'bar', 'baz'); +} diff --git a/test/js/node/test/parallel/test-tls-add-context.js b/test/js/node/test/parallel/test-tls-add-context.js new file mode 100644 index 0000000000..8d02866ce5 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-add-context.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const serverOptions = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ca: [ loadPEM('ca2-cert') ], + requestCert: true, + rejectUnauthorized: false, +}; + +let connections = 0; + +const server = tls.createServer(serverOptions, (c) => { + if (++connections === 3) { + server.close(); + } + if (c.servername === 'unknowncontext') { + assert.strictEqual(c.authorized, false); + return; + } + assert.strictEqual(c.authorized, true); +}); + +const secureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ], +}; +server.addContext('context1', secureContext); +server.addContext('context2', tls.createSecureContext(secureContext)); + +const clientOptionsBase = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ], + rejectUnauthorized: false, +}; + +server.listen(0, common.mustCall(() => { + const client1 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'context1', + }, common.mustCall(() => { + client1.end(); + })); + + const client2 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'context2', + }, common.mustCall(() => { + client2.end(); + })); + + const client3 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'unknowncontext', + }, common.mustCall(() => { + client3.end(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-alert-handling.js b/test/js/node/test/parallel/test-tls-alert-handling.js new file mode 100644 index 0000000000..bd86149bc5 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-alert-handling.js @@ -0,0 +1,96 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI'); + +const assert = require('assert'); +const net = require('net'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +let clientClosed = false; +let errorReceived = false; +function canCloseServer() { + return clientClosed && errorReceived; +} + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`, 'utf-8'); +} + +const opts = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert') +}; + +const max_iter = 20; +let iter = 0; + +const errorHandler = common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_SSL_WRONG_VERSION_NUMBER'); + assert.strictEqual(err.library, 'SSL routines'); + if (!common.hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); + assert.strictEqual(err.reason, 'wrong version number'); + errorReceived = true; + if (canCloseServer()) + server.close(); +}); +const server = tls.createServer(opts, common.mustCall(function(s) { + s.pipe(s); + s.on('error', errorHandler); +}, 2)); + +server.listen(0, common.mustCall(function() { + sendClient(); +})); + +server.on('tlsClientError', common.mustNotCall()); + +server.on('error', common.mustNotCall()); + +function sendClient() { + const client = tls.connect(server.address().port, { + rejectUnauthorized: false + }); + client.on('data', common.mustCall(function() { + if (iter++ === 2) sendBADTLSRecord(); + if (iter < max_iter) { + client.write('a'); + return; + } + client.end(); + }, max_iter)); + client.write('a', common.mustCall()); + client.on('error', common.mustNotCall()); + client.on('close', common.mustCall(function() { + clientClosed = true; + if (canCloseServer()) + server.close(); + })); +} + + +function sendBADTLSRecord() { + const BAD_RECORD = Buffer.from([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + const socket = net.connect(server.address().port); + const client = tls.connect({ + socket: socket, + rejectUnauthorized: false + }, common.mustCall(function() { + client.write('x'); + client.on('data', (data) => { + socket.end(BAD_RECORD); + }); + })); + client.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'); + assert.strictEqual(err.library, 'SSL routines'); + if (!common.hasOpenSSL3) + assert.strictEqual(err.function, 'ssl3_read_bytes'); + assert.strictEqual(err.reason, 'tlsv1 alert protocol version'); + })); +} diff --git a/test/js/node/test/parallel/test-tls-alert.js b/test/js/node/test/parallel/test-tls-alert.js new file mode 100644 index 0000000000..04000771aa --- /dev/null +++ b/test/js/node/test/parallel/test-tls-alert.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const { execFile } = require('child_process'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const server = tls.Server({ + secureProtocol: 'TLSv1_2_server_method', + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert') +}, null).listen(0, common.mustCall(() => { + const args = ['s_client', '-quiet', '-tls1_1', + '-cipher', (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-connect', `127.0.0.1:${server.address().port}`]; + + execFile(common.opensslCli, args, common.mustCall((err, _, stderr) => { + assert.strictEqual(err.code, 1); + assert.match(stderr, /SSL alert number 70/); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-ca-concat.js b/test/js/node/test/parallel/test-tls-ca-concat.js new file mode 100644 index 0000000000..38a6a4dfec --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ca-concat.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// Check ca option can contain concatenated certs by prepending an unrelated +// non-CA cert and showing that agent6's CA root is still found. + +const { + connect, keys +} = require(fixtures.path('tls-connect')); + +connect({ + client: { + checkServerIdentity: (servername, cert) => { }, + ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, + }, + server: { + cert: keys.agent6.cert, + key: keys.agent6.key, + }, +}, common.mustSucceed((pair, cleanup) => { + return cleanup(); +})); diff --git a/test/js/node/test/parallel/tls-cert-ext-encoding.test.js b/test/js/node/test/parallel/test-tls-cert-ext-encoding.js similarity index 76% rename from test/js/node/test/parallel/tls-cert-ext-encoding.test.js rename to test/js/node/test/parallel/test-tls-cert-ext-encoding.js index ed33875b57..4556b57918 100644 --- a/test/js/node/test/parallel/tls-cert-ext-encoding.test.js +++ b/test/js/node/test/parallel/test-tls-cert-ext-encoding.js @@ -1,9 +1,17 @@ -//#FILE: test-tls-cert-ext-encoding.js -//#SHA1: 235c886145290bd46f4f40110eace31e4cbcf55f -//----------------- -"use strict"; +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); -const tls = require("tls"); +if (common.hasOpenSSL3) + // TODO(danbev) This test fails with the following error: + // error:0D00008F:asn1 encoding routines::no matching choice type + // + // I've not been able to figure out the reason for this but there + // is a note in https://wiki.openssl.org/index.php/OpenSSL_3.0 which + // indicates that this might not work at the moment: + // "OCSP, PEM, ASN.1 have some very limited library context support" + common.skip('when using OpenSSL 3.x'); // NOTE: This certificate is hand-generated, hence it is not located in // `test/fixtures/keys` to avoid confusion. @@ -57,34 +65,25 @@ f79uOowv3lLTzQ9na5EThA0tp8d837hdYrrIHh5cfTqBDxG0Tu8= -----END CERTIFICATE----- `; +const tls = require('tls'); + const options = { key: pem, cert: pem, }; -test("TLS certificate extension encoding", async () => { - const server = tls.createServer(options, socket => { - socket.end(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const client = tls.connect( - { - port: server.address().port, - rejectUnauthorized: false, - }, - () => { - // This should not crash process: - client.getPeerCertificate(); - - server.close(); - client.end(); - resolve(); - }, - ); - }); - }); +const server = tls.createServer(options, (socket) => { + socket.end(); }); +server.listen(0, common.mustCall(function() { + const client = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(() => { + // This should not crash process: + client.getPeerCertificate(); -//<#END_FILE: test-tls-cert-ext-encoding.js + server.close(); + client.end(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-cert-regression.js b/test/js/node/test/parallel/test-tls-cert-regression.js new file mode 100644 index 0000000000..478402772e --- /dev/null +++ b/test/js/node/test/parallel/test-tls-cert-regression.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); + +const cert = +`-----BEGIN CERTIFICATE----- +MIIDNDCCAp2gAwIBAgIJAJvXLQpGPpm7MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIEwdHd3luZWRkMREwDwYDVQQHEwhXYXVuZmF3cjEUMBIG +A1UEChMLQWNrbmFjayBMdGQxEjAQBgNVBAsTCVRlc3QgQ2VydDESMBAGA1UEAxMJ +bG9jYWxob3N0MB4XDTA5MTEwMjE5MzMwNVoXDTEwMTEwMjE5MzMwNVowcDELMAkG +A1UEBhMCR0IxEDAOBgNVBAgTB0d3eW5lZGQxETAPBgNVBAcTCFdhdW5mYXdyMRQw +EgYDVQQKEwtBY2tuYWNrIEx0ZDESMBAGA1UECxMJVGVzdCBDZXJ0MRIwEAYDVQQD +Ewlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANdym7nGe2yw +6LlJfJrQtC5TmKOGrSXiyolYCbGOy4xZI4KD31d3097jhlQFJyF+10gwkE62DuJe +fLvBZDUsvLe1R8bzlVhZnBVn+3QJyUIWQAL+DsRj8P3KoD7k363QN5dIaA1GOAg2 +vZcPy1HCUsvOgvDXGRUCZqNLAyt+h/cpAgMBAAGjgdUwgdIwHQYDVR0OBBYEFK4s +VBV4shKUj3UX/fvSJnFaaPBjMIGiBgNVHSMEgZowgZeAFK4sVBV4shKUj3UX/fvS +JnFaaPBjoXSkcjBwMQswCQYDVQQGEwJHQjEQMA4GA1UECBMHR3d5bmVkZDERMA8G +A1UEBxMIV2F1bmZhd3IxFDASBgNVBAoTC0Fja25hY2sgTHRkMRIwEAYDVQQLEwlU +ZXN0IENlcnQxEjAQBgNVBAMTCWxvY2FsaG9zdIIJAJvXLQpGPpm7MAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAFxR7BA1mUlsYqPiogtxSIfLzHWh+s0bJ +SBuhNrHes4U8QxS8+x/KWjd/81gzsf9J1C2VzTlFaydAgigz3SkQYgs+TMnFkT2o +9jqoJrcdf4WpZ2DQXUALaZgwNzPumMUSx8Ac5gO+BY/RHyP6fCodYvdNwyKslnI3 +US7eCSHZsVo= +-----END CERTIFICATE-----`; + +const key = +`-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDXcpu5xntssOi5SXya0LQuU5ijhq0l4sqJWAmxjsuMWSOCg99X +d9Pe44ZUBSchftdIMJBOtg7iXny7wWQ1LLy3tUfG85VYWZwVZ/t0CclCFkAC/g7E +Y/D9yqA+5N+t0DeXSGgNRjgINr2XD8tRwlLLzoLw1xkVAmajSwMrfof3KQIDAQAB +AoGBAIBHR/tT93ce2mJAJAXV0AJpWc+7x2pwX2FpXtQujnlxNZhnRlrBCRCD7h4m +t0bVS/86kyGaesBDvAbavfx/N5keYzzmmSp5Ht8IPqKPydGWdigk4x90yWvktai7 +dWuRKF94FXr0GUuBONb/dfHdp4KBtzN7oIF9WydYGGXA9ZmBAkEA8/k01bfwQZIu +AgcdNEM94Zcug1gSspXtUu8exNQX4+PNVbadghZb1+OnUO4d3gvWfqvAnaXD3KV6 +N4OtUhQQ0QJBAOIRbKMfaymQ9yE3CQQxYfKmEhHXWARXVwuYqIFqjmhSjSXx0l/P +7mSHz1I9uDvxkJev8sQgu1TKIyTOdqPH1tkCQQDPa6H1yYoj1Un0Q2Qa2Mg1kTjk +Re6vkjPQ/KcmJEOjZjtekgFbZfLzmwLXFXqjG2FjFFaQMSxR3QYJSJQEYjbhAkEA +sy7OZcjcXnjZeEkv61Pc57/7qIp/6Aj2JGnefZ1gvI1Z9Q5kCa88rA/9Iplq8pA4 +ZBKAoDW1ZbJGAsFmxc/6mQJAdPilhci0qFN86IGmf+ZBnwsDflIwHKDaVofti4wQ +sPWhSOb9VQjMXekI4Y2l8fqAVTS2Fn6+8jkVKxXBywSVCw== +-----END RSA PRIVATE KEY-----`; + +function test(cert, key, cb) { + const server = tls.createServer({ + cert, + key + }).listen(0, function() { + server.close(cb); + }); +} + +test(cert, key, common.mustCall(function() { + test(Buffer.from(cert), Buffer.from(key), common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-tls-client-abort.js b/test/js/node/test/parallel/test-tls-client-abort.js new file mode 100644 index 0000000000..50c9a4b324 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-abort.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); + +const conn = tls.connect({ cert, key, port: 0 }, common.mustNotCall()); +conn.on('error', function() {}); +conn.destroy(); diff --git a/test/js/node/test/parallel/http-request-end.test.js b/test/js/node/test/parallel/test-tls-client-destroy-soon.js similarity index 50% rename from test/js/node/test/parallel/http-request-end.test.js rename to test/js/node/test/parallel/test-tls-client-destroy-soon.js index 9ed911d1ad..1d49a6094b 100644 --- a/test/js/node/test/parallel/http-request-end.test.js +++ b/test/js/node/test/parallel/test-tls-client-destroy-soon.js @@ -1,6 +1,3 @@ -//#FILE: test-http-request-end.js -//#SHA1: e86769441d8d182d32d60d4631e32fbcc2675ed9 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,53 +19,49 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +// Create an ssl server. First connection, validate that not resume. +// Cache session and close connection. Use session on second connection. +// ASSERT resumption. -const expected = "Post Body For Test"; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); -test("http request end", async () => { - const server = http.Server((req, res) => { - let result = ""; +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); - req.setEncoding("utf8"); - req.on("data", chunk => { - result += chunk; +const options = { + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') +}; + +const big = Buffer.alloc(2 * 1024 * 1024, 'Y'); + +// create server +const server = tls.createServer(options, common.mustCall(function(socket) { + socket.end(big); + socket.destroySoon(); +})); + +// start listening +server.listen(0, common.mustCall(function() { + const client = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(function() { + let bytesRead = 0; + + client.on('readable', function() { + const d = client.read(); + if (d) + bytesRead += d.length; }); - req.on("end", () => { - expect(result).toBe(expected); - res.writeHead(200); - res.end("hello world\n"); + client.on('end', common.mustCall(function() { server.close(); - }); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http - .request( - { - port: server.address().port, - path: "/", - method: "POST", - }, - res => { - expect(res.statusCode).toBe(200); - res.resume(); - resolve(); - }, - ) - .on("error", e => { - console.error(e.message); - process.exit(1); - }); - - const result = req.end(expected); - - expect(req).toBe(result); - }); - }); -}); - -//<#END_FILE: test-http-request-end.js + assert.strictEqual(big.length, bytesRead); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-client-renegotiation-limit.js b/test/js/node/test/parallel/test-tls-client-renegotiation-limit.js new file mode 100644 index 0000000000..71d7a85bae --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-renegotiation-limit.js @@ -0,0 +1,101 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +// Renegotiation as a protocol feature was dropped after TLS1.2. +tls.DEFAULT_MAX_VERSION = 'TLSv1.2'; + +// Renegotiation limits to test +const LIMITS = [0, 1, 2, 3, 5, 10, 16]; + +{ + let n = 0; + function next() { + if (n >= LIMITS.length) return; + tls.CLIENT_RENEG_LIMIT = LIMITS[n++]; + test(next); + } + next(); +} + +function test(next) { + const options = { + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem'), + }; + + const server = tls.createServer(options, (conn) => { + conn.on('error', (err) => { + console.error(`Caught exception: ${err}`); + assert.match(err.message, /TLS session renegotiation attack/); + conn.destroy(); + }); + conn.pipe(conn); + }); + + server.listen(0, () => { + const options = { + host: server.address().host, + port: server.address().port, + rejectUnauthorized: false, + }; + const client = tls.connect(options, spam); + + let renegs = 0; + + client.on('close', () => { + assert.strictEqual(renegs, tls.CLIENT_RENEG_LIMIT + 1); + server.close(); + process.nextTick(next); + }); + + client.on('error', (err) => { + console.log('CLIENT ERR', err); + throw err; + }); + + client.on('close', (hadErr) => { + assert.strictEqual(hadErr, false); + }); + + // Simulate renegotiation attack + function spam() { + client.write(''); + client.renegotiate({}, (err) => { + assert.ifError(err); + assert.ok(renegs <= tls.CLIENT_RENEG_LIMIT); + spam(); + }); + renegs++; + } + }); +} diff --git a/test/js/node/test/parallel/test-tls-client-verify.js b/test/js/node/test/parallel/test-tls-client-verify.js new file mode 100644 index 0000000000..a8de1078bf --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-verify.js @@ -0,0 +1,144 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const testCases = [ + { ca: ['ca1-cert'], + key: 'agent2-key', + cert: 'agent2-cert', + servers: [ + { ok: true, key: 'agent1-key', cert: 'agent1-cert' }, + { ok: false, key: 'agent2-key', cert: 'agent2-cert' }, + { ok: false, key: 'agent3-key', cert: 'agent3-cert' }, + ] }, + + { ca: [], + key: 'agent2-key', + cert: 'agent2-cert', + servers: [ + { ok: false, key: 'agent1-key', cert: 'agent1-cert' }, + { ok: false, key: 'agent2-key', cert: 'agent2-cert' }, + { ok: false, key: 'agent3-key', cert: 'agent3-cert' }, + ] }, + + { ca: ['ca1-cert', 'ca2-cert'], + key: 'agent2-key', + cert: 'agent2-cert', + servers: [ + { ok: true, key: 'agent1-key', cert: 'agent1-cert' }, + { ok: false, key: 'agent2-key', cert: 'agent2-cert' }, + { ok: true, key: 'agent3-key', cert: 'agent3-cert' }, + ] }, +]; + + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +let successfulTests = 0; + +function testServers(index, servers, clientOptions, cb) { + const serverOptions = servers[index]; + if (!serverOptions) { + cb(); + return; + } + + const ok = serverOptions.ok; + + if (serverOptions.key) { + serverOptions.key = loadPEM(serverOptions.key); + } + + if (serverOptions.cert) { + serverOptions.cert = loadPEM(serverOptions.cert); + } + + const server = tls.createServer(serverOptions, common.mustCall(function(s) { + s.end('hello world\n'); + })); + + server.listen(0, common.mustCall(function() { + let b = ''; + + console.error('connecting...'); + clientOptions.port = this.address().port; + const client = tls.connect(clientOptions, common.mustCall(function() { + const authorized = client.authorized || + (client.authorizationError === 'ERR_TLS_CERT_ALTNAME_INVALID'); + + console.error(`expected: ${ok} authed: ${authorized}`); + + assert.strictEqual(authorized, ok); + server.close(); + })); + + client.on('data', function(d) { + b += d.toString(); + }); + + client.on('end', common.mustCall(function() { + assert.strictEqual(b, 'hello world\n'); + })); + + client.on('close', common.mustCall(function() { + testServers(index + 1, servers, clientOptions, cb); + })); + })); +} + + +function runTest(testIndex) { + const tcase = testCases[testIndex]; + if (!tcase) return; + + const clientOptions = { + port: undefined, + ca: tcase.ca.map(loadPEM), + key: loadPEM(tcase.key), + cert: loadPEM(tcase.cert), + rejectUnauthorized: false + }; + + + testServers(0, tcase.servers, clientOptions, common.mustCall(function() { + successfulTests++; + runTest(testIndex + 1); + })); +} + + +runTest(0); + + +process.on('exit', function() { + console.log(`successful tests: ${successfulTests}`); + assert.strictEqual(successfulTests, testCases.length); +}); diff --git a/test/js/node/test/parallel/test-tls-connect-address-family.js b/test/js/node/test/parallel/test-tls-connect-address-family.js new file mode 100644 index 0000000000..083208cc1d --- /dev/null +++ b/test/js/node/test/parallel/test-tls-connect-address-family.js @@ -0,0 +1,49 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const tls = require('tls'); +const dns = require('dns'); + +function runTest() { + tls.createServer({ + cert: fixtures.readKey('agent1-cert.pem'), + key: fixtures.readKey('agent1-key.pem'), + }).on('connection', common.mustCall(function() { + this.close(); + })).listen(0, '::1', common.mustCall(function() { + const options = { + host: 'localhost', + port: this.address().port, + family: 6, + rejectUnauthorized: false, + }; + // Will fail with ECONNREFUSED if the address family is not honored. + tls.connect(options).once('secureConnect', common.mustCall(function() { + assert.strictEqual(this.remoteAddress, '::1'); + this.destroy(); + })); + })); +} + +dns.lookup('localhost', { + family: 6, all: true +}, common.mustCall((err, addresses) => { + if (err) { + if (err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN') + common.skip('localhost does not resolve to ::1'); + + throw err; + } + + if (addresses.some((val) => val.address === '::1')) + runTest(); + else + common.skip('localhost does not resolve to ::1'); +})); diff --git a/test/js/node/test/parallel/test-tls-connect-no-host.js b/test/js/node/test/parallel/test-tls-connect-no-host.js new file mode 100644 index 0000000000..97b95332c4 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-connect-no-host.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const assert = require('assert'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); + +// https://github.com/nodejs/node/issues/1489 +// tls.connect(options) with no options.host should accept a cert with +// CN:'localhost' +const server = tls.createServer({ + key, + cert +}).listen(0, common.mustCall(function() { + const socket = tls.connect({ + port: this.address().port, + ca: cert, + // No host set here. 'localhost' is the default, + // but tls.checkServerIdentity() breaks before the fix with: + // Error: Hostname/IP doesn't match certificate's altnames: + // "Host: undefined. is not cert's CN: localhost" + }, common.mustCall(function() { + assert(socket.authorized); + socket.destroy(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-connect-secure-context.js b/test/js/node/test/parallel/test-tls-connect-secure-context.js new file mode 100644 index 0000000000..31941656c0 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-connect-secure-context.js @@ -0,0 +1,53 @@ +'use strict'; +require('../common'); + +// Verify connection with explicitly created client SecureContext. + +const fixtures = require('../common/fixtures'); +const { + assert, connect, keys, tls +} = require(fixtures.path('tls-connect')); + +connect({ + client: { + servername: 'agent1', + secureContext: tls.createSecureContext({ + ca: keys.agent1.ca, + }), + }, + server: { + cert: keys.agent1.cert, + key: keys.agent1.key, + }, +}, function(err, pair, cleanup) { + assert.ifError(err); + return cleanup(); +}); + +connect({ + client: { + servername: 'agent1', + secureContext: tls.createSecureContext({ + ca: keys.agent1.ca, + ciphers: null, + clientCertEngine: null, + crl: null, + dhparam: null, + passphrase: null, + pfx: null, + privateKeyIdentifier: null, + privateKeyEngine: null, + sessionIdContext: null, + sessionTimeout: null, + sigalgs: null, + ticketKeys: null, + }), + }, + server: { + cert: keys.agent1.cert, + key: keys.agent1.key, + }, +}, function(err, pair, cleanup) { + assert.ifError(err); + return cleanup(); +}); diff --git a/test/js/node/test/parallel/test-tls-dhe.js b/test/js/node/test/parallel/test-tls-dhe.js new file mode 100644 index 0000000000..46779b09ff --- /dev/null +++ b/test/js/node/test/parallel/test-tls-dhe.js @@ -0,0 +1,112 @@ +// Flags: --no-warnings +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const { X509Certificate } = require('crypto'); +const { once } = require('events'); +const tls = require('tls'); +const { execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const key = fixtures.readKey('agent2-key.pem'); +const cert = fixtures.readKey('agent2-cert.pem'); + +// Prefer DHE over ECDHE when possible. +const dheCipher = 'DHE-RSA-AES128-SHA256'; +const ecdheCipher = 'ECDHE-RSA-AES128-SHA256'; +const ciphers = `${dheCipher}:${ecdheCipher}`; + +// Test will emit a warning because the DH parameter size is < 2048 bits +common.expectWarning('SecurityWarning', + 'DH parameter is less than 2048 bits'); + +function loadDHParam(n) { + const keyname = `dh${n}.pem`; + return fixtures.readKey(keyname); +} + +function test(dhparam, keylen, expectedCipher) { + const options = { + key, + cert, + ciphers, + dhparam, + maxVersion: 'TLSv1.2', + }; + + const server = tls.createServer(options, (conn) => conn.end()); + + server.listen(0, '127.0.0.1', common.mustCall(() => { + const args = ['s_client', '-connect', `127.0.0.1:${server.address().port}`, + '-cipher', `${ciphers}:@SECLEVEL=1`]; + + execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + assert(keylen === null || + stdout.includes(`Server Temp Key: DH, ${keylen} bits`)); + assert(stdout.includes(`Cipher : ${expectedCipher}`)); + server.close(); + })); + })); + + return once(server, 'close'); +} + +function testCustomParam(keylen, expectedCipher) { + const dhparam = loadDHParam(keylen); + if (keylen === 'error') keylen = null; + return test(dhparam, keylen, expectedCipher); +} + +(async () => { + // By default, DHE is disabled while ECDHE is enabled. + for (const dhparam of [undefined, null]) { + await test(dhparam, null, ecdheCipher); + } + + // The DHE parameters selected by OpenSSL depend on the strength of the + // certificate's key. For this test, we can assume that the modulus length + // of the certificate's key is equal to the size of the DHE parameter, but + // that is really only true for a few modulus lengths. + const { + publicKey: { asymmetricKeyDetails: { modulusLength } } + } = new X509Certificate(cert); + await test('auto', modulusLength, dheCipher); + + assert.throws(() => { + testCustomParam(512); + }, /DH parameter is less than 1024 bits/); + + // Custom DHE parameters are supported (but discouraged). + await testCustomParam(1024, dheCipher); + await testCustomParam(2048, dheCipher); + + // Invalid DHE parameters are discarded. ECDHE remains enabled. + await testCustomParam('error', ecdheCipher); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-tls-ecdh-auto.js b/test/js/node/test/parallel/test-tls-ecdh-auto.js new file mode 100644 index 0000000000..11c588d8ac --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ecdh-auto.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that the value "auto" on ecdhCurve option is +// supported to enable automatic curve selection in TLS server. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); +const { execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const options = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', + ecdhCurve: 'auto', + maxVersion: 'TLSv1.2', +}; + +const reply = 'I AM THE WALRUS'; // Something recognizable + +const server = tls.createServer(options, (conn) => { + conn.end(reply); +}).listen(0, common.mustCall(() => { + const args = ['s_client', + '-cipher', `${options.ciphers}`, + '-connect', `127.0.0.1:${server.address().port}`]; + + execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + assert(stdout.includes(reply)); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-ecdh-multiple.js b/test/js/node/test/parallel/test-tls-ecdh-multiple.js new file mode 100644 index 0000000000..5bf119f48b --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ecdh-multiple.js @@ -0,0 +1,61 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that ecdhCurve option of TLS server supports colon +// separated ECDH curve names as value. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); +const { execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const options = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', + ecdhCurve: 'secp256k1:prime256v1:secp521r1', + maxVersion: 'TLSv1.2', +}; + +const reply = 'I AM THE WALRUS'; // Something recognizable + +const server = tls.createServer(options, (conn) => { + conn.end(reply); +}).listen(0, common.mustCall(() => { + const args = ['s_client', + '-cipher', `${options.ciphers}`, + '-connect', `127.0.0.1:${server.address().port}`]; + + execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + assert(stdout.includes(reply)); + server.close(); + })); +})); + +{ + // Some unsupported curves. + const unsupportedCurves = [ + 'wap-wsg-idm-ecid-wtls1', + 'c2pnb163v1', + 'prime192v3', + ]; + + // Brainpool is not supported in FIPS mode. + if (common.hasFipsCrypto) + unsupportedCurves.push('brainpoolP256r1'); + + unsupportedCurves.forEach((ecdhCurve) => { + assert.throws(() => tls.createServer({ ecdhCurve }), + /Error: Failed to set ECDH curve/); + }); +} diff --git a/test/js/node/test/parallel/https-byteswritten.test.js b/test/js/node/test/parallel/test-tls-ecdh.js similarity index 54% rename from test/js/node/test/parallel/https-byteswritten.test.js rename to test/js/node/test/parallel/test-tls-ecdh.js index c695fa7027..8c879f850c 100644 --- a/test/js/node/test/parallel/https-byteswritten.test.js +++ b/test/js/node/test/parallel/test-tls-ecdh.js @@ -1,6 +1,3 @@ -//#FILE: test-https-byteswritten.js -//#SHA1: 8b808da3e55de553190426095aee298ec7d5df36 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,37 +19,41 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const fixtures = require("../common/fixtures"); -const https = require("https"); +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); + +const exec = require('child_process').exec; const options = { - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), + ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', + ecdhCurve: 'prime256v1', + maxVersion: 'TLSv1.2' }; -const body = "hello world\n"; +const reply = 'I AM THE WALRUS'; // Something recognizable -test("HTTPS server bytesWritten", async () => { - const httpsServer = https.createServer(options, (req, res) => { - res.on("finish", () => { - expect(typeof req.connection.bytesWritten).toBe("number"); - expect(req.connection.bytesWritten).toBeGreaterThan(0); - httpsServer.close(); - }); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end(body); - }); +const server = tls.createServer(options, common.mustCall(function(conn) { + conn.end(reply); +})); - await new Promise(resolve => { - httpsServer.listen(0, () => { - https.get({ - port: httpsServer.address().port, - rejectUnauthorized: false, - }); - resolve(); - }); - }); -}); +server.listen(0, '127.0.0.1', common.mustCall(function() { + const cmd = `"${common.opensslCli}" s_client -cipher ${ + options.ciphers} -connect 127.0.0.1:${this.address().port}`; -//<#END_FILE: test-https-byteswritten.js + exec(cmd, common.mustSucceed((stdout, stderr) => { + assert(stdout.includes(reply)); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-env-extra-ca-no-crypto.js b/test/js/node/test/parallel/test-tls-env-extra-ca-no-crypto.js new file mode 100644 index 0000000000..6f2aca505e --- /dev/null +++ b/test/js/node/test/parallel/test-tls-env-extra-ca-no-crypto.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { fork } = require('child_process'); + +// This test ensures that trying to load extra certs won't throw even when +// there is no crypto support, i.e., built with "./configure --without-ssl". +if (process.argv[2] === 'child') { + // exit +} else { + const NODE_EXTRA_CA_CERTS = fixtures.path('keys', 'ca1-cert.pem'); + + fork( + __filename, + ['child'], + { env: { ...process.env, NODE_EXTRA_CA_CERTS } }, + ).on('exit', common.mustCall(function(status) { + // Client did not succeed in connecting + assert.strictEqual(status, 0); + })); +} diff --git a/test/js/node/test/parallel/test-tls-fast-writing.js b/test/js/node/test/parallel/test-tls-fast-writing.js new file mode 100644 index 0000000000..4718acf285 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-fast-writing.js @@ -0,0 +1,76 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); + +const options = { key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt'), + ca: [ fixtures.readKey('rsa_ca.crt') ] }; + +const server = tls.createServer(options, onconnection); +let gotChunk = false; +let gotDrain = false; + +function onconnection(conn) { + conn.on('data', function(c) { + if (!gotChunk) { + gotChunk = true; + console.log('ok - got chunk'); + } + + // Just some basic sanity checks. + assert(c.length); + assert(Buffer.isBuffer(c)); + + if (gotDrain) + process.exit(0); + }); +} + +server.listen(0, function() { + const chunk = Buffer.alloc(1024, 'x'); + const opt = { port: this.address().port, rejectUnauthorized: false }; + const conn = tls.connect(opt, function() { + conn.on('drain', ondrain); + write(); + }); + function ondrain() { + if (!gotDrain) { + gotDrain = true; + console.log('ok - got drain'); + } + if (gotChunk) + process.exit(0); + write(); + } + + function write() { + // This needs to return false eventually + while (false !== conn.write(chunk)); + } +}); diff --git a/test/js/node/test/parallel/tls-inception.test.js b/test/js/node/test/parallel/test-tls-inception.js similarity index 51% rename from test/js/node/test/parallel/tls-inception.test.js rename to test/js/node/test/parallel/test-tls-inception.js index 6aef54d4c3..7310308e6f 100644 --- a/test/js/node/test/parallel/tls-inception.test.js +++ b/test/js/node/test/parallel/test-tls-inception.js @@ -1,6 +1,3 @@ -//#FILE: test-tls-inception.js -//#SHA1: 410893674973cae3603656c032a18e01a1cd759a -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,77 +19,68 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); -const crypto = require("crypto"); -const fixtures = require("../common/fixtures"); -const tls = require("tls"); -const net = require("net"); +if (!common.hasCrypto) + common.skip('missing crypto'); -if (!crypto.getCurves().includes("prime256v1")) { - test.skip("missing crypto support"); -} +const assert = require('assert'); +const tls = require('tls'); + +const net = require('net'); const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt') }; -const body = "A".repeat(40000); - -test("TLS inception", async () => { - // the "proxy" server - const a = tls.createServer(options, socket => { - const myOptions = { - host: "127.0.0.1", - port: b.address().port, - rejectUnauthorized: false, - }; - const dest = net.connect(myOptions); - dest.pipe(socket); - socket.pipe(dest); - - dest.on("end", () => { - socket.destroy(); - }); - }); - - // the "target" server - const b = tls.createServer(options, socket => { - socket.end(body); - }); - - await new Promise(resolve => { - a.listen(0, () => { - b.listen(0, resolve); - }); - }); +const body = 'A'.repeat(40000); +// the "proxy" server +const a = tls.createServer(options, function(socket) { const myOptions = { - host: "127.0.0.1", - port: a.address().port, - rejectUnauthorized: false, + host: '127.0.0.1', + port: b.address().port, + rejectUnauthorized: false }; + const dest = net.connect(myOptions); + dest.pipe(socket); + socket.pipe(dest); - return new Promise(resolve => { - const socket = tls.connect(myOptions); - const ssl = tls.connect({ - socket: socket, - rejectUnauthorized: false, - }); - ssl.setEncoding("utf8"); - let buf = ""; - ssl.on("data", data => { - buf += data; - }); - ssl.on("end", () => { - expect(buf).toBe(body); - ssl.end(); - a.close(); - b.close(); - resolve(); - }); + dest.on('end', function() { + socket.destroy(); }); }); -//<#END_FILE: test-tls-inception.js +// the "target" server +const b = tls.createServer(options, function(socket) { + socket.end(body); +}); + +a.listen(0, function() { + b.listen(0, function() { + const myOptions = { + host: '127.0.0.1', + port: a.address().port, + rejectUnauthorized: false + }; + const socket = tls.connect(myOptions); + const ssl = tls.connect({ + socket: socket, + rejectUnauthorized: false + }); + ssl.setEncoding('utf8'); + let buf = ''; + ssl.on('data', function(data) { + buf += data; + }); + ssl.on('end', common.mustCall(function() { + assert.strictEqual(buf, body); + ssl.end(); + a.close(); + b.close(); + })); + }); +}); diff --git a/test/js/node/test/parallel/test-tls-multiple-cas-as-string.js b/test/js/node/test/parallel/test-tls-multiple-cas-as-string.js new file mode 100644 index 0000000000..679d6b6c4c --- /dev/null +++ b/test/js/node/test/parallel/test-tls-multiple-cas-as-string.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +// Verify that multiple CA certificates can be provided, and that for +// convenience that can also be in newline-separated strings. + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const ca1 = fixtures.readKey('ca1-cert.pem', 'utf8'); +const ca2 = fixtures.readKey('ca2-cert.pem', 'utf8'); +const cert = fixtures.readKey('agent3-cert.pem', 'utf8'); +const key = fixtures.readKey('agent3-key.pem', 'utf8'); + +function test(ca) { + const server = tls.createServer({ ca, cert, key }); + + server.addContext('agent3', { ca, cert, key }); + + const host = common.localhostIPv4; + server.listen(0, host, common.mustCall(() => { + const socket = tls.connect({ + servername: 'agent3', + host, + port: server.address().port, + ca + }, common.mustCall(() => { + socket.end(); + })); + + socket.on('close', () => { + server.close(); + }); + })); +} + +// `ca1` is not actually necessary for the certificate validation -- maybe +// the fixtures should be written in a way that requires it? +test([ca1, ca2]); +test(`${ca1}\n${ca2}`); diff --git a/test/js/node/test/parallel/test-tls-net-connect-prefer-path.js b/test/js/node/test/parallel/test-tls-net-connect-prefer-path.js new file mode 100644 index 0000000000..cefeb5d471 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-net-connect-prefer-path.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// This tests that both tls and net will ignore host and port if path is +// provided. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const tls = require('tls'); +const net = require('net'); +const assert = require('assert'); + +function libName(lib) { + return lib === net ? 'net' : 'tls'; +} + +function mkServer(lib, tcp, cb) { + const handler = (socket) => { + socket.write(`${libName(lib)}:${ + server.address().port || server.address() + }`); + socket.end(); + }; + const args = [handler]; + if (lib === tls) { + args.unshift({ + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem') + }); + } + const server = lib.createServer(...args); + server.listen(tcp ? 0 : common.PIPE, common.mustCall(() => cb(server))); +} + +function testLib(lib, cb) { + mkServer(lib, true, (tcpServer) => { + mkServer(lib, false, (unixServer) => { + const client = lib.connect({ + path: unixServer.address(), + port: tcpServer.address().port, + host: 'localhost', + rejectUnauthorized: false + }, () => { + const bufs = []; + client.on('data', common.mustCall((d) => { + bufs.push(d); + })); + client.on('end', common.mustCall(() => { + const resp = Buffer.concat(bufs).toString(); + assert.strictEqual(resp, `${libName(lib)}:${unixServer.address()}`); + tcpServer.close(); + unixServer.close(); + cb(); + })); + }); + }); + }); +} + +testLib(net, common.mustCall(() => testLib(tls, common.mustCall()))); diff --git a/test/js/node/test/parallel/test-tls-no-sslv3.js b/test/js/node/test/parallel/test-tls-no-sslv3.js new file mode 100644 index 0000000000..9282beb4bd --- /dev/null +++ b/test/js/node/test/parallel/test-tls-no-sslv3.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (common.opensslCli === false) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const tls = require('tls'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); +const server = tls.createServer({ cert, key }, common.mustNotCall()); +const errors = []; +let stderr = ''; + +server.listen(0, '127.0.0.1', function() { + const address = `${this.address().address}:${this.address().port}`; + const args = ['s_client', + '-ssl3', + '-connect', address]; + + const client = spawn(common.opensslCli, args, { stdio: 'pipe' }); + client.stdout.pipe(process.stdout); + client.stderr.pipe(process.stderr); + client.stderr.setEncoding('utf8'); + client.stderr.on('data', (data) => stderr += data); + + client.once('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 1); + server.close(); + })); +}); + +server.on('tlsClientError', (err) => errors.push(err)); + +process.on('exit', function() { + if (/[Uu]nknown option:? -ssl3/.test(stderr)) { + common.printSkipMessage('`openssl s_client -ssl3` not supported.'); + } else { + assert.strictEqual(errors.length, 1); + assert(/:version too low/.test(errors[0].message)); + } +}); diff --git a/test/js/node/test/parallel/test-tls-ocsp-callback.js b/test/js/node/test/parallel/test-tls-ocsp-callback.js new file mode 100644 index 0000000000..04a60a0890 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ocsp-callback.js @@ -0,0 +1,113 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); + +const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; + +const pfx = fixtures.readKey('agent1.pfx'); +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); +const ca = fixtures.readKey('ca1-cert.pem'); + +function test(testOptions, cb) { + const options = { + key, + cert, + ca: [ca] + }; + const requestCount = testOptions.response ? 0 : 1; + + if (!testOptions.ocsp) + assert.strictEqual(testOptions.response, undefined); + + if (testOptions.pfx) { + delete options.key; + delete options.cert; + options.pfx = testOptions.pfx; + options.passphrase = testOptions.passphrase; + } + + const server = tls.createServer(options, common.mustCall((cleartext) => { + cleartext.on('error', function(er) { + // We're ok with getting ECONNRESET in this test, but it's + // timing-dependent, and thus unreliable. Any other errors + // are just failures, though. + if (er.code !== 'ECONNRESET') + throw er; + }); + cleartext.end(); + }, requestCount)); + + if (!testOptions.ocsp) + server.on('OCSPRequest', common.mustNotCall()); + else + server.on('OCSPRequest', common.mustCall((cert, issuer, callback) => { + assert.ok(Buffer.isBuffer(cert)); + assert.ok(Buffer.isBuffer(issuer)); + + // Callback a little later to ensure that async really works. + return setTimeout(callback, 100, null, testOptions.response ? + Buffer.from(testOptions.response) : null); + })); + + server.listen(0, function() { + const client = tls.connect({ + port: this.address().port, + requestOCSP: testOptions.ocsp, + secureOptions: testOptions.ocsp ? 0 : SSL_OP_NO_TICKET, + rejectUnauthorized: false + }, common.mustCall(requestCount)); + + client.on('OCSPResponse', common.mustCall((resp) => { + if (testOptions.response) { + assert.strictEqual(resp.toString(), testOptions.response); + client.destroy(); + } else { + assert.strictEqual(resp, null); + } + }, testOptions.ocsp === false ? 0 : 1)); + + client.on('close', common.mustCall(() => { + server.close(cb); + })); + }); +} + +test({ ocsp: true, response: false }); +test({ ocsp: true, response: 'hello world' }); +test({ ocsp: false }); + +if (!common.hasFipsCrypto) { + test({ ocsp: true, response: 'hello pfx', pfx: pfx, passphrase: 'sample' }); +} diff --git a/test/js/node/test/parallel/test-tls-on-empty-socket.js b/test/js/node/test/parallel/test-tls-on-empty-socket.js new file mode 100644 index 0000000000..87d51a81bb --- /dev/null +++ b/test/js/node/test/parallel/test-tls-on-empty-socket.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const net = require('net'); +const fixtures = require('../common/fixtures'); + +let out = ''; + +const server = tls.createServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}, function(c) { + c.end('hello'); +}).listen(0, function() { + const socket = new net.Socket(); + + const s = tls.connect({ + socket: socket, + rejectUnauthorized: false + }, function() { + s.on('data', function(chunk) { + out += chunk; + }); + s.on('end', function() { + s.destroy(); + server.close(); + }); + }); + + socket.connect(this.address().port); +}); + +process.on('exit', function() { + assert.strictEqual(out, 'hello'); +}); diff --git a/test/js/node/test/parallel/http-url.parse-search.test.js b/test/js/node/test/parallel/test-tls-peer-certificate-encoding.js similarity index 57% rename from test/js/node/test/parallel/http-url.parse-search.test.js rename to test/js/node/test/parallel/test-tls-peer-certificate-encoding.js index fe07df2f63..154c31c0a1 100644 --- a/test/js/node/test/parallel/http-url.parse-search.test.js +++ b/test/js/node/test/parallel/test-tls-peer-certificate-encoding.js @@ -1,6 +1,3 @@ -//#FILE: test-http-url.parse-search.js -//#SHA1: 11d08b9c62625b7b554d5fb46d63c4aaa77c1a7c -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,34 +19,35 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); -test("HTTP request URL parsing with search params", async () => { - function check(request) { - // A path should come over with params - expect(request.url).toBe("/asdf?qwer=zxcv"); - } +const assert = require('assert'); +const tls = require('tls'); +const util = require('util'); +const fixtures = require('../common/fixtures'); - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); +const options = { + key: fixtures.readKey('agent5-key.pem'), + cert: fixtures.readKey('agent5-cert.pem'), + ca: [ fixtures.readKey('ca2-cert.pem') ] +}; - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - const testURL = url.parse(`http://localhost:${port}/asdf?qwer=zxcv`); - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); +const server = tls.createServer(options, (cleartext) => { + cleartext.end('World'); }); +server.listen(0, common.mustCall(function() { + const socket = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(() => { + const peerCert = socket.getPeerCertificate(); -//<#END_FILE: test-http-url.parse-search.js + console.error(util.inspect(peerCert)); + assert.strictEqual(peerCert.subject.CN, 'Ádám Lippai'); + server.close(); + })); + socket.end('Hello'); +})); diff --git a/test/js/node/test/parallel/http-client-timeout.test.js b/test/js/node/test/parallel/test-tls-peer-certificate-multi-keys.js similarity index 52% rename from test/js/node/test/parallel/http-client-timeout.test.js rename to test/js/node/test/parallel/test-tls-peer-certificate-multi-keys.js index 557b469490..ce4a0d406f 100644 --- a/test/js/node/test/parallel/http-client-timeout.test.js +++ b/test/js/node/test/parallel/test-tls-peer-certificate-multi-keys.js @@ -1,6 +1,3 @@ -//#FILE: test-http-client-timeout.js -//#SHA1: f99a3189acb9c566e378f9fa48c66dd503034d0d -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,39 +19,44 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); const options = { - method: "GET", - port: undefined, - host: "127.0.0.1", - path: "/", + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt') }; -test("http client timeout", done => { - const server = http.createServer((req, res) => { - // This space intentionally left blank - }); - - server.listen(0, options.host, () => { - options.port = server.address().port; - const req = http.request(options, res => { - // This space intentionally left blank - }); - req.on("close", () => { - expect(req.destroyed).toBe(true); - server.close(); - done(); - }); - function destroy() { - req.destroy(); - } - const s = req.setTimeout(1, destroy); - expect(s).toBeInstanceOf(http.ClientRequest); - req.on("error", destroy); - req.end(); - }); +const server = tls.createServer(options, function(cleartext) { + cleartext.end('World'); }); -//<#END_FILE: test-http-client-timeout.js +server.once('secureConnection', common.mustCall(function(socket) { + const cert = socket.getCertificate(); + // The server's local cert is the client's peer cert. + assert.deepStrictEqual( + cert.subject.OU, + ['Test TLS Certificate', 'Engineering'] + ); +})); + +server.listen(0, common.mustCall(function() { + const socket = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(function() { + const peerCert = socket.getPeerCertificate(); + assert.deepStrictEqual( + peerCert.subject.OU, + ['Test TLS Certificate', 'Engineering'] + ); + server.close(); + })); + socket.end('Hello'); +})); diff --git a/test/js/node/test/parallel/test-tls-psk-server.js b/test/js/node/test/parallel/test-tls-psk-server.js new file mode 100644 index 0000000000..b926095840 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-psk-server.js @@ -0,0 +1,77 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.opensslCli) + common.skip('missing openssl cli'); + +const assert = require('assert'); + +const tls = require('tls'); +const spawn = require('child_process').spawn; + +const CIPHERS = 'PSK+HIGH'; +const KEY = 'd731ef57be09e5204f0b205b60627028'; +const IDENTITY = 'TestUser'; + +const server = tls.createServer({ + ciphers: CIPHERS, + pskIdentityHint: IDENTITY, + pskCallback(socket, identity) { + assert.ok(socket instanceof tls.TLSSocket); + assert.ok(typeof identity === 'string'); + if (identity === IDENTITY) + return Buffer.from(KEY, 'hex'); + } +}); + +server.on('connection', common.mustCall()); + +server.on('secureConnection', (socket) => { + socket.write('hello\r\n'); + + socket.on('data', (data) => { + socket.write(data); + }); +}); + +let gotHello = false; +let sentWorld = false; +let gotWorld = false; + +server.listen(0, () => { + const client = spawn(common.opensslCli, [ + 's_client', + '-connect', `127.0.0.1:${server.address().port}`, + '-cipher', CIPHERS, + '-psk', KEY, + '-psk_identity', IDENTITY, + ]); + + let out = ''; + + client.stdout.setEncoding('utf8'); + client.stdout.on('data', (d) => { + out += d; + + if (!gotHello && /hello/.test(out)) { + gotHello = true; + client.stdin.write('world\r\n'); + sentWorld = true; + } + + if (!gotWorld && /world/.test(out)) { + gotWorld = true; + client.stdin.end(); + } + }); + + client.on('exit', common.mustCall((code) => { + assert.ok(gotHello); + assert.ok(sentWorld); + assert.ok(gotWorld); + assert.strictEqual(code, 0); + server.close(); + })); +}); diff --git a/test/js/node/test/parallel/test-tls-reuse-host-from-socket.js b/test/js/node/test/parallel/test-tls-reuse-host-from-socket.js new file mode 100644 index 0000000000..1a7705d911 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-reuse-host-from-socket.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const net = require('net'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const server = tls.createServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}).listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port, common.mustCall(() => { + const opts = { socket, rejectUnauthorized: false }; + const secureSocket = tls.connect(opts, common.mustCall(() => { + secureSocket.destroy(); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-secure-context-usage-order.js b/test/js/node/test/parallel/test-tls-secure-context-usage-order.js new file mode 100644 index 0000000000..c79a3eac77 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-secure-context-usage-order.js @@ -0,0 +1,99 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This test ensures that when a TLS connection is established, the server +// selects the most recently added SecureContext that matches the servername. + +const assert = require('assert'); +const tls = require('tls'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const serverOptions = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + requestCert: true, + rejectUnauthorized: false, +}; + +const badSecureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca2-cert') ] +}; + +const goodSecureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ] +}; + +const server = tls.createServer(serverOptions, (c) => { + // The 'a' and 'b' subdomains are used to distinguish between client + // connections. + // Connection to subdomain 'a' is made when the 'bad' secure context is + // the only one in use. + if ('a.example.com' === c.servername) { + assert.strictEqual(c.authorized, false); + } + // Connection to subdomain 'b' is made after the 'good' context has been + // added. + if ('b.example.com' === c.servername) { + assert.strictEqual(c.authorized, true); + } +}); + +// 1. Add the 'bad' secure context. A connection using this context will not be +// authorized. +server.addContext('*.example.com', badSecureContext); + +server.listen(0, () => { + const options = { + port: server.address().port, + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [loadPEM('ca1-cert')], + servername: 'a.example.com', + rejectUnauthorized: false, + }; + + // 2. Make a connection using servername 'a.example.com'. Since a 'bad' + // secure context is used, this connection should not be authorized. + const client = tls.connect(options, () => { + client.end(); + }); + + client.on('close', common.mustCall(() => { + // 3. Add a 'good' secure context. + server.addContext('*.example.com', goodSecureContext); + + options.servername = 'b.example.com'; + // 4. Make a connection using servername 'b.example.com'. This connection + // should be authorized because the 'good' secure context is the most + // recently added matching context. + + const other = tls.connect(options, () => { + other.end(); + }); + + other.on('close', common.mustCall(() => { + // 5. Make another connection using servername 'b.example.com' to ensure + // that the array of secure contexts is not reversed in place with each + // SNICallback call, as someone might be tempted to refactor this piece of + // code by using Array.prototype.reverse() method. + const onemore = tls.connect(options, () => { + onemore.end(); + }); + + onemore.on('close', common.mustCall(() => { + server.close(); + })); + })); + })); +}); diff --git a/test/js/node/test/parallel/test-tls-securepair-server.js b/test/js/node/test/parallel/test-tls-securepair-server.js new file mode 100644 index 0000000000..78cd9f7254 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-securepair-server.js @@ -0,0 +1,145 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); +const net = require('net'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const key = fixtures.readKey('rsa_private.pem'); +const cert = fixtures.readKey('rsa_cert.crt'); + +function log(a) { + console.error('***server***', a); +} + +const server = net.createServer(common.mustCall(function(socket) { + log(`connection fd=${socket.fd}`); + const sslcontext = tls.createSecureContext({ key, cert }); + sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA'); + + const pair = tls.createSecurePair(sslcontext, true); + + assert.ok(pair.encrypted.writable); + assert.ok(pair.cleartext.writable); + + pair.encrypted.pipe(socket); + socket.pipe(pair.encrypted); + + log('i set it secure'); + + pair.on('secure', function() { + log('connected+secure!'); + pair.cleartext.write('hello\r\n'); + log(pair.cleartext.getPeerCertificate()); + log(pair.cleartext.getCipher()); + }); + + pair.cleartext.on('data', function(data) { + log(`read bytes ${data.length}`); + pair.cleartext.write(data); + }); + + socket.on('end', function() { + log('socket end'); + }); + + pair.cleartext.on('error', function(err) { + log('got error: '); + log(err); + socket.destroy(); + }); + + pair.encrypted.on('error', function(err) { + log('encrypted error: '); + log(err); + socket.destroy(); + }); + + socket.on('error', function(err) { + log('socket error: '); + log(err); + socket.destroy(); + }); + + socket.on('close', function(err) { + log('socket closed'); + }); + + pair.on('error', function(err) { + log('secure error: '); + log(err); + socket.destroy(); + }); +})); + +let gotHello = false; +let sentWorld = false; +let gotWorld = false; + +server.listen(0, common.mustCall(function() { + // To test use: openssl s_client -connect localhost:8000 + + const args = ['s_client', '-connect', `127.0.0.1:${this.address().port}`]; + + const client = spawn(common.opensslCli, args); + + + let out = ''; + + client.stdout.setEncoding('utf8'); + client.stdout.on('data', function(d) { + out += d; + + if (!gotHello && /hello/.test(out)) { + gotHello = true; + client.stdin.write('world\r\n'); + sentWorld = true; + } + + if (!gotWorld && /world/.test(out)) { + gotWorld = true; + client.stdin.end(); + } + }); + + client.stdout.pipe(process.stdout, { end: false }); + + client.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); + server.close(); + })); +})); + +process.on('exit', function() { + assert.ok(gotHello); + assert.ok(sentWorld); + assert.ok(gotWorld); +}); diff --git a/test/js/node/test/parallel/test-tls-server-connection-server.js b/test/js/node/test/parallel/test-tls-server-connection-server.js new file mode 100644 index 0000000000..7fb2c74996 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-server-connection-server.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = tls.createServer(options, function(s) { + s.end('hello'); +}).listen(0, function() { + const opts = { + port: this.address().port, + rejectUnauthorized: false + }; + + server.on('connection', common.mustCall(function(socket) { + assert.strictEqual(socket.server, server); + server.close(); + })); + + const client = tls.connect(opts, function() { + client.end(); + }); +}); diff --git a/test/js/node/test/parallel/test-tls-server-verify.js b/test/js/node/test/parallel/test-tls-server-verify.js new file mode 100644 index 0000000000..51ccd0d747 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-server-verify.js @@ -0,0 +1,348 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +// This is a rather complex test which sets up various TLS servers with node +// and connects to them using the 'openssl s_client' command line utility +// with various keys. Depending on the certificate authority and other +// parameters given to the server, the various clients are +// - rejected, +// - accepted and "unauthorized", or +// - accepted and "authorized". + +const assert = require('assert'); +const { spawn } = require('child_process'); +const { SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION } = + require('crypto').constants; +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const testCases = + [{ title: 'Do not request certs. Everyone is unauthorized.', + requestCert: false, + rejectUnauthorized: false, + renegotiate: false, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: false }, + { name: 'agent2', shouldReject: false, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: false }, + { name: 'nocert', shouldReject: false, shouldAuth: false }, + ] }, + + { title: 'Allow both authed and unauthed connections with CA1', + requestCert: true, + rejectUnauthorized: false, + renegotiate: false, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: false, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: false }, + { name: 'nocert', shouldReject: false, shouldAuth: false }, + ] }, + + { title: 'Do not request certs at connection. Do that later', + requestCert: false, + rejectUnauthorized: false, + renegotiate: true, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: false, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: false }, + { name: 'nocert', shouldReject: false, shouldAuth: false }, + ] }, + + { title: 'Allow only authed connections with CA1', + requestCert: true, + rejectUnauthorized: true, + renegotiate: false, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: true }, + { name: 'agent3', shouldReject: true }, + { name: 'nocert', shouldReject: true }, + ] }, + + { title: 'Allow only authed connections with CA1 and CA2', + requestCert: true, + rejectUnauthorized: true, + renegotiate: false, + CAs: ['ca1-cert', 'ca2-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: true }, + { name: 'agent3', shouldReject: false, shouldAuth: true }, + { name: 'nocert', shouldReject: true }, + ] }, + + + { title: 'Allow only certs signed by CA2 but not in the CRL', + requestCert: true, + rejectUnauthorized: true, + renegotiate: false, + CAs: ['ca2-cert'], + crl: 'ca2-crl', + clients: [ + { name: 'agent1', shouldReject: true, shouldAuth: false }, + { name: 'agent2', shouldReject: true, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: true }, + // Agent4 has a cert in the CRL. + { name: 'agent4', shouldReject: true, shouldAuth: false }, + { name: 'nocert', shouldReject: true }, + ] }, + ]; + +function filenamePEM(n) { + return fixtures.path('keys', `${n}.pem`); +} + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + + +const serverKey = loadPEM('agent2-key'); +const serverCert = loadPEM('agent2-cert'); + + +function runClient(prefix, port, options, cb) { + + // Client can connect in three ways: + // - Self-signed cert + // - Certificate, but not signed by CA. + // - Certificate signed by CA. + + const args = ['s_client', '-connect', `127.0.0.1:${port}`]; + + console.log(`${prefix} connecting with`, options.name); + + switch (options.name) { + case 'agent1': + // Signed by CA1 + args.push('-key'); + args.push(filenamePEM('agent1-key')); + args.push('-cert'); + args.push(filenamePEM('agent1-cert')); + break; + + case 'agent2': + // Self-signed + // This is also the key-cert pair that the server will use. + args.push('-key'); + args.push(filenamePEM('agent2-key')); + args.push('-cert'); + args.push(filenamePEM('agent2-cert')); + break; + + case 'agent3': + // Signed by CA2 + args.push('-key'); + args.push(filenamePEM('agent3-key')); + args.push('-cert'); + args.push(filenamePEM('agent3-cert')); + break; + + case 'agent4': + // Signed by CA2 (rejected by ca2-crl) + args.push('-key'); + args.push(filenamePEM('agent4-key')); + args.push('-cert'); + args.push(filenamePEM('agent4-cert')); + break; + + case 'nocert': + // Do not send certificate + break; + + default: + throw new Error(`${prefix}Unknown agent name`); + } + + // To test use: openssl s_client -connect localhost:8000 + const client = spawn(common.opensslCli, args); + + let out = ''; + + let rejected = true; + let authed = false; + let goodbye = false; + + client.stdout.setEncoding('utf8'); + client.stdout.on('data', function(d) { + out += d; + + if (!goodbye && /_unauthed/.test(out)) { + console.error(`${prefix} * unauthed`); + goodbye = true; + client.kill(); + authed = false; + rejected = false; + } + + if (!goodbye && /_authed/.test(out)) { + console.error(`${prefix} * authed`); + goodbye = true; + client.kill(); + authed = true; + rejected = false; + } + }); + + client.on('exit', function(code) { + if (options.shouldReject) { + assert.strictEqual( + rejected, true, + `${prefix}${options.name} NOT rejected, but should have been`); + } else { + assert.strictEqual( + rejected, false, + `${prefix}${options.name} rejected, but should NOT have been`); + assert.strictEqual( + authed, options.shouldAuth, + `${prefix}${options.name} authed is ${authed} but should have been ${ + options.shouldAuth}`); + } + + cb(); + }); +} + + +// Run the tests +let successfulTests = 0; +function runTest(port, testIndex) { + const prefix = `${testIndex} `; + const tcase = testCases[testIndex]; + if (!tcase) return; + + console.error(`${prefix}Running '${tcase.title}'`); + + const cas = tcase.CAs.map(loadPEM); + + const crl = tcase.crl ? loadPEM(tcase.crl) : null; + + const serverOptions = { + key: serverKey, + cert: serverCert, + ca: cas, + crl: crl, + requestCert: tcase.requestCert, + rejectUnauthorized: tcase.rejectUnauthorized + }; + + // If renegotiating - session might be resumed and openssl won't request + // client's certificate (probably because of bug in the openssl) + if (tcase.renegotiate) { + serverOptions.secureOptions = + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + // Renegotiation as a protocol feature was dropped after TLS1.2. + serverOptions.maxVersion = 'TLSv1.2'; + } + + let renegotiated = false; + const server = tls.Server(serverOptions, function handleConnection(c) { + c.on('error', function(e) { + // child.kill() leads ECONNRESET error in the TLS connection of + // openssl s_client via spawn(). A test result is already + // checked by the data of client.stdout before child.kill() so + // these tls errors can be ignored. + }); + if (tcase.renegotiate && !renegotiated) { + renegotiated = true; + setTimeout(function() { + console.error(`${prefix}- connected, renegotiating`); + c.write('\n_renegotiating\n'); + return c.renegotiate({ + requestCert: true, + rejectUnauthorized: false + }, function(err) { + assert.ifError(err); + c.write('\n_renegotiated\n'); + handleConnection(c); + }); + }, 200); + return; + } + + if (c.authorized) { + console.error(`${prefix}- authed connection: ${ + c.getPeerCertificate().subject.CN}`); + c.write('\n_authed\n'); + } else { + console.error(`${prefix}- unauthed connection: %s`, c.authorizationError); + c.write('\n_unauthed\n'); + } + }); + + function runNextClient(clientIndex) { + const options = tcase.clients[clientIndex]; + if (options) { + runClient(`${prefix}${clientIndex} `, port, options, function() { + runNextClient(clientIndex + 1); + }); + } else { + server.close(); + successfulTests++; + runTest(0, nextTest++); + } + } + + server.listen(port, function() { + port = server.address().port; + if (tcase.debug) { + console.error(`${prefix}TLS server running on port ${port}`); + } else if (tcase.renegotiate) { + runNextClient(0); + } else { + let clientsCompleted = 0; + for (let i = 0; i < tcase.clients.length; i++) { + runClient(`${prefix}${i} `, port, tcase.clients[i], function() { + clientsCompleted++; + if (clientsCompleted === tcase.clients.length) { + server.close(); + successfulTests++; + runTest(0, nextTest++); + } + }); + } + } + }); +} + + +let nextTest = 0; +runTest(0, nextTest++); + + +process.on('exit', function() { + assert.strictEqual(successfulTests, testCases.length); +}); diff --git a/test/js/node/test/parallel/test-tls-session-cache.js b/test/js/node/test/parallel/test-tls-session-cache.js new file mode 100644 index 0000000000..e4ecb53282 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-session-cache.js @@ -0,0 +1,164 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); +const { spawn } = require('child_process'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + + +doTest({ tickets: false }, function() { + doTest({ tickets: true }, function() { + doTest({ tickets: false, invalidSession: true }, function() { + console.error('all done'); + }); + }); +}); + +function doTest(testOptions, callback) { + const key = fixtures.readKey('rsa_private.pem'); + const cert = fixtures.readKey('rsa_cert.crt'); + const options = { + key, + cert, + ca: [cert], + requestCert: true, + rejectUnauthorized: false, + secureProtocol: 'TLS_method', + ciphers: 'RSA@SECLEVEL=0' + }; + let requestCount = 0; + let resumeCount = 0; + let newSessionCount = 0; + let session; + + const server = tls.createServer(options, function(cleartext) { + cleartext.on('error', function(er) { + // We're ok with getting ECONNRESET in this test, but it's + // timing-dependent, and thus unreliable. Any other errors + // are just failures, though. + if (er.code !== 'ECONNRESET') + throw er; + }); + ++requestCount; + cleartext.end(''); + }); + server.on('newSession', function(id, data, cb) { + ++newSessionCount; + // Emulate asynchronous store + setImmediate(() => { + assert.ok(!session); + session = { id, data }; + cb(); + }); + }); + server.on('resumeSession', function(id, callback) { + ++resumeCount; + assert.ok(session); + assert.strictEqual(session.id.toString('hex'), id.toString('hex')); + + let data = session.data; + + // Return an invalid session to test Node does not crash. + if (testOptions.invalidSession) { + data = Buffer.from('INVALID SESSION'); + session = null; + } + + // Just to check that async really works there + setImmediate(() => { + callback(null, data); + }); + }); + + server.listen(0, function() { + const args = [ + 's_client', + '-tls1', + '-cipher', (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-connect', `localhost:${this.address().port}`, + '-servername', 'ohgod', + '-key', fixtures.path('keys/rsa_private.pem'), + '-cert', fixtures.path('keys/rsa_cert.crt'), + '-reconnect', + ].concat(testOptions.tickets ? [] : '-no_ticket'); + + function spawnClient() { + const client = spawn(common.opensslCli, args, { + stdio: [ 0, 1, 'pipe' ] + }); + let err = ''; + client.stderr.setEncoding('utf8'); + client.stderr.on('data', function(chunk) { + err += chunk; + }); + + client.on('exit', common.mustCall(function(code, signal) { + if (code !== 0) { + // If SmartOS and connection refused, then retry. See + // https://github.com/nodejs/node/issues/2663. + if (common.isSunOS && err.includes('Connection refused')) { + requestCount = 0; + spawnClient(); + return; + } + assert.fail(`code: ${code}, signal: ${signal}, output: ${err}`); + } + assert.strictEqual(code, 0); + server.close(common.mustCall(function() { + setImmediate(callback); + })); + })); + } + + spawnClient(); + }); + + process.on('exit', function() { + // Each test run connects 6 times: an initial request and 5 reconnect + // requests. + assert.strictEqual(requestCount, 6); + + if (testOptions.tickets) { + // No session cache callbacks are called. + assert.strictEqual(resumeCount, 0); + assert.strictEqual(newSessionCount, 0); + } else if (testOptions.invalidSession) { + // The resume callback was called, but each connection established a + // fresh session. + assert.strictEqual(resumeCount, 5); + assert.strictEqual(newSessionCount, 6); + } else { + // The resume callback was called, and only the initial connection + // establishes a fresh session. + assert.ok(session); + assert.strictEqual(resumeCount, 5); + assert.strictEqual(newSessionCount, 1); + } + }); +} diff --git a/test/js/node/test/parallel/test-tls-set-ciphers.js b/test/js/node/test/parallel/test-tls-set-ciphers.js new file mode 100644 index 0000000000..313c5e2389 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-set-ciphers.js @@ -0,0 +1,131 @@ +'use strict'; +const common = require('../common'); +if (!common.hasOpenSSL3) + common.skip('missing crypto, or OpenSSL version lower than 3'); + +const fixtures = require('../common/fixtures'); +const { inspect } = require('util'); + +// Test cipher: option for TLS. + +const { + assert, connect, keys +} = require(fixtures.path('tls-connect')); + + +function test(cciphers, sciphers, cipher, cerr, serr, options) { + assert(cipher || cerr || serr, 'test missing any expectations'); + const where = inspect(new Error()).split('\n')[2].replace(/[^(]*/, ''); + + const max_tls_ver = (ciphers, options) => { + if (options instanceof Object && Object.hasOwn(options, 'maxVersion')) + return options.maxVersion; + if ((typeof ciphers === 'string' || ciphers instanceof String) && ciphers.length > 0 && !ciphers.includes('TLS_')) + return 'TLSv1.2'; + + return 'TLSv1.3'; + }; + + connect({ + client: { + checkServerIdentity: (servername, cert) => { }, + ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, + ciphers: cciphers, + maxVersion: max_tls_ver(cciphers, options), + }, + server: { + cert: keys.agent6.cert, + key: keys.agent6.key, + ciphers: sciphers, + maxVersion: max_tls_ver(sciphers, options), + }, + }, common.mustCall((err, pair, cleanup) => { + function u(_) { return _ === undefined ? 'U' : _; } + console.log('test:', u(cciphers), u(sciphers), + 'expect', u(cipher), u(cerr), u(serr)); + console.log(' ', where); + if (!cipher) { + console.log('client', pair.client.err ? pair.client.err.code : undefined); + console.log('server', pair.server.err ? pair.server.err.code : undefined); + if (cerr) { + assert(pair.client.err); + assert.strictEqual(pair.client.err.code, cerr); + } + if (serr) { + assert(pair.server.err); + assert.strictEqual(pair.server.err.code, serr); + } + return cleanup(); + } + + const reply = 'So long and thanks for all the fish.'; + + assert.ifError(err); + assert.ifError(pair.server.err); + assert.ifError(pair.client.err); + assert(pair.server.conn); + assert(pair.client.conn); + assert.strictEqual(pair.client.conn.getCipher().name, cipher); + assert.strictEqual(pair.server.conn.getCipher().name, cipher); + + pair.server.conn.write(reply); + + pair.client.conn.on('data', common.mustCall((data) => { + assert.strictEqual(data.toString(), reply); + return cleanup(); + })); + })); +} + +const U = undefined; + +// Have shared ciphers. +test(U, 'AES256-SHA', 'AES256-SHA'); +test('AES256-SHA', U, 'AES256-SHA'); + +test(U, 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384'); +test('TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384'); +test('TLS_AES_256_GCM_SHA384:!TLS_CHACHA20_POLY1305_SHA256', U, 'TLS_AES_256_GCM_SHA384'); + +// Do not have shared ciphers. +test('TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256', + U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); + +test('AES128-SHA', 'AES256-SHA', U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', + 'ERR_SSL_NO_SHARED_CIPHER'); +test('AES128-SHA:TLS_AES_256_GCM_SHA384', + 'TLS_CHACHA20_POLY1305_SHA256:AES256-SHA', + U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); + +// Cipher order ignored, TLS1.3 chosen before TLS1.2. +test('AES256-SHA:TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384'); +test(U, 'AES256-SHA:TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384'); + +// Cipher order ignored, TLS1.3 before TLS1.2 and +// cipher suites are not disabled if TLS ciphers are set only +// TODO: maybe these tests should be reworked so maxVersion clamping +// is done explicitly and not implicitly in the test() function +test('AES256-SHA', U, 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' }); +test(U, 'AES256-SHA', 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' }); + +// TLS_AES_128_CCM_8_SHA256 & TLS_AES_128_CCM_SHA256 are not enabled by +// default, but work. +test('TLS_AES_128_CCM_8_SHA256', U, + U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); + +test('TLS_AES_128_CCM_8_SHA256', 'TLS_AES_128_CCM_8_SHA256', + 'TLS_AES_128_CCM_8_SHA256'); + +// Invalid cipher values +test(9, 'AES256-SHA', U, 'ERR_INVALID_ARG_TYPE', U); +test('AES256-SHA', 9, U, U, 'ERR_INVALID_ARG_TYPE'); +test(':', 'AES256-SHA', U, 'ERR_INVALID_ARG_VALUE', U); +test('AES256-SHA', ':', U, U, 'ERR_INVALID_ARG_VALUE'); + +// Using '' is synonymous for "use default ciphers" +test('TLS_AES_256_GCM_SHA384', '', 'TLS_AES_256_GCM_SHA384'); +test('', 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384'); + +// Using null should be treated the same as undefined. +test(null, 'AES256-SHA', 'AES256-SHA'); +test('AES256-SHA', null, 'AES256-SHA'); diff --git a/test/js/node/test/parallel/test-tls-startcom-wosign-whitelist.js b/test/js/node/test/parallel/test-tls-startcom-wosign-whitelist.js new file mode 100644 index 0000000000..56ffd73aac --- /dev/null +++ b/test/js/node/test/parallel/test-tls-startcom-wosign-whitelist.js @@ -0,0 +1,84 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +let finished = 0; + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const testCases = [ + { // agent8 is signed by fake-startcom-root with notBefore of + // Oct 20 23:59:59 2016 GMT. It passes StartCom/WoSign check. + serverOpts: { + key: loadPEM('agent8-key'), + cert: loadPEM('agent8-cert') + }, + clientOpts: { + ca: loadPEM('fake-startcom-root-cert'), + port: undefined, + rejectUnauthorized: true + }, + errorCode: 'CERT_REVOKED' + }, + { // agent9 is signed by fake-startcom-root with notBefore of + // Oct 21 00:00:01 2016 GMT. It fails StartCom/WoSign check. + serverOpts: { + key: loadPEM('agent9-key'), + cert: loadPEM('agent9-cert') + }, + clientOpts: { + ca: loadPEM('fake-startcom-root-cert'), + port: undefined, + rejectUnauthorized: true + }, + errorCode: 'CERT_REVOKED' + }, +]; + + +function runNextTest(server, tindex) { + server.close(function() { + finished++; + runTest(tindex + 1); + }); +} + + +function runTest(tindex) { + const tcase = testCases[tindex]; + + if (!tcase) return; + + const server = tls.createServer(tcase.serverOpts, function(s) { + s.resume(); + }).listen(0, function() { + tcase.clientOpts.port = this.address().port; + const client = tls.connect(tcase.clientOpts); + client.on('error', function(e) { + assert.strictEqual(e.code, tcase.errorCode); + runNextTest(server, tindex); + }); + + client.on('secureConnect', function() { + // agent8 can pass StartCom/WoSign check so that the secureConnect + // is established. + assert.strictEqual(tcase.errorCode, 'CERT_REVOKED'); + client.end(); + runNextTest(server, tindex); + }); + }); +} + + +runTest(0); + +process.on('exit', function() { + assert.strictEqual(finished, testCases.length); +}); diff --git a/test/js/node/test/parallel/test-tls-tlswrap-segfault-2.js b/test/js/node/test/parallel/test-tls-tlswrap-segfault-2.js new file mode 100644 index 0000000000..d4f3b12b99 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-tlswrap-segfault-2.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This test ensures that Node.js doesn't incur a segfault while +// adding session or keylog listeners after destroy. +// https://github.com/nodejs/node/issues/38133 +// https://github.com/nodejs/node/issues/38135 + +const tls = require('tls'); +const tlsSocketKeyLog = tls.connect('cause-error'); +tlsSocketKeyLog.on('error', common.mustCall()); +tlsSocketKeyLog.on('close', common.mustCall(() => { + tlsSocketKeyLog.on('keylog', common.mustNotCall()); +})); + +const tlsSocketSession = tls.connect('cause-error-2'); +tlsSocketSession.on('error', common.mustCall()); +tlsSocketSession.on('close', common.mustCall(() => { + tlsSocketSession.on('session', common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-tls-tlswrap-segfault.js b/test/js/node/test/parallel/test-tls-tlswrap-segfault.js new file mode 100644 index 0000000000..a36016efa4 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-tlswrap-segfault.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); + +// This test ensures that Node.js doesn't incur a segfault while accessing +// TLSWrap fields after the parent handle was destroyed. +// https://github.com/nodejs/node/issues/5108 + +const assert = require('assert'); +const tls = require('tls'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = tls.createServer(options, function(s) { + s.end('hello'); +}).listen(0, function() { + const opts = { + port: this.address().port, + rejectUnauthorized: false + }; + const client = tls.connect(opts, function() { + putImmediate(client); + }); + client.resume(); +}); + +function putImmediate(client) { + setImmediate(function() { + if (client.ssl) { + const fd = client.ssl.fd; + assert(!!fd); + putImmediate(client); + } else { + server.close(); + } + }); +} diff --git a/test/js/node/test/parallel/test-tls-wrap-econnreset-socket.js b/test/js/node/test/parallel/test-tls-wrap-econnreset-socket.js new file mode 100644 index 0000000000..ec305b785e --- /dev/null +++ b/test/js/node/test/parallel/test-tls-wrap-econnreset-socket.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const net = require('net'); +const tls = require('tls'); + +const server = net.createServer((c) => { + c.end(); +}).listen(common.mustCall(() => { + const port = server.address().port; + + const socket = new net.Socket(); + + let errored = false; + tls.connect({ socket }) + .once('error', common.mustCall((e) => { + assert.strictEqual(e.code, 'ECONNRESET'); + assert.strictEqual(e.path, undefined); + assert.strictEqual(e.host, undefined); + assert.strictEqual(e.port, undefined); + assert.strictEqual(e.localAddress, undefined); + errored = true; + server.close(); + })) + .on('close', common.mustCall(() => { + assert.strictEqual(errored, true); + })); + + socket.connect(port); +})); diff --git a/test/js/node/test/parallel/test-tls-zero-clear-in.js b/test/js/node/test/parallel/test-tls-zero-clear-in.js new file mode 100644 index 0000000000..f24fb6f992 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-zero-clear-in.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); + +const server = tls.createServer({ + cert, + key +}, function(c) { + // Nop + setTimeout(function() { + c.end(); + server.close(); + }, 20); +}).listen(0, common.mustCall(function() { + const conn = tls.connect({ + cert: cert, + key: key, + rejectUnauthorized: false, + port: this.address().port + }, function() { + setTimeout(function() { + conn.destroy(); + }, 20); + }); + + // SSL_write() call's return value, when called 0 bytes, should not be + // treated as error. + conn.end(''); + + conn.on('error', common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-trace-events-net-abstract-socket.js b/test/js/node/test/parallel/test-trace-events-net-abstract-socket.js new file mode 100644 index 0000000000..d2e1546743 --- /dev/null +++ b/test/js/node/test/parallel/test-trace-events-net-abstract-socket.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +if (!common.isLinux) common.skip(); + +const CODE = ` + const net = require('net'); + net.connect('${common.PIPE}').on('error', () => {}); + net.connect('\\0${common.PIPE}').on('error', () => {}); +`; + +tmpdir.refresh(); +const FILE_NAME = tmpdir.resolve('node_trace.1.log'); + +const proc = cp.spawn(process.execPath, + [ '--trace-events-enabled', + '--trace-event-categories', 'node.net.native', + '-e', CODE ], + { cwd: tmpdir.path }); + +proc.once('exit', common.mustCall(() => { + assert(fs.existsSync(FILE_NAME)); + fs.readFile(FILE_NAME, common.mustCall((err, data) => { + const traces = JSON.parse(data.toString()).traceEvents; + assert(traces.length > 0); + let count = 0; + traces.forEach((trace) => { + if (trace.cat === 'node,node.net,node.net.native' && + trace.name === 'connect') { + count++; + if (trace.ph === 'b') { + assert.ok(!!trace.args.path_type); + assert.ok(!!trace.args.pipe_path); + } + } + }); + assert.strictEqual(count, 4); + })); +})); diff --git a/test/js/node/test/parallel/test-tty-stdin-end.js b/test/js/node/test/parallel/test-tty-stdin-end.js new file mode 100644 index 0000000000..c78f58446d --- /dev/null +++ b/test/js/node/test/parallel/test-tty-stdin-end.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. +// https://github.com/nodejs/node/issues/1068 + +process.stdin.emit('end'); diff --git a/test/js/node/test/parallel/test-tz-version.js b/test/js/node/test/parallel/test-tz-version.js new file mode 100644 index 0000000000..6e4b14e1ac --- /dev/null +++ b/test/js/node/test/parallel/test-tz-version.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasIntl) { + common.skip('missing Intl'); +} + +// Refs: https://github.com/nodejs/node/blob/1af63a90ca3a59ca05b3a12ad7dbea04008db7d9/configure.py#L1694-L1711 +if (process.config.variables.icu_path !== 'deps/icu-small') { + // If Node.js is configured to use its built-in ICU, it uses a strict subset + // of ICU formed using `tools/icu/shrink-icu-src.py`, which is present in + // `deps/icu-small`. It is not the same as configuring the build with + // `./configure --with-intl=small-icu`. The latter only uses a subset of the + // locales, i.e., it uses the English locale, `root,en`, by default and other + // locales can also be specified using the `--with-icu-locales` option. + common.skip('not using the icu data file present in deps/icu-small/source/data/in/icudt##l.dat.bz2'); +} + +const fixtures = require('../common/fixtures'); + +// This test ensures the correctness of the automated timezone upgrade PRs. + +const { strictEqual } = require('assert'); +const { readFileSync } = require('fs'); + +const expectedVersion = readFileSync(fixtures.path('tz-version.txt'), 'utf8').trim(); +strictEqual(process.versions.tz, expectedVersion); diff --git a/test/js/node/test/parallel/test-unhandled-exception-with-worker-inuse.js b/test/js/node/test/parallel/test-unhandled-exception-with-worker-inuse.js new file mode 100644 index 0000000000..a3e823ca70 --- /dev/null +++ b/test/js/node/test/parallel/test-unhandled-exception-with-worker-inuse.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); + +// https://github.com/nodejs/node/issues/45421 +// +// Check that node will NOT call v8::Isolate::SetIdle() when exiting +// due to an unhandled exception, otherwise the assertion(enabled in +// debug build only) in the SetIdle(), which checks that the vm state +// is either EXTERNAL or IDLE will fail. +// +// The root cause of this issue is that before PerIsolateMessageListener() +// is invoked by v8, v8 preserves the JS vm state, although it should +// switch to EXTERNEL. https://bugs.chromium.org/p/v8/issues/detail?id=13464 +// +// Therefore, this commit can be considered as an workaround of the v8 bug, +// but we also find it not useful to call SetIdle() when terminating. + +if (process.argv[2] === 'child') { + const { Worker } = require('worker_threads'); + new Worker('', { eval: true }); + throw new Error('xxx'); +} else { + const assert = require('assert'); + const { spawnSync } = require('child_process'); + const result = spawnSync(process.execPath, [__filename, 'child']); + + const stderr = result.stderr.toString().trim(); + // Expect error message to be preserved + assert.match(stderr, /xxx/); + // Expect no crash + assert(!common.nodeProcessAborted(result.status, result.signal), stderr); +} diff --git a/test/js/node/test/parallel/test-url-canParse-whatwg.js b/test/js/node/test/parallel/test-url-canParse-whatwg.js new file mode 100644 index 0000000000..d5ffee7053 --- /dev/null +++ b/test/js/node/test/parallel/test-url-canParse-whatwg.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// One argument is required +assert.throws(() => { + URL.canParse(); +}, { + code: 'ERR_MISSING_ARGS', + name: 'TypeError', +}); + +{ + // This test is to ensure that the v8 fast api works. + for (let i = 0; i < 1e5; i++) { + assert(URL.canParse('https://www.example.com/path/?query=param#hash')); + } +} diff --git a/test/js/node/test/parallel/test-url-domain-ascii-unicode.js b/test/js/node/test/parallel/test-url-domain-ascii-unicode.js new file mode 100644 index 0000000000..737294c241 --- /dev/null +++ b/test/js/node/test/parallel/test-url-domain-ascii-unicode.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasIntl) + common.skip('missing Intl'); + +const strictEqual = require('assert').strictEqual; +const url = require('url'); + +const domainToASCII = url.domainToASCII; +const domainToUnicode = url.domainToUnicode; + +const domainWithASCII = [ + ['ıíd', 'xn--d-iga7r'], + ['يٴ', 'xn--mhb8f'], + ['www.ϧƽəʐ.com', 'www.xn--cja62apfr6c.com'], + ['новини.com', 'xn--b1amarcd.com'], + ['名がドメイン.com', 'xn--v8jxj3d1dzdz08w.com'], + ['افغانستا.icom.museum', 'xn--mgbaal8b0b9b2b.icom.museum'], + ['الجزائر.icom.fake', 'xn--lgbbat1ad8j.icom.fake'], + ['भारत.org', 'xn--h2brj9c.org'], +]; + +domainWithASCII.forEach((pair) => { + const domain = pair[0]; + const ascii = pair[1]; + const domainConvertedToASCII = domainToASCII(domain); + strictEqual(domainConvertedToASCII, ascii); + const asciiConvertedToUnicode = domainToUnicode(ascii); + strictEqual(asciiConvertedToUnicode, domain); +}); diff --git a/test/js/node/test/parallel/test-url-format-whatwg.js b/test/js/node/test/parallel/test-url-format-whatwg.js new file mode 100644 index 0000000000..bf9f8eaac6 --- /dev/null +++ b/test/js/node/test/parallel/test-url-format-whatwg.js @@ -0,0 +1,147 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const url = require('url'); + +const myURL = new URL('http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'); + +assert.strictEqual( + url.format(myURL), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, {}), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +{ + [true, 1, 'test', Infinity].forEach((value) => { + assert.throws( + () => url.format(myURL, value), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options" argument must be of type object.' + + common.invalidArgTypeHelper(value) + } + ); + }); +} + +// Any falsy value other than undefined will be treated as false. +// Any truthy value will be treated as true. + +assert.strictEqual( + url.format(myURL, { auth: false }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: '' }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: 0 }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { fragment: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: '' }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { fragment: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { search: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: '' }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { search: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: true }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: 1 }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: {} }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(new URL('http://user:pass@xn--0zwm56d.com:8080/path'), { unicode: true }), + 'http://user:pass@测试.com:8080/path' +); + +assert.strictEqual( + url.format(new URL('tel:123')), + url.format(new URL('tel:123'), { unicode: true }) +); diff --git a/test/js/node/test/parallel/test-url-format.js b/test/js/node/test/parallel/test-url-format.js new file mode 100644 index 0000000000..883d060ac2 --- /dev/null +++ b/test/js/node/test/parallel/test-url-format.js @@ -0,0 +1,277 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const url = require('url'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +// Formatting tests to verify that it'll format slightly wonky content to a +// valid URL. +const formatTests = { + 'http://example.com?': { + href: 'http://example.com/?', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + search: '?', + query: {}, + pathname: '/' + }, + 'http://example.com?foo=bar#frag': { + href: 'http://example.com/?foo=bar#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=bar', + query: 'foo=bar', + pathname: '/' + }, + 'http://example.com?foo=@bar#frag': { + href: 'http://example.com/?foo=@bar#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=@bar', + query: 'foo=@bar', + pathname: '/' + }, + 'http://example.com?foo=/bar/#frag': { + href: 'http://example.com/?foo=/bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=/bar/', + query: 'foo=/bar/', + pathname: '/' + }, + 'http://example.com?foo=?bar/#frag': { + href: 'http://example.com/?foo=?bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=?bar/', + query: 'foo=?bar/', + pathname: '/' + }, + 'http://example.com#frag=?bar/#frag': { + href: 'http://example.com/#frag=?bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag=?bar/#frag', + pathname: '/' + }, + 'http://google.com" onload="alert(42)/': { + href: 'http://google.com/%22%20onload=%22alert(42)/', + protocol: 'http:', + host: 'google.com', + pathname: '/%22%20onload=%22alert(42)/' + }, + 'http://a.com/a/b/c?s#h': { + href: 'http://a.com/a/b/c?s#h', + protocol: 'http', + host: 'a.com', + pathname: 'a/b/c', + hash: 'h', + search: 's' + }, + 'xmpp:isaacschlueter@jabber.org': { + href: 'xmpp:isaacschlueter@jabber.org', + protocol: 'xmpp:', + host: 'jabber.org', + auth: 'isaacschlueter', + hostname: 'jabber.org' + }, + 'http://atpass:foo%40bar@127.0.0.1/': { + href: 'http://atpass:foo%40bar@127.0.0.1/', + auth: 'atpass:foo@bar', + hostname: '127.0.0.1', + protocol: 'http:', + pathname: '/' + }, + 'http://atslash%2F%40:%2F%40@foo/': { + href: 'http://atslash%2F%40:%2F%40@foo/', + auth: 'atslash/@:/@', + hostname: 'foo', + protocol: 'http:', + pathname: '/' + }, + 'svn+ssh://foo/bar': { + href: 'svn+ssh://foo/bar', + hostname: 'foo', + protocol: 'svn+ssh:', + pathname: '/bar', + slashes: true + }, + 'dash-test://foo/bar': { + href: 'dash-test://foo/bar', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + slashes: true + }, + 'dash-test:foo/bar': { + href: 'dash-test:foo/bar', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar' + }, + 'dot.test://foo/bar': { + href: 'dot.test://foo/bar', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + slashes: true + }, + 'dot.test:foo/bar': { + href: 'dot.test:foo/bar', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar' + }, + // IPv6 support + 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { + href: 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', + protocol: 'coap:', + auth: 'u:p', + hostname: '::1', + port: '61616', + pathname: '/.well-known/r', + search: 'n=Temperature' + }, + 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { + href: 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', + protocol: 'coap', + host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', + pathname: '/s/stopButton' + }, + 'http://[::]/': { + href: 'http://[::]/', + protocol: 'http:', + hostname: '[::]', + pathname: '/' + }, + + // Encode context-specific delimiters in path and query, but do not touch + // other non-delimiter chars like `%`. + // + + // `#`,`?` in path + '/path/to/%%23%3F+=&.txt?foo=theA1#bar': { + href: '/path/to/%%23%3F+=&.txt?foo=theA1#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'theA1' + }, + hash: '#bar' + }, + + // `#`,`?` in path + `#` in query + '/path/to/%%23%3F+=&.txt?foo=the%231#bar': { + href: '/path/to/%%23%3F+=&.txt?foo=the%231#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'the#1' + }, + hash: '#bar' + }, + + // `#` in path end + `#` in query + '/path/to/%%23?foo=the%231#bar': { + href: '/path/to/%%23?foo=the%231#bar', + pathname: '/path/to/%#', + query: { + foo: 'the#1' + }, + hash: '#bar' + }, + + // `?` and `#` in path and search + 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/foo?100%m#r', + }, + + // `?` and `#` in search only + 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/fooA100%mBr', + }, + + // Multiple `#` in search + 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag': { + href: 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=bar#1#2#3&abc=#4##5', + query: {}, + pathname: '/' + }, + + // More than 255 characters in hostname which exceeds the limit + [`http://${'a'.repeat(255)}.com/node`]: { + href: 'http:///node', + protocol: 'http:', + slashes: true, + host: '', + hostname: '', + pathname: '/node', + path: '/node' + }, + + // Greater than or equal to 63 characters after `.` in hostname + [`http://www.${'z'.repeat(63)}example.com/node`]: { + href: `http://www.${'z'.repeat(63)}example.com/node`, + protocol: 'http:', + slashes: true, + host: `www.${'z'.repeat(63)}example.com`, + hostname: `www.${'z'.repeat(63)}example.com`, + pathname: '/node', + path: '/node' + }, + + // https://github.com/nodejs/node/issues/3361 + 'file:///home/user': { + href: 'file:///home/user', + protocol: 'file', + pathname: '/home/user', + path: '/home/user' + }, + + // surrogate in auth + 'http://%F0%9F%98%80@www.example.com/': { + href: 'http://%F0%9F%98%80@www.example.com/', + protocol: 'http:', + auth: '\uD83D\uDE00', + hostname: 'www.example.com', + pathname: '/' + } +}; +for (const u in formatTests) { + const expect = formatTests[u].href; + delete formatTests[u].href; + const actual = url.format(u); + const actualObj = url.format(formatTests[u]); + assert.strictEqual(actual, expect, + `wonky format(${u}) == ${expect}\nactual:${actual}`); + assert.strictEqual(actualObj, expect, + `wonky format(${JSON.stringify(formatTests[u])}) == ${ + expect}\nactual: ${actualObj}`); +} diff --git a/test/js/node/test/parallel/test-url-parse-format.js b/test/js/node/test/parallel/test-url-parse-format.js new file mode 100644 index 0000000000..f8761514a3 --- /dev/null +++ b/test/js/node/test/parallel/test-url-parse-format.js @@ -0,0 +1,1077 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const inspect = require('util').inspect; + +const url = require('url'); + +// URLs to parse, and expected data +// { url : parsed } +const parseTests = { + '//some_path': { + href: '//some_path', + pathname: '//some_path', + path: '//some_path' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + search: '?json=%22%5C%22foo%5C%22%22', + query: 'json=%22%5C%22foo%5C%22%22', + path: '/foo.html?json=%22%5C%22foo%5C%22%22', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch?blarg', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg' + }, + + + 'http:\\\\evil-phisher\\foo.html': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + href: 'http://evil-phisher/foo.html' + }, + + 'HTTP://www.example.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'HTTP://www.example.com': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://www.ExAmPlE.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user:pw@www.ExAmPlE.com/': { + href: 'http://user:pw@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user:pw', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://USER:PW@www.ExAmPlE.com/': { + href: 'http://USER:PW@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'USER:PW', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user@www.example.com/': { + href: 'http://user@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user%3Apw@www.example.com/': { + href: 'http://user:pw@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user:pw', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://x.com/path?that\'s#all, folks': { + href: 'http://x.com/path?that%27s#all,%20folks', + protocol: 'http:', + slashes: true, + host: 'x.com', + hostname: 'x.com', + search: '?that%27s', + query: 'that%27s', + pathname: '/path', + hash: '#all,%20folks', + path: '/path?that%27s' + }, + + 'HTTP://X.COM/Y': { + href: 'http://x.com/Y', + protocol: 'http:', + slashes: true, + host: 'x.com', + hostname: 'x.com', + pathname: '/Y', + path: '/Y' + }, + + // Whitespace in the front + ' http://www.example.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + // + not an invalid host character + // per https://url.spec.whatwg.org/#host-parsing + 'http://x.y.com+a/b/c': { + href: 'http://x.y.com+a/b/c', + protocol: 'http:', + slashes: true, + host: 'x.y.com+a', + hostname: 'x.y.com+a', + pathname: '/b/c', + path: '/b/c' + }, + + // An unexpected invalid char in the hostname. + 'HtTp://x.y.cOm;a/b/c?d=e#f gi': { + href: 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'x.y.com', + hostname: 'x.y.com', + pathname: ';a/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';a/b/c?d=e' + }, + + // Make sure that we don't accidentally lcast the path parts. + 'HtTp://x.y.cOm;A/b/c?d=e#f gi': { + href: 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'x.y.com', + hostname: 'x.y.com', + pathname: ';A/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';A/b/c?d=e' + }, + + 'http://x...y...#p': { + href: 'http://x...y.../#p', + protocol: 'http:', + slashes: true, + host: 'x...y...', + hostname: 'x...y...', + hash: '#p', + pathname: '/', + path: '/' + }, + + 'http://x/p/"quoted"': { + href: 'http://x/p/%22quoted%22', + protocol: 'http:', + slashes: true, + host: 'x', + hostname: 'x', + pathname: '/p/%22quoted%22', + path: '/p/%22quoted%22' + }, + + ' Is a URL!': { + href: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + pathname: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + path: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!' + }, + + 'http://www.narwhaljs.org/blog/categories?id=news': { + href: 'http://www.narwhaljs.org/blog/categories?id=news', + protocol: 'http:', + slashes: true, + host: 'www.narwhaljs.org', + hostname: 'www.narwhaljs.org', + search: '?id=news', + query: 'id=news', + pathname: '/blog/categories', + path: '/blog/categories?id=news' + }, + + 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + hostname: 'mt0.google.com', + pathname: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + path: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' + + '&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + hostname: 'mt0.google.com', + search: '???&hl=en&src=api&x=2&y=2&z=3&s=', + query: '??&hl=en&src=api&x=2&y=2&z=3&s=', + pathname: '/vt/lyrs=m@114', + path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + auth: 'user:pass', + hostname: 'mt0.google.com', + search: '???&hl=en&src=api&x=2&y=2&z=3&s=', + query: '??&hl=en&src=api&x=2&y=2&z=3&s=', + pathname: '/vt/lyrs=m@114', + path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'file:///etc/passwd': { + href: 'file:///etc/passwd', + slashes: true, + protocol: 'file:', + pathname: '/etc/passwd', + hostname: '', + host: '', + path: '/etc/passwd' + }, + + 'file://localhost/etc/passwd': { + href: 'file://localhost/etc/passwd', + protocol: 'file:', + slashes: true, + pathname: '/etc/passwd', + hostname: 'localhost', + host: 'localhost', + path: '/etc/passwd' + }, + + 'file://foo/etc/passwd': { + href: 'file://foo/etc/passwd', + protocol: 'file:', + slashes: true, + pathname: '/etc/passwd', + hostname: 'foo', + host: 'foo', + path: '/etc/passwd' + }, + + 'file:///etc/node/': { + href: 'file:///etc/node/', + slashes: true, + protocol: 'file:', + pathname: '/etc/node/', + hostname: '', + host: '', + path: '/etc/node/' + }, + + 'file://localhost/etc/node/': { + href: 'file://localhost/etc/node/', + protocol: 'file:', + slashes: true, + pathname: '/etc/node/', + hostname: 'localhost', + host: 'localhost', + path: '/etc/node/' + }, + + 'file://foo/etc/node/': { + href: 'file://foo/etc/node/', + protocol: 'file:', + slashes: true, + pathname: '/etc/node/', + hostname: 'foo', + host: 'foo', + path: '/etc/node/' + }, + + 'http:/baz/../foo/bar': { + href: 'http:/baz/../foo/bar', + protocol: 'http:', + pathname: '/baz/../foo/bar', + path: '/baz/../foo/bar' + }, + + 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag': { + href: 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag', + protocol: 'http:', + slashes: true, + host: 'example.com:8000', + auth: 'user:pass', + port: '8000', + hostname: 'example.com', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + '//user:pass@example.com:8000/foo/bar?baz=quux#frag': { + href: '//user:pass@example.com:8000/foo/bar?baz=quux#frag', + slashes: true, + host: 'example.com:8000', + auth: 'user:pass', + port: '8000', + hostname: 'example.com', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + '/foo/bar?baz=quux#frag': { + href: '/foo/bar?baz=quux#frag', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + 'http:/foo/bar?baz=quux#frag': { + href: 'http:/foo/bar?baz=quux#frag', + protocol: 'http:', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + 'mailto:foo@bar.com?subject=hello': { + href: 'mailto:foo@bar.com?subject=hello', + protocol: 'mailto:', + host: 'bar.com', + auth: 'foo', + hostname: 'bar.com', + search: '?subject=hello', + query: 'subject=hello', + path: '?subject=hello' + }, + + 'javascript:alert(\'hello\');': { + href: 'javascript:alert(\'hello\');', + protocol: 'javascript:', + pathname: 'alert(\'hello\');', + path: 'alert(\'hello\');' + }, + + 'xmpp:isaacschlueter@jabber.org': { + href: 'xmpp:isaacschlueter@jabber.org', + protocol: 'xmpp:', + host: 'jabber.org', + auth: 'isaacschlueter', + hostname: 'jabber.org' + }, + + 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar': { + href: 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', + protocol: 'http:', + slashes: true, + host: '127.0.0.1:8080', + auth: 'atpass:foo@bar', + hostname: '127.0.0.1', + port: '8080', + pathname: '/path', + search: '?search=foo', + query: 'search=foo', + hash: '#bar', + path: '/path?search=foo' + }, + + 'svn+ssh://foo/bar': { + href: 'svn+ssh://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'svn+ssh:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dash-test://foo/bar': { + href: 'dash-test://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dash-test:foo/bar': { + href: 'dash-test:foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + path: '/bar' + }, + + 'dot.test://foo/bar': { + href: 'dot.test://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dot.test:foo/bar': { + href: 'dot.test:foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + path: '/bar' + }, + + // IDNA tests + 'http://www.日本語.com/': { + href: 'http://www.xn--wgv71a119e.com/', + protocol: 'http:', + slashes: true, + host: 'www.xn--wgv71a119e.com', + hostname: 'www.xn--wgv71a119e.com', + pathname: '/', + path: '/' + }, + + 'http://example.Bücher.com/': { + href: 'http://example.xn--bcher-kva.com/', + protocol: 'http:', + slashes: true, + host: 'example.xn--bcher-kva.com', + hostname: 'example.xn--bcher-kva.com', + pathname: '/', + path: '/' + }, + + 'http://www.Äffchen.com/': { + href: 'http://www.xn--ffchen-9ta.com/', + protocol: 'http:', + slashes: true, + host: 'www.xn--ffchen-9ta.com', + hostname: 'www.xn--ffchen-9ta.com', + pathname: '/', + path: '/' + }, + + 'http://www.Äffchen.cOm;A/b/c?d=e#f gi': { + href: 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'www.xn--ffchen-9ta.com', + hostname: 'www.xn--ffchen-9ta.com', + pathname: ';A/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';A/b/c?d=e' + }, + + 'http://SÉLIER.COM/': { + href: 'http://xn--slier-bsa.com/', + protocol: 'http:', + slashes: true, + host: 'xn--slier-bsa.com', + hostname: 'xn--slier-bsa.com', + pathname: '/', + path: '/' + }, + + 'http://ليهمابتكلموشعربي؟.ي؟/': { + href: 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', + protocol: 'http:', + slashes: true, + host: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + hostname: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + pathname: '/', + path: '/' + }, + + 'http://➡.ws/➡': { + href: 'http://xn--hgi.ws/➡', + protocol: 'http:', + slashes: true, + host: 'xn--hgi.ws', + hostname: 'xn--hgi.ws', + pathname: '/➡', + path: '/➡' + }, + + 'http://bucket_name.s3.amazonaws.com/image.jpg': { + protocol: 'http:', + slashes: true, + host: 'bucket_name.s3.amazonaws.com', + hostname: 'bucket_name.s3.amazonaws.com', + pathname: '/image.jpg', + href: 'http://bucket_name.s3.amazonaws.com/image.jpg', + path: '/image.jpg' + }, + + 'git+http://github.com/joyent/node.git': { + protocol: 'git+http:', + slashes: true, + host: 'github.com', + hostname: 'github.com', + pathname: '/joyent/node.git', + path: '/joyent/node.git', + href: 'git+http://github.com/joyent/node.git' + }, + + // If local1@domain1 is uses as a relative URL it may + // be parse into auth@hostname, but here there is no + // way to make it work in url.parse, I add the test to be explicit + 'local1@domain1': { + pathname: 'local1@domain1', + path: 'local1@domain1', + href: 'local1@domain1' + }, + + // While this may seem counter-intuitive, a browser will parse + // as a path. + 'www.example.com': { + href: 'www.example.com', + pathname: 'www.example.com', + path: 'www.example.com' + }, + + // ipv6 support + '[fe80::1]': { + href: '[fe80::1]', + pathname: '[fe80::1]', + path: '[fe80::1]' + }, + + 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { + protocol: 'coap:', + slashes: true, + host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', + hostname: 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', + href: 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', + pathname: '/', + path: '/' + }, + + 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { + protocol: 'coap:', + slashes: true, + host: '[1080:0:0:0:8:800:200c:417a]:61616', + port: '61616', + hostname: '1080:0:0:0:8:800:200c:417a', + href: 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', + pathname: '/', + path: '/' + }, + + 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { + protocol: 'http:', + slashes: true, + auth: 'user:password', + host: '[3ffe:2a00:100:7031::1]:8080', + port: '8080', + hostname: '3ffe:2a00:100:7031::1', + href: 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', + pathname: '/', + path: '/' + }, + + 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { + protocol: 'coap:', + slashes: true, + auth: 'u:p', + host: '[::192.9.5.5]:61616', + port: '61616', + hostname: '::192.9.5.5', + href: 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', + search: '?n=Temperature', + query: 'n=Temperature', + pathname: '/.well-known/r', + path: '/.well-known/r?n=Temperature' + }, + + // empty port + 'http://example.com:': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/', + pathname: '/', + path: '/' + }, + + 'http://example.com:/a/b.html': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/a/b.html', + pathname: '/a/b.html', + path: '/a/b.html' + }, + + 'http://example.com:?a=b': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/?a=b', + search: '?a=b', + query: 'a=b', + pathname: '/', + path: '/?a=b' + }, + + 'http://example.com:#abc': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/#abc', + hash: '#abc', + pathname: '/', + path: '/' + }, + + 'http://[fe80::1]:/a/b?a=b#abc': { + protocol: 'http:', + slashes: true, + host: '[fe80::1]', + hostname: 'fe80::1', + href: 'http://[fe80::1]/a/b?a=b#abc', + search: '?a=b', + query: 'a=b', + hash: '#abc', + pathname: '/a/b', + path: '/a/b?a=b' + }, + + 'http://-lovemonsterz.tumblr.com/rss': { + protocol: 'http:', + slashes: true, + host: '-lovemonsterz.tumblr.com', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://-lovemonsterz.tumblr.com/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://-lovemonsterz.tumblr.com:80/rss': { + protocol: 'http:', + slashes: true, + port: '80', + host: '-lovemonsterz.tumblr.com:80', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://-lovemonsterz.tumblr.com:80/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com/rss': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: '-lovemonsterz.tumblr.com', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://user:pass@-lovemonsterz.tumblr.com/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + port: '80', + host: '-lovemonsterz.tumblr.com:80', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://user:pass@-lovemonsterz.tumblr.com:80/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://_jabber._tcp.google.com/test': { + protocol: 'http:', + slashes: true, + host: '_jabber._tcp.google.com', + hostname: '_jabber._tcp.google.com', + href: 'http://_jabber._tcp.google.com/test', + pathname: '/test', + path: '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com/test': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: '_jabber._tcp.google.com', + hostname: '_jabber._tcp.google.com', + href: 'http://user:pass@_jabber._tcp.google.com/test', + pathname: '/test', + path: '/test', + }, + + 'http://_jabber._tcp.google.com:80/test': { + protocol: 'http:', + slashes: true, + port: '80', + host: '_jabber._tcp.google.com:80', + hostname: '_jabber._tcp.google.com', + href: 'http://_jabber._tcp.google.com:80/test', + pathname: '/test', + path: '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com:80/test': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + port: '80', + host: '_jabber._tcp.google.com:80', + hostname: '_jabber._tcp.google.com', + href: 'http://user:pass@_jabber._tcp.google.com:80/test', + pathname: '/test', + path: '/test', + }, + + 'http://x:1/\' <>"`/{}|\\^~`/': { + protocol: 'http:', + slashes: true, + host: 'x:1', + port: '1', + hostname: 'x', + pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/' + }, + + 'http://a@b@c/': { + protocol: 'http:', + slashes: true, + auth: 'a@b', + host: 'c', + hostname: 'c', + href: 'http://a%40b@c/', + path: '/', + pathname: '/' + }, + + 'http://a@b?@c': { + protocol: 'http:', + slashes: true, + auth: 'a', + host: 'b', + hostname: 'b', + href: 'http://a@b/?@c', + path: '/?@c', + pathname: '/', + search: '?@c', + query: '@c' + }, + + 'http://a.b/\tbc\ndr\ref g"hq\'j?mn\\op^q=r`99{st|uv}wz': { + protocol: 'http:', + slashes: true, + host: 'a.b', + port: null, + hostname: 'a.b', + hash: null, + pathname: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E', + path: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + search: '?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + query: 'mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + href: 'http://a.b/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz' + }, + + 'http://a\r" \t\n<\'b:b@c\r\nd/e?f': { + protocol: 'http:', + slashes: true, + auth: 'a" <\'b:b', + host: 'cd', + port: null, + hostname: 'cd', + hash: null, + search: '?f', + query: 'f', + pathname: '/e', + path: '/e?f', + href: 'http://a%22%20%3C\'b:b@cd/e?f' + }, + + // Git urls used by npm + 'git+ssh://git@github.com:npm/npm': { + protocol: 'git+ssh:', + slashes: true, + auth: 'git', + host: 'github.com', + port: null, + hostname: 'github.com', + hash: null, + search: null, + query: null, + pathname: '/:npm/npm', + path: '/:npm/npm', + href: 'git+ssh://git@github.com/:npm/npm' + }, + + 'https://*': { + protocol: 'https:', + slashes: true, + auth: null, + host: '*', + port: null, + hostname: '*', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'https://*/' + }, + + // The following two URLs are the same, but they differ for a capital A. + // Verify that the protocol is checked in a case-insensitive manner. + 'javascript:alert(1);a=\x27@white-listed.com\x27': { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }, + + 'javAscript:alert(1);a=\x27@white-listed.com\x27': { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }, + + 'ws://www.example.com': { + protocol: 'ws:', + slashes: true, + hostname: 'www.example.com', + host: 'www.example.com', + pathname: '/', + path: '/', + href: 'ws://www.example.com/' + }, + + 'wss://www.example.com': { + protocol: 'wss:', + slashes: true, + hostname: 'www.example.com', + host: 'www.example.com', + pathname: '/', + path: '/', + href: 'wss://www.example.com/' + }, + + '//fhqwhgads@example.com/everybody-to-the-limit': { + protocol: null, + slashes: true, + auth: 'fhqwhgads', + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/everybody-to-the-limit', + path: '/everybody-to-the-limit', + href: '//fhqwhgads@example.com/everybody-to-the-limit' + }, + + '//fhqwhgads@example.com/everybody#to-the-limit': { + protocol: null, + slashes: true, + auth: 'fhqwhgads', + host: 'example.com', + port: null, + hostname: 'example.com', + hash: '#to-the-limit', + search: null, + query: null, + pathname: '/everybody', + path: '/everybody', + href: '//fhqwhgads@example.com/everybody#to-the-limit' + }, + + '\bhttp://example.com/\b': { + protocol: 'http:', + slashes: true, + auth: null, + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'http://example.com/' + }, + + 'https://evil.com$.example.com': { + protocol: 'https:', + slashes: true, + auth: null, + host: 'evil.com$.example.com', + port: null, + hostname: 'evil.com$.example.com', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'https://evil.com$.example.com/' + }, + + // Validate the output of hostname with commas. + 'x://0.0,1.1/': { + protocol: 'x:', + slashes: true, + auth: null, + host: '0.0,1.1', + port: null, + hostname: '0.0,1.1', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'x://0.0,1.1/' + } +}; + +for (const u in parseTests) { + let actual = url.parse(u); + const spaced = url.parse(` \t ${u}\n\t`); + let expected = Object.assign(new url.Url(), parseTests[u]); + + Object.keys(actual).forEach(function(i) { + if (expected[i] === undefined && actual[i] === null) { + expected[i] = null; + } + }); + + assert.deepStrictEqual( + actual, + expected, + `parsing ${u} and expected ${inspect(expected)} but got ${inspect(actual)}` + ); + assert.deepStrictEqual( + spaced, + expected, + `expected ${inspect(expected)}, got ${inspect(spaced)}` + ); + + expected = parseTests[u].href; + actual = url.format(parseTests[u]); + + assert.strictEqual(actual, expected, + `format(${u}) == ${u}\nactual:${actual}`); +} + +{ + const parsed = url.parse('http://nodejs.org/') + .resolveObject('jAvascript:alert(1);a=\x27@white-listed.com\x27'); + + const expected = Object.assign(new url.Url(), { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }); + + assert.deepStrictEqual(parsed, expected); +} diff --git a/test/js/node/test/parallel/test-url-revokeobjecturl.js b/test/js/node/test/parallel/test-url-revokeobjecturl.js new file mode 100644 index 0000000000..dae980c4d0 --- /dev/null +++ b/test/js/node/test/parallel/test-url-revokeobjecturl.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); + +// Test ensures that the function receives the url argument. + +const assert = require('node:assert'); + +assert.throws(() => { + URL.revokeObjectURL(); +}, { + code: 'ERR_MISSING_ARGS', + name: 'TypeError', +}); diff --git a/test/js/node/test/parallel/test-url-urltooptions.js b/test/js/node/test/parallel/test-url-urltooptions.js new file mode 100644 index 0000000000..cc4838eeec --- /dev/null +++ b/test/js/node/test/parallel/test-url-urltooptions.js @@ -0,0 +1,37 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { urlToHttpOptions } = require('url'); + +// Test urlToHttpOptions +const urlObj = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test'); +const opts = urlToHttpOptions(urlObj); +assert.strictEqual(opts instanceof URL, false); +assert.strictEqual(opts.protocol, 'http:'); +assert.strictEqual(opts.auth, 'user:pass'); +assert.strictEqual(opts.hostname, 'foo.bar.com'); +assert.strictEqual(opts.port, 21); +assert.strictEqual(opts.path, '/aaa/zzz?l=24'); +assert.strictEqual(opts.pathname, '/aaa/zzz'); +assert.strictEqual(opts.search, '?l=24'); +assert.strictEqual(opts.hash, '#test'); + +const { hostname } = urlToHttpOptions(new URL('http://[::1]:21')); +assert.strictEqual(hostname, '::1'); + +// If a WHATWG URL object is copied, it is possible that the resulting copy +// contains the Symbols that Node uses for brand checking, but not the data +// properties, which are getters. Verify that urlToHttpOptions() can handle +// such a case. +const copiedUrlObj = { ...urlObj }; +const copiedOpts = urlToHttpOptions(copiedUrlObj); +assert.strictEqual(copiedOpts instanceof URL, false); +assert.strictEqual(copiedOpts.protocol, undefined); +assert.strictEqual(copiedOpts.auth, undefined); +assert.strictEqual(copiedOpts.hostname, undefined); +assert.strictEqual(copiedOpts.port, NaN); +assert.strictEqual(copiedOpts.path, ''); +assert.strictEqual(copiedOpts.pathname, undefined); +assert.strictEqual(copiedOpts.search, undefined); +assert.strictEqual(copiedOpts.hash, undefined); +assert.strictEqual(copiedOpts.href, undefined); diff --git a/test/js/node/test/parallel/test-utf8-scripts.js b/test/js/node/test/parallel/test-utf8-scripts.js new file mode 100644 index 0000000000..4bf5b0cd5b --- /dev/null +++ b/test/js/node/test/parallel/test-utf8-scripts.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// üäö + +console.log('Σὲ γνωρίζω ἀπὸ τὴν κόψη'); + +assert.match('Hellö Wörld', /Hellö Wörld/); diff --git a/test/js/node/test/parallel/test-util-inspect-getters-accessing-this.js b/test/js/node/test/parallel/test-util-inspect-getters-accessing-this.js new file mode 100644 index 0000000000..998cd82db8 --- /dev/null +++ b/test/js/node/test/parallel/test-util-inspect-getters-accessing-this.js @@ -0,0 +1,67 @@ +'use strict'; + +require('../common'); + +// This test ensures that util.inspect logs getters +// which access this. + +const assert = require('assert'); + +const { inspect } = require('util'); + +{ + class X { + constructor() { + this._y = 123; + } + + get y() { + return this._y; + } + } + + const result = inspect(new X(), { + getters: true, + showHidden: true + }); + + assert.strictEqual( + result, + 'X { _y: 123, [y]: [Getter: 123] }' + ); +} + +// Regression test for https://github.com/nodejs/node/issues/37054 +{ + class A { + constructor(B) { + this.B = B; + } + get b() { + return this.B; + } + } + + class B { + constructor() { + this.A = new A(this); + } + get a() { + return this.A; + } + } + + const result = inspect(new B(), { + depth: 1, + getters: true, + showHidden: true + }); + + assert.strictEqual( + result, + ' B {\n' + + ' A: A { B: [Circular *1], [b]: [Getter] [Circular *1] },\n' + + ' [a]: [Getter] A { B: [Circular *1], [b]: [Getter] [Circular *1] }\n' + + '}', + ); +} diff --git a/test/js/node/test/parallel/test-util-primordial-monkeypatching.js b/test/js/node/test/parallel/test-util-primordial-monkeypatching.js new file mode 100644 index 0000000000..bf282a1212 --- /dev/null +++ b/test/js/node/test/parallel/test-util-primordial-monkeypatching.js @@ -0,0 +1,11 @@ +'use strict'; + +// Monkeypatch Object.keys() so that it throws an unexpected error. This tests +// that `util.inspect()` is unaffected by monkey-patching `Object`. + +require('../common'); +const assert = require('assert'); +const util = require('util'); + +Object.keys = () => { throw new Error('fhqwhgads'); }; +assert.strictEqual(util.inspect({}), '{}'); diff --git a/test/js/node/test/parallel/test-v8-deserialize-buffer.js b/test/js/node/test/parallel/test-v8-deserialize-buffer.js new file mode 100644 index 0000000000..f05631a72a --- /dev/null +++ b/test/js/node/test/parallel/test-v8-deserialize-buffer.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); + +process.on('warning', common.mustNotCall()); +v8.deserialize(v8.serialize(Buffer.alloc(0))); diff --git a/test/js/node/test/parallel/v8-global-setter.test.js b/test/js/node/test/parallel/test-v8-global-setter.js similarity index 80% rename from test/js/node/test/parallel/v8-global-setter.test.js rename to test/js/node/test/parallel/test-v8-global-setter.js index eb5fe888f2..1cb0898e61 100644 --- a/test/js/node/test/parallel/v8-global-setter.test.js +++ b/test/js/node/test/parallel/test-v8-global-setter.js @@ -1,6 +1,3 @@ -//#FILE: test-v8-global-setter.js -//#SHA1: 8711ed3c5a3491001fcf441ed56e14fcca67c8ef -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,16 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +require('../common'); // This test ensures v8 correctly sets a property on the global object if it // has a setter interceptor in strict mode. // https://github.com/nodejs/node-v0.x-archive/issues/6235 -test("v8 global setter in strict mode", () => { - expect(() => { - require("vm").runInNewContext('"use strict"; var v = 1; v = 2'); - }).not.toThrow(); -}); - -//<#END_FILE: test-v8-global-setter.js +require('vm').runInNewContext('"use strict"; var v = 1; v = 2'); diff --git a/test/js/node/test/parallel/vm-access-process-env.test.js b/test/js/node/test/parallel/test-vm-access-process-env.js similarity index 76% rename from test/js/node/test/parallel/vm-access-process-env.test.js rename to test/js/node/test/parallel/test-vm-access-process-env.js index 4bac2e235c..c6b18ec902 100644 --- a/test/js/node/test/parallel/vm-access-process-env.test.js +++ b/test/js/node/test/parallel/test-vm-access-process-env.js @@ -1,6 +1,3 @@ -//#FILE: test-vm-access-process-env.js -//#SHA1: ffc014abc0d92ea62b7c0510d69cad5e89d632af -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,17 +19,15 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // Tests that node does neither crash nor throw an error when accessing // process.env when inside a VM context. // See https://github.com/nodejs/node-v0.x-archive/issues/7511. -const vm = require("vm"); +require('../common'); +const assert = require('assert'); +const vm = require('vm'); -test("VM context can access process.env", () => { - const context = vm.createContext({ process }); - const result = vm.runInContext('process.env["PATH"]', context); - expect(result).not.toBeUndefined(); -}); - -//<#END_FILE: test-vm-access-process-env.js +const context = vm.createContext({ process }); +const result = vm.runInContext('process.env["PATH"]', context); +assert.notStrictEqual(undefined, result); diff --git a/test/js/node/test/parallel/test-vm-attributes-property-not-on-sandbox.js b/test/js/node/test/parallel/test-vm-attributes-property-not-on-sandbox.js new file mode 100644 index 0000000000..313dd71e47 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-attributes-property-not-on-sandbox.js @@ -0,0 +1,18 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// Assert that accessor descriptors are not flattened on the sandbox. +// Issue: https://github.com/nodejs/node/issues/2734 +const sandbox = {}; +vm.createContext(sandbox); +const code = `Object.defineProperty( + this, + 'foo', + { get: function() {return 17} } + ); + var desc = Object.getOwnPropertyDescriptor(this, 'foo');`; + +vm.runInContext(code, sandbox); +assert.strictEqual(typeof sandbox.desc.get, 'function'); diff --git a/test/js/node/test/parallel/test-vm-context-async-script.js b/test/js/node/test/parallel/test-vm-context-async-script.js new file mode 100644 index 0000000000..879315e37b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-context-async-script.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { setTimeout }; + +const ctx = vm.createContext(sandbox); + +vm.runInContext('setTimeout(function() { x = 3; }, 0);', ctx); +setTimeout(common.mustCall(() => { + assert.strictEqual(sandbox.x, 3); + assert.strictEqual(ctx.x, 3); +}), 1); diff --git a/test/js/node/test/parallel/test-vm-context-property-forwarding.js b/test/js/node/test/parallel/test-vm-context-property-forwarding.js new file mode 100644 index 0000000000..53d38c1467 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-context-property-forwarding.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { x: 3 }; + +const ctx = vm.createContext(sandbox); + +assert.strictEqual(vm.runInContext('x;', ctx), 3); +vm.runInContext('y = 4;', ctx); +assert.strictEqual(sandbox.y, 4); +assert.strictEqual(ctx.y, 4); + +// Test `IndexedPropertyGetterCallback` and `IndexedPropertyDeleterCallback` +const x = { get 1() { return 5; } }; +const pd_expected = Object.getOwnPropertyDescriptor(x, 1); +const ctx2 = vm.createContext(x); +const pd_actual = Object.getOwnPropertyDescriptor(ctx2, 1); + +assert.deepStrictEqual(pd_actual, pd_expected); +assert.strictEqual(ctx2[1], 5); +delete ctx2[1]; +assert.strictEqual(ctx2[1], undefined); + +// https://github.com/nodejs/node/issues/33806 +{ + const ctx = vm.createContext(); + + Object.defineProperty(ctx, 'prop', { + get() { + return undefined; + }, + set(val) { + throw new Error('test error'); + }, + }); + + assert.throws(() => { + vm.runInContext('prop = 42', ctx); + }, { + message: 'test error', + }); +} diff --git a/test/js/node/test/parallel/vm-context-async-script.test.js b/test/js/node/test/parallel/test-vm-create-context-accessors.js similarity index 68% rename from test/js/node/test/parallel/vm-context-async-script.test.js rename to test/js/node/test/parallel/test-vm-create-context-accessors.js index 52d90dde39..39fc5c4eec 100644 --- a/test/js/node/test/parallel/vm-context-async-script.test.js +++ b/test/js/node/test/parallel/test-vm-create-context-accessors.js @@ -1,6 +1,3 @@ -//#FILE: test-vm-context-async-script.js -//#SHA1: 411e3a9c4ea8c062420ae8f112c62ed7c2a1a962 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,20 +19,31 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const vm = require("vm"); +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); -test("vm.runInContext with async script", done => { - const sandbox = { setTimeout }; - const ctx = vm.createContext(sandbox); +let ctx = {}; - vm.runInContext("setTimeout(function() { x = 3; }, 0);", ctx); - - setTimeout(() => { - expect(sandbox.x).toBe(3); - expect(ctx.x).toBe(3); - done(); - }, 1); +Object.defineProperty(ctx, 'getter', { + get: function() { + return 'ok'; + } }); -//<#END_FILE: test-vm-context-async-script.js +let val; +Object.defineProperty(ctx, 'setter', { + set: function(_val) { + val = _val; + }, + get: function() { + return `ok=${val}`; + } +}); + +ctx = vm.createContext(ctx); + +const result = vm.runInContext('setter = "test";[getter,setter]', ctx); +assert.strictEqual(result[0], 'ok'); +assert.strictEqual(result[1], 'ok=test'); diff --git a/test/js/node/test/parallel/test-vm-create-context-circular-reference.js b/test/js/node/test/parallel/test-vm-create-context-circular-reference.js new file mode 100644 index 0000000000..7ea22781bd --- /dev/null +++ b/test/js/node/test/parallel/test-vm-create-context-circular-reference.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +let sbx = {}; +sbx.window = sbx; + +sbx = vm.createContext(sbx); + +sbx.test = 123; + +assert.strictEqual(sbx.window.window.window.window.window.test, 123); diff --git a/test/js/node/test/parallel/test-vm-cross-context.js b/test/js/node/test/parallel/test-vm-cross-context.js new file mode 100644 index 0000000000..b7cf1309d3 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-cross-context.js @@ -0,0 +1,29 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +const vm = require('vm'); +const ctx = vm.createContext(global); + +// Should not throw. +vm.runInContext('!function() { var x = console.log; }()', ctx); diff --git a/test/js/node/test/parallel/test-vm-data-property-writable.js b/test/js/node/test/parallel/test-vm-data-property-writable.js new file mode 100644 index 0000000000..00937ae412 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-data-property-writable.js @@ -0,0 +1,28 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/10223 + +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +const context = vm.createContext({}); + +let code = ` + Object.defineProperty(this, 'foo', {value: 5}); + Object.getOwnPropertyDescriptor(this, 'foo'); +`; + +let desc = vm.runInContext(code, context); + +assert.strictEqual(desc.writable, false); + +// Check that interceptors work for symbols. +code = ` + const bar = Symbol('bar'); + Object.defineProperty(this, bar, {value: 6}); + Object.getOwnPropertyDescriptor(this, bar); +`; + +desc = vm.runInContext(code, context); + +assert.strictEqual(desc.value, 6); diff --git a/test/js/node/test/parallel/test-vm-deleting-property.js b/test/js/node/test/parallel/test-vm-deleting-property.js new file mode 100644 index 0000000000..994aa0aff9 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-deleting-property.js @@ -0,0 +1,15 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/6287 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const context = vm.createContext(); +const res = vm.runInContext(` + this.x = 'prop'; + delete this.x; + Object.getOwnPropertyDescriptor(this, 'x'); +`, context); + +assert.strictEqual(res, undefined); diff --git a/test/js/node/test/parallel/test-vm-function-declaration.js b/test/js/node/test/parallel/test-vm-function-declaration.js new file mode 100644 index 0000000000..766e5ec78b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-function-declaration.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); +const o = vm.createContext({ console }); + +// Function declaration and expression should both be copied to the +// sandboxed context. +let code = 'let a = function() {};\n'; +code += 'function b(){}\n'; +code += 'var c = function() {};\n'; +code += 'var d = () => {};\n'; +code += 'let e = () => {};\n'; + +// Grab the global b function as the completion value, to ensure that +// we are getting the global function, and not some other thing +code += '(function(){return this})().b;\n'; + +const res = vm.runInContext(code, o, 'test'); +assert.strictEqual(typeof res, 'function'); +assert.strictEqual(res.name, 'b'); +assert.strictEqual(typeof o.a, 'undefined'); +assert.strictEqual(typeof o.b, 'function'); +assert.strictEqual(typeof o.c, 'function'); +assert.strictEqual(typeof o.d, 'function'); +assert.strictEqual(typeof o.e, 'undefined'); +assert.strictEqual(res, o.b); diff --git a/test/js/node/test/parallel/test-vm-function-redefinition.js b/test/js/node/test/parallel/test-vm-function-redefinition.js new file mode 100644 index 0000000000..fa1ddde389 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-function-redefinition.js @@ -0,0 +1,11 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/548 +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const context = vm.createContext(); + +vm.runInContext('function test() { return 0; }', context); +vm.runInContext('function test() { return 1; }', context); +const result = vm.runInContext('test()', context); +assert.strictEqual(result, 1); diff --git a/test/js/node/test/parallel/test-vm-global-assignment.js b/test/js/node/test/parallel/test-vm-global-assignment.js new file mode 100644 index 0000000000..3fb3470b4c --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-assignment.js @@ -0,0 +1,15 @@ +'use strict'; +// Regression test for https://github.com/nodejs/node/issues/10806 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const ctx = vm.createContext({ open() { } }); +const window = vm.runInContext('this', ctx); +const other = 123; + +assert.notStrictEqual(window.open, other); +window.open = other; +assert.strictEqual(window.open, other); +window.open = other; +assert.strictEqual(window.open, other); diff --git a/test/js/node/test/parallel/test-vm-global-configurable-properties.js b/test/js/node/test/parallel/test-vm-global-configurable-properties.js new file mode 100644 index 0000000000..4428e747ea --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-configurable-properties.js @@ -0,0 +1,15 @@ +'use strict'; +// https://github.com/nodejs/node/issues/47799 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const ctx = vm.createContext(); + +const window = vm.runInContext('this', ctx); + +Object.defineProperty(window, 'x', { value: '1', configurable: true }); +assert.strictEqual(window.x, '1'); +Object.defineProperty(window, 'x', { value: '2', configurable: true }); +assert.strictEqual(window.x, '2'); diff --git a/test/js/node/test/parallel/test-vm-global-get-own.js b/test/js/node/test/parallel/test-vm-global-get-own.js new file mode 100644 index 0000000000..246fcbf866 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-get-own.js @@ -0,0 +1,105 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// These assertions check that we can set new keys to the global context, +// get them back and also list them via getOwnProperty* or in. +// +// Related to: +// - https://github.com/nodejs/node/issues/45983 + +const global = vm.runInContext('this', vm.createContext()); + +function runAssertions(data, property, viaDefine, value1, value2, value3) { + // Define the property for the first time + setPropertyAndAssert(data, property, viaDefine, value1); + // Update the property + setPropertyAndAssert(data, property, viaDefine, value2); + // Delete the property + deletePropertyAndAssert(data, property); + // Re-define the property + setPropertyAndAssert(data, property, viaDefine, value3); + // Delete the property again + deletePropertyAndAssert(data, property); +} + +const fun1 = () => 1; +const fun2 = () => 2; +const fun3 = () => 3; + +function runAssertionsOnSandbox(builder) { + const sandboxContext = vm.createContext({ runAssertions, fun1, fun2, fun3 }); + vm.runInContext(builder('this'), sandboxContext); + vm.runInContext(builder('{}'), sandboxContext); +} + +// Assertions on: define property +runAssertions(global, 'toto', true, 1, 2, 3); +runAssertions(global, Symbol.for('toto'), true, 1, 2, 3); +runAssertions(global, 'tutu', true, fun1, fun2, fun3); +runAssertions(global, Symbol.for('tutu'), true, fun1, fun2, fun3); +runAssertions(global, 'tyty', true, fun1, 2, 3); +runAssertions(global, Symbol.for('tyty'), true, fun1, 2, 3); + +// Assertions on: direct assignment +runAssertions(global, 'titi', false, 1, 2, 3); +runAssertions(global, Symbol.for('titi'), false, 1, 2, 3); +runAssertions(global, 'tata', false, fun1, fun2, fun3); +runAssertions(global, Symbol.for('tata'), false, fun1, fun2, fun3); +runAssertions(global, 'tztz', false, fun1, 2, 3); +runAssertions(global, Symbol.for('tztz'), false, fun1, 2, 3); + +// Assertions on: define property from sandbox +runAssertionsOnSandbox( + (variable) => ` + runAssertions(${variable}, 'toto', true, 1, 2, 3); + runAssertions(${variable}, Symbol.for('toto'), true, 1, 2, 3); + runAssertions(${variable}, 'tutu', true, fun1, fun2, fun3); + runAssertions(${variable}, Symbol.for('tutu'), true, fun1, fun2, fun3); + runAssertions(${variable}, 'tyty', true, fun1, 2, 3); + runAssertions(${variable}, Symbol.for('tyty'), true, fun1, 2, 3);` +); + +// Assertions on: direct assignment from sandbox +runAssertionsOnSandbox( + (variable) => ` + runAssertions(${variable}, 'titi', false, 1, 2, 3); + runAssertions(${variable}, Symbol.for('titi'), false, 1, 2, 3); + runAssertions(${variable}, 'tata', false, fun1, fun2, fun3); + runAssertions(${variable}, Symbol.for('tata'), false, fun1, fun2, fun3); + runAssertions(${variable}, 'tztz', false, fun1, 2, 3); + runAssertions(${variable}, Symbol.for('tztz'), false, fun1, 2, 3);` +); + +// Helpers + +// Set the property on data and assert it worked +function setPropertyAndAssert(data, property, viaDefine, value) { + if (viaDefine) { + Object.defineProperty(data, property, { + enumerable: true, + writable: true, + value: value, + configurable: true, + }); + } else { + data[property] = value; + } + assert.strictEqual(data[property], value); + assert.ok(property in data); + if (typeof property === 'string') { + assert.ok(Object.getOwnPropertyNames(data).includes(property)); + } else { + assert.ok(Object.getOwnPropertySymbols(data).includes(property)); + } +} + +// Delete the property from data and assert it worked +function deletePropertyAndAssert(data, property) { + delete data[property]; + assert.strictEqual(data[property], undefined); + assert.ok(!(property in data)); + assert.ok(!Object.getOwnPropertyNames(data).includes(property)); + assert.ok(!Object.getOwnPropertySymbols(data).includes(property)); +} diff --git a/test/js/node/test/parallel/test-vm-harmony-symbols.js b/test/js/node/test/parallel/test-vm-harmony-symbols.js new file mode 100644 index 0000000000..5936025070 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-harmony-symbols.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// The sandbox should have its own Symbol constructor. +let sandbox = {}; +vm.runInNewContext('this.Symbol = Symbol', sandbox); +assert.strictEqual(typeof sandbox.Symbol, 'function'); +assert.notStrictEqual(sandbox.Symbol, Symbol); + +// Unless we copy the Symbol constructor explicitly, of course. +sandbox = { Symbol }; +vm.runInNewContext('this.Symbol = Symbol', sandbox); +assert.strictEqual(typeof sandbox.Symbol, 'function'); +assert.strictEqual(sandbox.Symbol, Symbol); diff --git a/test/js/node/test/parallel/test-vm-indexed-properties.js b/test/js/node/test/parallel/test-vm-indexed-properties.js new file mode 100644 index 0000000000..34ef8d020b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-indexed-properties.js @@ -0,0 +1,17 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const code = `Object.defineProperty(this, 99, { + value: 20, + enumerable: true + });`; + + +const sandbox = {}; +const ctx = vm.createContext(sandbox); +vm.runInContext(code, ctx); + +assert.strictEqual(sandbox[99], 20); diff --git a/test/js/node/test/parallel/test-vm-low-stack-space.js b/test/js/node/test/parallel/test-vm-low-stack-space.js new file mode 100644 index 0000000000..6a49a983d5 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-low-stack-space.js @@ -0,0 +1,26 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +function a() { + try { + return a(); + } catch { + // Throw an exception as near to the recursion-based RangeError as possible. + return vm.runInThisContext('() => 42')(); + } +} + +assert.strictEqual(a(), 42); + +function b() { + try { + return b(); + } catch { + // This writes a lot of noise to stderr, but it still works. + return vm.runInNewContext('() => 42')(); + } +} + +assert.strictEqual(b(), 42); diff --git a/test/js/node/test/parallel/test-vm-new-script-this-context.js b/test/js/node/test/parallel/test-vm-new-script-this-context.js new file mode 100644 index 0000000000..18f39f9086 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-new-script-this-context.js @@ -0,0 +1,68 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Script = require('vm').Script; + +// Run a string +let script = new Script('\'passed\';'); +const result = script.runInThisContext(script); +assert.strictEqual(result, 'passed'); + +// Thrown error +script = new Script('throw new Error(\'test\');'); +assert.throws(() => { + script.runInThisContext(script); +}, /^Error: test$/); + +global.hello = 5; +script = new Script('hello = 2'); +script.runInThisContext(script); +assert.strictEqual(global.hello, 2); + + +// Pass values +global.code = 'foo = 1;' + + 'bar = 2;' + + 'if (typeof baz !== "undefined") throw new Error("test fail");'; +global.foo = 2; +global.obj = { foo: 0, baz: 3 }; +script = new Script(global.code); +script.runInThisContext(script); +assert.strictEqual(global.obj.foo, 0); +assert.strictEqual(global.bar, 2); +assert.strictEqual(global.foo, 1); + +// Call a function +global.f = function() { global.foo = 100; }; +script = new Script('f()'); +script.runInThisContext(script); +assert.strictEqual(global.foo, 100); + +common.allowGlobals( + global.hello, + global.code, + global.foo, + global.obj, + global.f +); diff --git a/test/js/node/test/parallel/test-vm-parse-abort-on-uncaught-exception.js b/test/js/node/test/parallel/test-vm-parse-abort-on-uncaught-exception.js new file mode 100644 index 0000000000..64bbb4dd4f --- /dev/null +++ b/test/js/node/test/parallel/test-vm-parse-abort-on-uncaught-exception.js @@ -0,0 +1,18 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +require('../common'); +const vm = require('vm'); + +// Regression test for https://github.com/nodejs/node/issues/13258 + +try { + new vm.Script({ toString() { throw new Error('foo'); } }, {}); +} catch { + // Continue regardless of error. +} + +try { + new vm.Script('[', {}); +} catch { + // Continue regardless of error. +} diff --git a/test/js/node/test/parallel/test-vm-proxies.js b/test/js/node/test/parallel/test-vm-proxies.js new file mode 100644 index 0000000000..405f730577 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-proxies.js @@ -0,0 +1,18 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// src/node_contextify.cc filters out the Proxy object from the parent +// context. Make sure that the new context has a Proxy object of its own. +let sandbox = {}; +vm.runInNewContext('this.Proxy = Proxy', sandbox); +assert.strictEqual(typeof sandbox.Proxy, 'function'); +assert.notStrictEqual(sandbox.Proxy, Proxy); + +// Unless we copy the Proxy object explicitly, of course. +sandbox = { Proxy }; +vm.runInNewContext('this.Proxy = Proxy', sandbox); +assert.strictEqual(typeof sandbox.Proxy, 'function'); +assert.strictEqual(sandbox.Proxy, Proxy); diff --git a/test/js/node/test/parallel/test-vm-proxy-failure-CP.js b/test/js/node/test/parallel/test-vm-proxy-failure-CP.js new file mode 100644 index 0000000000..93027576d8 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-proxy-failure-CP.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); +const vm = require('vm'); + +// Check that we do not accidentally query attributes. +// Issue: https://github.com/nodejs/node/issues/11902 +const handler = { + getOwnPropertyDescriptor: (target, prop) => { + throw new Error('whoops'); + } +}; +const sandbox = new Proxy({ foo: 'bar' }, handler); +const context = vm.createContext(sandbox); + +vm.runInContext('', context); diff --git a/test/js/node/test/parallel/test-vm-script-throw-in-tostring.js b/test/js/node/test/parallel/test-vm-script-throw-in-tostring.js new file mode 100644 index 0000000000..20e7a75079 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-script-throw-in-tostring.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); + +assert.throws(() => { + new vm.Script({ + toString() { + throw new Error(); + } + }); +}, Error); diff --git a/test/js/node/test/parallel/child-process-kill.test.js b/test/js/node/test/parallel/test-vm-static-this.js similarity index 51% rename from test/js/node/test/parallel/child-process-kill.test.js rename to test/js/node/test/parallel/test-vm-static-this.js index 5a9ece5e38..e9382d6c3b 100644 --- a/test/js/node/test/parallel/child-process-kill.test.js +++ b/test/js/node/test/parallel/test-vm-static-this.js @@ -1,6 +1,3 @@ -//#FILE: test-child-process-kill.js -//#SHA1: 308c453d51a13e0e2c640969456c0092ab00967d -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,37 +19,47 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const { spawn } = require("child_process"); -const isWindows = process.platform === "win32"; +/* eslint-disable strict */ +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); -test("child process kill behavior", async () => { - const cat = spawn(isWindows ? "cmd" : "cat"); +// Run a string +const result = vm.runInThisContext('\'passed\';'); +assert.strictEqual(result, 'passed'); - const stdoutEndPromise = new Promise(resolve => { - cat.stdout.on("end", resolve); - }); +// thrown error +assert.throws(function() { + vm.runInThisContext('throw new Error(\'test\');'); +}, /test/); - const stderrDataPromise = new Promise((resolve, reject) => { - cat.stderr.on("data", () => reject(new Error("stderr should not receive data"))); - cat.stderr.on("end", resolve); - }); +global.hello = 5; +vm.runInThisContext('hello = 2'); +assert.strictEqual(global.hello, 2); - const exitPromise = new Promise(resolve => { - cat.on("exit", (code, signal) => { - expect(code).toBeNull(); - expect(signal).toBe("SIGTERM"); - expect(cat.signalCode).toBe("SIGTERM"); - resolve(); - }); - }); - expect(cat.signalCode).toBeNull(); - expect(cat.killed).toBe(false); - cat.kill(); - expect(cat.killed).toBe(true); +// pass values +const code = 'foo = 1;' + + 'bar = 2;' + + 'if (typeof baz !== \'undefined\')' + + 'throw new Error(\'test fail\');'; +global.foo = 2; +global.obj = { foo: 0, baz: 3 }; +/* eslint-disable no-unused-vars */ +const baz = vm.runInThisContext(code); +/* eslint-enable no-unused-vars */ +assert.strictEqual(global.obj.foo, 0); +assert.strictEqual(global.bar, 2); +assert.strictEqual(global.foo, 1); - await Promise.all([stdoutEndPromise, stderrDataPromise, exitPromise]); -}); +// call a function +global.f = function() { global.foo = 100; }; +vm.runInThisContext('f()'); +assert.strictEqual(global.foo, 100); -//<#END_FILE: test-child-process-kill.js +common.allowGlobals( + global.hello, + global.foo, + global.obj, + global.f +); diff --git a/test/js/node/test/parallel/test-vm-strict-mode.js b/test/js/node/test/parallel/test-vm-strict-mode.js new file mode 100644 index 0000000000..b1b233664d --- /dev/null +++ b/test/js/node/test/parallel/test-vm-strict-mode.js @@ -0,0 +1,14 @@ +'use strict'; +// https://github.com/nodejs/node/issues/12300 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const ctx = vm.createContext({ x: 42 }); + +// This might look as if x has not been declared, but x is defined on the +// sandbox and the assignment should not throw. +vm.runInContext('"use strict"; x = 1', ctx); + +assert.strictEqual(ctx.x, 1); diff --git a/test/js/node/test/parallel/test-vm-syntax-error-message.js b/test/js/node/test/parallel/test-vm-syntax-error-message.js new file mode 100644 index 0000000000..c49ff6aeb1 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-syntax-error-message.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); + +const p = child_process.spawn(process.execPath, [ + '-e', + 'vm = require("vm");' + + 'context = vm.createContext({});' + + 'try { vm.runInContext("throw new Error(\'boo\')", context); } ' + + 'catch (e) { console.log(e.message); }', +]); + +p.stderr.on('data', common.mustNotCall()); + +let output = ''; + +p.stdout.on('data', (data) => output += data); + +p.stdout.on('end', common.mustCall(() => { + assert.strictEqual(output.replace(/[\r\n]+/g, ''), 'boo'); +})); diff --git a/test/js/node/test/parallel/test-webcrypto-encrypt-decrypt.js b/test/js/node/test/parallel/test-webcrypto-encrypt-decrypt.js new file mode 100644 index 0000000000..cba193b8c7 --- /dev/null +++ b/test/js/node/test/parallel/test-webcrypto-encrypt-decrypt.js @@ -0,0 +1,124 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +// This is only a partial test. The WebCrypto Web Platform Tests +// will provide much greater coverage. + +// Test Encrypt/Decrypt RSA-OAEP +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + + async function test() { + const ec = new TextEncoder(); + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'RSA-OAEP', + modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), + hash: 'SHA-384', + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt({ + name: 'RSA-OAEP', + label: ec.encode('a label') + }, publicKey, buf); + + const plaintext = await subtle.decrypt({ + name: 'RSA-OAEP', + label: ec.encode('a label') + }, privateKey, ciphertext); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} + +// Test Encrypt/Decrypt AES-CTR +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + const counter = globalThis.crypto.getRandomValues(new Uint8Array(16)); + + async function test() { + const key = await subtle.generateKey({ + name: 'AES-CTR', + length: 256 + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt( + { name: 'AES-CTR', counter, length: 64 }, key, buf, + ); + + const plaintext = await subtle.decrypt( + { name: 'AES-CTR', counter, length: 64 }, key, ciphertext, + ); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} + +// Test Encrypt/Decrypt AES-CBC +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + const iv = globalThis.crypto.getRandomValues(new Uint8Array(16)); + + async function test() { + const key = await subtle.generateKey({ + name: 'AES-CBC', + length: 256 + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt( + { name: 'AES-CBC', iv }, key, buf, + ); + + const plaintext = await subtle.decrypt( + { name: 'AES-CBC', iv }, key, ciphertext, + ); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} + +// Test Encrypt/Decrypt AES-GCM +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + const iv = globalThis.crypto.getRandomValues(new Uint8Array(12)); + + async function test() { + const key = await subtle.generateKey({ + name: 'AES-GCM', + length: 256 + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt( + { name: 'AES-GCM', iv }, key, buf, + ); + + const plaintext = await subtle.decrypt( + { name: 'AES-GCM', iv }, key, ciphertext, + ); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-webcrypto-getRandomValues.js b/test/js/node/test/parallel/test-webcrypto-getRandomValues.js new file mode 100644 index 0000000000..f0fbe61a20 --- /dev/null +++ b/test/js/node/test/parallel/test-webcrypto-getRandomValues.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { getRandomValues } = globalThis.crypto; + +assert.throws(() => getRandomValues(new Uint8Array()), { code: 'ERR_INVALID_THIS' }); diff --git a/test/js/node/test/parallel/test-websocket.js b/test/js/node/test/parallel/test-websocket.js new file mode 100644 index 0000000000..c595ec12bf --- /dev/null +++ b/test/js/node/test/parallel/test-websocket.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(typeof WebSocket, 'function'); diff --git a/test/js/node/test/parallel/test-webstream-string-tag.js b/test/js/node/test/parallel/test-webstream-string-tag.js new file mode 100644 index 0000000000..980a204a9b --- /dev/null +++ b/test/js/node/test/parallel/test-webstream-string-tag.js @@ -0,0 +1,18 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +const classesToBeTested = [ WritableStream, WritableStreamDefaultWriter, WritableStreamDefaultController, + ReadableStream, ReadableStreamBYOBRequest, ReadableStreamDefaultReader, + ReadableStreamBYOBReader, ReadableStreamDefaultController, ReadableByteStreamController, + ByteLengthQueuingStrategy, CountQueuingStrategy, TransformStream, + TransformStreamDefaultController]; + + +classesToBeTested.forEach((cls) => { + assert.strictEqual(cls.prototype[Symbol.toStringTag], cls.name); + assert.deepStrictEqual(Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag), + { configurable: true, enumerable: false, value: cls.name, writable: false }); +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-api-basics.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-api-basics.js new file mode 100644 index 0000000000..71b573a8df --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-api-basics.js @@ -0,0 +1,61 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/master/encoding/api-basics.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +function testDecodeSample(encoding, string, bytes) { + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes)), + string); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer), + string); +} + +// `z` (ASCII U+007A), cent (Latin-1 U+00A2), CJK water (BMP U+6C34), +// G-Clef (non-BMP U+1D11E), PUA (BMP U+F8FF), PUA (non-BMP U+10FFFD) +// byte-swapped BOM (non-character U+FFFE) +const sample = 'z\xA2\u6C34\uD834\uDD1E\uF8FF\uDBFF\uDFFD\uFFFE'; + +{ + const encoding = 'utf-8'; + const string = sample; + const bytes = [ + 0x7A, 0xC2, 0xA2, 0xE6, 0xB0, 0xB4, + 0xF0, 0x9D, 0x84, 0x9E, 0xEF, 0xA3, + 0xBF, 0xF4, 0x8F, 0xBF, 0xBD, 0xEF, + 0xBF, 0xBE, + ]; + const encoded = new TextEncoder().encode(string); + assert.deepStrictEqual([].slice.call(encoded), bytes); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes)), + string); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer), + string); +} + +testDecodeSample( + 'utf-16le', + sample, + [ + 0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, + 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xF8, + 0xFF, 0xDB, 0xFD, 0xDF, 0xFE, 0xFF, + ] +); + +testDecodeSample( + 'utf-16', + sample, + [ + 0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, + 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xF8, + 0xFF, 0xDB, 0xFD, 0xDF, 0xFE, 0xFF, + ] +); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js new file mode 100644 index 0000000000..164088270c --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js @@ -0,0 +1,61 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/d74324b53c/encoding/textdecoder-fatal-streaming.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); +const assert = require('assert'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +{ + [ + { encoding: 'utf-8', sequence: [0xC0] }, + { encoding: 'utf-16le', sequence: [0x00] }, + { encoding: 'utf-16be', sequence: [0x00] }, + ].forEach((testCase) => { + const data = new Uint8Array([testCase.sequence]); + assert.throws( + () => { + const decoder = new TextDecoder(testCase.encoding, { fatal: true }); + decoder.decode(data); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + `The encoded data was not valid for encoding ${testCase.encoding}` + } + ); + }); +} + +{ + const decoder = new TextDecoder('utf-16le', { fatal: true }); + const odd = new Uint8Array([0x00]); + const even = new Uint8Array([0x00, 0x00]); + + assert.throws( + () => { + decoder.decode(even, { stream: true }); + decoder.decode(odd); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + 'The encoded data was not valid for encoding utf-16le' + } + ); + + assert.throws( + () => { + decoder.decode(odd, { stream: true }); + decoder.decode(even); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + 'The encoded data was not valid for encoding utf-16le' + } + ); +} diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js new file mode 100644 index 0000000000..8778fa018e --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js @@ -0,0 +1,84 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-fatal.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); + +const bad = [ + { encoding: 'utf-8', input: [0xFF], name: 'invalid code' }, + { encoding: 'utf-8', input: [0xC0], name: 'ends early' }, + { encoding: 'utf-8', input: [0xE0], name: 'ends early 2' }, + { encoding: 'utf-8', input: [0xC0, 0x00], name: 'invalid trail' }, + { encoding: 'utf-8', input: [0xC0, 0xC0], name: 'invalid trail 2' }, + { encoding: 'utf-8', input: [0xE0, 0x00], name: 'invalid trail 3' }, + { encoding: 'utf-8', input: [0xE0, 0xC0], name: 'invalid trail 4' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0x00], name: 'invalid trail 5' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0xC0], name: 'invalid trail 6' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], + name: '> 0x10FFFF' }, + { encoding: 'utf-8', input: [0xFE, 0x80, 0x80, 0x80, 0x80, 0x80], + name: 'obsolete lead byte' }, + // Overlong encodings + { encoding: 'utf-8', input: [0xC0, 0x80], name: 'overlong U+0000 - 2 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0x80], + name: 'overlong U+0000 - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 6 bytes' }, + { encoding: 'utf-8', input: [0xC1, 0xBF], name: 'overlong U+007F - 2 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x81, 0xBF], + name: 'overlong U+007F - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 6 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x9F, 0xBF], + name: 'overlong U+07FF - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 6 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 6 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x84, 0x8F, 0xBF, 0xBF], + name: 'overlong U+10FFFF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x84, 0x8F, 0xBF, 0xBF], + name: 'overlong U+10FFFF - 6 bytes' }, + // UTF-16 surrogates encoded as code points in UTF-8 + { encoding: 'utf-8', input: [0xED, 0xA0, 0x80], name: 'lead surrogate' }, + { encoding: 'utf-8', input: [0xED, 0xB0, 0x80], name: 'trail surrogate' }, + { encoding: 'utf-8', input: [0xED, 0xA0, 0x80, 0xED, 0xB0, 0x80], + name: 'surrogate pair' }, + { encoding: 'utf-16le', input: [0x00], name: 'truncated code unit' }, + // Mismatched UTF-16 surrogates are exercised in utf16-surrogates.html + // FIXME: Add legacy encoding cases +]; + +bad.forEach((t) => { + assert.throws( + () => { + new TextDecoder(t.encoding, { fatal: true }) + .decode(new Uint8Array(t.input)); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js new file mode 100644 index 0000000000..94fc3318d1 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js @@ -0,0 +1,30 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/7f567fa29c/encoding/textdecoder-ignorebom.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +const cases = [ + { + encoding: 'utf-8', + bytes: [0xEF, 0xBB, 0xBF, 0x61, 0x62, 0x63] + }, + { + encoding: 'utf-16le', + bytes: [0xFF, 0xFE, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00] + }, +]; + +cases.forEach((testCase) => { + const BOM = '\uFEFF'; + let decoder = new TextDecoder(testCase.encoding, { ignoreBOM: true }); + const bytes = new Uint8Array(testCase.bytes); + assert.strictEqual(decoder.decode(bytes), `${BOM}abc`); + decoder = new TextDecoder(testCase.encoding, { ignoreBOM: false }); + assert.strictEqual(decoder.decode(bytes), 'abc'); + decoder = new TextDecoder(testCase.encoding); + assert.strictEqual(decoder.decode(bytes), 'abc'); +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-invalid-arg.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-invalid-arg.js new file mode 100644 index 0000000000..5c8a9837f6 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-invalid-arg.js @@ -0,0 +1,19 @@ +'use strict'; + +// This tests that ERR_INVALID_ARG_TYPE are thrown when +// invalid arguments are passed to TextDecoder. + +require('../common'); +const assert = require('assert'); + +{ + const notArrayBufferViewExamples = [false, {}, 1, '', new Error()]; + notArrayBufferViewExamples.forEach((invalidInputType) => { + assert.throws(() => { + new TextDecoder(undefined, null).decode(invalidInputType); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js new file mode 100644 index 0000000000..5484929326 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js @@ -0,0 +1,38 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/fa9436d12c/encoding/textdecoder-streaming.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +const string = + '\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF'; +const octets = { + 'utf-8': [ + 0x00, 0x31, 0x32, 0x33, 0x41, 0x42, 0x43, 0x61, 0x62, 0x63, 0xc2, 0x80, + 0xc3, 0xbf, 0xc4, 0x80, 0xe1, 0x80, 0x80, 0xef, 0xbf, 0xbd, 0xf0, 0x90, + 0x80, 0x80, 0xf4, 0x8f, 0xbf, 0xbf], + 'utf-16le': [ + 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x41, 0x00, 0x42, 0x00, + 0x43, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x80, 0x00, 0xFF, 0x00, + 0x00, 0x01, 0x00, 0x10, 0xFD, 0xFF, 0x00, 0xD8, 0x00, 0xDC, 0xFF, 0xDB, + 0xFF, 0xDF] +}; + +Object.keys(octets).forEach((encoding) => { + for (let len = 1; len <= 5; ++len) { + const encoded = octets[encoding]; + const decoder = new TextDecoder(encoding); + let out = ''; + for (let i = 0; i < encoded.length; i += len) { + const sub = []; + for (let j = i; j < encoded.length && j < i + len; ++j) + sub.push(encoded[j]); + out += decoder.decode(new Uint8Array(sub), { stream: true }); + } + out += decoder.decode(); + assert.strictEqual(out, string); + } +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js new file mode 100644 index 0000000000..a2a31af28c --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js @@ -0,0 +1,56 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-utf16-surrogates.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); + +const bad = [ + { + encoding: 'utf-16le', + input: [0x00, 0xd8], + expected: '\uFFFD', + name: 'lone surrogate lead' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc], + expected: '\uFFFD', + name: 'lone surrogate trail' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xd8, 0x00, 0x00], + expected: '\uFFFD\u0000', + name: 'unmatched surrogate lead' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc, 0x00, 0x00], + expected: '\uFFFD\u0000', + name: 'unmatched surrogate trail' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc, 0x00, 0xd8], + expected: '\uFFFD\uFFFD', + name: 'swapped surrogate pair' + }, +]; + +for (const t of bad) { + assert.throws( + () => { + new TextDecoder(t.encoding, { fatal: true }) + .decode(new Uint8Array(t.input)); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError' + } + ); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-passive.js b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-passive.js new file mode 100644 index 0000000000..97984bd9af --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-passive.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); + +// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html +// in order to define the `document` ourselves + +const { + fail, + ok, + strictEqual +} = require('assert'); + +{ + const document = new EventTarget(); + let supportsPassive = false; + const query_options = { + get passive() { + supportsPassive = true; + return false; + }, + get dummy() { + fail('dummy value getter invoked'); + return false; + } + }; + + document.addEventListener('test_event', null, query_options); + ok(supportsPassive); + + supportsPassive = false; + document.removeEventListener('test_event', null, query_options); + strictEqual(supportsPassive, false); +} +{ + function testPassiveValue(optionsValue, expectedDefaultPrevented) { + const document = new EventTarget(); + let defaultPrevented; + function handler(e) { + if (e.defaultPrevented) { + fail('Event prematurely marked defaultPrevented'); + } + e.preventDefault(); + defaultPrevented = e.defaultPrevented; + } + document.addEventListener('test', handler, optionsValue); + // TODO the WHATWG test is more extensive here and tests dispatching on + // document.body, if we ever support getParent we should amend this + const ev = new Event('test', { bubbles: true, cancelable: true }); + const uncanceled = document.dispatchEvent(ev); + + strictEqual(defaultPrevented, expectedDefaultPrevented); + strictEqual(uncanceled, !expectedDefaultPrevented); + + document.removeEventListener('test', handler, optionsValue); + } + testPassiveValue(undefined, true); + testPassiveValue({}, true); + testPassiveValue({ passive: false }, true); + + common.skip('TODO: passive listeners is still broken'); + testPassiveValue({ passive: 1 }, false); + testPassiveValue({ passive: true }, false); + testPassiveValue({ passive: 0 }, true); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-signal.js b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-signal.js new file mode 100644 index 0000000000..460d2ee3f2 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-signal.js @@ -0,0 +1,168 @@ +'use strict'; + +require('../common'); + +const { + strictEqual, + throws, +} = require('assert'); + +// Manually ported from: wpt@dom/events/AddEventListenerOptions-signal.any.js + +{ + // Passing an AbortSignal to addEventListener does not prevent + // removeEventListener + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', handler, { signal: controller.signal }); + et.dispatchEvent(new Event('test')); + strictEqual(count, 1, 'Adding a signal still adds a listener'); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'The listener was not added with the once flag'); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'Aborting on the controller removes the listener'); + // See: https://github.com/nodejs/node/pull/37696 , adding an event listener + // should always return undefined. + strictEqual( + et.addEventListener('test', handler, { signal: controller.signal }), + undefined); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'Passing an aborted signal never adds the handler'); +} + +{ + // Passing an AbortSignal to addEventListener works with the once flag + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', handler, { signal: controller.signal }); + et.removeEventListener('test', handler); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Removing a once listener works with a passed signal + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('test', handler, options); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('test', handler, options); + et.removeEventListener('test', handler); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Passing an AbortSignal to multiple listeners + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('first', handler, options); + et.addEventListener('second', handler, options); + controller.abort(); + et.dispatchEvent(new Event('first')); + et.dispatchEvent(new Event('second')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Passing an AbortSignal to addEventListener works with the capture flag + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, capture: true }; + et.addEventListener('test', handler, options); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Aborting from a listener does not call future listeners + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal }; + et.addEventListener('test', () => { + controller.abort(); + }, options); + et.addEventListener('test', handler, options); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Adding then aborting a listener in another listener does not call it + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', () => { + et.addEventListener('test', handler, { signal: controller.signal }); + controller.abort(); + }, { signal: controller.signal }); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Aborting from a nested listener should remove it + const et = new EventTarget(); + const ac = new AbortController(); + let count = 0; + et.addEventListener('foo', () => { + et.addEventListener('foo', () => { + count++; + if (count > 5) ac.abort(); + et.dispatchEvent(new Event('foo')); + }, { signal: ac.signal }); + et.dispatchEvent(new Event('foo')); + }, { once: true }); + et.dispatchEvent(new Event('foo')); +} +{ + const et = new EventTarget(); + [1, 1n, {}, [], null, true, 'hi', Symbol(), () => {}].forEach((signal) => { + throws(() => et.addEventListener('foo', () => {}, { signal }), { + name: 'TypeError', + }); + }); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-customevent.js b/test/js/node/test/parallel/test-whatwg-events-customevent.js new file mode 100644 index 0000000000..e21ea1783f --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-customevent.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); + +const { strictEqual, throws, equal } = require('assert'); + +// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/CustomEvent.html +// in order to define the `document` ourselves + +{ + const type = 'foo'; + const target = new EventTarget(); + + target.addEventListener(type, common.mustCall((evt) => { + strictEqual(evt.type, type); + })); + + target.dispatchEvent(new Event(type)); +} + +{ + throws(() => { + new Event(); + }, TypeError); +} + +{ + const event = new Event('foo'); + equal(event.type, 'foo'); + equal(event.bubbles, false); + equal(event.cancelable, false); + equal(event.detail, null); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-event-constructors.js b/test/js/node/test/parallel/test-whatwg-events-event-constructors.js new file mode 100644 index 0000000000..7880b10043 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-event-constructors.js @@ -0,0 +1,29 @@ +'use strict'; + +require('../common'); +const { test, assert_equals, assert_array_equals } = + require('../common/wpt').harness; + +// Source: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/Event-constructors.any.js#L91-L112 +test(function() { + const called = []; + const ev = new Event('Xx', { + get cancelable() { + called.push('cancelable'); + return false; + }, + get bubbles() { + called.push('bubbles'); + return true; + }, + get sweet() { + called.push('sweet'); + return 'x'; + }, + }); + assert_array_equals(called, ['bubbles', 'cancelable']); + assert_equals(ev.type, 'Xx'); + assert_equals(ev.bubbles, true); + assert_equals(ev.cancelable, false); + assert_equals(ev.sweet, undefined); +}); diff --git a/test/js/node/test/parallel/test-whatwg-events-eventtarget-this-of-listener.js b/test/js/node/test/parallel/test-whatwg-events-eventtarget-this-of-listener.js new file mode 100644 index 0000000000..16ee14feab --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-eventtarget-this-of-listener.js @@ -0,0 +1,119 @@ +'use strict'; + +require('../common'); +const { test, assert_equals, assert_unreached } = + require('../common/wpt').harness; + +// Manually ported from: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/EventTarget-this-of-listener.html + +// Mock document +const document = { + createElement: () => new EventTarget(), + createTextNode: () => new EventTarget(), + createDocumentFragment: () => new EventTarget(), + createComment: () => new EventTarget(), + createProcessingInstruction: () => new EventTarget(), +}; + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + node.addEventListener('someevent', function() { + ++callCount; + assert_equals(this, node); + }); + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'the this value inside the event listener callback should be the node'); + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + const handler = {}; + + node.addEventListener('someevent', handler); + handler.handleEvent = function() { + ++callCount; + assert_equals(this, handler); + }; + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'addEventListener should not require handleEvent to be defined on object listeners'); + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + function handler() { + ++callCount; + assert_equals(this, node); + } + + handler.handleEvent = () => { + assert_unreached('should not call the handleEvent method on a function'); + }; + + node.addEventListener('someevent', handler); + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'handleEvent properties added to a function before addEventListener are not reached'); + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + function handler() { + ++callCount; + assert_equals(this, node); + } + + node.addEventListener('someevent', handler); + + handler.handleEvent = () => { + assert_unreached('should not call the handleEvent method on a function'); + }; + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'handleEvent properties added to a function after addEventListener are not reached'); diff --git a/test/js/node/test/parallel/test-whatwg-readablestream.mjs b/test/js/node/test/parallel/test-whatwg-readablestream.mjs new file mode 100644 index 0000000000..57ebed6045 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-readablestream.mjs @@ -0,0 +1,70 @@ +import { mustCall } from '../common/index.mjs'; +import { ReadableStream } from 'stream/web'; +import assert from 'assert'; + +{ + // Test tee() with close in the nextTick after enqueue + async function read(stream) { + const chunks = []; + for await (const chunk of stream) + chunks.push(chunk); + return Buffer.concat(chunks).toString(); + } + + const [r1, r2] = new ReadableStream({ + start(controller) { + process.nextTick(() => { + controller.enqueue(new Uint8Array([102, 111, 111, 98, 97, 114])); + + process.nextTick(() => { + controller.close(); + }); + }); + } + }).tee(); + + (async () => { + const [dataReader1, dataReader2] = await Promise.all([ + read(r1), + read(r2), + ]); + + assert.strictEqual(dataReader1, dataReader2); + assert.strictEqual(dataReader1, 'foobar'); + assert.strictEqual(dataReader2, 'foobar'); + })().then(mustCall()); +} + +{ + // Test ReadableByteStream.tee() with close in the nextTick after enqueue + async function read(stream) { + const chunks = []; + for await (const chunk of stream) + chunks.push(chunk); + return Buffer.concat(chunks).toString(); + } + + const [r1, r2] = new ReadableStream({ + type: 'bytes', + start(controller) { + process.nextTick(() => { + controller.enqueue(new Uint8Array([102, 111, 111, 98, 97, 114])); + + process.nextTick(() => { + controller.close(); + }); + }); + } + }).tee(); + + (async () => { + const [dataReader1, dataReader2] = await Promise.all([ + read(r1), + read(r2), + ]); + + assert.strictEqual(dataReader1, dataReader2); + assert.strictEqual(dataReader1, 'foobar'); + assert.strictEqual(dataReader2, 'foobar'); + })().then(mustCall()); +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-deepequal.js b/test/js/node/test/parallel/test-whatwg-url-custom-deepequal.js new file mode 100644 index 0000000000..9150b1561b --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-deepequal.js @@ -0,0 +1,18 @@ +'use strict'; +// This tests that the internal flags in URL objects are consistent, as manifest +// through assert libraries. +// See https://github.com/nodejs/node/issues/24211 + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +assert.deepStrictEqual( + new URL('./foo', 'https://example.com/'), + new URL('https://example.com/foo') +); +assert.deepStrictEqual( + new URL('./foo', 'https://user:pass@example.com/'), + new URL('https://user:pass@example.com/foo') +); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-domainto.js b/test/js/node/test/parallel/test-whatwg-url-custom-domainto.js new file mode 100644 index 0000000000..b7458d7a8e --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-domainto.js @@ -0,0 +1,57 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const { domainToASCII, domainToUnicode } = require('url'); + +const tests = require('../fixtures/url-idna'); +const fixtures = require('../common/fixtures'); +const wptToASCIITests = require( + fixtures.path('wpt', 'url', 'resources', 'toascii.json') +); + +{ + const expectedError = { code: 'ERR_MISSING_ARGS', name: 'TypeError' }; + assert.throws(() => domainToASCII(), expectedError); + assert.throws(() => domainToUnicode(), expectedError); + assert.strictEqual(domainToASCII(undefined), 'undefined'); + assert.strictEqual(domainToUnicode(undefined), 'undefined'); +} + +{ + for (const [i, { ascii, unicode }] of tests.entries()) { + assert.strictEqual(ascii, domainToASCII(unicode), + `domainToASCII(${i + 1})`); + assert.strictEqual(unicode, domainToUnicode(ascii), + `domainToUnicode(${i + 1})`); + assert.strictEqual(ascii, domainToASCII(domainToUnicode(ascii)), + `domainToASCII(domainToUnicode(${i + 1}))`); + assert.strictEqual(unicode, domainToUnicode(domainToASCII(unicode)), + `domainToUnicode(domainToASCII(${i + 1}))`); + } +} + +{ + for (const [i, test] of wptToASCIITests.entries()) { + if (typeof test === 'string') + continue; // skip comments + const { comment, input, output } = test; + let caseComment = `Case ${i + 1}`; + if (comment) + caseComment += ` (${comment})`; + if (output === null) { + assert.strictEqual(domainToASCII(input), '', caseComment); + assert.strictEqual(domainToUnicode(input), '', caseComment); + } else { + assert.strictEqual(domainToASCII(input), output, caseComment); + const roundtripped = domainToASCII(domainToUnicode(input)); + assert.strictEqual(roundtripped, output, caseComment); + } + } +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-href-side-effect.js b/test/js/node/test/parallel/test-whatwg-url-custom-href-side-effect.js new file mode 100644 index 0000000000..30967d9fea --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-href-side-effect.js @@ -0,0 +1,15 @@ +'use strict'; + +// Tests below are not from WPT. +require('../common'); +const assert = require('assert'); + +const ref = new URL('http://example.com/path'); +const url = new URL('http://example.com/path'); +assert.throws(() => { + url.href = ''; +}, { + name: 'TypeError' +}); + +assert.deepStrictEqual(url, ref); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-inspect.js b/test/js/node/test/parallel/test-whatwg-url-custom-inspect.js new file mode 100644 index 0000000000..946c097eac --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-inspect.js @@ -0,0 +1,70 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const util = require('util'); +const assert = require('assert'); + +const url = new URL('https://username:password@host.name:8080/path/name/?que=ry#hash'); + +assert.strictEqual( + util.inspect(url), + `URL { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + origin: 'https://host.name:8080', + protocol: 'https:', + username: 'username', + password: 'password', + host: 'host.name:8080', + hostname: 'host.name', + port: '8080', + pathname: '/path/name/', + search: '?que=ry', + searchParams: URLSearchParams { 'que' => 'ry' }, + hash: '#hash' +}`); + +assert.strictEqual( + util.inspect(url, { showHidden: true }), + `URL { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + origin: 'https://host.name:8080', + protocol: 'https:', + username: 'username', + password: 'password', + host: 'host.name:8080', + hostname: 'host.name', + port: '8080', + pathname: '/path/name/', + search: '?que=ry', + searchParams: URLSearchParams { 'que' => 'ry' }, + hash: '#hash', + [Symbol(context)]: URLContext { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + protocol_end: 6, + username_end: 16, + host_start: 25, + host_end: 35, + pathname_start: 40, + search_start: 51, + hash_start: 58, + port: 8080, + scheme_type: 2, + [hasPort]: [Getter], + [hasSearch]: [Getter], + [hasHash]: [Getter] + } +}`); + +assert.strictEqual( + util.inspect({ a: url }, { depth: 0 }), + '{ a: URL {} }'); + +class MyURL extends URL {} +assert(util.inspect(new MyURL(url.href)).startsWith('MyURL {')); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-parsing.js b/test/js/node/test/parallel/test-whatwg-url-custom-parsing.js new file mode 100644 index 0000000000..cdeda59eec --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-parsing.js @@ -0,0 +1,87 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const tests = require( + fixtures.path('wpt', 'url', 'resources', 'urltestdata.json') +); + +const originalFailures = tests.filter((test) => test.failure); + +const typeFailures = [ + { input: '' }, + { input: 'test' }, + { input: undefined }, + { input: 0 }, + { input: true }, + { input: false }, + { input: null }, + { input: new Date() }, + { input: new RegExp() }, + { input: 'test', base: null }, + { input: 'http://nodejs.org', base: null }, + { input: () => {} }, +]; + +// See https://github.com/w3c/web-platform-tests/pull/10955 +// > If `failure` is true, parsing `about:blank` against `base` +// > must give failure. This tests that the logic for converting +// > base URLs into strings properly fails the whole parsing +// > algorithm if the base URL cannot be parsed. +const aboutBlankFailures = originalFailures + .map((test) => ({ + input: 'about:blank', + base: test.input, + failure: true + })); + +const failureTests = originalFailures + .concat(typeFailures) + .concat(aboutBlankFailures); + +const expectedError = { code: 'ERR_INVALID_URL', name: 'TypeError' }; + +for (const test of failureTests) { + assert.throws( + () => new URL(test.input, test.base), + (error) => { + assert.throws(() => { throw error; }, expectedError); + assert.strictEqual(`${error}`, 'TypeError: Invalid URL'); + assert.strictEqual(error.message, 'Invalid URL'); + return true; + }); +} + +const additional_tests = + require(fixtures.path('url-tests-additional.js')); + +for (const test of additional_tests) { + const url = new URL(test.url); + if (test.href) assert.strictEqual(url.href, test.href); + if (test.origin) assert.strictEqual(url.origin, test.origin); + if (test.protocol) assert.strictEqual(url.protocol, test.protocol); + if (test.username) assert.strictEqual(url.username, test.username); + if (test.password) assert.strictEqual(url.password, test.password); + if (test.hostname) assert.strictEqual(url.hostname, test.hostname); + if (test.host) assert.strictEqual(url.host, test.host); + if (test.port !== undefined) assert.strictEqual(url.port, test.port); + if (test.pathname) assert.strictEqual(url.pathname, test.pathname); + if (test.search) assert.strictEqual(url.search, test.search); + if (test.hash) assert.strictEqual(url.hash, test.hash); +} + +assert.throws(() => { + new URL(); +}, { + name: 'TypeError', + code: 'ERR_MISSING_ARGS', +}); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-searchparams-sort.js b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams-sort.js new file mode 100644 index 0000000000..e0b0c5c1ed --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams-sort.js @@ -0,0 +1,51 @@ +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const { test, assert_array_equals } = require('../common/wpt').harness; + +// TODO(joyeecheung): upstream this to WPT, if possible - even +// just as a test for large inputs. Other implementations may +// have a similar cutoff anyway. + +// Test bottom-up iterative stable merge sort because we only use that +// algorithm to sort > 100 search params. +const tests = [{ input: '', output: [] }]; +const pairs = []; +for (let i = 10; i < 100; i++) { + pairs.push([`a${i}`, 'b']); + tests[0].output.push([`a${i}`, 'b']); +} +tests[0].input = pairs.sort(() => Math.random() > 0.5) + .map((pair) => pair.join('=')).join('&'); + +tests.push( + { + 'input': 'z=a&=b&c=d', + 'output': [['', 'b'], ['c', 'd'], ['z', 'a']] + } +); + +tests.forEach((val) => { + test(() => { + const params = new URLSearchParams(val.input); + let i = 0; + params.sort(); + for (const param of params) { + assert_array_equals(param, val.output[i]); + i++; + } + }, `Parse and sort: ${val.input}`); + + test(() => { + const url = new URL(`?${val.input}`, 'https://example/'); + url.searchParams.sort(); + const params = new URLSearchParams(url.search); + let i = 0; + for (const param of params) { + assert_array_equals(param, val.output[i]); + i++; + } + }, `URL parse and sort: ${val.input}`); +}); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-searchparams.js b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams.js new file mode 100644 index 0000000000..faec86e017 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams.js @@ -0,0 +1,147 @@ +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const serialized = 'a=a&a=1&a=true&a=undefined&a=null&a=%EF%BF%BD' + + '&a=%EF%BF%BD&a=%F0%9F%98%80&a=%EF%BF%BD%EF%BF%BD' + + '&a=%5Bobject+Object%5D'; +const values = ['a', 1, true, undefined, null, '\uD83D', '\uDE00', + '\uD83D\uDE00', '\uDE00\uD83D', {}]; +const normalizedValues = ['a', '1', 'true', 'undefined', 'null', '\uFFFD', + '\uFFFD', '\uD83D\uDE00', '\uFFFD\uFFFD', + '[object Object]']; + +const m = new URL('http://example.org'); +const ownSymbolsBeforeGetterAccess = Object.getOwnPropertySymbols(m); +const sp = m.searchParams; +assert.deepStrictEqual(Object.getOwnPropertySymbols(m), ownSymbolsBeforeGetterAccess); + +assert(sp); +assert.strictEqual(sp.toString(), ''); +assert.strictEqual(m.search, ''); + +assert(!sp.has('a')); +values.forEach((i) => sp.set('a', i)); +assert(sp.has('a')); +assert.strictEqual(sp.get('a'), '[object Object]'); +sp.delete('a'); +assert(!sp.has('a')); + +m.search = ''; +assert.strictEqual(sp.toString(), ''); + +values.forEach((i) => sp.append('a', i)); +assert(sp.has('a')); +assert.strictEqual(sp.getAll('a').length, values.length); +assert.strictEqual(sp.get('a'), 'a'); + +assert.strictEqual(sp.toString(), serialized); + +assert.strictEqual(m.search, `?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +assert.strictEqual(m.href, `http://example.org/?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +assert.strictEqual(m.toString(), `http://example.org/?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +assert.strictEqual(m.toJSON(), `http://example.org/?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.href = 'http://example.org'; +assert.strictEqual(m.href, 'http://example.org/'); +assert.strictEqual(sp.size, 0); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.search = ''; +assert.strictEqual(m.href, 'http://example.org/'); +assert.strictEqual(sp.size, 0); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.pathname = '/test'; +assert.strictEqual(m.href, `http://example.org/test?${serialized}`); +m.pathname = ''; + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.hash = '#test'; +assert.strictEqual(m.href, `http://example.org/?${serialized}#test`); +m.hash = ''; + +assert.strictEqual(sp[Symbol.iterator], sp.entries); + +let key, val; +let n = 0; +for ([key, val] of sp) { + assert.strictEqual(key, 'a', n); + assert.strictEqual(val, normalizedValues[n], n); + n++; +} +n = 0; +for (key of sp.keys()) { + assert.strictEqual(key, 'a', n); + n++; +} +n = 0; +for (val of sp.values()) { + assert.strictEqual(val, normalizedValues[n], n); + n++; +} +n = 0; +sp.forEach(function(val, key, obj) { + assert.strictEqual(this, undefined, n); + assert.strictEqual(key, 'a', n); + assert.strictEqual(val, normalizedValues[n], n); + assert.strictEqual(obj, sp, n); + n++; +}); +sp.forEach(function() { + assert.strictEqual(this, m); +}, m); + +{ + const callbackErr = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }; + assert.throws(() => sp.forEach(), callbackErr); + assert.throws(() => sp.forEach(1), callbackErr); +} + +m.search = '?a=a&b=b'; +assert.strictEqual(sp.toString(), 'a=a&b=b'); + +const tests = require(fixtures.path('url-searchparams.js')); + +for (const [input, expected, parsed] of tests) { + if (input[0] !== '?') { + const sp = new URLSearchParams(input); + assert.strictEqual(String(sp), expected); + assert.deepStrictEqual(Array.from(sp), parsed); + + m.search = input; + assert.strictEqual(String(m.searchParams), expected); + assert.deepStrictEqual(Array.from(m.searchParams), parsed); + } + + { + const sp = new URLSearchParams(`?${input}`); + assert.strictEqual(String(sp), expected); + assert.deepStrictEqual(Array.from(sp), parsed); + + m.search = `?${input}`; + assert.strictEqual(String(m.searchParams), expected); + assert.deepStrictEqual(Array.from(m.searchParams), parsed); + } +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-setters.js b/test/js/node/test/parallel/test-whatwg-url-custom-setters.js new file mode 100644 index 0000000000..b98bf5d8d3 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-setters.js @@ -0,0 +1,60 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const assert = require('assert'); +const { test, assert_equals } = require('../common/wpt').harness; +const fixtures = require('../common/fixtures'); + +// TODO(joyeecheung): we should submit these to the upstream +const additionalTestCases = + require(fixtures.path('url-setter-tests-additional.js')); + +{ + for (const attributeToBeSet in additionalTestCases) { + if (attributeToBeSet === 'comment') { + continue; + } + const testCases = additionalTestCases[attributeToBeSet]; + for (const testCase of testCases) { + let name = `Setting <${testCase.href}>.${attributeToBeSet}` + + ` = "${testCase.new_value}"`; + if ('comment' in testCase) { + name += ` ${testCase.comment}`; + } + test(function() { + const url = new URL(testCase.href); + url[attributeToBeSet] = testCase.new_value; + for (const attribute in testCase.expected) { + assert_equals(url[attribute], testCase.expected[attribute]); + } + }, `URL: ${name}`); + } + } +} + +{ + const url = new URL('http://example.com/'); + const obj = { + toString() { throw new Error('toString'); }, + valueOf() { throw new Error('valueOf'); } + }; + const sym = Symbol(); + const props = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(url)); + for (const [name, { set }] of Object.entries(props)) { + if (set) { + assert.throws(() => url[name] = obj, + /^Error: toString$/, + `url.${name} = { toString() { throw ... } }`); + assert.throws(() => url[name] = sym, + /^TypeError: Cannot convert a Symbol value to a string$/, + `url.${name} = ${String(sym)}`); + } + } +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-tostringtag.js b/test/js/node/test/parallel/test-whatwg-url-custom-tostringtag.js new file mode 100644 index 0000000000..54e5850a8f --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-tostringtag.js @@ -0,0 +1,32 @@ +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +const toString = Object.prototype.toString; + +const url = new URL('http://example.org'); +const sp = url.searchParams; +const spIterator = sp.entries(); + +const test = [ + [url, 'URL'], + [sp, 'URLSearchParams'], + [spIterator, 'URLSearchParams Iterator'], + // Web IDL spec says we have to return 'URLPrototype', but it is too + // expensive to implement; therefore, use Chrome's behavior for now, until + // spec is changed. + [Object.getPrototypeOf(url), 'URL'], + [Object.getPrototypeOf(sp), 'URLSearchParams'], + [Object.getPrototypeOf(spIterator), 'URLSearchParams Iterator'], +]; + +test.forEach(([obj, expected]) => { + assert.strictEqual(obj[Symbol.toStringTag], expected, + `${obj[Symbol.toStringTag]} !== ${expected}`); + const str = toString.call(obj); + assert.strictEqual(str, `[object ${expected}]`, + `${str} !== [object ${expected}]`); +}); diff --git a/test/js/node/test/parallel/test-whatwg-url-override-hostname.js b/test/js/node/test/parallel/test-whatwg-url-override-hostname.js new file mode 100644 index 0000000000..61e3412c6b --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-override-hostname.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + const url = new (class extends URL { get hostname() { return 'bar.com'; } })('http://foo.com/'); + assert.strictEqual(url.href, 'http://foo.com/'); + assert.strictEqual(url.toString(), 'http://foo.com/'); + assert.strictEqual(url.toJSON(), 'http://foo.com/'); + assert.strictEqual(url.hash, ''); + assert.strictEqual(url.host, 'foo.com'); + assert.strictEqual(url.hostname, 'bar.com'); + assert.strictEqual(url.origin, 'http://foo.com'); + assert.strictEqual(url.password, ''); + assert.strictEqual(url.protocol, 'http:'); + assert.strictEqual(url.username, ''); + assert.strictEqual(url.search, ''); + assert.strictEqual(url.searchParams.toString(), ''); +} diff --git a/test/js/node/test/parallel/test-whatwg-url-toascii.js b/test/js/node/test/parallel/test-whatwg-url-toascii.js new file mode 100644 index 0000000000..e5180bfb34 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-toascii.js @@ -0,0 +1,86 @@ +'use strict'; +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const fixtures = require('../common/fixtures'); +const { test, assert_equals, assert_throws } = require('../common/wpt').harness; + +const request = { + response: require( + fixtures.path('wpt', 'url', 'resources', 'toascii.json') + ) +}; + +// The following tests are copied from WPT. Modifications to them should be +// upstreamed first. +// Refs: https://github.com/w3c/web-platform-tests/blob/4839a0a804/url/toascii.window.js +// License: http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html + +/* eslint-disable */ +// async_test(t => { +// const request = new XMLHttpRequest() +// request.open("GET", "toascii.json") +// request.send() +// request.responseType = "json" +// request.onload = t.step_func_done(() => { + runTests(request.response) +// }) +// }, "Loading data…") + +function makeURL(type, input) { + input = "https://" + input + "/x" + if(type === "url") { + return new URL(input) + } else { + const url = document.createElement(type) + url.href = input + return url + } +} + +function runTests(tests) { + for(var i = 0, l = tests.length; i < l; i++) { + let hostTest = tests[i] + if (typeof hostTest === "string") { + continue // skip comments + } + const typeName = { "url": "URL", "a": "", "area": "" } + // ;["url", "a", "area"].forEach((type) => { + ;["url"].forEach((type) => { + test(() => { + if(hostTest.output !== null) { + const url = makeURL("url", hostTest.input) + assert_equals(url.host, hostTest.output) + assert_equals(url.hostname, hostTest.output) + assert_equals(url.pathname, "/x") + assert_equals(url.href, "https://" + hostTest.output + "/x") + } else { + if(type === "url") { + assert_throws(new TypeError, () => makeURL("url", hostTest.input)) + } else { + const url = makeURL(type, hostTest.input) + assert_equals(url.host, "") + assert_equals(url.hostname, "") + assert_equals(url.pathname, "") + assert_equals(url.href, "https://" + hostTest.input + "/x") + } + } + }, hostTest.input + " (using " + typeName[type] + ")") + ;["host", "hostname"].forEach((val) => { + test(() => { + const url = makeURL(type, "x") + url[val] = hostTest.input + if(hostTest.output !== null) { + assert_equals(url[val], hostTest.output) + } else { + assert_equals(url[val], "x") + } + }, hostTest.input + " (using " + typeName[type] + "." + val + ")") + }) + }) + } +} +/* eslint-enable */ diff --git a/test/js/node/test/parallel/test-windows-abort-exitcode.js b/test/js/node/test/parallel/test-windows-abort-exitcode.js new file mode 100644 index 0000000000..e2e6570c1c --- /dev/null +++ b/test/js/node/test/parallel/test-windows-abort-exitcode.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +if (!common.isWindows) + common.skip('test is windows specific'); + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +// This test makes sure that an aborted node process +// exits with code 3 on Windows. +// Spawn a child, force an abort, and then check the +// exit code in the parent. + +if (process.argv[2] === 'child') { + process.abort(); +} else { + const child = spawn(process.execPath, [__filename, 'child']); + child.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 134); + assert.strictEqual(signal, null); + })); +} diff --git a/test/js/node/test/parallel/test-windows-failed-heap-allocation.js b/test/js/node/test/parallel/test-windows-failed-heap-allocation.js new file mode 100644 index 0000000000..be901b7dc2 --- /dev/null +++ b/test/js/node/test/parallel/test-windows-failed-heap-allocation.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that an out of memory error exits with code 134 on Windows + +if (!common.isWindows) return common.skip('Windows-only'); + +const assert = require('assert'); +const { exec } = require('child_process'); + +if (process.argv[2] === 'heapBomb') { + // Heap bomb, imitates a memory leak quickly + const fn = (nM) => [...Array(nM)].map((i) => fn(nM * 2)); + fn(2); +} + +// Run child in tmpdir to avoid report files in repo +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// --max-old-space-size=3 is the min 'old space' in V8, explodes fast +const cmd = `"${process.execPath}" --max-old-space-size=30 "${__filename}"`; +exec(`${cmd} heapBomb`, { cwd: tmpdir.path }, common.mustCall((err, stdout, stderr) => { + const msg = `Wrong exit code of ${err.code}! Expected 134 for abort`; + // Note: common.nodeProcessAborted() is not asserted here because it + // returns true on 134 as well as 0x80000003 (V8's base::OS::Abort) + assert.strictEqual(err.code, 134, msg); +})); diff --git a/test/js/node/test/parallel/test-worker-arraybuffer-zerofill.js b/test/js/node/test/parallel/test-worker-arraybuffer-zerofill.js new file mode 100644 index 0000000000..3dcf4c006e --- /dev/null +++ b/test/js/node/test/parallel/test-worker-arraybuffer-zerofill.js @@ -0,0 +1,33 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Make sure that allocating uninitialized ArrayBuffers in one thread does not +// affect the zero-initialization in other threads. + +const w = new Worker(` +const { parentPort } = require('worker_threads'); + +function post() { + const uint32array = new Uint32Array(64); + parentPort.postMessage(uint32array.reduce((a, b) => a + b)); +} + +setInterval(post, 0); +`, { eval: true }); + +function allocBuffers() { + Buffer.allocUnsafe(32 * 1024 * 1024); +} + +const interval = setInterval(allocBuffers, 0); + +let messages = 0; +w.on('message', (sum) => { + assert.strictEqual(sum, 0); + if (messages++ === 100) { + clearInterval(interval); + w.terminate(); + } +}); diff --git a/test/js/node/test/parallel/test-worker-cjs-workerdata.js b/test/js/node/test/parallel/test-worker-cjs-workerdata.js new file mode 100644 index 0000000000..949011fee8 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-cjs-workerdata.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const workerData = 'Hello from main thread'; + +const worker = new Worker(fixtures.path('worker-data.cjs'), { + workerData +}); + +worker.on('message', common.mustCall((message) => { + assert.strictEqual(message, workerData); +})); diff --git a/test/js/node/test/parallel/test-worker-cleanexit-with-js.js b/test/js/node/test/parallel/test-worker-cleanexit-with-js.js new file mode 100644 index 0000000000..b4725e297a --- /dev/null +++ b/test/js/node/test/parallel/test-worker-cleanexit-with-js.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); + +// Harden the thread interactions on the exit path. +// Ensure workers are able to bail out safe at +// arbitrary execution points. By running a lot of +// JS code in a tight loop, the expectation +// is that those will be at various control flow points +// preferably in the JS land. + +const { Worker } = require('worker_threads'); +const code = 'setInterval(() => {' + + "require('v8').deserialize(require('v8').serialize({ foo: 'bar' }));" + + "require('vm').runInThisContext('x = \"foo\";');" + + "eval('const y = \"vm\";');}, 10);"; +for (let i = 0; i < 9; i++) { + new Worker(code, { eval: true }); +} +new Worker(code, { eval: true }).on('online', common.mustCall((msg) => { + process.exit(0); +})); diff --git a/test/js/node/test/parallel/test-worker-cleanexit-with-moduleload.js b/test/js/node/test/parallel/test-worker-cleanexit-with-moduleload.js new file mode 100644 index 0000000000..f2e8ad786f --- /dev/null +++ b/test/js/node/test/parallel/test-worker-cleanexit-with-moduleload.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +// Harden the thread interactions on the exit path. +// Ensure workers are able to bail out safe at +// arbitrary execution points. By using a number of +// internal modules as load candidates, the expectation +// is that those will be at various control flow points +// preferably in the C++ land. + +const { Worker } = require('worker_threads'); +const modules = [ 'fs', 'assert', 'async_hooks', 'buffer', 'child_process', + 'net', 'http', 'os', 'path', 'v8', 'vm', +]; +if (common.hasCrypto) { + modules.push('https'); +} + +for (let i = 0; i < 10; i++) { + new Worker(`const modules = [${modules.map((m) => `'${m}'`)}];` + + 'modules.forEach((module) => {' + + 'const m = require(module);' + + '});', { eval: true }); +} + +// Allow workers to go live. +setTimeout(() => { + process.exit(0); +}, 200); diff --git a/test/js/node/test/parallel/test-worker-esm-exit.js b/test/js/node/test/parallel/test-worker-esm-exit.js new file mode 100644 index 0000000000..8c38faf3b7 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-esm-exit.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const w = new Worker(fixtures.path('es-modules/import-process-exit.mjs')); +w.on('error', common.mustNotCall()); +w.on('exit', + common.mustCall((code) => assert.strictEqual(code, 42)) +); diff --git a/test/js/node/test/parallel/test-worker-esmodule.js b/test/js/node/test/parallel/test-worker-esmodule.js new file mode 100644 index 0000000000..e7f9bd7aea --- /dev/null +++ b/test/js/node/test/parallel/test-worker-esmodule.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const w = new Worker(fixtures.path('worker-script.mjs')); +w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); +})); diff --git a/test/js/node/test/parallel/test-worker-exit-event-error.js b/test/js/node/test/parallel/test-worker-exit-event-error.js new file mode 100644 index 0000000000..e2427c7dff --- /dev/null +++ b/test/js/node/test/parallel/test-worker-exit-event-error.js @@ -0,0 +1,8 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +process.on('uncaughtException', common.mustCall()); + +new Worker('', { eval: true }) + .on('exit', common.mustCall(() => { throw new Error('foo'); })); diff --git a/test/js/node/test/parallel/test-worker-exit-from-uncaught-exception.js b/test/js/node/test/parallel/test-worker-exit-from-uncaught-exception.js new file mode 100644 index 0000000000..b9f8aeee97 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-exit-from-uncaught-exception.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Check that `process.exit()` can be called inside a Worker from an uncaught +// exception handler. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 42); + })); + return; +} + +process.on('uncaughtException', () => { + process.exit(42); +}); + +throw new Error(); diff --git a/test/js/node/test/parallel/test-worker-fs-stat-watcher.js b/test/js/node/test/parallel/test-worker-fs-stat-watcher.js new file mode 100644 index 0000000000..c648792af7 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-fs-stat-watcher.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const { Worker, parentPort } = require('worker_threads'); +const fs = require('fs'); + +// Checks that terminating Workers does not crash the process if fs.watchFile() +// has active handles. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const worker = new Worker(__filename); + worker.on('message', common.mustCall(() => worker.terminate())); +} else { + fs.watchFile(__filename, () => {}); + parentPort.postMessage('running'); +} diff --git a/test/js/node/test/parallel/test-worker-http2-generic-streams-terminate.js b/test/js/node/test/parallel/test-worker-http2-generic-streams-terminate.js new file mode 100644 index 0000000000..234697fb4d --- /dev/null +++ b/test/js/node/test/parallel/test-worker-http2-generic-streams-terminate.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); +const { Duplex } = require('stream'); +const { Worker, workerData } = require('worker_threads'); + +// Tests the interaction between terminating a Worker thread and running +// the native SetImmediate queue, which may attempt to perform multiple +// calls into JS even though one already terminates the Worker. + +if (!workerData) { + const counter = new Int32Array(new SharedArrayBuffer(4)); + const worker = new Worker(__filename, { workerData: { counter } }); + worker.on('exit', common.mustCall(() => { + assert.strictEqual(counter[0], 1); + })); +} else { + const { counter } = workerData; + + // Start two HTTP/2 connections. This will trigger write()s call from inside + // the SetImmediate queue. + for (let i = 0; i < 2; i++) { + http2.connect('http://localhost', { + createConnection() { + return new Duplex({ + write(chunk, enc, cb) { + Atomics.add(counter, 0, 1); + process.exit(); + }, + read() { } + }); + } + }); + } +} diff --git a/test/js/node/test/parallel/test-worker-invalid-workerdata.js b/test/js/node/test/parallel/test-worker-invalid-workerdata.js new file mode 100644 index 0000000000..08fd78bdc0 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-invalid-workerdata.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// This tests verifies that failing to serialize workerData does not keep +// the process alive. +// Refs: https://github.com/nodejs/node/issues/22736 + +assert.throws(() => { + new Worker('./worker.js', { + workerData: { fn: () => {} } + }); +}, /DataCloneError/); diff --git a/test/js/node/test/parallel/test-worker-load-file-with-extension-other-than-js.js b/test/js/node/test/parallel/test-worker-load-file-with-extension-other-than-js.js new file mode 100644 index 0000000000..5dca297576 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-load-file-with-extension-other-than-js.js @@ -0,0 +1,9 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +const { Worker } = require('worker_threads'); + +(common.mustCall(() => { + new Worker(fixtures.path('worker-script.ts')); +}))(); diff --git a/test/js/node/test/parallel/test-worker-message-port-infinite-message-loop.js b/test/js/node/test/parallel/test-worker-message-port-infinite-message-loop.js new file mode 100644 index 0000000000..0cd1cc0680 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-port-infinite-message-loop.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { MessageChannel } = require('worker_threads'); + +// Make sure that an infinite asynchronous .on('message')/postMessage loop +// does not lead to a stack overflow and does not starve the event loop. +// We schedule timeouts both from before the .on('message') handler and +// inside of it, which both should run. + +const { port1, port2 } = new MessageChannel(); +let count = 0; +port1.on('message', () => { + if (count === 0) { + setTimeout(common.mustCall(() => { + port1.close(); + }), 0); + } + + port2.postMessage(0); + assert(count++ < 10000, `hit ${count} loop iterations`); +}); + +port2.postMessage(0); + +// This is part of the test -- the event loop should be available and not stall +// out due to the recursive .postMessage() calls. +setTimeout(common.mustCall(), 0); diff --git a/test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js b/test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js new file mode 100644 index 0000000000..dddf91e3f3 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js @@ -0,0 +1,17 @@ +'use strict'; +require('../common'); +const { Worker, MessageChannel } = require('worker_threads'); + +// Check the interaction of calling .terminate() while transferring +// MessagePort objects; in particular, that it does not crash the process. + +for (let i = 0; i < 10; ++i) { + const w = new Worker( + "require('worker_threads').parentPort.on('message', () => {})", + { eval: true }); + setImmediate(() => { + const port = new MessageChannel().port1; + w.postMessage({ port }, [ port ]); + w.terminate(); + }); +} diff --git a/test/js/node/test/parallel/test-worker-message-port-wasm-module.js b/test/js/node/test/parallel/test-worker-message-port-wasm-module.js new file mode 100644 index 0000000000..b1aa522dc4 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-port-wasm-module.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const { Worker } = require('worker_threads'); +const wasmModule = new WebAssembly.Module(fixtures.readSync('simple.wasm')); + +const worker = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.once('message', ({ wasmModule }) => { + const instance = new WebAssembly.Instance(wasmModule); + parentPort.postMessage(instance.exports.add(10, 20)); +}); +`, { eval: true }); + +worker.once('message', common.mustCall((num) => assert.strictEqual(num, 30))); +worker.postMessage({ wasmModule }); diff --git a/test/js/node/test/parallel/test-worker-mjs-workerdata.js b/test/js/node/test/parallel/test-worker-mjs-workerdata.js new file mode 100644 index 0000000000..b0a65e2e80 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-mjs-workerdata.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const workerData = 'Hello from main thread'; + +const worker = new Worker(fixtures.path('worker-data.mjs'), { + workerData +}); + +worker.on('message', common.mustCall((message) => { + assert.strictEqual(message, workerData); +})); diff --git a/test/js/node/test/parallel/test-worker-nested-on-process-exit.js b/test/js/node/test/parallel/test-worker-nested-on-process-exit.js new file mode 100644 index 0000000000..aa544fa328 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-nested-on-process-exit.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker, workerData } = require('worker_threads'); + +// Test that 'exit' events for nested Workers are not received when a Worker +// terminates itself through process.exit(). + +if (workerData === null) { + const nestedWorkerExitCounter = new Int32Array(new SharedArrayBuffer(4)); + const w = new Worker(__filename, { workerData: nestedWorkerExitCounter }); + w.on('exit', common.mustCall(() => { + assert.strictEqual(nestedWorkerExitCounter[0], 0); + })); +} else { + const nestedWorker = new Worker('setInterval(() => {}, 100)', { eval: true }); + // The counter should never be increased here. + nestedWorker.on('exit', () => workerData[0]++); + nestedWorker.on('online', () => process.exit()); +} diff --git a/test/js/node/test/parallel/test-worker-no-sab.js b/test/js/node/test/parallel/test-worker-no-sab.js new file mode 100644 index 0000000000..e96c987484 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-no-sab.js @@ -0,0 +1,21 @@ +// Flags: --enable-sharedarraybuffer-per-context + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Regression test for https://github.com/nodejs/node/issues/39717. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + + w.on('exit', common.mustCall((status) => { + assert.strictEqual(status, 2); + })); +} else { + process.exit(2); +} diff --git a/test/js/node/test/parallel/test-worker-non-fatal-uncaught-exception.js b/test/js/node/test/parallel/test-worker-non-fatal-uncaught-exception.js new file mode 100644 index 0000000000..01df55eec1 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-non-fatal-uncaught-exception.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Check that `process._fatalException()` returns a boolean when run inside a +// worker. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + })); + return; +} + +process.once('uncaughtException', () => { + process.nextTick(() => { + assert.strictEqual(res, true); + }); +}); + +const res = process._fatalException(new Error()); diff --git a/test/js/node/test/parallel/test-worker-on-process-exit.js b/test/js/node/test/parallel/test-worker-on-process-exit.js new file mode 100644 index 0000000000..ec1c4affd1 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-on-process-exit.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { Worker } = require('worker_threads'); + +// Test that 'exit' events for Workers are not received when the main thread +// terminates itself through process.exit(). + +if (process.argv[2] !== 'child') { + const { + stdout, stderr, status + } = spawnSync(process.execPath, [__filename, 'child'], { encoding: 'utf8' }); + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout, ''); + assert.strictEqual(status, 0); +} else { + const nestedWorker = new Worker('setInterval(() => {}, 100)', { eval: true }); + // This console.log() should never fire. + nestedWorker.on('exit', () => console.log('exit event received')); + nestedWorker.on('online', () => process.exit()); +} diff --git a/test/js/node/test/parallel/test-worker-onmessage-not-a-function.js b/test/js/node/test/parallel/test-worker-onmessage-not-a-function.js new file mode 100644 index 0000000000..df07353075 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-onmessage-not-a-function.js @@ -0,0 +1,25 @@ +// When MessagePort.onmessage is set to a value that is not a function, the +// setter should call .unref() and .stop(), clearing a previous onmessage +// listener from holding the event loop open. This test confirms that +// functionality. + +'use strict'; +const common = require('../common'); +const { Worker, parentPort } = require('worker_threads'); + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + w.postMessage(2); +} else { + // .onmessage uses a setter. Set .onmessage to a function that ultimately + // should not be called. This will call .ref() and .start() which will keep + // the event loop open (and prevent this from exiting) if the subsequent + // assignment of a value to .onmessage doesn't call .unref() and .stop(). + parentPort.onmessage = common.mustNotCall(); + // Setting `onmessage` to a value that is not a function should clear the + // previous value and also should allow the event loop to exit. (In other + // words, this test should exit rather than run indefinitely.) + parentPort.onmessage = 'fhqwhgads'; +} diff --git a/test/js/node/test/parallel/test-worker-onmessage.js b/test/js/node/test/parallel/test-worker-onmessage.js new file mode 100644 index 0000000000..3ed10755ce --- /dev/null +++ b/test/js/node/test/parallel/test-worker-onmessage.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker, parentPort } = require('worker_threads'); + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + const expectation = [ 4, undefined, null ]; + const actual = []; + w.on('message', common.mustCall((message) => { + actual.push(message); + if (actual.length === expectation.length) { + assert.deepStrictEqual(expectation, actual); + w.terminate(); + } + }, expectation.length)); + w.postMessage(2); +} else { + parentPort.onmessage = common.mustCall((message) => { + parentPort.postMessage(message.data * 2); + parentPort.postMessage(undefined); + parentPort.postMessage(null); + }); +} diff --git a/test/js/node/test/parallel/test-worker-parent-port-ref.js b/test/js/node/test/parallel/test-worker-parent-port-ref.js new file mode 100644 index 0000000000..c1e79b9faa --- /dev/null +++ b/test/js/node/test/parallel/test-worker-parent-port-ref.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { isMainThread, parentPort, Worker } = require('worker_threads'); + +// This test makes sure that we manipulate the references of +// `parentPort` correctly so that any worker threads will +// automatically exit when there are no any other references. +{ + if (isMainThread) { + const worker = new Worker(__filename); + + worker.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + }), 1); + + worker.on('online', common.mustCall()); + } else { + const messageCallback = () => {}; + parentPort.on('message', messageCallback); + // The thread won't exit if we don't make the 'message' listener off. + parentPort.off('message', messageCallback); + } +} diff --git a/test/js/node/test/parallel/test-worker-process-exit-async-module.js b/test/js/node/test/parallel/test-worker-process-exit-async-module.js new file mode 100644 index 0000000000..38d4ad74c7 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-process-exit-async-module.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Regression for https://github.com/nodejs/node/issues/43182. +const w = new Worker(new URL('data:text/javascript,process.exit(1);await new Promise(()=>{ process.exit(2); })')); +w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 1); +})); diff --git a/test/js/node/test/parallel/test-worker-ref-onexit.js b/test/js/node/test/parallel/test-worker-ref-onexit.js new file mode 100644 index 0000000000..24c940f8c8 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-ref-onexit.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Check that worker.unref() makes the 'exit' event not be emitted, if it is +// the only thing we would otherwise be waiting for. + +// Use `setInterval()` to make sure the worker is alive until the end of the +// event loop turn. +const w = new Worker('setInterval(() => {}, 100);', { eval: true }); +w.unref(); +w.on('exit', common.mustNotCall()); diff --git a/test/js/node/test/parallel/test-worker-ref.js b/test/js/node/test/parallel/test-worker-ref.js new file mode 100644 index 0000000000..645fc0fbad --- /dev/null +++ b/test/js/node/test/parallel/test-worker-ref.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Test that calling worker.unref() leads to 'beforeExit' being emitted, and +// that we can resurrect the worker using worker.ref() from there. + +const w = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.once('message', (msg) => { + parentPort.postMessage(msg); +}); +`, { eval: true }); + +process.once('beforeExit', common.mustCall(() => { + console.log('beforeExit'); + w.ref(); + w.postMessage({ hello: 'world' }); +})); + +w.once('message', common.mustCall((msg) => { + console.log('message', msg); +})); + +w.on('exit', common.mustCall(() => { + console.log('exit'); +})); + +w.unref(); diff --git a/test/js/node/test/parallel/test-worker-relative-path-double-dot.js b/test/js/node/test/parallel/test-worker-relative-path-double-dot.js new file mode 100644 index 0000000000..86707c1590 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-relative-path-double-dot.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +if (isMainThread) { + const cwdName = path.relative('../', '.'); + const relativePath = path.relative('.', __filename); + const w = new Worker(path.join('..', cwdName, relativePath)); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); + })); +} else { + parentPort.postMessage('Hello, world!'); +} diff --git a/test/js/node/test/parallel/test-worker-relative-path.js b/test/js/node/test/parallel/test-worker-relative-path.js new file mode 100644 index 0000000000..73dc5b3637 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-relative-path.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +if (isMainThread) { + const w = new Worker(`./${path.relative('.', __filename)}`); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); + })); +} else { + parentPort.postMessage('Hello, world!'); +} diff --git a/test/js/node/test/parallel/test-worker-safe-getters.js b/test/js/node/test/parallel/test-worker-safe-getters.js new file mode 100644 index 0000000000..69856659a5 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-safe-getters.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const { Worker, isMainThread } = require('worker_threads'); + +if (isMainThread) { + const w = new Worker(__filename, { + stdin: true, + stdout: true, + stderr: true + }); + + const { stdin, stdout, stderr } = w; + + w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + + // `postMessage` should not throw after termination + // (this mimics the browser behavior). + w.postMessage('foobar'); + w.ref(); + w.unref(); + + // Sanity check. + assert.strictEqual(w.threadId, -1); + assert.strictEqual(w.stdin, stdin); + assert.strictEqual(w.stdout, stdout); + assert.strictEqual(w.stderr, stderr); + })); +} else { + process.exit(0); +} diff --git a/test/js/node/test/parallel/test-worker-sharedarraybuffer-from-worker-thread.js b/test/js/node/test/parallel/test-worker-sharedarraybuffer-from-worker-thread.js new file mode 100644 index 0000000000..ce8410f6dd --- /dev/null +++ b/test/js/node/test/parallel/test-worker-sharedarraybuffer-from-worker-thread.js @@ -0,0 +1,28 @@ +// Flags: --debug-arraybuffer-allocations +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Regression test for https://github.com/nodejs/node/issues/28777 +// Make sure that SharedArrayBuffers and transferred ArrayBuffers created in +// Worker threads are accessible after the creating thread ended. + +for (const ctor of ['ArrayBuffer', 'SharedArrayBuffer']) { + const w = new Worker(` + const { parentPort } = require('worker_threads'); + const arrayBuffer = new ${ctor}(4); + parentPort.postMessage( + arrayBuffer, + '${ctor}' === 'SharedArrayBuffer' ? [] : [arrayBuffer]); + `, { eval: true }); + + let arrayBuffer; + w.once('message', common.mustCall((message) => arrayBuffer = message)); + w.once('exit', common.mustCall(() => { + assert.strictEqual(arrayBuffer.constructor.name, ctor); + const uint8array = new Uint8Array(arrayBuffer); + uint8array[0] = 42; + assert.deepStrictEqual(uint8array, new Uint8Array([42, 0, 0, 0])); + })); +} diff --git a/test/js/node/test/parallel/test-worker-terminate-nested.js b/test/js/node/test/parallel/test-worker-terminate-nested.js new file mode 100644 index 0000000000..3924528cea --- /dev/null +++ b/test/js/node/test/parallel/test-worker-terminate-nested.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Check that a Worker that's running another Worker can be terminated. + +const worker = new Worker(` +const { Worker, parentPort } = require('worker_threads'); +const worker = new Worker('setInterval(() => {}, 10);', { eval: true }); +worker.on('online', () => { + parentPort.postMessage({}); +}); +`, { eval: true }); + +worker.on('message', common.mustCall(() => worker.terminate())); diff --git a/test/js/node/test/parallel/test-worker-terminate-null-handler.js b/test/js/node/test/parallel/test-worker-terminate-null-handler.js new file mode 100644 index 0000000000..9db2e38b5c --- /dev/null +++ b/test/js/node/test/parallel/test-worker-terminate-null-handler.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Test that calling worker.terminate() if kHandler is null should return an +// empty promise that resolves to undefined, even when a callback is passed + +const worker = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.postMessage({ hello: 'world' }); +`, { eval: true }); + +process.once('beforeExit', common.mustCall(() => worker.ref())); + +worker.on('exit', common.mustCall(() => { + worker.terminate().then((res) => assert.strictEqual(res, undefined)); + worker.terminate(() => null).then( + (res) => assert.strictEqual(res, undefined) + ); +})); + +worker.unref(); diff --git a/test/js/node/test/parallel/test-worker-terminate-timers.js b/test/js/node/test/parallel/test-worker-terminate-timers.js new file mode 100644 index 0000000000..62360a6cdb --- /dev/null +++ b/test/js/node/test/parallel/test-worker-terminate-timers.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Test that calling .terminate() during a timer callback works fine. + +for (const fn of ['setTimeout', 'setImmediate', 'setInterval']) { + const worker = new Worker(` + const { parentPort } = require('worker_threads'); + ${fn}(() => { + require('worker_threads').parentPort.postMessage({}); + while (true); + });`, { eval: true }); + + worker.on('message', common.mustCallAtLeast(() => { + worker.terminate(); + })); +} diff --git a/test/js/node/test/parallel/test-worker-unref-from-message-during-exit.js b/test/js/node/test/parallel/test-worker-unref-from-message-during-exit.js new file mode 100644 index 0000000000..3e696f369e --- /dev/null +++ b/test/js/node/test/parallel/test-worker-unref-from-message-during-exit.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// This used to crash because the `.unref()` was unexpected while the Worker +// was exiting. + +const w = new Worker(` +require('worker_threads').parentPort.postMessage({}); +`, { eval: true }); +w.on('message', common.mustCall(() => { + w.unref(); +})); + +// Wait a bit so that the 'message' event is emitted while the Worker exits. +Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); diff --git a/test/js/node/test/parallel/test-worker.js b/test/js/node/test/parallel/test-worker.js new file mode 100644 index 0000000000..9154aaa12b --- /dev/null +++ b/test/js/node/test/parallel/test-worker.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +const kTestString = 'Hello, world!'; + +if (isMainThread) { + const w = new Worker(__filename); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, kTestString); + })); +} else { + setImmediate(() => { + process.nextTick(() => { + parentPort.postMessage(kTestString); + }); + }); +} diff --git a/test/js/node/test/parallel/test-worker.mjs b/test/js/node/test/parallel/test-worker.mjs new file mode 100644 index 0000000000..4ee3f7dc96 --- /dev/null +++ b/test/js/node/test/parallel/test-worker.mjs @@ -0,0 +1,18 @@ +import { mustCall } from '../common/index.mjs'; +import assert from 'assert'; +import { Worker, isMainThread, parentPort } from 'worker_threads'; + +const kTestString = 'Hello, world!'; + +if (isMainThread) { + const w = new Worker(new URL(import.meta.url)); + w.on('message', mustCall((message) => { + assert.strictEqual(message, kTestString); + })); +} else { + setImmediate(() => { + process.nextTick(() => { + parentPort.postMessage(kTestString); + }); + }); +} diff --git a/test/js/node/test/parallel/test-zlib-brotli-16GB.js b/test/js/node/test/parallel/test-zlib-brotli-16GB.js new file mode 100644 index 0000000000..9b894320e9 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-16GB.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const { createBrotliDecompress } = require('node:zlib'); +const strictEqual = require('node:assert').strictEqual; +const { getDefaultHighWaterMark } = require('stream'); + +// This tiny HEX string is a 16GB file. +// This test verifies that the stream actually stops. +/* eslint-disable @stylistic/js/max-len */ +const content = 'cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bff3f'; + +const buf = Buffer.from(content, 'hex'); + +const decoder = createBrotliDecompress(); +decoder.end(buf); + +// We need to wait to verify that the libuv thread pool had time +// to process the data and the buffer is not empty. +setTimeout(common.mustCall(() => { + // There is only one chunk in the buffer + strictEqual(decoder._readableState.buffer.length, getDefaultHighWaterMark() / (16 * 1024)); +}), common.platformTimeout(500)); diff --git a/test/js/node/test/parallel/test-zlib-brotli-flush.js b/test/js/node/test/parallel/test-zlib-brotli-flush.js new file mode 100644 index 0000000000..fd730bfacd --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-flush.js @@ -0,0 +1,27 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 16; +const deflater = new zlib.BrotliCompress(); + +const chunk = file.slice(0, chunkSize); +const expectedFull = Buffer.from('iweA/9j/4AAQSkZJRgABAQEASA==', 'base64'); +let actualFull; + +deflater.write(chunk, function() { + deflater.flush(function() { + const bufs = []; + let buf; + while ((buf = deflater.read()) !== null) + bufs.push(buf); + actualFull = Buffer.concat(bufs); + }); +}); + +process.once('exit', function() { + assert.deepStrictEqual(actualFull, expectedFull); +}); diff --git a/test/js/node/test/parallel/test-zlib-brotli-from-brotli.js b/test/js/node/test/parallel/test-zlib-brotli-from-brotli.js new file mode 100644 index 0000000000..7e6bfb419e --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-from-brotli.js @@ -0,0 +1,31 @@ +'use strict'; +// Test unzipping a file that was created with a non-node brotli lib, +// piped in as fast as possible. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const decompress = new zlib.BrotliDecompress(); + +const fs = require('fs'); + +const fixture = fixtures.path('person.jpg.br'); +const unzippedFixture = fixtures.path('person.jpg'); +const outputFile = tmpdir.resolve('person.jpg'); +const expect = fs.readFileSync(unzippedFixture); +const inp = fs.createReadStream(fixture); +const out = fs.createWriteStream(outputFile); + +inp.pipe(decompress).pipe(out); +out.on('close', common.mustCall(() => { + const actual = fs.readFileSync(outputFile); + assert.strictEqual(actual.length, expect.length); + for (let i = 0, l = actual.length; i < l; i++) { + assert.strictEqual(actual[i], expect[i], `byte[${i}]`); + } +})); diff --git a/test/js/node/test/parallel/test-zlib-brotli-from-string.js b/test/js/node/test/parallel/test-zlib-brotli-from-string.js new file mode 100644 index 0000000000..30be44517a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-from-string.js @@ -0,0 +1,38 @@ +'use strict'; +// Test compressing and uncompressing a string with brotli + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const compressedString = 'G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB' + + 'K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7' + + 'wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn' + + 'f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1' + + 'MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD' + + 'mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu' + + 'Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5' + + '2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE' + + 'KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM='; + +zlib.brotliCompress(inputString, common.mustCall((err, buffer) => { + assert(inputString.length > buffer.length); + + zlib.brotliDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); + })); +})); + +const buffer = Buffer.from(compressedString, 'base64'); +zlib.brotliDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/test/js/node/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js b/test/js/node/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js new file mode 100644 index 0000000000..6a59ad34b0 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); + +// This test ensures that zlib throws a RangeError if the final buffer needs to +// be larger than kMaxLength and concatenation fails. +// https://github.com/nodejs/node/pull/1811 + +const assert = require('assert'); + +// Change kMaxLength for zlib to trigger the error without having to allocate +// large Buffers. +const buffer = require('buffer'); +const oldkMaxLength = buffer.kMaxLength; +buffer.kMaxLength = 64; +const zlib = require('zlib'); +buffer.kMaxLength = oldkMaxLength; + +const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64'); + +// Async +zlib.brotliDecompress(encoded, function(err) { + assert.ok(err instanceof RangeError); +}); + +// Sync +assert.throws(function() { + zlib.brotliDecompressSync(encoded); +}, RangeError); diff --git a/test/js/node/test/parallel/test-zlib-brotli.js b/test/js/node/test/parallel/test-zlib-brotli.js new file mode 100644 index 0000000000..ef31db3dd6 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli.js @@ -0,0 +1,94 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Test some brotli-specific properties of the brotli streams that can not +// be easily covered through expanding zlib-only tests. + +const sampleBuffer = fixtures.readSync('/pss-vectors.json'); + +{ + // Test setting the quality parameter at stream creation: + const sizes = []; + for (let quality = zlib.constants.BROTLI_MIN_QUALITY; + quality <= zlib.constants.BROTLI_MAX_QUALITY; + quality++) { + const encoded = zlib.brotliCompressSync(sampleBuffer, { + params: { + [zlib.constants.BROTLI_PARAM_QUALITY]: quality + } + }); + sizes.push(encoded.length); + } + + // Increasing quality should roughly correspond to decreasing compressed size: + for (let i = 0; i < sizes.length - 1; i++) { + assert(sizes[i + 1] <= sizes[i] * 1.05, sizes); // 5 % margin of error. + } + assert(sizes[0] > sizes[sizes.length - 1], sizes); +} + +{ + // Test that setting out-of-bounds option values or keys fails. + assert.throws(() => { + zlib.createBrotliCompress({ + params: { + 10000: 0 + } + }); + }, { + code: 'ERR_BROTLI_INVALID_PARAM', + name: 'RangeError', + message: '10000 is not a valid Brotli parameter' + }); + + // Test that accidentally using duplicate keys fails. + assert.throws(() => { + zlib.createBrotliCompress({ + params: { + '0': 0, + '00': 0 + } + }); + }, { + code: 'ERR_BROTLI_INVALID_PARAM', + name: 'RangeError', + message: '00 is not a valid Brotli parameter' + }); + + assert.throws(() => { + zlib.createBrotliCompress({ + params: { + // This is a boolean flag + [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: 42 + } + }); + }, { + code: 'ERR_ZLIB_INITIALIZATION_FAILED', + name: 'Error', + message: 'Initialization failed' + }); +} + +{ + // Test options.flush range + assert.throws(() => { + zlib.brotliCompressSync('', { flush: zlib.constants.Z_FINISH }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.flush" is out of range. It must be >= 0 ' + + 'and <= 3. Received 4', + }); + + assert.throws(() => { + zlib.brotliCompressSync('', { finishFlush: zlib.constants.Z_FINISH }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.finishFlush" is out of range. It must be ' + + '>= 0 and <= 3. Received 4', + }); +} diff --git a/test/js/node/test/parallel/test-zlib-close-after-error.js b/test/js/node/test/parallel/test-zlib-close-after-error.js new file mode 100644 index 0000000000..63d418be09 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-close-after-error.js @@ -0,0 +1,16 @@ +'use strict'; +// https://github.com/nodejs/node/issues/6034 + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const decompress = zlib.createGunzip(15); + +decompress.on('error', common.mustCall((err) => { + assert.strictEqual(decompress._closed, true); + decompress.close(); +})); + +assert.strictEqual(decompress._closed, false); +decompress.write('something invalid'); diff --git a/test/js/node/test/parallel/test-zlib-close-after-write.js b/test/js/node/test/parallel/test-zlib-close-after-write.js new file mode 100644 index 0000000000..211318dc5a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-close-after-write.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +zlib.gzip('hello', common.mustCall((err, out) => { + const unzip = zlib.createGunzip(); + unzip.write(out); + unzip.close(common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-zlib-close-in-ondata.js b/test/js/node/test/parallel/test-zlib-close-in-ondata.js new file mode 100644 index 0000000000..44d996311d --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-close-in-ondata.js @@ -0,0 +1,10 @@ +'use strict'; + +const common = require('../common'); +const zlib = require('zlib'); + +const ts = zlib.createGzip(); +const buf = Buffer.alloc(1024 * 1024 * 20); + +ts.on('data', common.mustCall(() => ts.close())); +ts.end(buf); diff --git a/test/js/node/test/parallel/test-zlib-const.js b/test/js/node/test/parallel/test-zlib-const.js new file mode 100644 index 0000000000..342c8c712a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-const.js @@ -0,0 +1,38 @@ +/* eslint-disable strict */ +require('../common'); +const assert = require('assert'); + +const zlib = require('zlib'); + +assert.strictEqual(zlib.constants.Z_OK, 0, + [ + 'Expected Z_OK to be 0;', + `got ${zlib.constants.Z_OK}`, + ].join(' ')); +zlib.constants.Z_OK = 1; +assert.strictEqual(zlib.constants.Z_OK, 0, + [ + 'Z_OK should be immutable.', + `Expected to get 0, got ${zlib.constants.Z_OK}`, + ].join(' ')); + +assert.strictEqual(zlib.codes.Z_OK, 0, + `Expected Z_OK to be 0; got ${zlib.codes.Z_OK}`); +zlib.codes.Z_OK = 1; +assert.strictEqual(zlib.codes.Z_OK, 0, + [ + 'Z_OK should be immutable.', + `Expected to get 0, got ${zlib.codes.Z_OK}`, + ].join(' ')); +zlib.codes = { Z_OK: 1 }; +assert.strictEqual(zlib.codes.Z_OK, 0, + [ + 'Z_OK should be immutable.', + `Expected to get 0, got ${zlib.codes.Z_OK}`, + ].join(' ')); + +assert.ok(Object.isFrozen(zlib.codes), + [ + 'Expected zlib.codes to be frozen, but Object.isFrozen', + `returned ${Object.isFrozen(zlib.codes)}`, + ].join(' ')); diff --git a/test/js/node/test/parallel/test-zlib-convenience-methods.js b/test/js/node/test/parallel/test-zlib-convenience-methods.js new file mode 100644 index 0000000000..01ec7e211b --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-convenience-methods.js @@ -0,0 +1,133 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test convenience methods with and without options supplied + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Must be a multiple of 4 characters in total to test all ArrayBufferView +// types. +const expectStr = 'blah'.repeat(8); +const expectBuf = Buffer.from(expectStr); + +const opts = { + level: 9, + chunkSize: 1024, +}; + +const optsInfo = { + info: true +}; + +for (const [type, expect] of [ + ['string', expectStr], + ['Buffer', expectBuf], + ...common.getBufferSources(expectBuf).map((obj) => + [obj[Symbol.toStringTag], obj] + ), +]) { + for (const method of [ + ['gzip', 'gunzip', 'Gzip', 'Gunzip'], + ['gzip', 'unzip', 'Gzip', 'Unzip'], + ['deflate', 'inflate', 'Deflate', 'Inflate'], + ['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'], + ['brotliCompress', 'brotliDecompress', + 'BrotliCompress', 'BrotliDecompress'], + ]) { + zlib[method[0]](expect, opts, common.mustCall((err, result) => { + zlib[method[1]](result, opts, common.mustCall((err, result) => { + assert.strictEqual(result.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} with options.`); + })); + })); + + zlib[method[0]](expect, common.mustCall((err, result) => { + zlib[method[1]](result, common.mustCall((err, result) => { + assert.strictEqual(result.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} without options.`); + })); + })); + + zlib[method[0]](expect, optsInfo, common.mustCall((err, result) => { + assert.ok(result.engine instanceof zlib[method[2]], + `Should get engine ${method[2]} after ${method[0]} ` + + `${type} with info option.`); + + const compressed = result.buffer; + zlib[method[1]](compressed, optsInfo, common.mustCall((err, result) => { + assert.strictEqual(result.buffer.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} with info option.`); + assert.ok(result.engine instanceof zlib[method[3]], + `Should get engine ${method[3]} after ${method[0]} ` + + `${type} with info option.`); + })); + })); + + { + const compressed = zlib[`${method[0]}Sync`](expect, opts); + const decompressed = zlib[`${method[1]}Sync`](compressed, opts); + assert.strictEqual(decompressed.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} with options.`); + } + + + { + const compressed = zlib[`${method[0]}Sync`](expect); + const decompressed = zlib[`${method[1]}Sync`](compressed); + assert.strictEqual(decompressed.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} without options.`); + } + + + { + const compressed = zlib[`${method[0]}Sync`](expect, optsInfo); + assert.ok(compressed.engine instanceof zlib[method[2]], + `Should get engine ${method[2]} after ${method[0]} ` + + `${type} with info option.`); + const decompressed = zlib[`${method[1]}Sync`](compressed.buffer, + optsInfo); + assert.strictEqual(decompressed.buffer.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} without options.`); + assert.ok(decompressed.engine instanceof zlib[method[3]], + `Should get engine ${method[3]} after ${method[0]} ` + + `${type} with info option.`); + } + } +} + +assert.throws( + () => zlib.gzip('abc'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "callback" argument must be of type function. ' + + 'Received undefined' + } +); diff --git a/test/js/node/test/parallel/test-zlib-crc32.js b/test/js/node/test/parallel/test-zlib-crc32.js new file mode 100644 index 0000000000..fb8d4958ec --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-crc32.js @@ -0,0 +1,211 @@ +'use strict'; + +require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); +const { Buffer } = require('buffer'); + +// The following test data comes from +// https://github.com/zlib-ng/zlib-ng/blob/5401b24/test/test_crc32.cc +// test_crc32.cc -- crc32 unit test +// Copyright (C) 2019-2021 IBM Corporation +// Authors: Rogerio Alves +// Matheus Castanho +// For conditions of distribution and use, see copyright notice in zlib.h +// +const tests = [ + [0x0, 0x0, 0, 0x0], + [0xffffffff, 0x0, 0, 0x0], + [0x0, 0x0, 255, 0x0], /* BZ 174799. */ + [0x0, 0x0, 256, 0x0], + [0x0, 0x0, 257, 0x0], + [0x0, 0x0, 32767, 0x0], + [0x0, 0x0, 32768, 0x0], + [0x0, 0x0, 32769, 0x0], + [0x0, '', 0, 0x0], + [0xffffffff, '', 0, 0xffffffff], + [0x0, 'abacus', 6, 0xc3d7115b], + [0x0, 'backlog', 7, 0x269205], + [0x0, 'campfire', 8, 0x22a515f8], + [0x0, 'delta', 5, 0x9643fed9], + [0x0, 'executable', 10, 0xd68eda01], + [0x0, 'file', 4, 0x8c9f3610], + [0x0, 'greatest', 8, 0xc1abd6cd], + [0x0, 'hello', 5, 0x3610a686], + [0x0, 'inverter', 8, 0xc9e962c9], + [0x0, 'jigsaw', 6, 0xce4e3f69], + [0x0, 'karate', 6, 0x890be0e2], + [0x0, 'landscape', 9, 0xc4e0330b], + [0x0, 'machine', 7, 0x1505df84], + [0x0, 'nanometer', 9, 0xd4e19f39], + [0x0, 'oblivion', 8, 0xdae9de77], + [0x0, 'panama', 6, 0x66b8979c], + [0x0, 'quest', 5, 0x4317f817], + [0x0, 'resource', 8, 0xbc91f416], + [0x0, 'secret', 6, 0x5ca2e8e5], + [0x0, 'test', 4, 0xd87f7e0c], + [0x0, 'ultimate', 8, 0x3fc79b0b], + [0x0, 'vector', 6, 0x1b6e485b], + [0x0, 'walrus', 6, 0xbe769b97], + [0x0, 'xeno', 4, 0xe7a06444], + [0x0, 'yelling', 7, 0xfe3944e5], + [0x0, 'zlib', 4, 0x73887d3a], + [0x0, '4BJD7PocN1VqX0jXVpWB', 20, 0xd487a5a1], + [0x0, 'F1rPWI7XvDs6nAIRx41l', 20, 0x61a0132e], + [0x0, 'ldhKlsVkPFOveXgkGtC2', 20, 0xdf02f76], + [0x0, '5KKnGOOrs8BvJ35iKTOS', 20, 0x579b2b0a], + [0x0, '0l1tw7GOcem06Ddu7yn4', 20, 0xf7d16e2d], + [0x0, 'MCr47CjPIn9R1IvE1Tm5', 20, 0x731788f5], + [0x0, 'UcixbzPKTIv0SvILHVdO', 20, 0x7112bb11], + [0x0, 'dGnAyAhRQDsWw0ESou24', 20, 0xf32a0dac], + [0x0, 'di0nvmY9UYMYDh0r45XT', 20, 0x625437bb], + [0x0, '2XKDwHfAhFsV0RhbqtvH', 20, 0x896930f9], + [0x0, 'ZhrANFIiIvRnqClIVyeD', 20, 0x8579a37], + [0x0, 'v7Q9ehzioTOVeDIZioT1', 20, 0x632aa8e0], + [0x0, 'Yod5hEeKcYqyhfXbhxj2', 20, 0xc829af29], + [0x0, 'GehSWY2ay4uUKhehXYb0', 20, 0x1b08b7e8], + [0x0, 'kwytJmq6UqpflV8Y8GoE', 20, 0x4e33b192], + [0x0, '70684206568419061514', 20, 0x59a179f0], + [0x0, '42015093765128581010', 20, 0xcd1013d7], + [0x0, '88214814356148806939', 20, 0xab927546], + [0x0, '43472694284527343838', 20, 0x11f3b20c], + [0x0, '49769333513942933689', 20, 0xd562d4ca], + [0x0, '54979784887993251199', 20, 0x233395f7], + [0x0, '58360544869206793220', 20, 0x2d167fd5], + [0x0, '27347953487840714234', 20, 0x8b5108ba], + [0x0, '07650690295365319082', 20, 0xc46b3cd8], + [0x0, '42655507906821911703', 20, 0xc10b2662], + [0x0, '29977409200786225655', 20, 0xc9a0f9d2], + [0x0, '85181542907229116674', 20, 0x9341357b], + [0x0, '87963594337989416799', 20, 0xf0424937], + [0x0, '21395988329504168551', 20, 0xd7c4c31f], + [0x0, '51991013580943379423', 20, 0xf11edcc4], + [0x0, '*]+@!);({_$;}[_},?{?;(_?,=-][@', 30, 0x40795df4], + [0x0, '_@:_).&(#.[:[{[:)$++-($_;@[)}+', 30, 0xdd61a631], + [0x0, '&[!,[$_==}+.]@!;*(+},[;:)$;)-@', 30, 0xca907a99], + [0x0, ']{.[.+?+[[=;[?}_#&;[=)__$$:+=_', 30, 0xf652deac], + [0x0, '-%.)=/[@].:.(:,()$;=%@-$?]{%+%', 30, 0xaf39a5a9], + [0x0, '+]#$(@&.=:,*];/.!]%/{:){:@(;)$', 30, 0x6bebb4cf], + // eslint-disable-next-line no-template-curly-in-string + [0x0, ')-._.:?[&:.=+}(*$/=!.${;(=$@!}', 30, 0x76430bac], + [0x0, ':(_*&%/[[}+,?#$&*+#[([*-/#;%(]', 30, 0x6c80c388], + [0x0, '{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:', 30, 0xd54d977d], + [0x0, '_{$*,}(&,@.)):=!/%(&(,,-?$}}}!', 30, 0xe3966ad5], + [0x0, + 'e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL', + 100, 0xe7c71db9], + [0x0, + 'r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)', + 100, 0xeaa52777], + [0x0, + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', + 100, 0xcd472048], + [0x7a30360d, 'abacus', 6, 0xf8655a84], + [0x6fd767ee, 'backlog', 7, 0x1ed834b1], + [0xefeb7589, 'campfire', 8, 0x686cfca], + [0x61cf7e6b, 'delta', 5, 0x1554e4b1], + [0xdc712e2, 'executable', 10, 0x761b4254], + [0xad23c7fd, 'file', 4, 0x7abdd09b], + [0x85cb2317, 'greatest', 8, 0x4ba91c6b], + [0x9eed31b0, 'inverter', 8, 0xd5e78ba5], + [0xb94f34ca, 'jigsaw', 6, 0x23649109], + [0xab058a2, 'karate', 6, 0xc5591f41], + [0x5bff2b7a, 'landscape', 9, 0xf10eb644], + [0x605c9a5f, 'machine', 7, 0xbaa0a636], + [0x51bdeea5, 'nanometer', 9, 0x6af89afb], + [0x85c21c79, 'oblivion', 8, 0xecae222b], + [0x97216f56, 'panama', 6, 0x47dffac4], + [0x18444af2, 'quest', 5, 0x70c2fe36], + [0xbe6ce359, 'resource', 8, 0x1471d925], + [0x843071f1, 'secret', 6, 0x50c9a0db], + [0xf2480c60, 'ultimate', 8, 0xf973daf8], + [0x2d2feb3d, 'vector', 6, 0x344ac03d], + [0x7490310a, 'walrus', 6, 0x6d1408ef], + [0x97d247d4, 'xeno', 4, 0xe62670b5], + [0x93cf7599, 'yelling', 7, 0x1b36da38], + [0x73c84278, 'zlib', 4, 0x6432d127], + [0x228a87d1, '4BJD7PocN1VqX0jXVpWB', 20, 0x997107d0], + [0xa7a048d0, 'F1rPWI7XvDs6nAIRx41l', 20, 0xdc567274], + [0x1f0ded40, 'ldhKlsVkPFOveXgkGtC2', 20, 0xdcc63870], + [0xa804a62f, '5KKnGOOrs8BvJ35iKTOS', 20, 0x6926cffd], + [0x508fae6a, '0l1tw7GOcem06Ddu7yn4', 20, 0xb52b38bc], + [0xe5adaf4f, 'MCr47CjPIn9R1IvE1Tm5', 20, 0xf83b8178], + [0x67136a40, 'UcixbzPKTIv0SvILHVdO', 20, 0xc5213070], + [0xb00c4a10, 'dGnAyAhRQDsWw0ESou24', 20, 0xbc7648b0], + [0x2e0c84b5, 'di0nvmY9UYMYDh0r45XT', 20, 0xd8123a72], + [0x81238d44, '2XKDwHfAhFsV0RhbqtvH', 20, 0xd5ac5620], + [0xf853aa92, 'ZhrANFIiIvRnqClIVyeD', 20, 0xceae099d], + [0x5a692325, 'v7Q9ehzioTOVeDIZioT1', 20, 0xb07d2b24], + [0x3275b9f, 'Yod5hEeKcYqyhfXbhxj2', 20, 0x24ce91df], + [0x38371feb, 'GehSWY2ay4uUKhehXYb0', 20, 0x707b3b30], + [0xafc8bf62, 'kwytJmq6UqpflV8Y8GoE', 20, 0x16abc6a9], + [0x9b07db73, '70684206568419061514', 20, 0xae1fb7b7], + [0xe75b214, '42015093765128581010', 20, 0xd4eecd2d], + [0x72d0fe6f, '88214814356148806939', 20, 0x4660ec7], + [0xf857a4b1, '43472694284527343838', 20, 0xfd8afdf7], + [0x54b8e14, '49769333513942933689', 20, 0xc6d1b5f2], + [0xd6aa5616, '54979784887993251199', 20, 0x32476461], + [0x11e63098, '58360544869206793220', 20, 0xd917cf1a], + [0xbe92385, '27347953487840714234', 20, 0x4ad14a12], + [0x49511de0, '07650690295365319082', 20, 0xe37b5c6c], + [0x3db13bc1, '42655507906821911703', 20, 0x7cc497f1], + [0xbb899bea, '29977409200786225655', 20, 0x99781bb2], + [0xf6cd9436, '85181542907229116674', 20, 0x132256a1], + [0x9109e6c3, '87963594337989416799', 20, 0xbfdb2c83], + [0x75770fc, '21395988329504168551', 20, 0x8d9d1e81], + [0x69b1d19b, '51991013580943379423', 20, 0x7b6d4404], + [0xc6132975, '*]+@!);({_$;}[_},?{?;(_?,=-][@', 30, 0x8619f010], + [0xd58cb00c, '_@:_).&(#.[:[{[:)$++-($_;@[)}+', 30, 0x15746ac3], + [0xb63b8caa, '&[!,[$_==}+.]@!;*(+},[;:)$;)-@', 30, 0xaccf812f], + [0x8a45a2b8, ']{.[.+?+[[=;[?}_#&;[=)__$$:+=_', 30, 0x78af45de], + [0xcbe95b78, '-%.)=/[@].:.(:,()$;=%@-$?]{%+%', 30, 0x25b06b59], + [0x4ef8a54b, '+]#$(@&.=:,*];/.!]%/{:){:@(;)$', 30, 0x4ba0d08f], + // eslint-disable-next-line no-template-curly-in-string + [0x76ad267a, ')-._.:?[&:.=+}(*$/=!.${;(=$@!}', 30, 0xe26b6aac], + [0x569e613c, ':(_*&%/[[}+,?#$&*+#[([*-/#;%(]', 30, 0x7e2b0a66], + [0x36aa61da, '{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:', 30, 0xb3430dc7], + [0xf67222df, '_{$*,}(&,@.)):=!/%(&(,,-?$}}}!', 30, 0x626c17a], + [0x74b34fd3, + 'e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL', + 100, 0xccf98060], + [0x351fd770, + 'r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)', + 100, 0xd8b95312], + [0xc45aef77, + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', + 100, 0xbb1c9912], + [0xc45aef77, + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', + 600, 0x888AFA5B], +]; + +for (const [ crc, data, len, expected ] of tests) { + if (data === 0) { + continue; + } + const buf = Buffer.from(data, 'utf8'); + assert.strictEqual(buf.length, len); + assert.strictEqual(zlib.crc32(buf, crc), expected, + `crc32('${data}', ${crc}) in buffer is not ${expected}`); + assert.strictEqual(zlib.crc32(buf.toString(), crc), expected, + `crc32('${data}', ${crc}) in string is not ${expected}`); + if (crc === 0) { + assert.strictEqual(zlib.crc32(buf), expected, + `crc32('${data}') in buffer is not ${expected}`); + assert.strictEqual(zlib.crc32(buf.toString()), expected, + `crc32('${data}') in string is not ${expected}`); + } +} + +[undefined, null, true, 1, () => {}, {}].forEach((invalid) => { + assert.throws(() => { zlib.crc32(invalid); }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); + +[null, true, () => {}, {}].forEach((invalid) => { + assert.throws(() => { zlib.crc32('test', invalid); }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); diff --git a/test/js/node/test/parallel/test-zlib-create-raw.js b/test/js/node/test/parallel/test-zlib-create-raw.js new file mode 100644 index 0000000000..92e21545e4 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-create-raw.js @@ -0,0 +1,15 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +{ + const inflateRaw = zlib.createInflateRaw(); + assert(inflateRaw instanceof zlib.InflateRaw); +} + +{ + const deflateRaw = zlib.createDeflateRaw(); + assert(deflateRaw instanceof zlib.DeflateRaw); +} diff --git a/test/js/node/test/parallel/test-zlib-deflate-raw-inherits.js b/test/js/node/test/parallel/test-zlib-deflate-raw-inherits.js new file mode 100644 index 0000000000..34bf31058a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-deflate-raw-inherits.js @@ -0,0 +1,27 @@ +'use strict'; + +require('../common'); +const { DeflateRaw } = require('zlib'); +const { Readable } = require('stream'); + +// Validates that zlib.DeflateRaw can be inherited +// with Object.setPrototypeOf + +function NotInitialized(options) { + DeflateRaw.call(this, options); + this.prop = true; +} +Object.setPrototypeOf(NotInitialized.prototype, DeflateRaw.prototype); +Object.setPrototypeOf(NotInitialized, DeflateRaw); + +const dest = new NotInitialized(); + +const read = new Readable({ + read() { + this.push(Buffer.from('a test string')); + this.push(null); + } +}); + +read.pipe(dest); +dest.resume(); diff --git a/test/js/node/test/parallel/test-zlib-destroy-pipe.js b/test/js/node/test/parallel/test-zlib-destroy-pipe.js new file mode 100644 index 0000000000..67821a21b6 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-destroy-pipe.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const zlib = require('zlib'); +const { Writable } = require('stream'); + +// Verify that the zlib transform does not error in case +// it is destroyed with data still in flight + +const ts = zlib.createGzip(); + +const ws = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + setImmediate(cb); + ts.destroy(); + }) +}); + +const buf = Buffer.allocUnsafe(1024 * 1024 * 20); +ts.end(buf); +ts.pipe(ws); diff --git a/test/js/node/test/parallel/test-zlib-destroy.js b/test/js/node/test/parallel/test-zlib-destroy.js new file mode 100644 index 0000000000..775b7020b4 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-destroy.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const zlib = require('zlib'); + +// Verify that the zlib transform does clean up +// the handle when calling destroy. + +{ + const ts = zlib.createGzip(); + ts.destroy(); + assert.strictEqual(ts._handle, null); + + ts.on('close', common.mustCall(() => { + ts.close(common.mustCall()); + })); +} + +{ + // Ensure 'error' is only emitted once. + const decompress = zlib.createGunzip(15); + + decompress.on('error', common.mustCall((err) => { + decompress.close(); + })); + + decompress.write('something invalid'); + decompress.destroy(new Error('asd')); +} diff --git a/test/js/node/test/parallel/test-zlib-dictionary-fail.js b/test/js/node/test/parallel/test-zlib-dictionary-fail.js new file mode 100644 index 0000000000..9546954841 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-dictionary-fail.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// String "test" encoded with dictionary "dict". +const input = Buffer.from([0x78, 0xBB, 0x04, 0x09, 0x01, 0xA5]); + +{ + const stream = zlib.createInflate(); + + stream.on('error', common.mustCall(function(err) { + assert.match(err.message, /Missing dictionary/); + })); + + stream.write(input); +} + +{ + const stream = zlib.createInflate({ dictionary: Buffer.from('fail') }); + + stream.on('error', common.mustCall(function(err) { + assert.match(err.message, /Bad dictionary/); + })); + + stream.write(input); +} + +{ + const stream = zlib.createInflateRaw({ dictionary: Buffer.from('fail') }); + + stream.on('error', common.mustCall(function(err) { + // It's not possible to separate invalid dict and invalid data when using + // the raw format + assert.match(err.message, /(invalid|Operation-Ending-Supplemental Code is 0x12)/); + })); + + stream.write(input); +} diff --git a/test/js/node/test/parallel/test-zlib-dictionary.js b/test/js/node/test/parallel/test-zlib-dictionary.js new file mode 100644 index 0000000000..49a01d5a03 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-dictionary.js @@ -0,0 +1,175 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test compression/decompression with dictionary + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const spdyDict = Buffer.from([ + 'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-', + 'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi', + 'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser', + '-agent10010120020120220320420520630030130230330430530630740040140240340440', + '5406407408409410411412413414415416417500501502503504505accept-rangesageeta', + 'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic', + 'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran', + 'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati', + 'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo', + 'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe', + 'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic', + 'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1', + '.1statusversionurl\0', +].join('')); + +const input = [ + 'HTTP/1.1 200 Ok', + 'Server: node.js', + 'Content-Length: 0', + '', +].join('\r\n'); + +function basicDictionaryTest(spdyDict) { + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.end(); +} + +function deflateResetDictionaryTest(spdyDict) { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.flush(function() { + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); + }); +} + +function rawDictionaryTest(spdyDict) { + let output = ''; + const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); + const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.end(); +} + +function deflateRawResetDictionaryTest(spdyDict) { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); + const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.flush(function() { + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); + }); +} + +for (const dict of [spdyDict, ...common.getBufferSources(spdyDict)]) { + basicDictionaryTest(dict); + deflateResetDictionaryTest(dict); + rawDictionaryTest(dict); + deflateRawResetDictionaryTest(dict); +} diff --git a/test/js/node/test/parallel/test-zlib-empty-buffer.js b/test/js/node/test/parallel/test-zlib-empty-buffer.js new file mode 100644 index 0000000000..27fd1340fd --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-empty-buffer.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const { inspect, promisify } = require('util'); +const assert = require('assert'); +const emptyBuffer = Buffer.alloc(0); + +(async function() { + for (const [ compress, decompress, method ] of [ + [ zlib.deflateRawSync, zlib.inflateRawSync, 'raw sync' ], + [ zlib.deflateSync, zlib.inflateSync, 'deflate sync' ], + [ zlib.gzipSync, zlib.gunzipSync, 'gzip sync' ], + [ zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync' ], + [ promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw' ], + [ promisify(zlib.deflate), promisify(zlib.inflate), 'deflate' ], + [ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ], + [ promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br' ], + ]) { + const compressed = await compress(emptyBuffer); + const decompressed = await decompress(compressed); + assert.deepStrictEqual( + emptyBuffer, decompressed, + `Expected ${inspect(compressed)} to match ${inspect(decompressed)} ` + + `to match for ${method}`); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-zlib-flush-drain-longblock.js b/test/js/node/test/parallel/test-zlib-flush-drain-longblock.js new file mode 100644 index 0000000000..e2f56ec762 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-drain-longblock.js @@ -0,0 +1,27 @@ +'use strict'; + +// Regression test for https://github.com/nodejs/node/issues/14523. +// Checks that flushes interact properly with writableState.needDrain, +// even if no flush callback was passed. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const zipper = zlib.createGzip({ highWaterMark: 16384 }); +const unzipper = zlib.createGunzip(); +zipper.pipe(unzipper); + +zipper.write('A'.repeat(17000)); +zipper.flush(); + +let received = 0; +unzipper.on('data', common.mustCallAtLeast((d) => { + received += d.length; +}, 2)); + +// Properly `.end()`ing the streams would interfere with checking that +// `.flush()` works. +process.on('exit', () => { + assert.strictEqual(received, 17000); +}); diff --git a/test/js/node/test/parallel/test-zlib-flush-drain.js b/test/js/node/test/parallel/test-zlib-flush-drain.js new file mode 100644 index 0000000000..6993d2c9fe --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-drain.js @@ -0,0 +1,51 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const bigData = Buffer.alloc(10240, 'x'); + +const opts = { + level: 0, + highWaterMark: 16 +}; + +const deflater = zlib.createDeflate(opts); + +// Shim deflater.flush so we can count times executed +let flushCount = 0; +let drainCount = 0; + +const flush = deflater.flush; +deflater.flush = function(kind, callback) { + flushCount++; + flush.call(this, kind, callback); +}; + +deflater.write(bigData); + +const ws = deflater._writableState; +const beforeFlush = ws.needDrain; +let afterFlush = ws.needDrain; + +deflater.on('data', () => { +}); + +deflater.flush(function(err) { + afterFlush = ws.needDrain; +}); + +deflater.on('drain', function() { + drainCount++; +}); + +process.once('exit', function() { + assert.strictEqual( + beforeFlush, true); + assert.strictEqual( + afterFlush, false); + assert.strictEqual( + drainCount, 1); + assert.strictEqual( + flushCount, 1); +}); diff --git a/test/js/node/test/parallel/test-zlib-flush-write-sync-interleaved.js b/test/js/node/test/parallel/test-zlib-flush-write-sync-interleaved.js new file mode 100644 index 0000000000..f8387f4006 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-write-sync-interleaved.js @@ -0,0 +1,57 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { createGzip, createGunzip, Z_PARTIAL_FLUSH } = require('zlib'); + +// Verify that .flush() behaves like .write() in terms of ordering, e.g. in +// a sequence like .write() + .flush() + .write() + .flush() each .flush() call +// only affects the data written before it. +// Refs: https://github.com/nodejs/node/issues/28478 + +const compress = createGzip(); +const decompress = createGunzip(); +decompress.setEncoding('utf8'); + +const events = []; +const compressedChunks = []; + +for (const chunk of ['abc', 'def', 'ghi']) { + compress.write(chunk, common.mustCall(() => events.push({ written: chunk }))); + compress.flush(Z_PARTIAL_FLUSH, common.mustCall(() => { + events.push('flushed'); + const chunk = compress.read(); + if (chunk !== null) + compressedChunks.push(chunk); + })); +} + +compress.end(common.mustCall(() => { + events.push('compress end'); + writeToDecompress(); +})); + +function writeToDecompress() { + // Write the compressed chunks to a decompressor, one by one, in order to + // verify that the flushes actually worked. + const chunk = compressedChunks.shift(); + if (chunk === undefined) return decompress.end(); + decompress.write(chunk, common.mustCall(() => { + events.push({ read: decompress.read() }); + writeToDecompress(); + })); +} + +process.on('exit', () => { + assert.deepStrictEqual(events, [ + { written: 'abc' }, + 'flushed', + { written: 'def' }, + 'flushed', + { written: 'ghi' }, + 'flushed', + 'compress end', + { read: 'abc' }, + { read: 'def' }, + { read: 'ghi' }, + ]); +}); diff --git a/test/js/node/test/parallel/test-zlib-flush.js b/test/js/node/test/parallel/test-zlib-flush.js new file mode 100644 index 0000000000..557775d509 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush.js @@ -0,0 +1,36 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 16; +const opts = { level: 0 }; +const deflater = zlib.createDeflate(opts); + +const chunk = file.slice(0, chunkSize); +const expectedNone = Buffer.from([0x78, 0x01]); +const blkhdr = Buffer.from([0x00, 0x10, 0x00, 0xef, 0xff]); +const adler32 = Buffer.from([0x00, 0x00, 0x00, 0xff, 0xff]); +const expectedFull = Buffer.concat([blkhdr, chunk, adler32]); +let actualNone; +let actualFull; + +deflater.write(chunk, function() { + deflater.flush(zlib.constants.Z_NO_FLUSH, function() { + actualNone = deflater.read(); + deflater.flush(function() { + const bufs = []; + let buf; + while ((buf = deflater.read()) !== null) + bufs.push(buf); + actualFull = Buffer.concat(bufs); + }); + }); +}); + +process.once('exit', function() { + assert.deepStrictEqual(actualNone, expectedNone); + assert.deepStrictEqual(actualFull, expectedFull); +}); diff --git a/test/js/node/test/parallel/test-zlib-from-concatenated-gzip.js b/test/js/node/test/parallel/test-zlib-from-concatenated-gzip.js new file mode 100644 index 0000000000..1de36dacf9 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-concatenated-gzip.js @@ -0,0 +1,83 @@ +'use strict'; +// Test unzipping a gzip file that contains multiple concatenated "members" + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const abc = 'abc'; +const def = 'def'; + +const abcEncoded = zlib.gzipSync(abc); +const defEncoded = zlib.gzipSync(def); + +const data = Buffer.concat([ + abcEncoded, + defEncoded, +]); + +assert.strictEqual(zlib.gunzipSync(data).toString(), (abc + def)); + +zlib.gunzip(data, common.mustSucceed((result) => { + assert.strictEqual(result.toString(), (abc + def)); +})); + +zlib.unzip(data, common.mustSucceed((result) => { + assert.strictEqual(result.toString(), (abc + def)); +})); + +// Multi-member support does not apply to zlib inflate/deflate. +zlib.unzip(Buffer.concat([ + zlib.deflateSync('abc'), + zlib.deflateSync('def'), +]), common.mustSucceed((result) => { + assert.strictEqual(result.toString(), abc); +})); + +// Files that have the "right" magic bytes for starting a new gzip member +// in the middle of themselves, even if they are part of a single +// regularly compressed member +const pmmFileZlib = fixtures.path('pseudo-multimember-gzip.z'); +const pmmFileGz = fixtures.path('pseudo-multimember-gzip.gz'); + +const pmmExpected = zlib.inflateSync(fs.readFileSync(pmmFileZlib)); +const pmmResultBuffers = []; + +fs.createReadStream(pmmFileGz) + .pipe(zlib.createGunzip()) + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => pmmResultBuffers.push(data)) + .on('finish', common.mustCall(() => { + // Result should match original random garbage + assert.deepStrictEqual(Buffer.concat(pmmResultBuffers), pmmExpected); + })); + +// Test that the next gzip member can wrap around the input buffer boundary +[0, 1, 2, 3, 4, defEncoded.length].forEach((offset) => { + const resultBuffers = []; + + const unzip = zlib.createGunzip() + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => resultBuffers.push(data)) + .on('finish', common.mustCall(() => { + assert.strictEqual( + Buffer.concat(resultBuffers).toString(), + 'abcdef', + `result should match original input (offset = ${offset})` + ); + })); + + // First write: write "abc" + the first bytes of "def" + unzip.write(Buffer.concat([ + abcEncoded, defEncoded.slice(0, offset), + ])); + + // Write remaining bytes of "def" + unzip.end(defEncoded.slice(offset)); +}); diff --git a/test/js/node/test/parallel/test-zlib-from-gzip.js b/test/js/node/test/parallel/test-zlib-from-gzip.js new file mode 100644 index 0000000000..f8fbe16764 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-gzip.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test unzipping a file that was created with a non-node gzip lib, +// piped in as fast as possible. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const gunzip = zlib.createGunzip(); + +const fs = require('fs'); + +const fixture = fixtures.path('person.jpg.gz'); +const unzippedFixture = fixtures.path('person.jpg'); +const outputFile = tmpdir.resolve('person.jpg'); +const expect = fs.readFileSync(unzippedFixture); +const inp = fs.createReadStream(fixture); +const out = fs.createWriteStream(outputFile); + +inp.pipe(gunzip).pipe(out); +out.on('close', common.mustCall(() => { + const actual = fs.readFileSync(outputFile); + assert.strictEqual(actual.length, expect.length); + for (let i = 0, l = actual.length; i < l; i++) { + assert.strictEqual(actual[i], expect[i], `byte[${i}]`); + } +})); diff --git a/test/js/node/test/parallel/test-zlib-from-string.js b/test/js/node/test/parallel/test-zlib-from-string.js new file mode 100644 index 0000000000..92b6f86646 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-string.js @@ -0,0 +1,83 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test compressing and uncompressing a string with zlib + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const expectedBase64Deflate = 'eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmX' + + 'AKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkD' + + 'lKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6oc' + + 'iBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoA' + + 'bWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+' + + 'RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKS' + + 'qGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqq' + + 'fKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOH' + + 'aFE4RdpnGavKtrB5xzfO/Ll9'; +const expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4' + + '96pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajN' + + 'Pk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9Lq' + + 'hyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBt' + + 'aHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCw' + + 'agLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+' + + 'eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5' + + 'mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2' + + 'sHnHNzRtagj5AQAA'; + +zlib.deflate(inputString, common.mustCall((err, buffer) => { + zlib.inflate(buffer, common.mustCall((err, inflated) => { + assert.strictEqual(inflated.toString(), inputString); + })); +})); + +zlib.gzip(inputString, common.mustCall((err, buffer) => { + // Can't actually guarantee that we'll get exactly the same + // deflated bytes when we compress a string, since the header + // depends on stuff other than the input string itself. + // However, decrypting it should definitely yield the same + // result that we're expecting, and this should match what we get + // from inflating the known valid deflate data. + zlib.gunzip(buffer, common.mustCall((err, gunzipped) => { + assert.strictEqual(gunzipped.toString(), inputString); + })); +})); + +let buffer = Buffer.from(expectedBase64Deflate, 'base64'); +zlib.unzip(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); + +buffer = Buffer.from(expectedBase64Gzip, 'base64'); +zlib.unzip(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/test/js/node/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js b/test/js/node/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js new file mode 100644 index 0000000000..688acddd16 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); + +// This test ensures that the BrotliCompress function throws +// ERR_INVALID_ARG_TYPE when the values of the `params` key-value object are +// neither numbers nor booleans. + +const assert = require('assert'); +const { BrotliCompress, constants } = require('zlib'); + +const opts = { + params: { + [constants.BROTLI_PARAM_MODE]: 'lol' + } +}; + +assert.throws(() => BrotliCompress(opts), { + code: 'ERR_INVALID_ARG_TYPE' +}); diff --git a/test/js/node/test/parallel/http-url.parse-only-support-http-https-protocol.test.js b/test/js/node/test/parallel/test-zlib-invalid-input.js similarity index 58% rename from test/js/node/test/parallel/http-url.parse-only-support-http-https-protocol.test.js rename to test/js/node/test/parallel/test-zlib-invalid-input.js index 4f3c5dd20a..7aa44dfe70 100644 --- a/test/js/node/test/parallel/http-url.parse-only-support-http-https-protocol.test.js +++ b/test/js/node/test/parallel/test-zlib-invalid-input.js @@ -1,6 +1,3 @@ -//#FILE: test-http-url.parse-only-support-http-https-protocol.js -//#SHA1: 924c029f73164388b765c128401affa763af7b56 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,31 +19,42 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const http = require("http"); -const url = require("url"); +'use strict'; +// Test uncompressing invalid input -const invalidUrls = [ - "file:///whatever", - "mailto:asdf@asdf.com", - "ftp://www.example.com", - "javascript:alert('hello');", - "xmpp:foo@bar.com", - "f://some.host/path", +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const nonStringInputs = [ + 1, + true, + { a: 1 }, + ['a'], ]; -describe("http.request with invalid protocols", () => { - test.each(invalidUrls)("throws for invalid URL: %s", invalid => { - expect(() => { - http.request(url.parse(invalid)); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_PROTOCOL", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); +// zlib.Unzip classes need to get valid data, or else they'll throw. +const unzips = [ + zlib.Unzip(), + zlib.Gunzip(), + zlib.Inflate(), + zlib.InflateRaw(), + zlib.BrotliDecompress(), +]; -//<#END_FILE: test-http-url.parse-only-support-http-https-protocol.js +nonStringInputs.forEach(common.mustCall((input) => { + assert.throws(() => { + zlib.gunzip(input); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); +}, nonStringInputs.length)); + +unzips.forEach(common.mustCall((uz, i) => { + uz.on('error', common.mustCall()); + uz.on('end', common.mustNotCall()); + + // This will trigger error event + uz.write('this is not valid compressed data.'); +}, unzips.length)); diff --git a/test/js/node/test/parallel/test-zlib-kmaxlength-rangeerror.js b/test/js/node/test/parallel/test-zlib-kmaxlength-rangeerror.js new file mode 100644 index 0000000000..9803630214 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-kmaxlength-rangeerror.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); + +// This test ensures that zlib throws a RangeError if the final buffer needs to +// be larger than kMaxLength and concatenation fails. +// https://github.com/nodejs/node/pull/1811 + +const assert = require('assert'); + +// Change kMaxLength for zlib to trigger the error without having to allocate +// large Buffers. +const buffer = require('buffer'); +const oldkMaxLength = buffer.kMaxLength; +buffer.kMaxLength = 64; +const zlib = require('zlib'); +buffer.kMaxLength = oldkMaxLength; + +const encoded = Buffer.from('H4sIAAAAAAAAA0tMHFgAAIw2K/GAAAAA', 'base64'); + +// Async +zlib.gunzip(encoded, function(err) { + assert.ok(err instanceof RangeError); +}); + +// Sync +assert.throws(function() { + zlib.gunzipSync(encoded); +}, RangeError); diff --git a/test/js/node/test/parallel/test-zlib-maxOutputLength.js b/test/js/node/test/parallel/test-zlib-maxOutputLength.js new file mode 100644 index 0000000000..9af0b3736f --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-maxOutputLength.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64'); + +// Async +zlib.brotliDecompress(encoded, { maxOutputLength: 64 }, common.expectsError({ + code: 'ERR_BUFFER_TOO_LARGE', + message: 'Cannot create a Buffer larger than 64 bytes' +})); + +// Sync +assert.throws(function() { + zlib.brotliDecompressSync(encoded, { maxOutputLength: 64 }); +}, RangeError); + +// Async +zlib.brotliDecompress(encoded, { maxOutputLength: 256 }, function(err) { + assert.strictEqual(err, null); +}); + +// Sync +zlib.brotliDecompressSync(encoded, { maxOutputLength: 256 }); diff --git a/test/js/node/test/parallel/test-zlib-no-stream.js b/test/js/node/test/parallel/test-zlib-no-stream.js new file mode 100644 index 0000000000..68da269ab8 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-no-stream.js @@ -0,0 +1,14 @@ +/* eslint-disable node-core/required-modules */ +/* eslint-disable node-core/require-common-first */ + +'use strict'; + +// We are not loading common because it will load the stream module, +// defeating the purpose of this test. + +const { gzipSync } = require('zlib'); + +// Avoid regressions such as https://github.com/nodejs/node/issues/36615 + +// This must not throw +gzipSync('fooobar'); diff --git a/test/js/node/test/parallel/test-zlib-object-write.js b/test/js/node/test/parallel/test-zlib-object-write.js new file mode 100644 index 0000000000..2be5edab89 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-object-write.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Gunzip } = require('zlib'); + +const gunzip = new Gunzip({ objectMode: true }); +gunzip.on('error', common.mustNotCall()); +assert.throws(() => { + gunzip.write({}); +}, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' +}); diff --git a/test/js/node/test/parallel/test-zlib-params.js b/test/js/node/test/parallel/test-zlib-params.js new file mode 100644 index 0000000000..18271fe022 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-params.js @@ -0,0 +1,40 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 12 * 1024; +const opts = { level: 9, strategy: zlib.constants.Z_DEFAULT_STRATEGY }; +const deflater = zlib.createDeflate(opts); + +const chunk1 = file.slice(0, chunkSize); +const chunk2 = file.slice(chunkSize); +const blkhdr = Buffer.from([0x00, 0x5a, 0x82, 0xa5, 0x7d]); +const blkftr = Buffer.from('010000ffff7dac3072', 'hex'); +const expected = Buffer.concat([blkhdr, chunk2, blkftr]); +const bufs = []; + +function read() { + let buf; + while ((buf = deflater.read()) !== null) { + bufs.push(buf); + } +} + +deflater.write(chunk1, function() { + deflater.params(0, zlib.constants.Z_DEFAULT_STRATEGY, function() { + while (deflater.read()); + + deflater.on('readable', read); + + deflater.end(chunk2); + }); + while (deflater.read()); +}); + +process.once('exit', function() { + const actual = Buffer.concat(bufs); + assert.deepStrictEqual(actual, expected); +}); diff --git a/test/js/node/test/parallel/test-zlib-premature-end.js b/test/js/node/test/parallel/test-zlib-premature-end.js new file mode 100644 index 0000000000..17446c907d --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-premature-end.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); + +const input = '0123456789'.repeat(4); + +for (const [ compress, decompressor ] of [ + [ zlib.deflateRawSync, zlib.createInflateRaw ], + [ zlib.deflateSync, zlib.createInflate ], + [ zlib.brotliCompressSync, zlib.createBrotliDecompress ], +]) { + const compressed = compress(input); + const trailingData = Buffer.from('not valid compressed data'); + + for (const variant of [ + (stream) => { stream.end(compressed); }, + (stream) => { stream.write(compressed); stream.write(trailingData); }, + (stream) => { stream.write(compressed); stream.end(trailingData); }, + (stream) => { stream.write(Buffer.concat([compressed, trailingData])); }, + (stream) => { stream.end(Buffer.concat([compressed, trailingData])); }, + ]) { + let output = ''; + const stream = decompressor(); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => output += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(output, input); + assert.strictEqual(stream.bytesWritten, compressed.length); + })); + variant(stream); + } +} diff --git a/test/js/node/test/parallel/zlib-random-byte-pipes.test.js b/test/js/node/test/parallel/test-zlib-random-byte-pipes.js similarity index 68% rename from test/js/node/test/parallel/zlib-random-byte-pipes.test.js rename to test/js/node/test/parallel/test-zlib-random-byte-pipes.js index fb37cf53f9..d8d039a6d6 100644 --- a/test/js/node/test/parallel/zlib-random-byte-pipes.test.js +++ b/test/js/node/test/parallel/test-zlib-random-byte-pipes.js @@ -1,6 +1,3 @@ -//#FILE: test-zlib-random-byte-pipes.js -//#SHA1: ef7e7d3683660b911f9e24a6f40947a47be3dbba -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,11 +19,15 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); -const crypto = require("crypto"); -const stream = require("stream"); -const zlib = require("zlib"); +const assert = require('assert'); +const crypto = require('crypto'); +const stream = require('stream'); +const zlib = require('zlib'); const Stream = stream.Stream; @@ -39,7 +40,7 @@ class RandomReadStream extends Stream { this._paused = false; this._processing = false; - this._hasher = crypto.createHash("sha1"); + this._hasher = crypto.createHash('sha1'); opt = opt || {}; // base block size. @@ -61,13 +62,13 @@ class RandomReadStream extends Stream { pause() { this._paused = true; - this.emit("pause"); + this.emit('pause'); } resume() { // console.error("rrs resume"); this._paused = false; - this.emit("resume"); + this.emit('resume'); this._process(); } @@ -78,10 +79,10 @@ class RandomReadStream extends Stream { this._processing = true; if (!this._remaining) { - this._hash = this._hasher.digest("hex").toLowerCase().trim(); + this._hash = this._hasher.digest('hex').toLowerCase().trim(); this._processing = false; - this.emit("end"); + this.emit('end'); return; } @@ -90,7 +91,7 @@ class RandomReadStream extends Stream { let block = this._opt.block; const jitter = this._opt.jitter; if (jitter) { - block += Math.ceil(Math.random() * jitter - jitter / 2); + block += Math.ceil(Math.random() * jitter - (jitter / 2)); } block = Math.min(block, this._remaining); const buf = Buffer.allocUnsafe(block); @@ -104,7 +105,7 @@ class RandomReadStream extends Stream { this._processing = false; - this.emit("data", buf); + this.emit('data', buf); process.nextTick(this._process); } } @@ -114,7 +115,7 @@ class HashStream extends Stream { constructor() { super(); this.readable = this.writable = true; - this._hasher = crypto.createHash("sha1"); + this._hasher = crypto.createHash('sha1'); } write(c) { @@ -126,41 +127,32 @@ class HashStream extends Stream { } resume() { - this.emit("resume"); - process.nextTick(() => this.emit("drain")); + this.emit('resume'); + process.nextTick(() => this.emit('drain')); } end(c) { if (c) { this.write(c); } - this._hash = this._hasher.digest("hex").toLowerCase().trim(); - this.emit("data", this._hash); - this.emit("end"); + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this.emit('data', this._hash); + this.emit('end'); } } -test("zlib random byte pipes", async () => { - const compressionMethods = [ - [zlib.createGzip, zlib.createGunzip], - [zlib.createBrotliCompress, zlib.createBrotliDecompress], - ]; +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); + const out = new HashStream(); + const gzip = createCompress(); + const gunz = createDecompress(); - for (const [createCompress, createDecompress] of compressionMethods) { - const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); - const out = new HashStream(); - const gzip = createCompress(); - const gunz = createDecompress(); + inp.pipe(gzip).pipe(gunz).pipe(out); - inp.pipe(gzip).pipe(gunz).pipe(out); - - await new Promise(resolve => { - out.on("data", c => { - expect(c).toBe(inp._hash); - resolve(); - }); - }); - } -}); - -//<#END_FILE: test-zlib-random-byte-pipes.js + out.on('data', common.mustCall((c) => { + assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`); + })); +} diff --git a/test/js/node/test/parallel/test-zlib-reset-before-write.js b/test/js/node/test/parallel/test-zlib-reset-before-write.js new file mode 100644 index 0000000000..afa207f12c --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-reset-before-write.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Tests that zlib streams support .reset() and .params() +// before the first write. That is important to ensure that +// lazy init of zlib native library handles these cases. + +for (const fn of [ + (z, cb) => { + z.reset(); + cb(); + }, + (z, cb) => z.params(0, zlib.constants.Z_DEFAULT_STRATEGY, cb), +]) { + const deflate = zlib.createDeflate(); + const inflate = zlib.createInflate(); + + deflate.pipe(inflate); + + const output = []; + inflate + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (chunk) => output.push(chunk)) + .on('end', common.mustCall( + () => assert.strictEqual(Buffer.concat(output).toString(), 'abc'))); + + fn(deflate, () => { + fn(inflate, () => { + deflate.write('abc'); + deflate.end(); + }); + }); +} diff --git a/test/js/node/test/parallel/test-zlib-sync-no-event.js b/test/js/node/test/parallel/test-zlib-sync-no-event.js new file mode 100644 index 0000000000..e7f25c8476 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-sync-no-event.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); + +const message = 'Come on, Fhqwhgads.'; +const buffer = Buffer.from(message); + +const zipper = new zlib.Gzip(); +zipper.on('close', common.mustNotCall()); + +const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); + +const unzipper = new zlib.Gunzip(); +unzipper.on('close', common.mustNotCall()); + +const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); +assert.notStrictEqual(zipped.toString(), message); +assert.strictEqual(unzipped.toString(), message); diff --git a/test/js/node/test/parallel/test-zlib-truncated.js b/test/js/node/test/parallel/test-zlib-truncated.js new file mode 100644 index 0000000000..94bc0e21cb --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-truncated.js @@ -0,0 +1,64 @@ +'use strict'; +// Tests zlib streams with truncated compressed input + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; + +const errMessage = /unexpected end of file/; + +[ + { comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' }, + { comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' }, + { comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' }, + { comp: 'deflateRaw', decomp: 'inflateRaw', decompSync: 'inflateRawSync' }, +].forEach(function(methods) { + zlib[methods.comp](inputString, function(err, compressed) { + assert.ifError(err); + const truncated = compressed.slice(0, compressed.length / 2); + const toUTF8 = (buffer) => buffer.toString('utf-8'); + + // sync sanity + const decompressed = zlib[methods.decompSync](compressed); + assert.strictEqual(toUTF8(decompressed), inputString); + + // async sanity + zlib[methods.decomp](compressed, function(err, result) { + assert.ifError(err); + assert.strictEqual(toUTF8(result), inputString); + }); + + // Sync truncated input test + assert.throws(function() { + zlib[methods.decompSync](truncated); + }, errMessage); + + // Async truncated input test + zlib[methods.decomp](truncated, function(err, result) { + assert.match(err.message, errMessage); + }); + + const syncFlushOpt = { finishFlush: zlib.constants.Z_SYNC_FLUSH }; + + // Sync truncated input test, finishFlush = Z_SYNC_FLUSH + const result = toUTF8(zlib[methods.decompSync](truncated, syncFlushOpt)); + assert.strictEqual(result, inputString.slice(0, result.length)); + + // Async truncated input test, finishFlush = Z_SYNC_FLUSH + zlib[methods.decomp](truncated, syncFlushOpt, function(err, decompressed) { + assert.ifError(err); + const result = toUTF8(decompressed); + assert.strictEqual(result, inputString.slice(0, result.length)); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-zlib-unzip-one-byte-chunks.js b/test/js/node/test/parallel/test-zlib-unzip-one-byte-chunks.js new file mode 100644 index 0000000000..51af5153a4 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-unzip-one-byte-chunks.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const data = Buffer.concat([ + zlib.gzipSync('abc'), + zlib.gzipSync('def'), +]); + +const resultBuffers = []; + +const unzip = zlib.createUnzip() + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => resultBuffers.push(data)) + .on('finish', common.mustCall(() => { + const unzipped = Buffer.concat(resultBuffers).toString(); + assert.strictEqual(unzipped, 'abcdef', + `'${unzipped}' should match 'abcdef' after zipping ` + + 'and unzipping'); + })); + +for (let i = 0; i < data.length; i++) { + // Write each single byte individually. + unzip.write(Buffer.from([data[i]])); +} + +unzip.end(); diff --git a/test/js/node/test/parallel/test-zlib-write-after-close.js b/test/js/node/test/parallel/test-zlib-write-after-close.js new file mode 100644 index 0000000000..eb8ff43539 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-write-after-close.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +zlib.gzip('hello', common.mustCall(function(err, out) { + const unzip = zlib.createGunzip(); + unzip.close(common.mustCall()); + unzip.write('asd', common.expectsError({ + code: 'ERR_STREAM_DESTROYED', + name: 'Error', + message: 'Cannot call write after a stream was destroyed' + })); +})); diff --git a/test/js/node/test/parallel/test-zlib-write-after-end.js b/test/js/node/test/parallel/test-zlib-write-after-end.js new file mode 100644 index 0000000000..2b31ff30dc --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-write-after-end.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +// Regression test for https://github.com/nodejs/node/issues/30976 +// Writes to a stream should finish even after the readable side has been ended. + +const data = zlib.deflateRawSync('Welcome'); + +const inflate = zlib.createInflateRaw(); + +inflate.resume(); +inflate.write(data, common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.flush(common.mustCall()); diff --git a/test/js/node/test/parallel/test-zlib-write-after-flush.js b/test/js/node/test/parallel/test-zlib-write-after-flush.js new file mode 100644 index 0000000000..6edcae2e2f --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-write-after-flush.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const gzip = createCompress(); + const gunz = createDecompress(); + + gzip.pipe(gunz); + + let output = ''; + const input = 'A line of data\n'; + gunz.setEncoding('utf8'); + gunz.on('data', (c) => output += c); + gunz.on('end', common.mustCall(() => { + assert.strictEqual(output, input); + })); + + // Make sure that flush/write doesn't trigger an assert failure + gzip.flush(); + gzip.write(input); + gzip.end(); + gunz.read(0); +} diff --git a/test/js/node/test/parallel/fs-read-file-sync-hostname.test.js b/test/js/node/test/parallel/test-zlib-zero-byte.js similarity index 64% rename from test/js/node/test/parallel/fs-read-file-sync-hostname.test.js rename to test/js/node/test/parallel/test-zlib-zero-byte.js index 9c4eb90ad8..fc57960f1e 100644 --- a/test/js/node/test/parallel/fs-read-file-sync-hostname.test.js +++ b/test/js/node/test/parallel/test-zlib-zero-byte.js @@ -1,6 +1,3 @@ -//#FILE: test-fs-read-file-sync-hostname.js -//#SHA1: 6e8bd1a34277c7b98b985ba23843555b05f80ccb -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,18 +19,25 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const fs = require("fs"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); -if (process.platform !== "linux") { - test.skip("Test is linux specific.", () => {}); -} else { - test("reading /proc/sys/kernel/hostname", () => { - // Test to make sure reading a file under the /proc directory works. See: - // https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0 - const hostname = fs.readFileSync("/proc/sys/kernel/hostname"); - expect(hostname.length).toBeGreaterThan(0); +for (const Compressor of [ zlib.Gzip, zlib.BrotliCompress ]) { + const gz = Compressor(); + const emptyBuffer = Buffer.alloc(0); + let received = 0; + gz.on('data', function(c) { + received += c.length; }); -} -//<#END_FILE: test-fs-read-file-sync-hostname.js + gz.on('end', common.mustCall(function() { + const expected = Compressor === zlib.Gzip ? 20 : 1; + assert.strictEqual(received, expected, + `${received}, ${expected}, ${Compressor.name}`); + })); + gz.on('finish', common.mustCall()); + gz.write(emptyBuffer); + gz.end(); +} diff --git a/test/js/node/test/parallel/test-zlib-zero-windowBits.js b/test/js/node/test/parallel/test-zlib-zero-windowBits.js new file mode 100644 index 0000000000..a27fd6734a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zero-windowBits.js @@ -0,0 +1,33 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + + +// windowBits is a special case in zlib. On the compression side, 0 is invalid. +// On the decompression side, it indicates that zlib should use the value from +// the header of the compressed stream. +{ + const inflate = zlib.createInflate({ windowBits: 0 }); + assert(inflate instanceof zlib.Inflate); +} + +{ + const gunzip = zlib.createGunzip({ windowBits: 0 }); + assert(gunzip instanceof zlib.Gunzip); +} + +{ + const unzip = zlib.createUnzip({ windowBits: 0 }); + assert(unzip instanceof zlib.Unzip); +} + +{ + assert.throws(() => zlib.createGzip({ windowBits: 0 }), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. ' + + 'It must be >= 9 and <= 15. Received 0' + }); +} diff --git a/test/js/node/test/parallel/zlib.test.js b/test/js/node/test/parallel/test-zlib.js similarity index 53% rename from test/js/node/test/parallel/zlib.test.js rename to test/js/node/test/parallel/test-zlib.js index d6b27408a1..65050b85a0 100644 --- a/test/js/node/test/parallel/zlib.test.js +++ b/test/js/node/test/parallel/test-zlib.js @@ -1,6 +1,3 @@ -//#FILE: test-zlib.js -//#SHA1: 0e67da3898d627175ffca51fdbd1042571d0c405 -//----------------- // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -22,22 +19,20 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const zlib = require("zlib"); -const stream = require("stream"); -const fs = require("fs"); -const path = require("path"); - -const fixturesPath = path.join(__dirname, "..", "fixtures"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const stream = require('stream'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); // Should not segfault. -test("gzipSync with invalid windowBits", () => { - expect(() => zlib.gzipSync(Buffer.alloc(0), { windowBits: 8 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); +assert.throws(() => zlib.gzipSync(Buffer.alloc(0), { windowBits: 8 }), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. ' + + 'It must be >= 9 and <= 15. Received 8', }); let zlibPairs = [ @@ -74,17 +69,18 @@ if (!process.env.PUMMEL) { strategy = [0]; } -let testFiles = ["person.jpg", "elipses.txt", "empty.txt"]; +let testFiles = ['person.jpg', 'elipses.txt', 'empty.txt']; if (process.env.FAST) { zlibPairs = [[zlib.Gzip, zlib.Unzip]]; - testFiles = ["person.jpg"]; + testFiles = ['person.jpg']; } const tests = {}; -testFiles.forEach(file => { - tests[file] = fs.readFileSync(path.join(fixturesPath, file)); -}); +testFiles.forEach(common.mustCall((file) => { + tests[file] = fixtures.readSync(file); +}, testFiles.length)); + // Stream that saves everything class BufferStream extends stream.Stream { @@ -107,12 +103,12 @@ class BufferStream extends stream.Stream { // flatten const buf = Buffer.allocUnsafe(this.length); let i = 0; - this.chunks.forEach(c => { + this.chunks.forEach((c) => { c.copy(buf, i); i += c.length; }); - this.emit("data", buf); - this.emit("end"); + this.emit('data', buf); + this.emit('end'); return true; } } @@ -126,12 +122,12 @@ class SlowStream extends stream.Stream { } write() { - throw new Error("not implemented, just call ss.end(chunk)"); + throw new Error('not implemented, just call ss.end(chunk)'); } pause() { this.paused = true; - this.emit("pause"); + this.emit('pause'); } resume() { @@ -139,17 +135,17 @@ class SlowStream extends stream.Stream { if (this.paused) return; if (this.offset >= this.length) { this.ended = true; - return this.emit("end"); + return this.emit('end'); } const end = Math.min(this.offset + this.trickle, this.length); const c = this.chunk.slice(this.offset, end); this.offset += c.length; - this.emit("data", c); + this.emit('data', c); process.nextTick(emit); }; if (this.ended) return; - this.emit("resume"); + this.emit('resume'); if (!this.chunk) return; this.paused = false; emit(); @@ -164,70 +160,73 @@ class SlowStream extends stream.Stream { } } -test("createDeflateRaw with windowBits 8", () => { - expect(() => zlib.createDeflateRaw({ windowBits: 8 })).not.toThrow(); -}); +// windowBits: 8 shouldn't throw +zlib.createDeflateRaw({ windowBits: 8 }); -test("inflate raw with windowBits 8", async () => { - const node = fs.createReadStream(path.join(fixturesPath, "person.jpg")); +{ + const node = fs.createReadStream(fixtures.path('person.jpg')); const raw = []; const reinflated = []; + node.on('data', (chunk) => raw.push(chunk)); - await new Promise((resolve, reject) => { - node.on("data", chunk => raw.push(chunk)); - - node - .pipe(zlib.createDeflateRaw({ windowBits: 9 })) + // Usually, the inflate windowBits parameter needs to be at least the + // value of the matching deflate’s windowBits. However, inflate raw with + // windowBits = 8 should be able to handle compressed data from a source + // that does not know about the silent 8-to-9 upgrade of windowBits + // that most versions of zlib/Node perform, and which *still* results in + // a valid 8-bit-window zlib stream. + node.pipe(zlib.createDeflateRaw({ windowBits: 9 })) .pipe(zlib.createInflateRaw({ windowBits: 8 })) - .on("data", chunk => reinflated.push(chunk)) - .on("end", () => { - expect(Buffer.concat(raw)).toEqual(Buffer.concat(reinflated)); - resolve(); - }) - .on("error", reject); - }); -}); + .on('data', (chunk) => reinflated.push(chunk)) + .on('end', common.mustCall( + () => assert(Buffer.concat(raw).equals(Buffer.concat(reinflated))))) + .on('close', common.mustCall(1)); +} // For each of the files, make sure that compressing and // decompressing results in the same data, for every combination // of the options set above. const testKeys = Object.keys(tests); -testKeys.forEach(file => { +testKeys.forEach(common.mustCall((file) => { const test = tests[file]; - chunkSize.forEach(chunkSize => { - trickle.forEach(trickle => { - windowBits.forEach(windowBits => { - level.forEach(level => { - memLevel.forEach(memLevel => { - strategy.forEach(strategy => { - zlibPairs.forEach(pair => { - const [Def, Inf] = pair; + chunkSize.forEach(common.mustCall((chunkSize) => { + trickle.forEach(common.mustCall((trickle) => { + windowBits.forEach(common.mustCall((windowBits) => { + level.forEach(common.mustCall((level) => { + memLevel.forEach(common.mustCall((memLevel) => { + strategy.forEach(common.mustCall((strategy) => { + zlibPairs.forEach(common.mustCall((pair) => { + const Def = pair[0]; + const Inf = pair[1]; const opts = { level, windowBits, memLevel, strategy }; - it(`${file} ${chunkSize} ${JSON.stringify(opts)} ${Def.name} -> ${Inf.name}`, done => { - const def = new Def(opts); - const inf = new Inf(opts); - const ss = new SlowStream(trickle); - const buf = new BufferStream(); + const def = new Def(opts); + const inf = new Inf(opts); + const ss = new SlowStream(trickle); + const buf = new BufferStream(); - // Verify that the same exact buffer comes out the other end. - buf.on("data", c => { - expect(c).toEqual(test); - done(); - }); + // Verify that the same exact buffer comes out the other end. + buf.on('data', common.mustCall((c) => { + const msg = `${file} ${chunkSize} ${ + JSON.stringify(opts)} ${Def.name} -> ${Inf.name}`; + let i; + for (i = 0; i < Math.max(c.length, test.length); i++) { + if (c[i] !== test[i]) { + assert.fail(msg); + break; + } + } + })); - // The magic happens here. - ss.pipe(def).pipe(inf).pipe(buf); - ss.end(test); - }); - }); - }); - }); - }); - }); - }); - }); -}); - -//<#END_FILE: test-zlib.js + // The magic happens here. + ss.pipe(def).pipe(inf).pipe(buf); + ss.end(test); + }, zlibPairs.length)); + }, strategy.length)); + }, memLevel.length)); + }, level.length)); + }, windowBits.length)); + }, trickle.length)); + }, chunkSize.length)); +}, testKeys.length)); diff --git a/test/js/node/test/parallel/timer-immediate.test.js b/test/js/node/test/parallel/timer-immediate.test.js deleted file mode 100644 index 1f74aa8bde..0000000000 --- a/test/js/node/test/parallel/timer-immediate.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-timer-immediate.js -//#SHA1: 00e4e451b5feda969bd3352a194ba0ee0e5bab85 -//----------------- -"use strict"; - -// Note: We're not using the 'common' module as it's not necessary for this test - -test("setImmediate should be called", done => { - // Recreate the global.process object - global.process = {}; - - // Use setImmediate and expect it to be called - setImmediate(() => { - expect(true).toBe(true); // This assertion is just to ensure the callback is called - done(); - }); -}); - -//<#END_FILE: test-timer-immediate.js diff --git a/test/js/node/test/parallel/timers-args.test.js b/test/js/node/test/parallel/timers-args.test.js deleted file mode 100644 index ca38fd5b37..0000000000 --- a/test/js/node/test/parallel/timers-args.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-timers-args.js -//#SHA1: 27f971d534c9bb3a1c14ae176ee8f34b0cdbed6f -//----------------- -"use strict"; - -function range(n) { - return "x" - .repeat(n + 1) - .split("") - .map(function (_, i) { - return i; - }); -} - -test("setTimeout with increasing number of arguments", done => { - function timeout(nargs) { - const args = range(nargs); - setTimeout(callback, 1, ...args); - - function callback(...receivedArgs) { - expect(receivedArgs).toEqual(args); - if (nargs < 128) { - timeout(nargs + 1); - } else { - done(); - } - } - } - - timeout(0); -}); - -test("setInterval with increasing number of arguments", done => { - function interval(nargs) { - const args = range(nargs); - const timer = setInterval(callback, 1, ...args); - - function callback(...receivedArgs) { - clearInterval(timer); - expect(receivedArgs).toEqual(args); - if (nargs < 128) { - interval(nargs + 1); - } else { - done(); - } - } - } - - interval(0); -}); - -//<#END_FILE: test-timers-args.js diff --git a/test/js/node/test/parallel/timers-clear-timeout-interval-equivalent.test.js b/test/js/node/test/parallel/timers-clear-timeout-interval-equivalent.test.js deleted file mode 100644 index 1fc88ba7ef..0000000000 --- a/test/js/node/test/parallel/timers-clear-timeout-interval-equivalent.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-timers-clear-timeout-interval-equivalent.js -//#SHA1: 2c98894fc8abe7d6e800533325274f39f9a16ff4 -//----------------- -"use strict"; - -// This test makes sure that timers created with setTimeout can be disarmed by -// clearInterval and that timers created with setInterval can be disarmed by -// clearTimeout. -// -// This behavior is documented in the HTML Living Standard: -// -// * Refs: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval - -test("Disarm interval with clearTimeout", () => { - const mockCallback = jest.fn(); - const interval = setInterval(mockCallback, 1); - clearTimeout(interval); - - return new Promise(resolve => { - setTimeout(() => { - expect(mockCallback).not.toHaveBeenCalled(); - resolve(); - }, 10); - }); -}); - -test("Disarm timeout with clearInterval", () => { - const mockCallback = jest.fn(); - const timeout = setTimeout(mockCallback, 1); - clearInterval(timeout); - - return new Promise(resolve => { - setTimeout(() => { - expect(mockCallback).not.toHaveBeenCalled(); - resolve(); - }, 10); - }); -}); - -//<#END_FILE: test-timers-clear-timeout-interval-equivalent.js diff --git a/test/js/node/test/parallel/timers-clearimmediate.test.js b/test/js/node/test/parallel/timers-clearimmediate.test.js deleted file mode 100644 index d29f715d38..0000000000 --- a/test/js/node/test/parallel/timers-clearimmediate.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-timers-clearImmediate.js -//#SHA1: 819914471d2e9d0a4629df9ef4b96e8e87ae7606 -//----------------- -"use strict"; - -const N = 3; - -function next() { - const fn = jest.fn(); - const immediate = setImmediate(fn); - clearImmediate(immediate); - expect(fn).not.toHaveBeenCalled(); -} - -test("clearImmediate cancels setImmediate", () => { - for (let i = 0; i < N; i++) { - next(); - } -}); - -//<#END_FILE: test-timers-clearImmediate.js diff --git a/test/js/node/test/parallel/timers-next-tick.test.js b/test/js/node/test/parallel/timers-next-tick.test.js deleted file mode 100644 index eced9d48cc..0000000000 --- a/test/js/node/test/parallel/timers-next-tick.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-timers-next-tick.js -//#SHA1: c6c6f667dac048dd0aed0a244e484ae1f0127d53 -//----------------- -"use strict"; - -// This test verifies that the next tick queue runs after each -// individual Timeout, as well as each individual Immediate. - -test("next tick queue runs after each Timeout and Immediate", async () => { - const timeoutSpy1 = jest.fn(); - const timeoutSpy2 = jest.fn(); - const immediateSpy1 = jest.fn(); - const immediateSpy2 = jest.fn(); - - setTimeout(timeoutSpy1, 1); - const t2 = setTimeout(jest.fn(), 1); - const t3 = setTimeout(jest.fn(), 1); - setTimeout(timeoutSpy2, 1); - - await new Promise(resolve => setTimeout(resolve, 5)); - - setImmediate(immediateSpy1); - const i2 = setImmediate(jest.fn()); - const i3 = setImmediate(jest.fn()); - setImmediate(immediateSpy2); - - await new Promise(resolve => setImmediate(resolve)); - - expect(timeoutSpy1).toHaveBeenCalledTimes(1); - expect(timeoutSpy2).toHaveBeenCalledTimes(1); - expect(immediateSpy1).toHaveBeenCalledTimes(1); - expect(immediateSpy2).toHaveBeenCalledTimes(1); - - // Confirm that clearing Timeouts from a next tick doesn't explode. - process.nextTick(() => { - clearTimeout(t2); - clearTimeout(t3); - }); - - // Confirm that clearing Immediates from a next tick doesn't explode. - process.nextTick(() => { - clearImmediate(i2); - clearImmediate(i3); - }); - - // Wait for next tick to complete - await new Promise(process.nextTick); -}); - -//<#END_FILE: test-timers-next-tick.js diff --git a/test/js/node/test/parallel/timers-now.test.js b/test/js/node/test/parallel/timers-now.test.js deleted file mode 100644 index 1a0e58d2eb..0000000000 --- a/test/js/node/test/parallel/timers-now.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-timers-now.js -//#SHA1: 6d35fc8681a7698d480f851c3c4ba5bfca7fb0b9 -//----------------- -"use strict"; -// Flags: --expose-internals - -test("getLibuvNow() return value fits in a SMI after start-up", () => { - // We can't use internal bindings in Jest, so we'll need to mock this behavior - // For the purpose of this test, we'll create a mock function that returns a small number - const mockGetLibuvNow = jest.fn(() => 1000); - - // Simulate the binding object - const binding = { - getLibuvNow: mockGetLibuvNow, - }; - - // Call the function and check the result - const result = binding.getLibuvNow(); - - // Check if the result is less than 0x3ffffff (67108863 in decimal) - expect(result).toBeLessThan(0x3ffffff); - - // Ensure the mock function was called - expect(mockGetLibuvNow).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-timers-now.js diff --git a/test/js/node/test/parallel/timers-promises.test.js b/test/js/node/test/parallel/timers-promises.test.js deleted file mode 100644 index 2bba7b563b..0000000000 --- a/test/js/node/test/parallel/timers-promises.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-timers-promises.js -//#SHA1: 78e3867e4cb1a41f11f7fa930e5a7319149c9452 -//----------------- -"use strict"; - -const timer = require("node:timers"); -const timerPromises = require("node:timers/promises"); - -test("(node:timers/promises) is equal to (node:timers).promises", () => { - expect(timerPromises).toEqual(timer.promises); -}); - -//<#END_FILE: test-timers-promises.js diff --git a/test/js/node/test/parallel/timers-setimmediate-infinite-loop.test.js b/test/js/node/test/parallel/timers-setimmediate-infinite-loop.test.js deleted file mode 100644 index d034693ae2..0000000000 --- a/test/js/node/test/parallel/timers-setimmediate-infinite-loop.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-timers-setimmediate-infinite-loop.js -//#SHA1: cd4dd01a6d06097758004eaa8d36d8712977d49d -//----------------- -"use strict"; - -// This test ensures that if an Immediate callback clears subsequent -// immediates we don't get stuck in an infinite loop. -// -// If the process does get stuck, it will be timed out by the test -// runner. -// -// Ref: https://github.com/nodejs/node/issues/9756 - -test("setImmediate clears subsequent immediates without infinite loop", done => { - const firstCallback = jest.fn(() => { - clearImmediate(i2); - clearImmediate(i3); - }); - - const secondCallback = jest.fn(); - const thirdCallback = jest.fn(); - - setImmediate(firstCallback); - - const i2 = setImmediate(secondCallback); - const i3 = setImmediate(thirdCallback); - - // Use a timeout to ensure all immediates have been processed - setTimeout(() => { - expect(firstCallback).toHaveBeenCalledTimes(1); - expect(secondCallback).not.toHaveBeenCalled(); - expect(thirdCallback).not.toHaveBeenCalled(); - done(); - }, 100); -}); - -//<#END_FILE: test-timers-setimmediate-infinite-loop.js diff --git a/test/js/node/test/parallel/timers-timeout-with-non-integer.test.js b/test/js/node/test/parallel/timers-timeout-with-non-integer.test.js deleted file mode 100644 index 2207d34ec6..0000000000 --- a/test/js/node/test/parallel/timers-timeout-with-non-integer.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-timers-timeout-with-non-integer.js -//#SHA1: 371503ab8a31c6749ef77eabac3054e5a43ab231 -//----------------- -"use strict"; - -/** - * This test is for https://github.com/nodejs/node/issues/24203 - */ -test("setTimeout with non-integer time", done => { - let count = 50; - const time = 1.00000000000001; - - const exec = jest.fn(() => { - if (--count === 0) { - expect(exec).toHaveBeenCalledTimes(50); - done(); - return; - } - setTimeout(exec, time); - }); - - exec(); -}, 10000); // Increased timeout to ensure all iterations complete - -//<#END_FILE: test-timers-timeout-with-non-integer.js diff --git a/test/js/node/test/parallel/timers-to-primitive.test.js b/test/js/node/test/parallel/timers-to-primitive.test.js deleted file mode 100644 index a47b84132c..0000000000 --- a/test/js/node/test/parallel/timers-to-primitive.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-timers-to-primitive.js -//#SHA1: 87671c76fb38458f9d4f68aa747b2d95076f6bb4 -//----------------- -"use strict"; - -test("Timeout and Interval primitives", () => { - const timeouts = [setTimeout(() => {}, 1), setInterval(() => {}, 1)]; - - timeouts.forEach(timeout => { - expect(Number.isNaN(+timeout)).toBe(false); - expect(+timeout).toBe(timeout[Symbol.toPrimitive]()); - expect(`${timeout}`).toBe(timeout[Symbol.toPrimitive]().toString()); - expect(Object.keys({ [timeout]: timeout })).toEqual([`${timeout}`]); - clearTimeout(+timeout); - }); -}); - -test("clearTimeout works with number id", () => { - const timeout = setTimeout(() => {}, 1); - const id = +timeout; - expect(() => clearTimeout(id)).not.toThrow(); -}); - -test("clearTimeout works with string id", () => { - const timeout = setTimeout(() => {}, 1); - const id = `${timeout}`; - expect(() => clearTimeout(id)).not.toThrow(); -}); - -//<#END_FILE: test-timers-to-primitive.js diff --git a/test/js/node/test/parallel/timers-unrefed-in-beforeexit.test.js b/test/js/node/test/parallel/timers-unrefed-in-beforeexit.test.js deleted file mode 100644 index 79967d0376..0000000000 --- a/test/js/node/test/parallel/timers-unrefed-in-beforeexit.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-timers-unrefed-in-beforeexit.js -//#SHA1: c873b545965c8b223c0bc38f2acdd4bc952427ea -//----------------- -"use strict"; - -test("unrefed timer in beforeExit should not prevent exit", () => { - const beforeExitHandler = jest.fn(() => { - setTimeout(jest.fn(), 1).unref(); - }); - - process.on("beforeExit", beforeExitHandler); - - // Simulate process exit - process.emit("beforeExit"); - - expect(beforeExitHandler).toHaveBeenCalledTimes(1); - - // Clean up the event listener - process.removeListener("beforeExit", beforeExitHandler); -}); - -//<#END_FILE: test-timers-unrefed-in-beforeexit.js diff --git a/test/js/node/test/parallel/timers-zero-timeout.test.js b/test/js/node/test/parallel/timers-zero-timeout.test.js deleted file mode 100644 index 3f16eaf6ae..0000000000 --- a/test/js/node/test/parallel/timers-zero-timeout.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-timers-zero-timeout.js -//#SHA1: f6d7cfab9ecf6f2f94001a4e153baf29dd3203b1 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args -test("setTimeout with zero timeout and extra args", done => { - const f = jest.fn((a, b, c) => { - expect(a).toBe("foo"); - expect(b).toBe("bar"); - expect(c).toBe("baz"); - done(); - }); - - setTimeout(f, 0, "foo", "bar", "baz"); - setTimeout(() => {}, 0); -}); - -test("setInterval with zero timeout and extra args", done => { - let ncalled = 3; - - const f = jest.fn((a, b, c) => { - expect(a).toBe("foo"); - expect(b).toBe("bar"); - expect(c).toBe("baz"); - if (--ncalled === 0) { - clearTimeout(iv); - expect(f).toHaveBeenCalledTimes(3); - done(); - } - }); - - const iv = setInterval(f, 0, "foo", "bar", "baz"); -}); - -//<#END_FILE: test-timers-zero-timeout.js diff --git a/test/js/node/test/parallel/tls-add-context.test.js b/test/js/node/test/parallel/tls-add-context.test.js deleted file mode 100644 index ca3d03c578..0000000000 --- a/test/js/node/test/parallel/tls-add-context.test.js +++ /dev/null @@ -1,93 +0,0 @@ -//#FILE: test-tls-add-context.js -//#SHA1: 61f134fe8c8fb63a00278b2d70dfecf11efb5df9 -//----------------- -"use strict"; - -const crypto = require("crypto"); -const fixtures = require("../common/fixtures"); -const tls = require("tls"); - -// Skip test if crypto is not available -if (!crypto) { - test.skip("missing crypto", () => {}); -} else { - function loadPEM(n) { - return fixtures.readKey(`${n}.pem`); - } - - const serverOptions = { - key: loadPEM("agent2-key"), - cert: loadPEM("agent2-cert"), - ca: [loadPEM("ca2-cert")], - requestCert: true, - rejectUnauthorized: false, - }; - - let connections = 0; - - test("TLS add context", done => { - const server = tls.createServer(serverOptions, c => { - if (++connections === 3) { - server.close(); - } - if (c.servername === "unknowncontext") { - expect(c.authorized).toBe(false); - return; - } - expect(c.authorized).toBe(true); - }); - - const secureContext = { - key: loadPEM("agent1-key"), - cert: loadPEM("agent1-cert"), - ca: [loadPEM("ca1-cert")], - }; - server.addContext("context1", secureContext); - server.addContext("context2", tls.createSecureContext(secureContext)); - - const clientOptionsBase = { - key: loadPEM("agent1-key"), - cert: loadPEM("agent1-cert"), - ca: [loadPEM("ca1-cert")], - rejectUnauthorized: false, - }; - - server.listen(0, () => { - const client1 = tls.connect( - { - ...clientOptionsBase, - port: server.address().port, - servername: "context1", - }, - () => { - client1.end(); - }, - ); - - const client2 = tls.connect( - { - ...clientOptionsBase, - port: server.address().port, - servername: "context2", - }, - () => { - client2.end(); - }, - ); - - const client3 = tls.connect( - { - ...clientOptionsBase, - port: server.address().port, - servername: "unknowncontext", - }, - () => { - client3.end(); - done(); - }, - ); - }); - }); -} - -//<#END_FILE: test-tls-add-context.js diff --git a/test/js/node/test/parallel/tls-ca-concat.test.js b/test/js/node/test/parallel/tls-ca-concat.test.js deleted file mode 100644 index dcee3b9522..0000000000 --- a/test/js/node/test/parallel/tls-ca-concat.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-tls-ca-concat.js -//#SHA1: 23b19f45d7777ee95a3c8d8ba4a727b149ea7409 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); - -// Check ca option can contain concatenated certs by prepending an unrelated -// non-CA cert and showing that agent6's CA root is still found. - -const { connect, keys } = require(fixtures.path("tls-connect")); - -test("ca option can contain concatenated certs", async () => { - await new Promise((resolve, reject) => { - connect( - { - client: { - checkServerIdentity: (servername, cert) => {}, - ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, - }, - server: { - cert: keys.agent6.cert, - key: keys.agent6.key, - }, - }, - (err, pair, cleanup) => { - if (err) { - cleanup(); - reject(err); - } else { - cleanup(); - resolve(); - } - }, - ); - }); -}); - -//<#END_FILE: test-tls-ca-concat.js diff --git a/test/js/node/test/parallel/tls-env-extra-ca-no-crypto.test.js b/test/js/node/test/parallel/tls-env-extra-ca-no-crypto.test.js deleted file mode 100644 index 5b48f8f775..0000000000 --- a/test/js/node/test/parallel/tls-env-extra-ca-no-crypto.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-tls-env-extra-ca-no-crypto.js -//#SHA1: da8421700b140b9bef4723eee10dbae7786423b6 -//----------------- -"use strict"; - -const { fork } = require("child_process"); -const path = require("path"); - -// This test ensures that trying to load extra certs won't throw even when -// there is no crypto support, i.e., built with "./configure --without-ssl". -if (process.argv[2] === "child") { - // exit -} else { - const NODE_EXTRA_CA_CERTS = path.join(__dirname, "..", "fixtures", "keys", "ca1-cert.pem"); - - test("Loading extra certs without crypto support", () => { - return new Promise(resolve => { - fork(__filename, ["child"], { env: { ...process.env, NODE_EXTRA_CA_CERTS } }).on("exit", status => { - // Client did not succeed in connecting - expect(status).toBe(0); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-tls-env-extra-ca-no-crypto.js diff --git a/test/js/node/test/parallel/tls-fast-writing.test.js b/test/js/node/test/parallel/tls-fast-writing.test.js deleted file mode 100644 index 7656d5eb15..0000000000 --- a/test/js/node/test/parallel/tls-fast-writing.test.js +++ /dev/null @@ -1,97 +0,0 @@ -//#FILE: test-tls-fast-writing.js -//#SHA1: 3a9ce4612ccf460fb5c0dfd4be6f8f5cad06c4a4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fixtures = require("../common/fixtures"); -const tls = require("tls"); - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), - ca: [fixtures.readKey("rsa_ca.crt")], -}; - -let gotChunk = false; -let gotDrain = false; - -function onconnection(conn) { - conn.on("data", function (c) { - if (!gotChunk) { - gotChunk = true; - console.log("ok - got chunk"); - } - - // Just some basic sanity checks. - expect(c.length).toBeGreaterThan(0); - expect(Buffer.isBuffer(c)).toBe(true); - - if (gotDrain) { - process.exit(0); - } - }); -} - -test("TLS fast writing", done => { - if (!process.versions.openssl) { - console.log("1..0 # Skipped: missing crypto"); - return done(); - } - - const server = tls.createServer(options, onconnection); - - server.listen(0, function () { - const chunk = Buffer.alloc(1024, "x"); - const opt = { port: this.address().port, rejectUnauthorized: false }; - const conn = tls.connect(opt, function () { - conn.on("drain", ondrain); - write(); - }); - - function ondrain() { - if (!gotDrain) { - gotDrain = true; - console.log("ok - got drain"); - } - if (gotChunk) { - process.exit(0); - } - write(); - } - - function write() { - // This needs to return false eventually - while (false !== conn.write(chunk)); - } - }); - - // Clean up - process.on("exit", () => { - server.close(); - expect(gotChunk).toBe(true); - expect(gotDrain).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-tls-fast-writing.js diff --git a/test/js/node/test/parallel/tls-js-stream.test.js b/test/js/node/test/parallel/tls-js-stream.test.js deleted file mode 100644 index 811e6a23d3..0000000000 --- a/test/js/node/test/parallel/tls-js-stream.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-tls-js-stream.js -//#SHA1: 90a8d9d14bac997dbf3abb56da784bfcba8efee6 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const net = require("net"); -const stream = require("stream"); -const tls = require("tls"); - -if (!tls.createSecureContext) { - test.skip("missing crypto"); -} - -test("TLS over JavaScript stream", done => { - const server = tls.createServer( - { - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }, - c => { - console.log("new client"); - c.resume(); - c.end("ohai"); - }, - ); - - server.listen(0, () => { - const raw = net.connect(server.address().port); - - let pending = false; - raw.on("readable", () => { - if (pending) p._read(); - }); - - raw.on("end", () => { - p.push(null); - }); - - const p = new stream.Duplex({ - read: function read() { - pending = false; - - const chunk = raw.read(); - if (chunk) { - console.log("read", chunk); - this.push(chunk); - } else { - pending = true; - } - }, - write: function write(data, enc, cb) { - console.log("write", data, enc); - raw.write(data, enc, cb); - }, - }); - - const socket = tls.connect( - { - socket: p, - rejectUnauthorized: false, - }, - () => { - console.log("client secure"); - socket.resume(); - socket.end("hello"); - }, - ); - - socket.once("close", () => { - console.log("client close"); - server.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-tls-js-stream.js diff --git a/test/js/node/test/parallel/tls-net-socket-keepalive.test.js b/test/js/node/test/parallel/tls-net-socket-keepalive.test.js deleted file mode 100644 index a0378791cc..0000000000 --- a/test/js/node/test/parallel/tls-net-socket-keepalive.test.js +++ /dev/null @@ -1,80 +0,0 @@ -//#FILE: test-tls-net-socket-keepalive.js -//#SHA1: 9ae6965b63d37c0f9cb2ab7d068d4da2a68b8b1f -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const tls = require("tls"); -const net = require("net"); - -// This test ensures that when tls sockets are created with `allowHalfOpen`, -// they won't hang. -const key = fixtures.readKey("agent1-key.pem"); -const cert = fixtures.readKey("agent1-cert.pem"); -const ca = fixtures.readKey("ca1-cert.pem"); -const options = { - key, - cert, - ca: [ca], -}; - -test("TLS sockets with allowHalfOpen do not hang", done => { - const server = tls.createServer(options, conn => { - conn.write("hello", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("data", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("end", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("data", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("close", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.end(); - }); - - server.listen(0, () => { - const netSocket = new net.Socket({ - allowHalfOpen: true, - }); - - const socket = tls.connect({ - socket: netSocket, - rejectUnauthorized: false, - }); - - const { port, address } = server.address(); - - // Doing `net.Socket.connect()` after `tls.connect()` will make tls module - // wrap the socket in StreamWrap. - netSocket.connect({ - port, - address, - }); - - socket.on("secureConnect", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - socket.on("end", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - socket.on("data", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - socket.on("close", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - server.close(); - done(); - }); - - socket.write("hello"); - socket.end(); - }); -}); - -//<#END_FILE: test-tls-net-socket-keepalive.js diff --git a/test/js/node/test/parallel/tls-peer-certificate-multi-keys.test.js b/test/js/node/test/parallel/tls-peer-certificate-multi-keys.test.js deleted file mode 100644 index d1cfe70e15..0000000000 --- a/test/js/node/test/parallel/tls-peer-certificate-multi-keys.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-tls-peer-certificate-multi-keys.js -//#SHA1: d30d685d74ebea73274e19cea1a19a2b8cea5120 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); -const fixtures = require("../common/fixtures"); - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), -}; - -let server; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("TLS peer certificate with multiple keys", async () => { - server = tls.createServer(options, cleartext => { - cleartext.end("World"); - }); - - const serverSecureConnectionPromise = new Promise(resolve => { - server.once("secureConnection", socket => { - const cert = socket.getCertificate(); - // The server's local cert is the client's peer cert. - expect(cert.subject.OU).toEqual(["Test TLS Certificate", "Engineering"]); - resolve(); - }); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const clientConnectionPromise = new Promise((resolve, reject) => { - const socket = tls.connect( - { - port: server.address().port, - rejectUnauthorized: false, - }, - () => { - const peerCert = socket.getPeerCertificate(); - expect(peerCert.subject.OU).toEqual(["Test TLS Certificate", "Engineering"]); - socket.end("Hello"); - resolve(); - }, - ); - - socket.on("error", reject); - }); - - await Promise.all([serverSecureConnectionPromise, clientConnectionPromise]); -}); - -//<#END_FILE: test-tls-peer-certificate-multi-keys.js diff --git a/test/js/node/test/parallel/tls-startcom-wosign-whitelist.test.js b/test/js/node/test/parallel/tls-startcom-wosign-whitelist.test.js deleted file mode 100644 index fde4c90e29..0000000000 --- a/test/js/node/test/parallel/tls-startcom-wosign-whitelist.test.js +++ /dev/null @@ -1,82 +0,0 @@ -//#FILE: test-tls-startcom-wosign-whitelist.js -//#SHA1: 6742ecdeeaa94ca1efad850b021d5308b3077358 -//----------------- -"use strict"; - -const tls = require("tls"); -const fixtures = require("../common/fixtures"); - -function loadPEM(n) { - return fixtures.readKey(`${n}.pem`); -} - -const testCases = [ - { - // agent8 is signed by fake-startcom-root with notBefore of - // Oct 20 23:59:59 2016 GMT. It passes StartCom/WoSign check. - serverOpts: { - key: loadPEM("agent8-key"), - cert: loadPEM("agent8-cert"), - }, - clientOpts: { - ca: loadPEM("fake-startcom-root-cert"), - port: undefined, - rejectUnauthorized: true, - }, - errorCode: "CERT_REVOKED", - }, - { - // agent9 is signed by fake-startcom-root with notBefore of - // Oct 21 00:00:01 2016 GMT. It fails StartCom/WoSign check. - serverOpts: { - key: loadPEM("agent9-key"), - cert: loadPEM("agent9-cert"), - }, - clientOpts: { - ca: loadPEM("fake-startcom-root-cert"), - port: undefined, - rejectUnauthorized: true, - }, - errorCode: "CERT_REVOKED", - }, -]; - -describe("TLS StartCom/WoSign Whitelist", () => { - let finishedTests = 0; - - afterAll(() => { - expect(finishedTests).toBe(testCases.length); - }); - - testCases.forEach((tcase, index) => { - it(`should handle case ${index + 1} correctly`, async () => { - const server = tls.createServer(tcase.serverOpts, s => { - s.resume(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - tcase.clientOpts.port = server.address().port; - const client = tls.connect(tcase.clientOpts); - - client.on("error", e => { - expect(e.code).toBe(tcase.errorCode); - server.close(resolve); - }); - - client.on("secureConnect", () => { - // agent8 can pass StartCom/WoSign check so that the secureConnect - // is established. - expect(tcase.errorCode).toBe("CERT_REVOKED"); - client.end(); - server.close(resolve); - }); - }); - }); - - finishedTests++; - }); - }); -}); - -//<#END_FILE: test-tls-startcom-wosign-whitelist.js diff --git a/test/js/node/test/parallel/tls-ticket-cluster.test.js b/test/js/node/test/parallel/tls-ticket-cluster.test.js deleted file mode 100644 index 2def9c478c..0000000000 --- a/test/js/node/test/parallel/tls-ticket-cluster.test.js +++ /dev/null @@ -1,153 +0,0 @@ -//#FILE: test-tls-ticket-cluster.js -//#SHA1: a65d425111990ee164b458fd373b9f5bb083f6df -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); -const cluster = require("cluster"); -const fixtures = require("../common/fixtures"); - -const workerCount = 4; -const expectedReqCount = 16; - -if (!process.env.NODE_SKIP_NO_CRYPTO_TEST) { - test("skip if missing crypto", () => { - const hasCrypto = typeof process.versions.openssl === "string"; - if (!hasCrypto) { - console.log("1..0 # Skipped: missing crypto"); - } - expect(hasCrypto).toBe(true); - }); -} - -if (cluster.isPrimary) { - let listeningCount = 0; - let reusedCount = 0; - let reqCount = 0; - let lastSession = null; - let workerPort = null; - - function shoot() { - console.error("[primary] connecting", workerPort, "session?", !!lastSession); - const c = tls - .connect( - workerPort, - { - session: lastSession, - rejectUnauthorized: false, - }, - () => { - c.on("end", c.end); - }, - ) - .on("close", () => { - // Wait for close to shoot off another connection. We don't want to shoot - // until a new session is allocated, if one will be. The new session is - // not guaranteed on secureConnect (it depends on TLS1.2 vs TLS1.3), but - // it is guaranteed to happen before the connection is closed. - if (++reqCount === expectedReqCount) { - Object.keys(cluster.workers).forEach(function (id) { - cluster.workers[id].send("die"); - }); - } else { - shoot(); - } - }) - .once("session", session => { - expect(lastSession).toBeFalsy(); - lastSession = session; - }); - - c.resume(); // See close_notify comment in server - } - - function fork() { - const worker = cluster.fork(); - worker.on("message", ({ msg, port }) => { - console.error("[primary] got %j", msg); - if (msg === "reused") { - ++reusedCount; - } else if (msg === "listening" && ++listeningCount === workerCount) { - workerPort = port; - shoot(); - } - }); - - worker.on("exit", () => { - console.error("[primary] worker died"); - }); - } - - test("cluster primary", () => { - for (let i = 0; i < workerCount; i++) { - fork(); - } - - process.on("exit", () => { - expect(reqCount).toBe(expectedReqCount); - expect(reusedCount + 1).toBe(reqCount); - }); - }); -} else { - const key = fixtures.readKey("rsa_private.pem"); - const cert = fixtures.readKey("rsa_cert.crt"); - - const options = { key, cert }; - - const server = tls.createServer(options, c => { - console.error("[worker] connection reused?", c.isSessionReused()); - if (c.isSessionReused()) { - process.send({ msg: "reused" }); - } else { - process.send({ msg: "not-reused" }); - } - // Used to just .end(), but that means client gets close_notify before - // NewSessionTicket. Send data until that problem is solved. - c.end("x"); - }); - - server.listen(0, () => { - const { port } = server.address(); - process.send({ - msg: "listening", - port, - }); - }); - - process.on("message", function listener(msg) { - console.error("[worker] got %j", msg); - if (msg === "die") { - server.close(() => { - console.error("[worker] server close"); - process.exit(); - }); - } - }); - - process.on("exit", () => { - console.error("[worker] exit"); - }); -} - -//<#END_FILE: test-tls-ticket-cluster.js diff --git a/test/js/node/test/parallel/tls-transport-destroy-after-own-gc.test.js b/test/js/node/test/parallel/tls-transport-destroy-after-own-gc.test.js deleted file mode 100644 index 1f7c20dda3..0000000000 --- a/test/js/node/test/parallel/tls-transport-destroy-after-own-gc.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-tls-transport-destroy-after-own-gc.js -//#SHA1: e2ef35ad88444196d24664e93fb9efdad050c876 -//----------------- -// Flags: --expose-gc -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/17475 -// Unfortunately, this tests only "works" reliably when checked with valgrind or -// a similar tool. - -const { TLSSocket } = require("tls"); -const makeDuplexPair = require("../common/duplexpair"); - -test("TLSSocket destruction after garbage collection", done => { - if (!process.versions.openssl) { - done(); - return; - } - - let { clientSide } = makeDuplexPair(); - - let clientTLS = new TLSSocket(clientSide, { isServer: false }); - let clientTLSHandle = clientTLS._handle; // eslint-disable-line no-unused-vars - - setImmediate(() => { - clientTLS = null; - global.gc(); - clientTLSHandle = null; - global.gc(); - setImmediate(() => { - clientSide = null; - global.gc(); - // If we've reached this point without crashing, the test has passed - done(); - }); - }); -}); - -//<#END_FILE: test-tls-transport-destroy-after-own-gc.js diff --git a/test/js/node/test/parallel/tls-wrap-econnreset.test.js b/test/js/node/test/parallel/tls-wrap-econnreset.test.js deleted file mode 100644 index 3f11b13be2..0000000000 --- a/test/js/node/test/parallel/tls-wrap-econnreset.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-tls-wrap-econnreset.js -//#SHA1: 22f57b68ee3c5d271a9235972865773da523a34e -//----------------- -"use strict"; - -const net = require("net"); -const tls = require("tls"); - -// Skip the test if crypto is not available -if (!("crypto" in process.versions)) { - test.skip("missing crypto", () => {}); -} else { - test("TLS connection reset", async () => { - const server = net.createServer(c => { - c.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const port = server.address().port; - - let errored = false; - const tlsConnection = tls.connect(port, "127.0.0.1"); - - await expect( - new Promise((_, reject) => { - tlsConnection.once("error", reject); - }), - ).rejects.toMatchObject({ - code: "ECONNRESET", - path: undefined, - host: "127.0.0.1", - port: port, - localAddress: undefined, - message: expect.any(String), - }); - - errored = true; - server.close(); - - await new Promise(resolve => { - tlsConnection.on("close", resolve); - }); - - expect(errored).toBe(true); - }); -} - -//<#END_FILE: test-tls-wrap-econnreset.js diff --git a/test/js/node/test/parallel/tty-stdin-end.test.js b/test/js/node/test/parallel/tty-stdin-end.test.js deleted file mode 100644 index cb2f825045..0000000000 --- a/test/js/node/test/parallel/tty-stdin-end.test.js +++ /dev/null @@ -1,15 +0,0 @@ -//#FILE: test-tty-stdin-end.js -//#SHA1: 66243635acf852b7a108d06e289e5db2b2573bad -//----------------- -"use strict"; - -// This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. -// https://github.com/nodejs/node/issues/1068 - -test("process.stdin.emit('end') doesn't crash", () => { - expect(() => { - process.stdin.emit("end"); - }).not.toThrow(); -}); - -//<#END_FILE: test-tty-stdin-end.js diff --git a/test/js/node/test/parallel/tz-version.test.js b/test/js/node/test/parallel/tz-version.test.js deleted file mode 100644 index 04bac4dcdb..0000000000 --- a/test/js/node/test/parallel/tz-version.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-tz-version.js -//#SHA1: 7c06414dda474dff448a3998499dddffdf084126 -//----------------- -"use strict"; - -// Skip the test if Intl is not available -if (typeof Intl === "undefined") { - test.skip("missing Intl", () => {}); -} else { - // Refs: https://github.com/nodejs/node/blob/1af63a90ca3a59ca05b3a12ad7dbea04008db7d9/configure.py#L1694-L1711 - if (process.config.variables.icu_path !== "deps/icu-small") { - // If Node.js is configured to use its built-in ICU, it uses a strict subset - // of ICU formed using `tools/icu/shrink-icu-src.py`, which is present in - // `deps/icu-small`. It is not the same as configuring the build with - // `./configure --with-intl=small-icu`. The latter only uses a subset of the - // locales, i.e., it uses the English locale, `root,en`, by default and other - // locales can also be specified using the `--with-icu-locales` option. - test.skip("not using the icu data file present in deps/icu-small/source/data/in/icudt##l.dat.bz2", () => {}); - } else { - const { readFileSync } = require("fs"); - const path = require("path"); - - // This test ensures the correctness of the automated timezone upgrade PRs. - - test("timezone version matches expected version", () => { - const expectedVersion = readFileSync(path.join(__dirname, "fixtures", "tz-version.txt"), "utf8").trim(); - expect(process.versions.tz).toBe(expectedVersion); - }); - } -} - -//<#END_FILE: test-tz-version.js diff --git a/test/js/node/test/parallel/unhandled-exception-with-worker-inuse.test.js b/test/js/node/test/parallel/unhandled-exception-with-worker-inuse.test.js deleted file mode 100644 index 6acae8cf95..0000000000 --- a/test/js/node/test/parallel/unhandled-exception-with-worker-inuse.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-unhandled-exception-with-worker-inuse.js -//#SHA1: 4eb89fdb9d675dbef4f89ed61ab77225b9200b4a -//----------------- -"use strict"; - -// https://github.com/nodejs/node/issues/45421 -// -// Check that node will NOT call v8::Isolate::SetIdle() when exiting -// due to an unhandled exception, otherwise the assertion(enabled in -// debug build only) in the SetIdle(), which checks that the vm state -// is either EXTERNAL or IDLE will fail. -// -// The root cause of this issue is that before PerIsolateMessageListener() -// is invoked by v8, v8 preserves the JS vm state, although it should -// switch to EXTERNEL. https://bugs.chromium.org/p/v8/issues/detail?id=13464 -// -// Therefore, this commit can be considered as an workaround of the v8 bug, -// but we also find it not useful to call SetIdle() when terminating. - -if (process.argv[2] === "child") { - const { Worker } = require("worker_threads"); - new Worker("", { eval: true }); - throw new Error("xxx"); -} else { - const { spawnSync } = require("child_process"); - - test("unhandled exception with worker in use", () => { - const result = spawnSync(process.execPath, [__filename, "child"]); - - const stderr = result.stderr.toString().trim(); - // Expect error message to be preserved - expect(stderr).toMatch(/xxx/); - // Expect no crash - expect(result.status).not.toBe(null); - expect(result.signal).toBe(null); - }); -} - -//<#END_FILE: test-unhandled-exception-with-worker-inuse.js diff --git a/test/js/node/test/parallel/url-canparse-whatwg.test.js b/test/js/node/test/parallel/url-canparse-whatwg.test.js deleted file mode 100644 index a6c920c536..0000000000 --- a/test/js/node/test/parallel/url-canparse-whatwg.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-url-canParse-whatwg.js -//#SHA1: e1170e8b8d0057443bfb307c64dbd27b204ac2f0 -//----------------- -"use strict"; - -test("URL.canParse requires one argument", () => { - expect(() => { - URL.canParse(); - }).toThrow( - expect.objectContaining({ - code: "ERR_MISSING_ARGS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("URL.canParse works with v8 fast api", () => { - // This test is to ensure that the v8 fast api works. - for (let i = 0; i < 1e5; i++) { - expect(URL.canParse("https://www.example.com/path/?query=param#hash")).toBe(true); - } -}); - -//<#END_FILE: test-url-canParse-whatwg.js diff --git a/test/js/node/test/parallel/url-domain-ascii-unicode.test.js b/test/js/node/test/parallel/url-domain-ascii-unicode.test.js deleted file mode 100644 index 0a716939a9..0000000000 --- a/test/js/node/test/parallel/url-domain-ascii-unicode.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-url-domain-ascii-unicode.js -//#SHA1: 717d40eef6d2d8f5adccf01fe09dc43f8b776e13 -//----------------- -"use strict"; - -const url = require("url"); - -const domainToASCII = url.domainToASCII; -const domainToUnicode = url.domainToUnicode; - -const domainWithASCII = [ - ["ıíd", "xn--d-iga7r"], - ["يٴ", "xn--mhb8f"], - ["www.ϧƽəʐ.com", "www.xn--cja62apfr6c.com"], - ["новини.com", "xn--b1amarcd.com"], - ["名がドメイン.com", "xn--v8jxj3d1dzdz08w.com"], - ["افغانستا.icom.museum", "xn--mgbaal8b0b9b2b.icom.museum"], - ["الجزائر.icom.fake", "xn--lgbbat1ad8j.icom.fake"], - ["भारत.org", "xn--h2brj9c.org"], -]; - -describe("URL domain ASCII and Unicode conversion", () => { - // Skip the entire test suite if Intl is not available - beforeAll(() => { - if (typeof Intl === "undefined") { - throw new Error("missing Intl"); - } - }); - - test.each(domainWithASCII)("converts %s <-> %s", (domain, ascii) => { - const domainConvertedToASCII = domainToASCII(domain); - expect(domainConvertedToASCII).toBe(ascii); - - const asciiConvertedToUnicode = domainToUnicode(ascii); - expect(asciiConvertedToUnicode).toBe(domain); - }); -}); - -//<#END_FILE: test-url-domain-ascii-unicode.js diff --git a/test/js/node/test/parallel/url-is-url.test.js b/test/js/node/test/parallel/url-is-url.test.js deleted file mode 100644 index de30ec10f0..0000000000 --- a/test/js/node/test/parallel/url-is-url.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-url-is-url.js -//#SHA1: 337a63661b82d89cf2592ee32d91780acb8e5925 -//----------------- -"use strict"; - -const { URL, parse } = require("url"); - -// Remove the internal module usage -// const { isURL } = require('internal/url'); - -// Implement a simple isURL function for testing purposes -function isURL(input) { - return input instanceof URL; -} - -test("isURL function", () => { - expect(isURL(new URL("https://www.nodejs.org"))).toBe(true); - expect(isURL(parse("https://www.nodejs.org"))).toBe(false); - expect( - isURL({ - href: "https://www.nodejs.org", - protocol: "https:", - path: "/", - }), - ).toBe(false); -}); - -//<#END_FILE: test-url-is-url.js diff --git a/test/js/node/test/parallel/url-revokeobjecturl.test.js b/test/js/node/test/parallel/url-revokeobjecturl.test.js deleted file mode 100644 index 190ed562f5..0000000000 --- a/test/js/node/test/parallel/url-revokeobjecturl.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-url-revokeobjecturl.js -//#SHA1: 573bfad806102976807ad71fef71b079005d8bfa -//----------------- -"use strict"; - -// Test ensures that the function receives the url argument. - -test("URL.revokeObjectURL() throws with missing argument", () => { - expect(() => { - URL.revokeObjectURL(); - }).toThrow( - expect.objectContaining({ - code: "ERR_MISSING_ARGS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-url-revokeobjecturl.js diff --git a/test/js/node/test/parallel/url-urltooptions.test.js b/test/js/node/test/parallel/url-urltooptions.test.js deleted file mode 100644 index 3cb50f21b5..0000000000 --- a/test/js/node/test/parallel/url-urltooptions.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-url-urltooptions.js -//#SHA1: 0ba1cb976ec5888306f7c820a39afcb93b882d03 -//----------------- -"use strict"; - -const { URL } = require("url"); -const { urlToHttpOptions } = require("url"); - -describe("urlToHttpOptions", () => { - test("converts URL object to HTTP options", () => { - const urlObj = new URL("http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test"); - const opts = urlToHttpOptions(urlObj); - - expect(opts).not.toBeInstanceOf(URL); - expect(opts.protocol).toBe("http:"); - expect(opts.auth).toBe("user:pass"); - expect(opts.hostname).toBe("foo.bar.com"); - expect(opts.port).toBe(21); - expect(opts.path).toBe("/aaa/zzz?l=24"); - expect(opts.pathname).toBe("/aaa/zzz"); - expect(opts.search).toBe("?l=24"); - expect(opts.hash).toBe("#test"); - }); - - test("handles IPv6 hostname correctly", () => { - const { hostname } = urlToHttpOptions(new URL("http://[::1]:21")); - expect(hostname).toBe("::1"); - }); - - test("handles copied URL object with missing data properties", () => { - const urlObj = new URL("http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test"); - const copiedUrlObj = { ...urlObj }; - const copiedOpts = urlToHttpOptions(copiedUrlObj); - - expect(copiedOpts).not.toBeInstanceOf(URL); - expect(copiedOpts.protocol).toBeUndefined(); - expect(copiedOpts.auth).toBeUndefined(); - expect(copiedOpts.hostname).toBeUndefined(); - expect(copiedOpts.port).toBeNaN(); - expect(copiedOpts.path).toBe(""); - expect(copiedOpts.pathname).toBeUndefined(); - expect(copiedOpts.search).toBeUndefined(); - expect(copiedOpts.hash).toBeUndefined(); - expect(copiedOpts.href).toBeUndefined(); - }); -}); - -//<#END_FILE: test-url-urltooptions.js diff --git a/test/js/node/test/parallel/util-deprecate.test.js b/test/js/node/test/parallel/util-deprecate.test.js deleted file mode 100644 index 319c02c3b0..0000000000 --- a/test/js/node/test/parallel/util-deprecate.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-util-deprecate.js -//#SHA1: 43c232bacd8dcc9f39194125c071db2ab14dfb51 -//----------------- -"use strict"; - -const util = require("util"); - -// Tests basic functionality of util.deprecate(). - -// Mock process.on for warnings -const mockWarningListener = jest.fn(); -const mockExitListener = jest.fn(); -process.on = jest.fn((event, listener) => { - if (event === "warning") mockWarningListener.mockImplementation(listener); - if (event === "exit") mockExitListener.mockImplementation(listener); -}); - -const expectedWarnings = new Map(); - -test("Emits deprecation only once if same function is called", () => { - const msg = "fhqwhgads"; - const fn = util.deprecate(() => {}, msg); - expectedWarnings.set(msg, { code: undefined, count: 1 }); - fn(); - fn(); -}); - -test("Emits deprecation twice for different functions", () => { - const msg = "sterrance"; - const fn1 = util.deprecate(() => {}, msg); - const fn2 = util.deprecate(() => {}, msg); - expectedWarnings.set(msg, { code: undefined, count: 2 }); - fn1(); - fn2(); -}); - -test("Emits deprecation only once if optional code is the same, even for different functions", () => { - const msg = "cannonmouth"; - const code = "deprecatesque"; - const fn1 = util.deprecate(() => {}, msg, code); - const fn2 = util.deprecate(() => {}, msg, code); - expectedWarnings.set(msg, { code, count: 1 }); - fn1(); - fn2(); - fn1(); - fn2(); -}); - -test("Handles warnings correctly", () => { - expectedWarnings.forEach((expected, message) => { - for (let i = 0; i < expected.count; i++) { - mockWarningListener({ - name: "DeprecationWarning", - message: message, - code: expected.code, - }); - } - }); - - expect(mockWarningListener).toHaveBeenCalledTimes( - Array.from(expectedWarnings.values()).reduce((acc, curr) => acc + curr.count, 0), - ); - - mockWarningListener.mock.calls.forEach(([warning]) => { - expect(warning.name).toBe("DeprecationWarning"); - expect(expectedWarnings.has(warning.message)).toBe(true); - const expected = expectedWarnings.get(warning.message); - expect(warning.code).toBe(expected.code); - expected.count--; - if (expected.count === 0) { - expectedWarnings.delete(warning.message); - } - }); -}); - -test("All warnings are processed", () => { - mockExitListener(); - expect(expectedWarnings.size).toBe(0); -}); diff --git a/test/js/node/test/parallel/util-inspect-getters-accessing-this.test.js b/test/js/node/test/parallel/util-inspect-getters-accessing-this.test.js deleted file mode 100644 index 78cec627d2..0000000000 --- a/test/js/node/test/parallel/util-inspect-getters-accessing-this.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-util-inspect-getters-accessing-this.js -//#SHA1: 92c41c06f838da46cbbfcd7f695a19784af3f581 -//----------------- -"use strict"; - -const { inspect } = require("util"); - -// This test ensures that util.inspect logs getters -// which access this. - -test("util.inspect logs getters accessing this", () => { - class X { - constructor() { - this._y = 123; - } - - get y() { - return this._y; - } - } - - const result = inspect(new X(), { - getters: true, - showHidden: true, - }); - - expect(result).toBe("X { _y: 123, [y]: [Getter: 123] }"); -}); - -// Regression test for https://github.com/nodejs/node/issues/37054 -test("util.inspect handles circular references in getters", () => { - class A { - constructor(B) { - this.B = B; - } - get b() { - return this.B; - } - } - - class B { - constructor() { - this.A = new A(this); - } - get a() { - return this.A; - } - } - - const result = inspect(new B(), { - depth: 1, - getters: true, - showHidden: true, - }); - - expect(result).toBe( - " B {\n" + - " A: A { B: [Circular *1], [b]: [Getter] [Circular *1] },\n" + - " [a]: [Getter] A { B: [Circular *1], [b]: [Getter] [Circular *1] }\n" + - "}", - ); -}); - -//<#END_FILE: test-util-inspect-getters-accessing-this.js diff --git a/test/js/node/test/parallel/util-inspect-long-running.test.js b/test/js/node/test/parallel/util-inspect-long-running.test.js deleted file mode 100644 index 7fba0e08b7..0000000000 --- a/test/js/node/test/parallel/util-inspect-long-running.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-util-inspect-long-running.js -//#SHA1: 2e4cbb5743a4dfcf869e84ce6d58795f96465aeb -//----------------- -"use strict"; - -// Test that huge objects don't crash due to exceeding the maximum heap size. - -const util = require("util"); - -test("util.inspect handles huge objects without crashing", () => { - // Create a difficult to stringify object. Without the artificial limitation - // this would crash or throw an maximum string size error. - let last = {}; - const obj = last; - - for (let i = 0; i < 1000; i++) { - last.next = { circular: obj, last, obj: { a: 1, b: 2, c: true } }; - last = last.next; - obj[i] = last; - } - - // This should not throw an error or crash - expect(() => { - util.inspect(obj, { depth: Infinity }); - }).not.toThrow(); -}); - -//<#END_FILE: test-util-inspect-long-running.js diff --git a/test/js/node/test/parallel/util-primordial-monkeypatching.test.js b/test/js/node/test/parallel/util-primordial-monkeypatching.test.js deleted file mode 100644 index 8f369c1d8f..0000000000 --- a/test/js/node/test/parallel/util-primordial-monkeypatching.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-util-primordial-monkeypatching.js -//#SHA1: 72e754f1abd435e598620901f26b087e0bf9d5a7 -//----------------- -"use strict"; - -// Monkeypatch Object.keys() so that it throws an unexpected error. This tests -// that `util.inspect()` is unaffected by monkey-patching `Object`. - -const util = require("util"); - -test("util.inspect() is unaffected by monkey-patching Object.keys()", () => { - const originalObjectKeys = Object.keys; - - // Monkey-patch Object.keys - Object.keys = () => { - throw new Error("fhqwhgads"); - }; - - try { - expect(util.inspect({})).toBe("{}"); - } finally { - // Restore original Object.keys to avoid affecting other tests - Object.keys = originalObjectKeys; - } -}); diff --git a/test/js/node/test/parallel/util-sigint-watchdog.test.js b/test/js/node/test/parallel/util-sigint-watchdog.test.js deleted file mode 100644 index 8f265aa240..0000000000 --- a/test/js/node/test/parallel/util-sigint-watchdog.test.js +++ /dev/null @@ -1,83 +0,0 @@ -//#FILE: test-util-sigint-watchdog.js -//#SHA1: 8731497954a21e75af21af0adde10619d98a703f -//----------------- -"use strict"; - -// Skip test on Windows platforms -if (process.platform === "win32") { - test.skip("platform not supported", () => {}); -} else { - const binding = { - startSigintWatchdog: jest.fn(), - stopSigintWatchdog: jest.fn(), - watchdogHasPendingSigint: jest.fn(), - }; - - // Mock the process.kill function - const originalKill = process.kill; - beforeAll(() => { - process.kill = jest.fn(); - }); - afterAll(() => { - process.kill = originalKill; - }); - - function waitForPendingSignal(cb) { - if (binding.watchdogHasPendingSigint()) cb(); - else setTimeout(waitForPendingSignal, 10, cb); - } - - test("with no signal observed", () => { - binding.startSigintWatchdog(); - binding.stopSigintWatchdog.mockReturnValue(false); - const hadPendingSignals = binding.stopSigintWatchdog(); - expect(hadPendingSignals).toBe(false); - }); - - test("with one call to the watchdog, one signal", done => { - binding.startSigintWatchdog(); - process.kill(process.pid, "SIGINT"); - binding.watchdogHasPendingSigint.mockReturnValue(true); - binding.stopSigintWatchdog.mockReturnValue(true); - - waitForPendingSignal(() => { - const hadPendingSignals = binding.stopSigintWatchdog(); - expect(hadPendingSignals).toBe(true); - done(); - }); - }); - - test("nested calls are okay", done => { - binding.startSigintWatchdog(); - binding.startSigintWatchdog(); - process.kill(process.pid, "SIGINT"); - binding.watchdogHasPendingSigint.mockReturnValue(true); - binding.stopSigintWatchdog.mockReturnValueOnce(true).mockReturnValueOnce(false); - - waitForPendingSignal(() => { - const hadPendingSignals1 = binding.stopSigintWatchdog(); - const hadPendingSignals2 = binding.stopSigintWatchdog(); - expect(hadPendingSignals1).toBe(true); - expect(hadPendingSignals2).toBe(false); - done(); - }); - }); - - test("signal comes in after first call to stop", done => { - binding.startSigintWatchdog(); - binding.startSigintWatchdog(); - binding.stopSigintWatchdog.mockReturnValueOnce(false).mockReturnValueOnce(true); - const hadPendingSignals1 = binding.stopSigintWatchdog(); - process.kill(process.pid, "SIGINT"); - binding.watchdogHasPendingSigint.mockReturnValue(true); - - waitForPendingSignal(() => { - const hadPendingSignals2 = binding.stopSigintWatchdog(); - expect(hadPendingSignals1).toBe(false); - expect(hadPendingSignals2).toBe(true); - done(); - }); - }); -} - -//<#END_FILE: test-util-sigint-watchdog.js diff --git a/test/js/node/test/parallel/uv-unmapped-exception.test.js b/test/js/node/test/parallel/uv-unmapped-exception.test.js deleted file mode 100644 index c8a5147fdc..0000000000 --- a/test/js/node/test/parallel/uv-unmapped-exception.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-uv-unmapped-exception.js -//#SHA1: 2878fb9a4523acb34f6283c980cde15b06fdc055 -//----------------- -"use strict"; - -// We can't use internal modules, so we'll need to implement our own UVException and UVExceptionWithHostPort -class UVException extends Error { - constructor({ errno, syscall }) { - super(`UNKNOWN: unknown error, ${syscall}`); - this.errno = errno; - this.syscall = syscall; - this.code = "UNKNOWN"; - } -} - -class UVExceptionWithHostPort extends Error { - constructor(errno, syscall, address, port) { - super(`${syscall} UNKNOWN: unknown error ${address}:${port}`); - this.code = "UNKNOWN"; - this.errno = errno; - this.syscall = syscall; - this.address = address; - this.port = port; - } -} - -test("UVException", () => { - const exception = new UVException({ errno: 100, syscall: "open" }); - - expect(exception.message).toBe("UNKNOWN: unknown error, open"); - expect(exception.errno).toBe(100); - expect(exception.syscall).toBe("open"); - expect(exception.code).toBe("UNKNOWN"); -}); - -test("UVExceptionWithHostPort", () => { - const exception = new UVExceptionWithHostPort(100, "listen", "127.0.0.1", 80); - - expect(exception.message).toBe("listen UNKNOWN: unknown error 127.0.0.1:80"); - expect(exception.code).toBe("UNKNOWN"); - expect(exception.errno).toBe(100); - expect(exception.syscall).toBe("listen"); - expect(exception.address).toBe("127.0.0.1"); - expect(exception.port).toBe(80); -}); - -//<#END_FILE: test-uv-unmapped-exception.js diff --git a/test/js/node/test/parallel/v8-collect-gc-profile-in-worker.test.js b/test/js/node/test/parallel/v8-collect-gc-profile-in-worker.test.js deleted file mode 100644 index ff43b62a54..0000000000 --- a/test/js/node/test/parallel/v8-collect-gc-profile-in-worker.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-v8-collect-gc-profile-in-worker.js -//#SHA1: 212956c00ed8e788f682c6335879f6844972a424 -//----------------- -// Flags: --expose-gc -"use strict"; - -const { Worker } = require("worker_threads"); - -// Replace testGCProfiler with a Jest-compatible mock -const testGCProfiler = jest.fn(); - -if (!process.env.isWorker) { - test("Worker thread creation", () => { - process.env.isWorker = 1; - const worker = new Worker(__filename); - expect(worker).toBeDefined(); - }); -} else { - test("GC profiler in worker thread", () => { - testGCProfiler(); - for (let i = 0; i < 100; i++) { - new Array(100); - } - - // Check if global.gc is available - if (typeof global.gc === "function") { - global.gc(); - } else { - console.warn("global.gc is not available. Make sure to run with --expose-gc flag."); - } - - expect(testGCProfiler).toHaveBeenCalledTimes(1); - }); -} - -//<#END_FILE: test-v8-collect-gc-profile-in-worker.js diff --git a/test/js/node/test/parallel/v8-deserialize-buffer.test.js b/test/js/node/test/parallel/v8-deserialize-buffer.test.js deleted file mode 100644 index d84675a6c0..0000000000 --- a/test/js/node/test/parallel/v8-deserialize-buffer.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-v8-deserialize-buffer.js -//#SHA1: ef80f8c41f9e9b893ea639f4558713addcf82b9a -//----------------- -"use strict"; - -const v8 = require("v8"); - -test("v8.deserialize should not emit warnings for Buffer.alloc(0)", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - v8.deserialize(v8.serialize(Buffer.alloc(0))); - - expect(warningListener).not.toHaveBeenCalled(); - - // Clean up the listener - process.removeListener("warning", warningListener); -}); - -//<#END_FILE: test-v8-deserialize-buffer.js diff --git a/test/js/node/test/parallel/v8-flag-pool-size-0.test.js b/test/js/node/test/parallel/v8-flag-pool-size-0.test.js deleted file mode 100644 index 0b32378981..0000000000 --- a/test/js/node/test/parallel/v8-flag-pool-size-0.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-v8-flag-pool-size-0.js -//#SHA1: 32e0b30d9305b4bf5509c6061176b2f87c47c66d -//----------------- -// Flags: --v8-pool-size=0 --expose-gc - -"use strict"; - -// This test doesn't require any specific assertions or expectations. -// It's primarily checking that the process doesn't crash or hang when -// running with the specified flags. - -test("V8 tasks scheduled by GC are handled on worker threads with --v8-pool-size=0", () => { - // This verifies that V8 tasks scheduled by GC are handled on worker threads if - // `--v8-pool-size=0` is given. The worker threads are managed by Node.js' - // `v8::Platform` implementation. - - // Trigger garbage collection - globalThis.gc(); - - // If we've reached this point without crashing or hanging, the test is successful - expect(true).toBe(true); -}); - -//<#END_FILE: test-v8-flag-pool-size-0.js diff --git a/test/js/node/test/parallel/v8-serialize-leak.test.js b/test/js/node/test/parallel/v8-serialize-leak.test.js deleted file mode 100644 index a8d0bb51e0..0000000000 --- a/test/js/node/test/parallel/v8-serialize-leak.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-v8-serialize-leak.js -//#SHA1: f1b10774a48610a130cac36679953f9af1ed15e1 -//----------------- -"use strict"; -// Flags: --expose-gc - -const v8 = require("v8"); - -// On IBMi, the rss memory always returns zero -if (process.platform === "os400") { - test.skip("On IBMi, the rss memory always returns zero"); -} - -test("v8.serialize should not leak memory", async () => { - const before = process.memoryUsage.rss(); - - for (let i = 0; i < 1000000; i++) { - v8.serialize(""); - } - - async function gcUntil(message, condition) { - for (let i = 0; i < 10; i++) { - global.gc(); - await new Promise(resolve => setTimeout(resolve, 100)); - if (condition()) { - return; - } - } - throw new Error(`${message} failed to be true in time`); - } - - await gcUntil("RSS should go down", () => { - const after = process.memoryUsage.rss(); - if (process.env.ASAN_OPTIONS) { - console.log(`ASan: before=${before} after=${after}`); - return after < before * 10; - } else if (process.config.variables.node_builtin_modules_path) { - console.log(`node_builtin_modules_path: before=${before} after=${after}`); - return after < before * 10; - } - console.log(`before=${before} after=${after}`); - return after < before * 10; - }); -}); - -//<#END_FILE: test-v8-serialize-leak.js diff --git a/test/js/node/test/parallel/vm-attributes-property-not-on-sandbox.test.js b/test/js/node/test/parallel/vm-attributes-property-not-on-sandbox.test.js deleted file mode 100644 index 3cfeab76a7..0000000000 --- a/test/js/node/test/parallel/vm-attributes-property-not-on-sandbox.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-vm-attributes-property-not-on-sandbox.js -//#SHA1: c864df0cb9b3ab90c8582ad86f50a8e94be92114 -//----------------- -"use strict"; -const vm = require("vm"); - -// Assert that accessor descriptors are not flattened on the sandbox. -// Issue: https://github.com/nodejs/node/issues/2734 -test("accessor descriptors are not flattened on the sandbox", () => { - const sandbox = {}; - vm.createContext(sandbox); - const code = `Object.defineProperty( - this, - 'foo', - { get: function() {return 17} } - ); - var desc = Object.getOwnPropertyDescriptor(this, 'foo');`; - - vm.runInContext(code, sandbox); - expect(typeof sandbox.desc.get).toBe("function"); -}); - -//<#END_FILE: test-vm-attributes-property-not-on-sandbox.js diff --git a/test/js/node/test/parallel/vm-context-property-forwarding.test.js b/test/js/node/test/parallel/vm-context-property-forwarding.test.js deleted file mode 100644 index ca3e22c38a..0000000000 --- a/test/js/node/test/parallel/vm-context-property-forwarding.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-vm-context-property-forwarding.js -//#SHA1: 611b4eee260bc77dfbaece761208e11a57fe226f -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("vm context property forwarding", () => { - const sandbox = { x: 3 }; - const ctx = vm.createContext(sandbox); - - expect(vm.runInContext("x;", ctx)).toBe(3); - vm.runInContext("y = 4;", ctx); - expect(sandbox.y).toBe(4); - expect(ctx.y).toBe(4); -}); - -test("IndexedPropertyGetterCallback and IndexedPropertyDeleterCallback", () => { - const x = { - get 1() { - return 5; - }, - }; - const pd_expected = Object.getOwnPropertyDescriptor(x, 1); - const ctx2 = vm.createContext(x); - const pd_actual = Object.getOwnPropertyDescriptor(ctx2, 1); - - expect(pd_actual).toEqual(pd_expected); - expect(ctx2[1]).toBe(5); - delete ctx2[1]; - expect(ctx2[1]).toBeUndefined(); -}); - -// https://github.com/nodejs/node/issues/33806 -test("property setter error propagation", () => { - const ctx = vm.createContext(); - - Object.defineProperty(ctx, "prop", { - get() { - return undefined; - }, - set() { - throw new Error("test error"); - }, - }); - - expect(() => { - vm.runInContext("prop = 42", ctx); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-vm-context-property-forwarding.js diff --git a/test/js/node/test/parallel/vm-create-and-run-in-context.test.js b/test/js/node/test/parallel/vm-create-and-run-in-context.test.js deleted file mode 100644 index e6e450ef9f..0000000000 --- a/test/js/node/test/parallel/vm-create-and-run-in-context.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-vm-create-and-run-in-context.js -//#SHA1: 4583962cfdb3266d8b850442cec0b4cfa49bc6d2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Flags: --expose-gc - -const vm = require("vm"); - -describe("vm.createContext and vm.runInContext", () => { - test("Run in a new empty context", () => { - const context = vm.createContext(); - const result = vm.runInContext('"passed";', context); - expect(result).toBe("passed"); - }); - - test("Create a new pre-populated context", () => { - const context = vm.createContext({ foo: "bar", thing: "lala" }); - expect(context.foo).toBe("bar"); - expect(context.thing).toBe("lala"); - }); - - test("Test updating context", () => { - const context = vm.createContext({ foo: "bar", thing: "lala" }); - vm.runInContext("var foo = 3;", context); - expect(context.foo).toBe(3); - expect(context.thing).toBe("lala"); - }); - - test("Run in contextified sandbox without referencing the context", () => { - // https://github.com/nodejs/node/issues/5768 - const sandbox = { x: 1 }; - vm.createContext(sandbox); - global.gc(); - vm.runInContext("x = 2", sandbox); - // Should not crash. - expect(sandbox.x).toBe(2); - }); -}); - -//<#END_FILE: test-vm-create-and-run-in-context.js diff --git a/test/js/node/test/parallel/vm-cross-context.test.js b/test/js/node/test/parallel/vm-cross-context.test.js deleted file mode 100644 index 52c716d18a..0000000000 --- a/test/js/node/test/parallel/vm-cross-context.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-vm-cross-context.js -//#SHA1: c8f367f7edfa69a5b052feab2bb6c9326f16c9d3 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const vm = require("vm"); - -test("vm.runInContext should not throw when accessing console.log", () => { - const ctx = vm.createContext(global); - - // Should not throw. - expect(() => { - vm.runInContext("!function() { var x = console.log; }()", ctx); - }).not.toThrow(); -}); - -//<#END_FILE: test-vm-cross-context.js diff --git a/test/js/node/test/parallel/vm-data-property-writable.test.js b/test/js/node/test/parallel/vm-data-property-writable.test.js deleted file mode 100644 index e85965fb83..0000000000 --- a/test/js/node/test/parallel/vm-data-property-writable.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-vm-data-property-writable.js -//#SHA1: fc562132c9b5c9f17d55e08df7456dea7a1f41e3 -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/10223 - -const vm = require("vm"); - -test("vm data property writable", () => { - const context = vm.createContext({}); - - let code = ` - Object.defineProperty(this, 'foo', {value: 5}); - Object.getOwnPropertyDescriptor(this, 'foo'); - `; - - let desc = vm.runInContext(code, context); - - expect(desc.writable).toBe(false); - - // Check that interceptors work for symbols. - code = ` - const bar = Symbol('bar'); - Object.defineProperty(this, bar, {value: 6}); - Object.getOwnPropertyDescriptor(this, bar); - `; - - desc = vm.runInContext(code, context); - - expect(desc.value).toBe(6); -}); - -//<#END_FILE: test-vm-data-property-writable.js diff --git a/test/js/node/test/parallel/vm-deleting-property.test.js b/test/js/node/test/parallel/vm-deleting-property.test.js deleted file mode 100644 index ac94e9a72d..0000000000 --- a/test/js/node/test/parallel/vm-deleting-property.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-vm-deleting-property.js -//#SHA1: 20a3a3752a4d1b140cd8762c1d66c8dc734ed3fa -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/6287 - -const vm = require("vm"); - -test("deleting property in vm context", () => { - const context = vm.createContext(); - const res = vm.runInContext( - ` - this.x = 'prop'; - delete this.x; - Object.getOwnPropertyDescriptor(this, 'x'); - `, - context, - ); - - expect(res).toBeUndefined(); -}); - -//<#END_FILE: test-vm-deleting-property.js diff --git a/test/js/node/test/parallel/vm-function-declaration.test.js b/test/js/node/test/parallel/vm-function-declaration.test.js deleted file mode 100644 index 2137b50b48..0000000000 --- a/test/js/node/test/parallel/vm-function-declaration.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-vm-function-declaration.js -//#SHA1: 002bfe8201b2483628b46efd3d5e081fb17fc4b4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const vm = require("vm"); - -test("Function declaration and expression in vm context", () => { - const o = vm.createContext({ console }); - - // Function declaration and expression should both be copied to the - // sandboxed context. - let code = "let a = function() {};\n"; - code += "function b(){}\n"; - code += "var c = function() {};\n"; - code += "var d = () => {};\n"; - code += "let e = () => {};\n"; - - // Grab the global b function as the completion value, to ensure that - // we are getting the global function, and not some other thing - code += "(function(){return this})().b;\n"; - - const res = vm.runInContext(code, o, "test"); - - expect(typeof res).toBe("function"); - expect(res.name).toBe("b"); - expect(typeof o.a).toBe("undefined"); - expect(typeof o.b).toBe("function"); - expect(typeof o.c).toBe("function"); - expect(typeof o.d).toBe("function"); - expect(typeof o.e).toBe("undefined"); - expect(res).toBe(o.b); -}); - -//<#END_FILE: test-vm-function-declaration.js diff --git a/test/js/node/test/parallel/vm-function-redefinition.test.js b/test/js/node/test/parallel/vm-function-redefinition.test.js deleted file mode 100644 index 6395cbd57e..0000000000 --- a/test/js/node/test/parallel/vm-function-redefinition.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-vm-function-redefinition.js -//#SHA1: afca30c05276f19448e4a2e01381c8a6fb13d544 -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/548 -const vm = require("vm"); - -test("function redefinition in vm context", () => { - const context = vm.createContext(); - - vm.runInContext("function test() { return 0; }", context); - vm.runInContext("function test() { return 1; }", context); - const result = vm.runInContext("test()", context); - expect(result).toBe(1); -}); - -//<#END_FILE: test-vm-function-redefinition.js diff --git a/test/js/node/test/parallel/vm-global-assignment.test.js b/test/js/node/test/parallel/vm-global-assignment.test.js deleted file mode 100644 index bacb3cf2d7..0000000000 --- a/test/js/node/test/parallel/vm-global-assignment.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-vm-global-assignment.js -//#SHA1: 54d8ce4e5d93c89a8573a3230065f4e244b198db -//----------------- -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/10806 - -const vm = require("vm"); - -describe("VM global assignment", () => { - let ctx; - let window; - const other = 123; - - beforeEach(() => { - ctx = vm.createContext({ open() {} }); - window = vm.runInContext("this", ctx); - }); - - test("window.open is not equal to other initially", () => { - expect(window.open).not.toBe(other); - }); - - test("window.open can be assigned", () => { - window.open = other; - expect(window.open).toBe(other); - }); - - test("window.open can be reassigned", () => { - window.open = other; - window.open = other; - expect(window.open).toBe(other); - }); -}); - -//<#END_FILE: test-vm-global-assignment.js diff --git a/test/js/node/test/parallel/vm-global-configurable-properties.test.js b/test/js/node/test/parallel/vm-global-configurable-properties.test.js deleted file mode 100644 index 860b260159..0000000000 --- a/test/js/node/test/parallel/vm-global-configurable-properties.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-vm-global-configurable-properties.js -//#SHA1: abaa38afe456cd1fcf98be472781d25e3918d1b5 -//----------------- -"use strict"; -// https://github.com/nodejs/node/issues/47799 - -const vm = require("vm"); - -test("VM global configurable properties", () => { - const ctx = vm.createContext(); - - const window = vm.runInContext("this", ctx); - - Object.defineProperty(window, "x", { value: "1", configurable: true }); - expect(window.x).toBe("1"); - - Object.defineProperty(window, "x", { value: "2", configurable: true }); - expect(window.x).toBe("2"); -}); - -//<#END_FILE: test-vm-global-configurable-properties.js diff --git a/test/js/node/test/parallel/vm-harmony-symbols.test.js b/test/js/node/test/parallel/vm-harmony-symbols.test.js deleted file mode 100644 index 1d8b86bdc4..0000000000 --- a/test/js/node/test/parallel/vm-harmony-symbols.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-vm-harmony-symbols.js -//#SHA1: 36768ab105e0bcc19dccae0fea22801068fdaedd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("The sandbox should have its own Symbol constructor", () => { - let sandbox = {}; - vm.runInNewContext("this.Symbol = Symbol", sandbox); - expect(typeof sandbox.Symbol).toBe("function"); - expect(sandbox.Symbol).not.toBe(Symbol); -}); - -test("Explicitly copying the Symbol constructor", () => { - let sandbox = { Symbol }; - vm.runInNewContext("this.Symbol = Symbol", sandbox); - expect(typeof sandbox.Symbol).toBe("function"); - expect(sandbox.Symbol).toBe(Symbol); -}); - -//<#END_FILE: test-vm-harmony-symbols.js diff --git a/test/js/node/test/parallel/vm-indexed-properties.test.js b/test/js/node/test/parallel/vm-indexed-properties.test.js deleted file mode 100644 index 6b9999fadf..0000000000 --- a/test/js/node/test/parallel/vm-indexed-properties.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-vm-indexed-properties.js -//#SHA1: 5938ca1da86f05ceda978b0f2d9640734d6c0ab6 -//----------------- -"use strict"; - -const vm = require("vm"); - -test("vm indexed properties", () => { - const code = `Object.defineProperty(this, 99, { - value: 20, - enumerable: true - });`; - - const sandbox = {}; - const ctx = vm.createContext(sandbox); - vm.runInContext(code, ctx); - - expect(sandbox[99]).toBe(20); -}); - -//<#END_FILE: test-vm-indexed-properties.js diff --git a/test/js/node/test/parallel/vm-low-stack-space.test.js b/test/js/node/test/parallel/vm-low-stack-space.test.js deleted file mode 100644 index e4ccd2e9b2..0000000000 --- a/test/js/node/test/parallel/vm-low-stack-space.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-vm-low-stack-space.js -//#SHA1: fffd6c9c17b9ff755e1dd126a42d6ea176282d00 -//----------------- -"use strict"; -const vm = require("vm"); - -test("vm.runInThisContext in low stack space", () => { - function a() { - try { - return a(); - } catch { - // Throw an exception as near to the recursion-based RangeError as possible. - return vm.runInThisContext("() => 42")(); - } - } - - expect(a()).toBe(42); -}); - -test("vm.runInNewContext in low stack space", () => { - function b() { - try { - return b(); - } catch { - // This writes a lot of noise to stderr, but it still works. - return vm.runInNewContext("() => 42")(); - } - } - - expect(b()).toBe(42); -}); - -//<#END_FILE: test-vm-low-stack-space.js diff --git a/test/js/node/test/parallel/vm-new-script-new-context.test.js b/test/js/node/test/parallel/vm-new-script-new-context.test.js deleted file mode 100644 index ea14f33bc6..0000000000 --- a/test/js/node/test/parallel/vm-new-script-new-context.test.js +++ /dev/null @@ -1,127 +0,0 @@ -//#FILE: test-vm-new-script-new-context.js -//#SHA1: 444a86a44203350903ab84a30de39e24a28732ec -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Script } = require("vm"); - -test("Script runInNewContext returns expected result", () => { - const script = new Script("'passed';"); - const result1 = script.runInNewContext(); - const result2 = script.runInNewContext(); - expect(result1).toBe("passed"); - expect(result2).toBe("passed"); -}); - -test("Script throws error when expected", () => { - const script = new Script("throw new Error('test');"); - expect(() => { - script.runInNewContext(); - }).toThrow( - expect.objectContaining({ - name: "Error", - message: expect.any(String), - }), - ); -}); - -test("Script throws ReferenceError for undefined variable", () => { - const script = new Script("foo.bar = 5;"); - expect(() => { - script.runInNewContext(); - }).toThrow( - expect.objectContaining({ - name: "ReferenceError", - message: expect.any(String), - }), - ); -}); - -test("Script does not affect global scope", () => { - global.hello = 5; - const script = new Script("hello = 2"); - script.runInNewContext(); - expect(global.hello).toBe(5); - - // Cleanup - delete global.hello; -}); - -test("Script runs in new context with provided object", () => { - global.code = "foo = 1;" + "bar = 2;" + "if (baz !== 3) throw new Error('test fail');"; - global.foo = 2; - global.obj = { foo: 0, baz: 3 }; - const script = new Script(global.code); - script.runInNewContext(global.obj); - expect(global.obj.foo).toBe(1); - expect(global.obj.bar).toBe(2); - expect(global.foo).toBe(2); - - // cleanup - delete global.code; - delete global.foo; - delete global.obj; -}); - -test("Script can modify global scope through provided function", () => { - const script = new Script("f()"); - function changeFoo() { - global.foo = 100; - } - script.runInNewContext({ f: changeFoo }); - expect(global.foo).toBe(100); - - // cleanup - delete global.foo; -}); - -test("Script modifies provided object and throws when object is not provided", () => { - const script = new Script("f.a = 2"); - const f = { a: 1 }; - script.runInNewContext({ f }); - expect(f.a).toBe(2); - - expect(() => { - script.runInNewContext(); - }).toThrow( - expect.objectContaining({ - name: "ReferenceError", - message: expect.any(String), - }), - ); -}); - -test("Script.runInNewContext throws when called with invalid this", () => { - const script = new Script(""); - expect(() => { - script.runInNewContext.call("'hello';"); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-vm-new-script-new-context.js diff --git a/test/js/node/test/parallel/vm-proxies.test.js b/test/js/node/test/parallel/vm-proxies.test.js deleted file mode 100644 index b713c7773a..0000000000 --- a/test/js/node/test/parallel/vm-proxies.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-vm-proxies.js -//#SHA1: 3119c41e6c3cc80d380c9467c2f922b2df3e5616 -//----------------- -"use strict"; - -const vm = require("vm"); - -test("Proxy object in new context", () => { - // src/node_contextify.cc filters out the Proxy object from the parent - // context. Make sure that the new context has a Proxy object of its own. - let sandbox = {}; - vm.runInNewContext("this.Proxy = Proxy", sandbox); - expect(typeof sandbox.Proxy).toBe("function"); - expect(sandbox.Proxy).not.toBe(Proxy); -}); - -test("Explicitly copied Proxy object in new context", () => { - // Unless we copy the Proxy object explicitly, of course. - const sandbox = { Proxy }; - vm.runInNewContext("this.Proxy = Proxy", sandbox); - expect(typeof sandbox.Proxy).toBe("function"); - expect(sandbox.Proxy).toBe(Proxy); -}); - -//<#END_FILE: test-vm-proxies.js diff --git a/test/js/node/test/parallel/vm-proxy-failure-cp.test.js b/test/js/node/test/parallel/vm-proxy-failure-cp.test.js deleted file mode 100644 index 05d2c1e7b7..0000000000 --- a/test/js/node/test/parallel/vm-proxy-failure-cp.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-vm-proxy-failure-CP.js -//#SHA1: d3eb5284a94f718a6ae1e07c0b30e01dad295ea9 -//----------------- -"use strict"; -const vm = require("vm"); - -// Check that we do not accidentally query attributes. -// Issue: https://github.com/nodejs/node/issues/11902 -test("vm does not accidentally query attributes", () => { - const handler = { - getOwnPropertyDescriptor: (target, prop) => { - throw new Error("whoops"); - }, - }; - const sandbox = new Proxy({ foo: "bar" }, handler); - const context = vm.createContext(sandbox); - - expect(() => { - vm.runInContext("", context); - }).not.toThrow(); -}); - -//<#END_FILE: test-vm-proxy-failure-CP.js diff --git a/test/js/node/test/parallel/vm-script-throw-in-tostring.test.js b/test/js/node/test/parallel/vm-script-throw-in-tostring.test.js deleted file mode 100644 index acd0eeb817..0000000000 --- a/test/js/node/test/parallel/vm-script-throw-in-tostring.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-vm-script-throw-in-tostring.js -//#SHA1: 16675c8942dbb81a032c117fa42c9611cda082e0 -//----------------- -"use strict"; - -const vm = require("vm"); - -test("vm.Script throws when toString throws", () => { - expect(() => { - new vm.Script({ - toString() { - throw new Error(); - }, - }); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-vm-script-throw-in-tostring.js diff --git a/test/js/node/test/parallel/vm-strict-mode.test.js b/test/js/node/test/parallel/vm-strict-mode.test.js deleted file mode 100644 index e2f06f8555..0000000000 --- a/test/js/node/test/parallel/vm-strict-mode.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-vm-strict-mode.js -//#SHA1: ab6c6c72920e9bf095b41b255872b9d0604301c7 -//----------------- -"use strict"; -// https://github.com/nodejs/node/issues/12300 - -const vm = require("vm"); - -test("vm strict mode assignment", () => { - const ctx = vm.createContext({ x: 42 }); - - // This might look as if x has not been declared, but x is defined on the - // sandbox and the assignment should not throw. - expect(() => { - vm.runInContext('"use strict"; x = 1', ctx); - }).not.toThrow(); - - expect(ctx.x).toBe(1); -}); - -//<#END_FILE: test-vm-strict-mode.js diff --git a/test/js/node/test/parallel/vm-syntax-error-message.test.js b/test/js/node/test/parallel/vm-syntax-error-message.test.js deleted file mode 100644 index 49be35047c..0000000000 --- a/test/js/node/test/parallel/vm-syntax-error-message.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-vm-syntax-error-message.js -//#SHA1: dbd3683e08ad5cf574d1824108446e9c425adf1b -//----------------- -"use strict"; - -const child_process = require("child_process"); - -test("vm syntax error message", done => { - const p = child_process.spawn(process.execPath, [ - "-e", - 'vm = require("vm");' + - "context = vm.createContext({});" + - "try { vm.runInContext(\"throw new Error('boo')\", context); } " + - "catch (e) { console.log(e.message); }", - ]); - - p.stderr.on("data", () => { - throw new Error("stderr should not receive any data"); - }); - - let output = ""; - - p.stdout.on("data", data => (output += data)); - - p.stdout.on("end", () => { - expect(output.replace(/[\r\n]+/g, "")).toBe("boo"); - done(); - }); -}); - -//<#END_FILE: test-vm-syntax-error-message.js diff --git a/test/js/node/test/parallel/webcrypto-encrypt-decrypt.test.js b/test/js/node/test/parallel/webcrypto-encrypt-decrypt.test.js deleted file mode 100644 index 560edf8eb6..0000000000 --- a/test/js/node/test/parallel/webcrypto-encrypt-decrypt.test.js +++ /dev/null @@ -1,110 +0,0 @@ -//#FILE: test-webcrypto-encrypt-decrypt.js -//#SHA1: 791cad35ebee437d2a982e6101d47daa2f775a4b -//----------------- -"use strict"; - -const { subtle } = globalThis.crypto; - -// This is only a partial test. The WebCrypto Web Platform Tests -// will provide much greater coverage. - -// Test Encrypt/Decrypt RSA-OAEP -test("Encrypt/Decrypt RSA-OAEP", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const ec = new TextEncoder(); - const { publicKey, privateKey } = await subtle.generateKey( - { - name: "RSA-OAEP", - modulusLength: 2048, - publicExponent: new Uint8Array([1, 0, 1]), - hash: "SHA-384", - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt( - { - name: "RSA-OAEP", - label: ec.encode("a label"), - }, - publicKey, - buf, - ); - - const plaintext = await subtle.decrypt( - { - name: "RSA-OAEP", - label: ec.encode("a label"), - }, - privateKey, - ciphertext, - ); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -// Test Encrypt/Decrypt AES-CTR -test("Encrypt/Decrypt AES-CTR", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const counter = globalThis.crypto.getRandomValues(new Uint8Array(16)); - - const key = await subtle.generateKey( - { - name: "AES-CTR", - length: 256, - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt({ name: "AES-CTR", counter, length: 64 }, key, buf); - - const plaintext = await subtle.decrypt({ name: "AES-CTR", counter, length: 64 }, key, ciphertext); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -// Test Encrypt/Decrypt AES-CBC -test("Encrypt/Decrypt AES-CBC", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const iv = globalThis.crypto.getRandomValues(new Uint8Array(16)); - - const key = await subtle.generateKey( - { - name: "AES-CBC", - length: 256, - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt({ name: "AES-CBC", iv }, key, buf); - - const plaintext = await subtle.decrypt({ name: "AES-CBC", iv }, key, ciphertext); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -// Test Encrypt/Decrypt AES-GCM -test("Encrypt/Decrypt AES-GCM", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const iv = globalThis.crypto.getRandomValues(new Uint8Array(12)); - - const key = await subtle.generateKey( - { - name: "AES-GCM", - length: 256, - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt({ name: "AES-GCM", iv }, key, buf); - - const plaintext = await subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -//<#END_FILE: test-webcrypto-encrypt-decrypt.js diff --git a/test/js/node/test/parallel/webcrypto-getrandomvalues.test.js b/test/js/node/test/parallel/webcrypto-getrandomvalues.test.js deleted file mode 100644 index 3ec9961927..0000000000 --- a/test/js/node/test/parallel/webcrypto-getrandomvalues.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-webcrypto-getRandomValues.js -//#SHA1: d5d696eb0e68968d2411efa24e6e4c9bd46a1678 -//----------------- -"use strict"; - -if (!globalThis.crypto) { - it("skips test when crypto is missing", () => { - console.log("missing crypto"); - return; - }); -} else { - describe("webcrypto getRandomValues", () => { - const { getRandomValues } = globalThis.crypto; - - it("throws ERR_INVALID_THIS when called without proper this", () => { - expect(() => getRandomValues(new Uint8Array())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - message: expect.any(String), - }), - ); - }); - }); -} - -//<#END_FILE: test-webcrypto-getRandomValues.js diff --git a/test/js/node/test/parallel/webstream-string-tag.test.js b/test/js/node/test/parallel/webstream-string-tag.test.js deleted file mode 100644 index f8791dbe76..0000000000 --- a/test/js/node/test/parallel/webstream-string-tag.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-webstream-string-tag.js -//#SHA1: 53f13b84555ff37eeee23ca4552540d76b88c2ad -//----------------- -"use strict"; - -test("Web Stream classes have correct Symbol.toStringTag", () => { - const classesToBeTested = [ - WritableStream, - WritableStreamDefaultWriter, - WritableStreamDefaultController, - ReadableStream, - ReadableStreamBYOBRequest, - ReadableStreamDefaultReader, - ReadableStreamBYOBReader, - ReadableStreamDefaultController, - ReadableByteStreamController, - ByteLengthQueuingStrategy, - CountQueuingStrategy, - TransformStream, - TransformStreamDefaultController, - ]; - - classesToBeTested.forEach(cls => { - expect(cls.prototype[Symbol.toStringTag]).toBe(cls.name); - expect(Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag)).toEqual({ - configurable: true, - enumerable: false, - value: cls.name, - writable: false, - }); - }); -}); - -//<#END_FILE: test-webstream-string-tag.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-api-basics.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-api-basics.test.js deleted file mode 100644 index b686f76961..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-api-basics.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-api-basics.js -//#SHA1: 8181a892b0d1e5885b29b1165631875e587b7700 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/master/encoding/api-basics.html -// This is the part that can be run without ICU - -function testDecodeSample(encoding, string, bytes) { - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes))).toBe(string); - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer)).toBe(string); -} - -// `z` (ASCII U+007A), cent (Latin-1 U+00A2), CJK water (BMP U+6C34), -// G-Clef (non-BMP U+1D11E), PUA (BMP U+F8FF), PUA (non-BMP U+10FFFD) -// byte-swapped BOM (non-character U+FFFE) -const sample = "z\xA2\u6C34\uD834\uDD1E\uF8FF\uDBFF\uDFFD\uFFFE"; - -test("utf-8 encoding and decoding", () => { - const encoding = "utf-8"; - const string = sample; - const bytes = [ - 0x7a, 0xc2, 0xa2, 0xe6, 0xb0, 0xb4, 0xf0, 0x9d, 0x84, 0x9e, 0xef, 0xa3, 0xbf, 0xf4, 0x8f, 0xbf, 0xbd, 0xef, 0xbf, - 0xbe, - ]; - const encoded = new TextEncoder().encode(string); - expect([...encoded]).toEqual(bytes); - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes))).toBe(string); - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer)).toBe(string); -}); - -test("utf-16le decoding", () => { - testDecodeSample( - "utf-16le", - sample, - [0x7a, 0x00, 0xa2, 0x00, 0x34, 0x6c, 0x34, 0xd8, 0x1e, 0xdd, 0xff, 0xf8, 0xff, 0xdb, 0xfd, 0xdf, 0xfe, 0xff], - ); -}); - -test("utf-16 decoding", () => { - testDecodeSample( - "utf-16", - sample, - [0x7a, 0x00, 0xa2, 0x00, 0x34, 0x6c, 0x34, 0xd8, 0x1e, 0xdd, 0xff, 0xf8, 0xff, 0xdb, 0xfd, 0xdf, 0xfe, 0xff], - ); -}); - -//<#END_FILE: test-whatwg-encoding-custom-api-basics.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-fatal-streaming.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-fatal-streaming.test.js deleted file mode 100644 index ce4cd6051f..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-fatal-streaming.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-fatal-streaming.js -//#SHA1: 325f0a1e055a1756532d2818d4e563dfbddb928b -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/d74324b53c/encoding/textdecoder-fatal-streaming.html -// With the twist that we specifically test for Node.js error codes - -if (!globalThis.Intl) { - test.skip("missing Intl", () => {}); -} else { - test("TextDecoder with fatal option and invalid sequences", () => { - [ - { encoding: "utf-8", sequence: [0xc0] }, - { encoding: "utf-16le", sequence: [0x00] }, - { encoding: "utf-16be", sequence: [0x00] }, - ].forEach(testCase => { - const data = new Uint8Array(testCase.sequence); - expect(() => { - const decoder = new TextDecoder(testCase.encoding, { fatal: true }); - decoder.decode(data); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - }); - - test("TextDecoder with utf-16le and streaming", () => { - const decoder = new TextDecoder("utf-16le", { fatal: true }); - const odd = new Uint8Array([0x00]); - const even = new Uint8Array([0x00, 0x00]); - - expect(() => { - decoder.decode(even, { stream: true }); - decoder.decode(odd); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => { - decoder.decode(odd, { stream: true }); - decoder.decode(even); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -} - -//<#END_FILE: test-whatwg-encoding-custom-fatal-streaming.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-fatal.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-fatal.test.js deleted file mode 100644 index 14aa0d31c3..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-fatal.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-fatal.js -//#SHA1: e2b44e43b78b053687ab4a8dc5de7557f6637643 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-fatal.html -// With the twist that we specifically test for Node.js error codes - -if (!globalThis.Intl) { - test.skip("missing Intl"); -} - -const bad = [ - { encoding: "utf-8", input: [0xff], name: "invalid code" }, - { encoding: "utf-8", input: [0xc0], name: "ends early" }, - { encoding: "utf-8", input: [0xe0], name: "ends early 2" }, - { encoding: "utf-8", input: [0xc0, 0x00], name: "invalid trail" }, - { encoding: "utf-8", input: [0xc0, 0xc0], name: "invalid trail 2" }, - { encoding: "utf-8", input: [0xe0, 0x00], name: "invalid trail 3" }, - { encoding: "utf-8", input: [0xe0, 0xc0], name: "invalid trail 4" }, - { encoding: "utf-8", input: [0xe0, 0x80, 0x00], name: "invalid trail 5" }, - { encoding: "utf-8", input: [0xe0, 0x80, 0xc0], name: "invalid trail 6" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], name: "> 0x10FFFF" }, - { encoding: "utf-8", input: [0xfe, 0x80, 0x80, 0x80, 0x80, 0x80], name: "obsolete lead byte" }, - // Overlong encodings - { encoding: "utf-8", input: [0xc0, 0x80], name: "overlong U+0000 - 2 bytes" }, - { encoding: "utf-8", input: [0xe0, 0x80, 0x80], name: "overlong U+0000 - 3 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x80, 0x80, 0x80], name: "overlong U+0000 - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x80, 0x80, 0x80], name: "overlong U+0000 - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], name: "overlong U+0000 - 6 bytes" }, - { encoding: "utf-8", input: [0xc1, 0xbf], name: "overlong U+007F - 2 bytes" }, - { encoding: "utf-8", input: [0xe0, 0x81, 0xbf], name: "overlong U+007F - 3 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x80, 0x81, 0xbf], name: "overlong U+007F - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x80, 0x81, 0xbf], name: "overlong U+007F - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x81, 0xbf], name: "overlong U+007F - 6 bytes" }, - { encoding: "utf-8", input: [0xe0, 0x9f, 0xbf], name: "overlong U+07FF - 3 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x80, 0x9f, 0xbf], name: "overlong U+07FF - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x80, 0x9f, 0xbf], name: "overlong U+07FF - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x9f, 0xbf], name: "overlong U+07FF - 6 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x8f, 0xbf, 0xbf], name: "overlong U+FFFF - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x8f, 0xbf, 0xbf], name: "overlong U+FFFF - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x8f, 0xbf, 0xbf], name: "overlong U+FFFF - 6 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x84, 0x8f, 0xbf, 0xbf], name: "overlong U+10FFFF - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x84, 0x8f, 0xbf, 0xbf], name: "overlong U+10FFFF - 6 bytes" }, - // UTF-16 surrogates encoded as code points in UTF-8 - { encoding: "utf-8", input: [0xed, 0xa0, 0x80], name: "lead surrogate" }, - { encoding: "utf-8", input: [0xed, 0xb0, 0x80], name: "trail surrogate" }, - { encoding: "utf-8", input: [0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80], name: "surrogate pair" }, - { encoding: "utf-16le", input: [0x00], name: "truncated code unit" }, - // Mismatched UTF-16 surrogates are exercised in utf16-surrogates.html - // FIXME: Add legacy encoding cases -]; - -bad.forEach(t => { - test(`TextDecoder fatal error: ${t.name}`, () => { - expect(() => { - new TextDecoder(t.encoding, { fatal: true }).decode(new Uint8Array(t.input)); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-fatal.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-ignorebom.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-ignorebom.test.js deleted file mode 100644 index 43a7a005ce..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-ignorebom.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-ignorebom.js -//#SHA1: 8a119026559b0341b524a97eaaf87e948a502dd6 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/7f567fa29c/encoding/textdecoder-ignorebom.html -// This is the part that can be run without ICU - -const cases = [ - { - encoding: "utf-8", - bytes: [0xef, 0xbb, 0xbf, 0x61, 0x62, 0x63], - }, - { - encoding: "utf-16le", - bytes: [0xff, 0xfe, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00], - }, -]; - -cases.forEach(testCase => { - test(`TextDecoder with ${testCase.encoding} encoding`, () => { - const BOM = "\uFEFF"; - let decoder = new TextDecoder(testCase.encoding, { ignoreBOM: true }); - const bytes = new Uint8Array(testCase.bytes); - expect(decoder.decode(bytes)).toBe(`${BOM}abc`); - - decoder = new TextDecoder(testCase.encoding, { ignoreBOM: false }); - expect(decoder.decode(bytes)).toBe("abc"); - - decoder = new TextDecoder(testCase.encoding); - expect(decoder.decode(bytes)).toBe("abc"); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-ignorebom.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-invalid-arg.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-invalid-arg.test.js deleted file mode 100644 index 6389f03fbd..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-invalid-arg.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-invalid-arg.js -//#SHA1: eaf5b5a330366828645f6a0be0dbd859cf9f1bda -//----------------- -"use strict"; - -// This tests that ERR_INVALID_ARG_TYPE are thrown when -// invalid arguments are passed to TextDecoder. - -test("TextDecoder throws ERR_INVALID_ARG_TYPE for invalid input types", () => { - const notArrayBufferViewExamples = [false, {}, 1, "", new Error()]; - notArrayBufferViewExamples.forEach(invalidInputType => { - expect(() => { - new TextDecoder(undefined, null).decode(invalidInputType); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-invalid-arg.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-streaming.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-streaming.test.js deleted file mode 100644 index af2ab322ed..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-streaming.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-streaming.js -//#SHA1: d98fc14ce0348f22b3bae160b4e1e4882ce75749 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/fa9436d12c/encoding/textdecoder-streaming.html -// This is the part that can be run without ICU - -const string = "\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF"; -const octets = { - "utf-8": [ - 0x00, 0x31, 0x32, 0x33, 0x41, 0x42, 0x43, 0x61, 0x62, 0x63, 0xc2, 0x80, 0xc3, 0xbf, 0xc4, 0x80, 0xe1, 0x80, 0x80, - 0xef, 0xbf, 0xbd, 0xf0, 0x90, 0x80, 0x80, 0xf4, 0x8f, 0xbf, 0xbf, - ], - "utf-16le": [ - 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, - 0x00, 0x80, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x10, 0xfd, 0xff, 0x00, 0xd8, 0x00, 0xdc, 0xff, 0xdb, 0xff, 0xdf, - ], -}; - -Object.keys(octets).forEach(encoding => { - describe(`TextDecoder streaming for ${encoding}`, () => { - for (let len = 1; len <= 5; ++len) { - it(`should correctly decode with chunk size ${len}`, () => { - const encoded = octets[encoding]; - const decoder = new TextDecoder(encoding); - let out = ""; - for (let i = 0; i < encoded.length; i += len) { - const sub = []; - for (let j = i; j < encoded.length && j < i + len; ++j) sub.push(encoded[j]); - out += decoder.decode(new Uint8Array(sub), { stream: true }); - } - out += decoder.decode(); - expect(out).toBe(string); - }); - } - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-streaming.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-utf16-surrogates.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-utf16-surrogates.test.js deleted file mode 100644 index 22c5f5f5da..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-utf16-surrogates.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js -//#SHA1: 0b74fafedb0961a831dff991c01aba65b15efb80 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-utf16-surrogates.html -// With the twist that we specifically test for Node.js error codes - -if (!globalThis.Intl) { - test.skip("missing Intl"); -} - -const bad = [ - { - encoding: "utf-16le", - input: [0x00, 0xd8], - expected: "\uFFFD", - name: "lone surrogate lead", - }, - { - encoding: "utf-16le", - input: [0x00, 0xdc], - expected: "\uFFFD", - name: "lone surrogate trail", - }, - { - encoding: "utf-16le", - input: [0x00, 0xd8, 0x00, 0x00], - expected: "\uFFFD\u0000", - name: "unmatched surrogate lead", - }, - { - encoding: "utf-16le", - input: [0x00, 0xdc, 0x00, 0x00], - expected: "\uFFFD\u0000", - name: "unmatched surrogate trail", - }, - { - encoding: "utf-16le", - input: [0x00, 0xdc, 0x00, 0xd8], - expected: "\uFFFD\uFFFD", - name: "swapped surrogate pair", - }, -]; - -bad.forEach(t => { - test(`TextDecoder with fatal option throws for ${t.name}`, () => { - expect(() => { - new TextDecoder(t.encoding, { fatal: true }).decode(new Uint8Array(t.input)); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js diff --git a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-passive.test.js b/test/js/node/test/parallel/whatwg-events-add-event-listener-options-passive.test.js deleted file mode 100644 index a918709441..0000000000 --- a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-passive.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-whatwg-events-add-event-listener-options-passive.js -//#SHA1: e3c00da24b307d0e8466611bee33f70db48ad39c -//----------------- -"use strict"; - -// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html -// in order to define the `document` ourselves - -test("AddEventListener options passive", () => { - const document = new EventTarget(); - let supportsPassive = false; - const query_options = { - get passive() { - supportsPassive = true; - return false; - }, - get dummy() { - throw new Error("dummy value getter invoked"); - }, - }; - - document.addEventListener("test_event", null, query_options); - expect(supportsPassive).toBe(true); - - supportsPassive = false; - document.removeEventListener("test_event", null, query_options); - expect(supportsPassive).toBe(false); -}); - -test("testPassiveValue", () => { - function testPassiveValue(optionsValue, expectedDefaultPrevented) { - const document = new EventTarget(); - let defaultPrevented; - function handler(e) { - if (e.defaultPrevented) { - throw new Error("Event prematurely marked defaultPrevented"); - } - e.preventDefault(); - defaultPrevented = e.defaultPrevented; - } - document.addEventListener("test", handler, optionsValue); - // TODO the WHATWG test is more extensive here and tests dispatching on - // document.body, if we ever support getParent we should amend this - const ev = new Event("test", { bubbles: true, cancelable: true }); - const uncanceled = document.dispatchEvent(ev); - - expect(defaultPrevented).toBe(expectedDefaultPrevented); - expect(uncanceled).toBe(!expectedDefaultPrevented); - - document.removeEventListener("test", handler, optionsValue); - } - testPassiveValue(undefined, true); - testPassiveValue({}, true); - testPassiveValue({ passive: false }, true); - - // TODO: passive listeners is still broken - // testPassiveValue({ passive: 1 }, false); - // testPassiveValue({ passive: true }, false); - // testPassiveValue({ passive: 0 }, true); -}); - -//<#END_FILE: test-whatwg-events-add-event-listener-options-passive.js diff --git a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-signal.test.js b/test/js/node/test/parallel/whatwg-events-add-event-listener-options-signal.test.js deleted file mode 100644 index db5c7ef849..0000000000 --- a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-signal.test.js +++ /dev/null @@ -1,176 +0,0 @@ -//#FILE: test-whatwg-events-add-event-listener-options-signal.js -//#SHA1: 2282c25dbc2f2c8bec3b2b97e0a68f3073c75c91 -//----------------- -"use strict"; - -// Manually ported from: wpt@dom/events/AddEventListenerOptions-signal.any.js - -test("Passing an AbortSignal to addEventListener does not prevent removeEventListener", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - et.addEventListener("test", handler, { signal: controller.signal }); - et.dispatchEvent(new Event("test")); - expect(count).toBe(1); - et.dispatchEvent(new Event("test")); - expect(count).toBe(2); - controller.abort(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(2); - // See: https://github.com/nodejs/node/pull/37696 , adding an event listener - // should always return undefined. - expect(et.addEventListener("test", handler, { signal: controller.signal })).toBeUndefined(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(2); -}); - -test("Passing an AbortSignal to addEventListener works with the once flag", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - et.addEventListener("test", handler, { signal: controller.signal }); - et.removeEventListener("test", handler); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Removing a once listener works with a passed signal", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, once: true }; - et.addEventListener("test", handler, options); - controller.abort(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Removing a once listener with options works", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, once: true }; - et.addEventListener("test", handler, options); - et.removeEventListener("test", handler); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Passing an AbortSignal to multiple listeners", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, once: true }; - et.addEventListener("first", handler, options); - et.addEventListener("second", handler, options); - controller.abort(); - et.dispatchEvent(new Event("first")); - et.dispatchEvent(new Event("second")); - expect(count).toBe(0); -}); - -test("Passing an AbortSignal to addEventListener works with the capture flag", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, capture: true }; - et.addEventListener("test", handler, options); - controller.abort(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Aborting from a listener does not call future listeners", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal }; - et.addEventListener( - "test", - () => { - controller.abort(); - }, - options, - ); - et.addEventListener("test", handler, options); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Adding then aborting a listener in another listener does not call it", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - et.addEventListener( - "test", - () => { - et.addEventListener("test", handler, { signal: controller.signal }); - controller.abort(); - }, - { signal: controller.signal }, - ); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Aborting from a nested listener should remove it", () => { - const et = new EventTarget(); - const ac = new AbortController(); - let count = 0; - et.addEventListener( - "foo", - () => { - et.addEventListener( - "foo", - () => { - count++; - if (count > 5) ac.abort(); - et.dispatchEvent(new Event("foo")); - }, - { signal: ac.signal }, - ); - et.dispatchEvent(new Event("foo")); - }, - { once: true }, - ); - et.dispatchEvent(new Event("foo")); - expect(count).toBe(6); -}); - -test("Invalid signal values throw TypeError", () => { - const et = new EventTarget(); - [1, 1n, {}, [], null, true, "hi", Symbol(), () => {}].forEach(signal => { - expect(() => et.addEventListener("foo", () => {}, { signal })).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-events-add-event-listener-options-signal.js diff --git a/test/js/node/test/parallel/whatwg-events-event-constructors.test.js b/test/js/node/test/parallel/whatwg-events-event-constructors.test.js deleted file mode 100644 index 8f08b26d1b..0000000000 --- a/test/js/node/test/parallel/whatwg-events-event-constructors.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-whatwg-events-event-constructors.js -//#SHA1: cf82cf4c0bfbf8bd7cdc9e9328587c2a1266cad8 -//----------------- -"use strict"; - -// Source: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/Event-constructors.any.js#L91-L112 -test("Event constructor with getter options", () => { - const called = []; - const ev = new Event("Xx", { - get cancelable() { - called.push("cancelable"); - return false; - }, - get bubbles() { - called.push("bubbles"); - return true; - }, - get sweet() { - called.push("sweet"); - return "x"; - }, - }); - - expect(called).toEqual(["bubbles", "cancelable"]); - expect(ev.type).toBe("Xx"); - expect(ev.bubbles).toBe(true); - expect(ev.cancelable).toBe(false); - expect(ev.sweet).toBeUndefined(); -}); - -//<#END_FILE: test-whatwg-events-event-constructors.js diff --git a/test/js/node/test/parallel/whatwg-events-eventtarget-this-of-listener.test.js b/test/js/node/test/parallel/whatwg-events-eventtarget-this-of-listener.test.js deleted file mode 100644 index 9de2361e15..0000000000 --- a/test/js/node/test/parallel/whatwg-events-eventtarget-this-of-listener.test.js +++ /dev/null @@ -1,120 +0,0 @@ -//#FILE: test-whatwg-events-eventtarget-this-of-listener.js -//#SHA1: 8325e99e2f04d0fbf14abd12f002da81e4a6c338 -//----------------- -"use strict"; - -// Manually ported from: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/EventTarget-this-of-listener.html - -// Mock document -const document = { - createElement: () => new EventTarget(), - createTextNode: () => new EventTarget(), - createDocumentFragment: () => new EventTarget(), - createComment: () => new EventTarget(), - createProcessingInstruction: () => new EventTarget(), -}; - -test("the this value inside the event listener callback should be the node", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - node.addEventListener("someevent", function () { - ++callCount; - expect(this).toBe(node); - }); - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -test("addEventListener should not require handleEvent to be defined on object listeners", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - const handler = {}; - - node.addEventListener("someevent", handler); - handler.handleEvent = function () { - ++callCount; - expect(this).toBe(handler); - }; - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -test("handleEvent properties added to a function before addEventListener are not reached", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - function handler() { - ++callCount; - expect(this).toBe(node); - } - - handler.handleEvent = () => { - throw new Error("should not call the handleEvent method on a function"); - }; - - node.addEventListener("someevent", handler); - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -test("handleEvent properties added to a function after addEventListener are not reached", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - function handler() { - ++callCount; - expect(this).toBe(node); - } - - node.addEventListener("someevent", handler); - - handler.handleEvent = () => { - throw new Error("should not call the handleEvent method on a function"); - }; - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -//<#END_FILE: test-whatwg-events-eventtarget-this-of-listener.js diff --git a/test/js/node/test/parallel/whatwg-url-canparse.test.js b/test/js/node/test/parallel/whatwg-url-canparse.test.js deleted file mode 100644 index e8f7099fbe..0000000000 --- a/test/js/node/test/parallel/whatwg-url-canparse.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-whatwg-url-canparse.js -//#SHA1: 23d2ea01c951bea491747284dca1abaa17596ff1 -//----------------- -"use strict"; - -const { URL } = require("url"); - -// Note: We're not using internal bindings as per your instructions -// Instead, we'll mock the canParse function - -// Mock the canParse function -const canParse = jest.fn((url, base) => { - try { - new URL(url, base); - return true; - } catch { - return false; - } -}); - -describe("URL.canParse", () => { - test("should not throw when called without a base string", () => { - expect(() => URL.canParse("https://example.org")).not.toThrow(); - expect(URL.canParse("https://example.org")).toBe(true); - expect(canParse("https://example.org")).toBe(true); - }); - - test("should correctly parse URL with base", () => { - // This for-loop is used to test V8 Fast API optimizations - for (let i = 0; i < 100000; i++) { - // This example is used because only parsing the first parameter - // results in an invalid URL. They have to be used together to - // produce truthy value. - expect(URL.canParse("/", "http://n")).toBe(true); - } - }); -}); - -//<#END_FILE: test-whatwg-url-canparse.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-deepequal.test.js b/test/js/node/test/parallel/whatwg-url-custom-deepequal.test.js deleted file mode 100644 index f3bc0a35f7..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-deepequal.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-whatwg-url-custom-deepequal.js -//#SHA1: 57a28f56bb87a00fe2433fabebd4a85cb2da39d0 -//----------------- -"use strict"; -// This tests that the internal flags in URL objects are consistent, as manifest -// through assert libraries. -// See https://github.com/nodejs/node/issues/24211 - -// Tests below are not from WPT. - -test("URL objects are deeply equal", () => { - expect(new URL("./foo", "https://example.com/")).toEqual(new URL("https://example.com/foo")); - - expect(new URL("./foo", "https://user:pass@example.com/")).toEqual(new URL("https://user:pass@example.com/foo")); -}); - -//<#END_FILE: test-whatwg-url-custom-deepequal.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-href-side-effect.test.js b/test/js/node/test/parallel/whatwg-url-custom-href-side-effect.test.js deleted file mode 100644 index e535f59ad2..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-href-side-effect.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-whatwg-url-custom-href-side-effect.js -//#SHA1: c2abb976ed209d25f38bb1ff1e7d8c2110ee51d4 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test("URL href assignment side effect", () => { - const ref = new URL("http://example.com/path"); - const url = new URL("http://example.com/path"); - - expect(() => { - url.href = ""; - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(url).toEqual(ref); -}); - -//<#END_FILE: test-whatwg-url-custom-href-side-effect.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-entries.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-entries.test.js deleted file mode 100644 index 8d7bd72648..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-entries.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-entries.js -//#SHA1: 4ba98b18a2f44b46ac4e6e0ee5179e97083100be -//----------------- -"use strict"; - -// Tests below are not from WPT. -test("URLSearchParams entries", () => { - const params = new URLSearchParams("a=b&c=d"); - const entries = params.entries(); - - expect(typeof entries[Symbol.iterator]).toBe("function"); - expect(entries[Symbol.iterator]()).toBe(entries); - - expect(entries.next()).toEqual({ - value: ["a", "b"], - done: false, - }); - - expect(entries.next()).toEqual({ - value: ["c", "d"], - done: false, - }); - - expect(entries.next()).toEqual({ - value: undefined, - done: true, - }); - - expect(entries.next()).toEqual({ - value: undefined, - done: true, - }); -}); - -test("entries.next() throws with invalid this", () => { - const params = new URLSearchParams("a=b&c=d"); - const entries = params.entries(); - - expect(() => { - entries.next.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("params.entries() throws with invalid this", () => { - expect(() => { - URLSearchParams.prototype.entries.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-entries.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-foreach.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-foreach.test.js deleted file mode 100644 index 233a679a00..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-foreach.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-foreach.js -//#SHA1: affe74306c7fdeb688aadc771c4d7d5b769fc236 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test('URLSearchParams.forEach called with invalid "this"', () => { - const params = new URLSearchParams(); - expect(() => { - params.forEach.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-foreach.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-keys.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-keys.test.js deleted file mode 100644 index 0b4a13ea30..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-keys.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-keys.js -//#SHA1: 06abe929cfe842fcdd80b44cee8a0092358e5fdf -//----------------- -"use strict"; - -// Tests below are not from WPT. - -describe("URLSearchParams keys", () => { - let params; - let keys; - - beforeEach(() => { - params = new URLSearchParams("a=b&c=d"); - keys = params.keys(); - }); - - test("keys iterator is a function and returns self", () => { - expect(typeof keys[Symbol.iterator]).toBe("function"); - expect(keys[Symbol.iterator]()).toBe(keys); - }); - - test("keys iterator returns correct values", () => { - expect(keys.next()).toEqual({ - value: "a", - done: false, - }); - expect(keys.next()).toEqual({ - value: "c", - done: false, - }); - expect(keys.next()).toEqual({ - value: undefined, - done: true, - }); - expect(keys.next()).toEqual({ - value: undefined, - done: true, - }); - }); - - test("keys.next() throws with invalid this", () => { - expect(() => { - keys.next.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test("params.keys() throws with invalid this", () => { - expect(() => { - params.keys.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-keys.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-sort.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-sort.test.js deleted file mode 100644 index 2bbaa7c6a6..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-sort.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-sort.js -//#SHA1: 9a97c952cb19488375ead5a62f11fb579fbee211 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -// TODO(joyeecheung): upstream this to WPT, if possible - even -// just as a test for large inputs. Other implementations may -// have a similar cutoff anyway. - -// Test bottom-up iterative stable merge sort because we only use that -// algorithm to sort > 100 search params. -const tests = [{ input: "", output: [] }]; -const pairs = []; -for (let i = 10; i < 100; i++) { - pairs.push([`a${i}`, "b"]); - tests[0].output.push([`a${i}`, "b"]); -} -tests[0].input = pairs - .sort(() => Math.random() > 0.5) - .map(pair => pair.join("=")) - .join("&"); - -tests.push({ - input: "z=a&=b&c=d", - output: [ - ["", "b"], - ["c", "d"], - ["z", "a"], - ], -}); - -tests.forEach(val => { - test(`Parse and sort: ${val.input}`, () => { - const params = new URLSearchParams(val.input); - let i = 0; - params.sort(); - for (const param of params) { - expect(param).toEqual(val.output[i]); - i++; - } - }); - - test(`URL parse and sort: ${val.input}`, () => { - const url = new URL(`?${val.input}`, "https://example/"); - url.searchParams.sort(); - const params = new URLSearchParams(url.search); - let i = 0; - for (const param of params) { - expect(param).toEqual(val.output[i]); - i++; - } - }); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-sort.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-stringifier.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-stringifier.test.js deleted file mode 100644 index 94028430ba..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-stringifier.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-stringifier.js -//#SHA1: 588663b1cad21e26a4b8e25c0659d204a5d96542 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test("URLSearchParams toString called with invalid this", () => { - const params = new URLSearchParams(); - expect(() => { - params.toString.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -// The URLSearchParams stringifier mutates the base URL using -// different percent-encoding rules than the URL itself. -test("URLSearchParams stringifier mutates base URL with different percent-encoding", () => { - const myUrl = new URL("https://example.org?foo=~bar"); - expect(myUrl.search).toBe("?foo=~bar"); - myUrl.searchParams.sort(); - expect(myUrl.search).toBe("?foo=%7Ebar"); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-stringifier.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-values.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-values.test.js deleted file mode 100644 index 5b7d88801c..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-values.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-values.js -//#SHA1: 7df0ccf30363d589199bb3f71c68e5559e9e4f59 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test("URLSearchParams values() method", () => { - const params = new URLSearchParams("a=b&c=d"); - const values = params.values(); - - expect(typeof values[Symbol.iterator]).toBe("function"); - expect(values[Symbol.iterator]()).toBe(values); - expect(values.next()).toEqual({ - value: "b", - done: false, - }); - expect(values.next()).toEqual({ - value: "d", - done: false, - }); - expect(values.next()).toEqual({ - value: undefined, - done: true, - }); - expect(values.next()).toEqual({ - value: undefined, - done: true, - }); - - expect(() => { - values.next.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => { - params.values.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-values.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams.test.js deleted file mode 100644 index 7de337086e..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams.test.js +++ /dev/null @@ -1,191 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams.js -//#SHA1: 8308ed9fc341a1caaadcf01653bd49b96cebf599 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -const assert = require("assert"); -const fixtures = require("../common/fixtures"); - -const serialized = - "a=a&a=1&a=true&a=undefined&a=null&a=%EF%BF%BD" + - "&a=%EF%BF%BD&a=%F0%9F%98%80&a=%EF%BF%BD%EF%BF%BD" + - "&a=%5Bobject+Object%5D"; -const values = ["a", 1, true, undefined, null, "\uD83D", "\uDE00", "\uD83D\uDE00", "\uDE00\uD83D", {}]; -const normalizedValues = [ - "a", - "1", - "true", - "undefined", - "null", - "\uFFFD", - "\uFFFD", - "\uD83D\uDE00", - "\uFFFD\uFFFD", - "[object Object]", -]; - -describe("WHATWG URL Custom SearchParams", () => { - let m, sp; - - beforeEach(() => { - m = new URL("http://example.org"); - sp = m.searchParams; - }); - - it("should not modify own symbols when accessing searchParams", () => { - const ownSymbolsBeforeGetterAccess = Object.getOwnPropertySymbols(m); - expect(sp).toBeDefined(); - expect(Object.getOwnPropertySymbols(m)).toEqual(ownSymbolsBeforeGetterAccess); - }); - - it("should initialize with empty search params", () => { - expect(sp.toString()).toBe(""); - expect(m.search).toBe(""); - }); - - it("should handle setting and deleting search params", () => { - expect(sp.has("a")).toBe(false); - values.forEach(i => sp.set("a", i)); - expect(sp.has("a")).toBe(true); - expect(sp.get("a")).toBe("[object Object]"); - sp.delete("a"); - expect(sp.has("a")).toBe(false); - }); - - it("should handle appending search params", () => { - m.search = ""; - expect(sp.toString()).toBe(""); - - values.forEach(i => sp.append("a", i)); - expect(sp.has("a")).toBe(true); - expect(sp.getAll("a").length).toBe(values.length); - expect(sp.get("a")).toBe("a"); - - expect(sp.toString()).toBe(serialized); - expect(m.search).toBe(`?${serialized}`); - }); - - it("should update URL components when modifying search params", () => { - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - expect(m.href).toBe(`http://example.org/?${serialized}`); - expect(m.toString()).toBe(`http://example.org/?${serialized}`); - expect(m.toJSON()).toBe(`http://example.org/?${serialized}`); - }); - - it("should clear search params when setting href or search", () => { - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - m.href = "http://example.org"; - expect(m.href).toBe("http://example.org/"); - expect(sp.size).toBe(0); - - values.forEach(i => sp.append("a", i)); - m.search = ""; - expect(m.href).toBe("http://example.org/"); - expect(sp.size).toBe(0); - }); - - it("should update URL components when modifying pathname or hash", () => { - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - m.pathname = "/test"; - expect(m.href).toBe(`http://example.org/test?${serialized}`); - m.pathname = ""; - - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - m.hash = "#test"; - expect(m.href).toBe(`http://example.org/?${serialized}#test`); - m.hash = ""; - }); - - it("should have correct iteration behavior", () => { - expect(sp[Symbol.iterator]).toBe(sp.entries); - - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - - let n = 0; - for (const [key, val] of sp) { - expect(key).toBe("a"); - expect(val).toBe(normalizedValues[n]); - n++; - } - - n = 0; - for (const key of sp.keys()) { - expect(key).toBe("a"); - n++; - } - - n = 0; - for (const val of sp.values()) { - expect(val).toBe(normalizedValues[n]); - n++; - } - - n = 0; - sp.forEach(function (val, key, obj) { - expect(this).toBeUndefined(); - expect(key).toBe("a"); - expect(val).toBe(normalizedValues[n]); - expect(obj).toBe(sp); - n++; - }); - - sp.forEach(function () { - expect(this).toBe(m); - }, m); - }); - - it("should throw for invalid forEach arguments", () => { - expect(() => sp.forEach()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - expect(() => sp.forEach(1)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); - - it("should handle setting search directly", () => { - m.search = "?a=a&b=b"; - expect(sp.toString()).toBe("a=a&b=b"); - }); - - it("should pass URL search params tests", () => { - const tests = require(fixtures.path("url-searchparams.js")); - - for (const [input, expected, parsed] of tests) { - if (input[0] !== "?") { - const sp = new URLSearchParams(input); - expect(String(sp)).toBe(expected); - expect(Array.from(sp)).toEqual(parsed); - - m.search = input; - expect(String(m.searchParams)).toBe(expected); - expect(Array.from(m.searchParams)).toEqual(parsed); - } - - { - const sp = new URLSearchParams(`?${input}`); - expect(String(sp)).toBe(expected); - expect(Array.from(sp)).toEqual(parsed); - - m.search = `?${input}`; - expect(String(m.searchParams)).toBe(expected); - expect(Array.from(m.searchParams)).toEqual(parsed); - } - } - }); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams.js diff --git a/test/js/node/test/parallel/whatwg-url-override-hostname.test.js b/test/js/node/test/parallel/whatwg-url-override-hostname.test.js deleted file mode 100644 index 876de04206..0000000000 --- a/test/js/node/test/parallel/whatwg-url-override-hostname.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-whatwg-url-override-hostname.js -//#SHA1: 22f2c5e784a47cc59c7f0b3229a68f4bbcfeff3e -//----------------- -"use strict"; - -test("URL with overridden hostname getter", () => { - const url = new (class extends URL { - get hostname() { - return "bar.com"; - } - })("http://foo.com/"); - - expect(url.href).toBe("http://foo.com/"); - expect(url.toString()).toBe("http://foo.com/"); - expect(url.toJSON()).toBe("http://foo.com/"); - expect(url.hash).toBe(""); - expect(url.host).toBe("foo.com"); - expect(url.hostname).toBe("bar.com"); - expect(url.origin).toBe("http://foo.com"); - expect(url.password).toBe(""); - expect(url.protocol).toBe("http:"); - expect(url.username).toBe(""); - expect(url.search).toBe(""); - expect(url.searchParams.toString()).toBe(""); -}); - -//<#END_FILE: test-whatwg-url-override-hostname.js diff --git a/test/js/node/test/parallel/worker-arraybuffer-zerofill.test.js b/test/js/node/test/parallel/worker-arraybuffer-zerofill.test.js deleted file mode 100644 index 7f9380841c..0000000000 --- a/test/js/node/test/parallel/worker-arraybuffer-zerofill.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-worker-arraybuffer-zerofill.js -//#SHA1: 3fd8cc412e658cb491c61d2323ff3a45af4d6dd1 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Make sure that allocating uninitialized ArrayBuffers in one thread does not -// affect the zero-initialization in other threads. - -test("zero-initialization in other threads", done => { - const w = new Worker( - ` - const { parentPort } = require('worker_threads'); - - function post() { - const uint32array = new Uint32Array(64); - parentPort.postMessage(uint32array.reduce((a, b) => a + b)); - } - - setInterval(post, 0); - `, - { eval: true }, - ); - - function allocBuffers() { - Buffer.allocUnsafe(32 * 1024 * 1024); - } - - const interval = setInterval(allocBuffers, 0); - - let messages = 0; - w.on("message", sum => { - expect(sum).toBe(0); - if (messages++ === 100) { - clearInterval(interval); - w.terminate(); - done(); - } - }); -}); - -//<#END_FILE: test-worker-arraybuffer-zerofill.js diff --git a/test/js/node/test/parallel/worker-cjs-workerdata.test.js b/test/js/node/test/parallel/worker-cjs-workerdata.test.js deleted file mode 100644 index 0c0158346e..0000000000 --- a/test/js/node/test/parallel/worker-cjs-workerdata.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-worker-cjs-workerdata.js -//#SHA1: 8e5d70084de66c757d227df54612de48d1048ad3 -//----------------- -"use strict"; -const fixtures = require("../common/fixtures"); -const { Worker } = require("worker_threads"); - -const workerData = "Hello from main thread"; - -test("Worker with CJS module and workerData", done => { - const worker = new Worker(fixtures.path("worker-data.cjs"), { - workerData, - }); - - worker.on("message", message => { - expect(message).toBe(workerData); - done(); - }); -}); - -//<#END_FILE: test-worker-cjs-workerdata.js diff --git a/test/js/node/test/parallel/worker-cleanexit-with-js.test.js b/test/js/node/test/parallel/worker-cleanexit-with-js.test.js deleted file mode 100644 index 9e9acb2f4b..0000000000 --- a/test/js/node/test/parallel/worker-cleanexit-with-js.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-worker-cleanexit-with-js.js -//#SHA1: f47fd2ea6e4a0adf79207268baf613eb071e493a -//----------------- -"use strict"; - -// Harden the thread interactions on the exit path. -// Ensure workers are able to bail out safe at -// arbitrary execution points. By running a lot of -// JS code in a tight loop, the expectation -// is that those will be at various control flow points -// preferably in the JS land. - -const { Worker } = require("worker_threads"); - -test("Workers can bail out safely at arbitrary execution points", done => { - const code = - "setInterval(() => {" + - "require('v8').deserialize(require('v8').serialize({ foo: 'bar' }));" + - "require('vm').runInThisContext('x = \"foo\";');" + - "eval('const y = \"vm\";');}, 10);"; - - for (let i = 0; i < 9; i++) { - new Worker(code, { eval: true }); - } - - const lastWorker = new Worker(code, { eval: true }); - lastWorker.on("online", () => { - expect(true).toBe(true); // Ensure the worker came online - process.exit(0); - done(); - }); -}); - -//<#END_FILE: test-worker-cleanexit-with-js.js diff --git a/test/js/node/test/parallel/worker-cleanexit-with-moduleload.test.js b/test/js/node/test/parallel/worker-cleanexit-with-moduleload.test.js deleted file mode 100644 index ffad2cb95e..0000000000 --- a/test/js/node/test/parallel/worker-cleanexit-with-moduleload.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-worker-cleanexit-with-moduleload.js -//#SHA1: cdaed88b3a0ebbc07619e10f35ea0c62e5134b63 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Harden the thread interactions on the exit path. -// Ensure workers are able to bail out safe at -// arbitrary execution points. By using a number of -// internal modules as load candidates, the expectation -// is that those will be at various control flow points -// preferably in the C++ land. - -const modules = ["fs", "assert", "async_hooks", "buffer", "child_process", "net", "http", "os", "path", "v8", "vm"]; - -if (process.versions.openssl) { - modules.push("https"); -} - -test("Workers can exit cleanly while loading modules", done => { - for (let i = 0; i < 10; i++) { - new Worker( - `const modules = [${modules.map(m => `'${m}'`)}];` + - "modules.forEach((module) => {" + - "const m = require(module);" + - "});", - { eval: true }, - ); - } - - // Allow workers to go live. - setTimeout(() => { - done(); - }, 200); -}, 300); // Set timeout to 300ms to allow for the 200ms delay - -//<#END_FILE: test-worker-cleanexit-with-moduleload.js diff --git a/test/js/node/test/parallel/worker-esmodule.test.js b/test/js/node/test/parallel/worker-esmodule.test.js deleted file mode 100644 index e08d58041a..0000000000 --- a/test/js/node/test/parallel/worker-esmodule.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-worker-esmodule.js -//#SHA1: a40c6a55aa2fe45203bec4808e0d53efed2fa4e4 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const { Worker } = require("worker_threads"); - -test("Worker can load ES module", () => { - const w = new Worker(fixtures.path("worker-script.mjs")); - - return new Promise(resolve => { - w.on("message", message => { - expect(message).toBe("Hello, world!"); - resolve(); - }); - }); -}); - -//<#END_FILE: test-worker-esmodule.js diff --git a/test/js/node/test/parallel/worker-invalid-workerdata.test.js b/test/js/node/test/parallel/worker-invalid-workerdata.test.js deleted file mode 100644 index 1776a6d26c..0000000000 --- a/test/js/node/test/parallel/worker-invalid-workerdata.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-worker-invalid-workerdata.js -//#SHA1: 2e1989d95e34d3603c30290e012335261767ae90 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// This tests verifies that failing to serialize workerData does not keep -// the process alive. -// Refs: https://github.com/nodejs/node/issues/22736 - -test("Worker creation with unserializable workerData throws DataCloneError", () => { - expect(() => { - new Worker("./worker.js", { - workerData: { fn: () => {} }, - }); - }).toThrow( - expect.objectContaining({ - name: "DataCloneError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-worker-invalid-workerdata.js diff --git a/test/js/node/test/parallel/worker-memory.test.js b/test/js/node/test/parallel/worker-memory.test.js deleted file mode 100644 index d18eb5bccd..0000000000 --- a/test/js/node/test/parallel/worker-memory.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-worker-memory.js -//#SHA1: e425b7d8f32d04fae4a6e0c697a78aeae4de2f60 -//----------------- -"use strict"; - -const util = require("util"); -const { Worker } = require("worker_threads"); -const os = require("os"); - -if (process.platform === "os400") { - test.skip("On IBMi, the rss memory always returns zero"); -} - -let numWorkers = +process.env.JOBS || os.availableParallelism(); -if (numWorkers > 20) { - // Cap the number of workers at 20 (as an even divisor of 60 used as - // the total number of workers started) otherwise the test fails on - // machines with high core counts. - numWorkers = 20; -} - -// Verify that a Worker's memory isn't kept in memory after the thread finishes. - -function run(n, done) { - console.log(`run() called with n=${n} (numWorkers=${numWorkers})`); - if (n <= 0) return done(); - const worker = new Worker("require('worker_threads').parentPort.postMessage(2 + 2)", { eval: true }); - worker.on("message", value => { - expect(value).toBe(4); - }); - worker.on("exit", () => { - run(n - 1, done); - }); -} - -test("Worker memory is not kept after thread finishes", async () => { - const startStats = process.memoryUsage(); - let finished = 0; - - const runPromises = Array(numWorkers) - .fill() - .map( - () => - new Promise(resolve => { - run(60 / numWorkers, () => { - console.log(`done() called (finished=${finished})`); - if (++finished === numWorkers) { - const finishStats = process.memoryUsage(); - // A typical value for this ratio would be ~1.15. - // 5 as a upper limit is generous, but the main point is that we - // don't have the memory of 50 Isolates/Node.js environments just lying - // around somewhere. - expect(finishStats.rss / startStats.rss).toBeLessThan(5); - } - resolve(); - }); - }), - ); - - await Promise.all(runPromises); -}, 30000); // Increase timeout to 30 seconds - -//<#END_FILE: test-worker-memory.js diff --git a/test/js/node/test/parallel/worker-message-channel-sharedarraybuffer.test.js b/test/js/node/test/parallel/worker-message-channel-sharedarraybuffer.test.js deleted file mode 100644 index 088cb1530c..0000000000 --- a/test/js/node/test/parallel/worker-message-channel-sharedarraybuffer.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-worker-message-channel-sharedarraybuffer.js -//#SHA1: 567096de16f1ea1b7a50e53cbdbd47b531af0e2a -//----------------- -// Flags: --expose-gc -"use strict"; - -const { Worker } = require("worker_threads"); - -test("SharedArrayBuffer can be shared between main thread and worker", async () => { - const sharedArrayBuffer = new SharedArrayBuffer(12); - const local = Buffer.from(sharedArrayBuffer); - - const w = new Worker( - ` - const { parentPort } = require('worker_threads'); - parentPort.on('message', ({ sharedArrayBuffer }) => { - const local = Buffer.from(sharedArrayBuffer); - local.write('world!', 6); - parentPort.postMessage('written!'); - }); - `, - { eval: true }, - ); - - const messagePromise = new Promise(resolve => { - w.on("message", resolve); - }); - - w.postMessage({ sharedArrayBuffer }); - // This would be a race condition if the memory regions were overlapping - local.write("Hello "); - - await messagePromise; - - expect(local.toString()).toBe("Hello world!"); - - global.gc(); - await w.terminate(); -}); - -//<#END_FILE: test-worker-message-channel-sharedarraybuffer.js diff --git a/test/js/node/test/parallel/worker-message-port-transfer-terminate.test.js b/test/js/node/test/parallel/worker-message-port-transfer-terminate.test.js deleted file mode 100644 index c00d6d2251..0000000000 --- a/test/js/node/test/parallel/worker-message-port-transfer-terminate.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-worker-message-port-transfer-terminate.js -//#SHA1: ca776ac07835f8e6306ba671531b8e5a73e25f27 -//----------------- -"use strict"; - -const { Worker, MessageChannel } = require("worker_threads"); - -// Check the interaction of calling .terminate() while transferring -// MessagePort objects; in particular, that it does not crash the process. - -test("Worker termination while transferring MessagePort objects", done => { - let completedIterations = 0; - - for (let i = 0; i < 10; ++i) { - const w = new Worker("require('worker_threads').parentPort.on('message', () => {})", { eval: true }); - - setImmediate(() => { - const port = new MessageChannel().port1; - w.postMessage({ port }, [port]); - w.terminate().then(() => { - completedIterations++; - if (completedIterations === 10) { - done(); - } - }); - }); - } -}); - -//<#END_FILE: test-worker-message-port-transfer-terminate.js diff --git a/test/js/node/test/parallel/worker-mjs-workerdata.test.js b/test/js/node/test/parallel/worker-mjs-workerdata.test.js deleted file mode 100644 index d3bfe6d442..0000000000 --- a/test/js/node/test/parallel/worker-mjs-workerdata.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-worker-mjs-workerdata.js -//#SHA1: c4df37cd769b399dd8245d29bed7f385f1adb472 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const { Worker } = require("worker_threads"); - -const workerData = "Hello from main thread"; - -test("Worker with workerData in MJS", done => { - const worker = new Worker(fixtures.path("worker-data.mjs"), { - workerData, - }); - - worker.on("message", message => { - expect(message).toBe(workerData); - done(); - }); -}); - -//<#END_FILE: test-worker-mjs-workerdata.js diff --git a/test/js/node/test/parallel/worker-nested-on-process-exit.test.js b/test/js/node/test/parallel/worker-nested-on-process-exit.test.js deleted file mode 100644 index d198efaf1f..0000000000 --- a/test/js/node/test/parallel/worker-nested-on-process-exit.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-worker-nested-on-process-exit.js -//#SHA1: a76202f79766e7ca3f39552cc00b4f31e8f38a56 -//----------------- -"use strict"; -const { Worker, workerData } = require("worker_threads"); - -// Test that 'exit' events for nested Workers are not received when a Worker -// terminates itself through process.exit(). - -if (workerData === null) { - test("nested worker exit events", () => { - const nestedWorkerExitCounter = new Int32Array(new SharedArrayBuffer(4)); - const w = new Worker(__filename, { workerData: nestedWorkerExitCounter }); - - return new Promise(resolve => { - w.on("exit", () => { - expect(nestedWorkerExitCounter[0]).toBe(0); - resolve(); - }); - }); - }); -} else { - const nestedWorker = new Worker("setInterval(() => {}, 100)", { eval: true }); - // The counter should never be increased here. - nestedWorker.on("exit", () => workerData[0]++); - nestedWorker.on("online", () => process.exit()); -} - -//<#END_FILE: test-worker-nested-on-process-exit.js diff --git a/test/js/node/test/parallel/worker-nested-uncaught.test.js b/test/js/node/test/parallel/worker-nested-uncaught.test.js deleted file mode 100644 index 050f33a797..0000000000 --- a/test/js/node/test/parallel/worker-nested-uncaught.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-worker-nested-uncaught.js -//#SHA1: 948558ccf744615abbd0df028a83b36d36ad7aff -//----------------- -"use strict"; -const { Worker } = require("worker_threads"); - -// Regression test for https://github.com/nodejs/node/issues/34309 - -test("nested worker uncaught error", done => { - const w = new Worker( - `const { Worker } = require('worker_threads'); - new Worker("throw new Error('uncaught')", { eval:true })`, - { eval: true }, - ); - - w.on("error", error => { - expect(error).toEqual( - expect.objectContaining({ - name: "Error", - message: expect.any(String), - }), - ); - done(); - }); -}); - -//<#END_FILE: test-worker-nested-uncaught.js diff --git a/test/js/node/test/parallel/worker-ref-onexit.test.js b/test/js/node/test/parallel/worker-ref-onexit.test.js deleted file mode 100644 index fb360433d4..0000000000 --- a/test/js/node/test/parallel/worker-ref-onexit.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-worker-ref-onexit.js -//#SHA1: b34eb5ff18bd889eb47cd37e696410f428b0b11b -//----------------- -"use strict"; -const { Worker } = require("worker_threads"); - -// Check that worker.unref() makes the 'exit' event not be emitted, if it is -// the only thing we would otherwise be waiting for. - -test("worker.unref() prevents exit event emission", done => { - // Use `setInterval()` to make sure the worker is alive until the end of the - // event loop turn. - const w = new Worker("setInterval(() => {}, 100);", { eval: true }); - w.unref(); - - const exitHandler = jest.fn(); - w.on("exit", exitHandler); - - // We need to use a timeout here to allow the event loop to complete - // and ensure the exit event is not called - setTimeout(() => { - expect(exitHandler).not.toHaveBeenCalled(); - done(); - }, 500); -}); - -//<#END_FILE: test-worker-ref-onexit.js diff --git a/test/js/node/test/parallel/worker-relative-path-double-dot.test.js b/test/js/node/test/parallel/worker-relative-path-double-dot.test.js deleted file mode 100644 index daed4389b1..0000000000 --- a/test/js/node/test/parallel/worker-relative-path-double-dot.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-worker-relative-path-double-dot.js -//#SHA1: 2b605c02bfb9cfc2d42533a6a184dfdbd5a6e5b7 -//----------------- -"use strict"; -const path = require("path"); -const { Worker, isMainThread, parentPort } = require("worker_threads"); - -if (isMainThread) { - test("Worker with relative path using double dot", done => { - const cwdName = path.relative("../", "."); - const relativePath = path.relative(".", __filename); - const w = new Worker(path.join("..", cwdName, relativePath)); - - w.on("message", message => { - expect(message).toBe("Hello, world!"); - done(); - }); - }); -} else { - parentPort.postMessage("Hello, world!"); -} - -//<#END_FILE: test-worker-relative-path-double-dot.js diff --git a/test/js/node/test/parallel/worker-relative-path.test.js b/test/js/node/test/parallel/worker-relative-path.test.js deleted file mode 100644 index a6971694c1..0000000000 --- a/test/js/node/test/parallel/worker-relative-path.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-worker-relative-path.js -//#SHA1: 2d69899e96eee7500da11857679bf9f2ec18c259 -//----------------- -"use strict"; -const path = require("path"); -const { Worker, isMainThread, parentPort } = require("worker_threads"); - -if (isMainThread) { - test("Worker can be started with a relative path", done => { - const w = new Worker(`./${path.relative(".", __filename)}`); - w.on("message", message => { - expect(message).toBe("Hello, world!"); - done(); - }); - }); -} else { - parentPort.postMessage("Hello, world!"); -} - -//<#END_FILE: test-worker-relative-path.js diff --git a/test/js/node/test/parallel/worker-sharedarraybuffer-from-worker-thread.test.js b/test/js/node/test/parallel/worker-sharedarraybuffer-from-worker-thread.test.js deleted file mode 100644 index 0a4c29b25e..0000000000 --- a/test/js/node/test/parallel/worker-sharedarraybuffer-from-worker-thread.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-worker-sharedarraybuffer-from-worker-thread.js -//#SHA1: a33c9c03494dc79f7dd926382125f034d8ae4bbc -//----------------- -// Flags: --debug-arraybuffer-allocations -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/28777 -// Make sure that SharedArrayBuffers and transferred ArrayBuffers created in -// Worker threads are accessible after the creating thread ended. - -const { Worker } = require("worker_threads"); - -["ArrayBuffer", "SharedArrayBuffer"].forEach(ctor => { - test(`${ctor} from worker thread`, async () => { - const w = new Worker( - ` - const { parentPort } = require('worker_threads'); - const arrayBuffer = new ${ctor}(4); - parentPort.postMessage( - arrayBuffer, - '${ctor}' === 'SharedArrayBuffer' ? [] : [arrayBuffer]); - `, - { eval: true }, - ); - - let arrayBuffer; - await new Promise(resolve => { - w.once("message", message => { - arrayBuffer = message; - resolve(); - }); - }); - - await new Promise(resolve => { - w.once("exit", () => { - expect(arrayBuffer.constructor.name).toBe(ctor); - const uint8array = new Uint8Array(arrayBuffer); - uint8array[0] = 42; - expect(uint8array).toEqual(new Uint8Array([42, 0, 0, 0])); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-worker-sharedarraybuffer-from-worker-thread.js diff --git a/test/js/node/test/parallel/worker-terminate-http2-respond-with-file.test.js b/test/js/node/test/parallel/worker-terminate-http2-respond-with-file.test.js deleted file mode 100644 index c317604d79..0000000000 --- a/test/js/node/test/parallel/worker-terminate-http2-respond-with-file.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-worker-terminate-http2-respond-with-file.js -//#SHA1: e8ce958ee3283a8ec8c83acc27cc3a02824ebfb7 -//----------------- -"use strict"; - -const http2 = require("http2"); -const makeDuplexPair = require("../common/duplexpair"); -const { Worker, isMainThread } = require("worker_threads"); - -// This is a variant of test-http2-generic-streams-sendfile for checking -// that Workers can be terminated during a .respondWithFile() operation. - -if (isMainThread) { - test("Worker can be terminated during respondWithFile operation", () => { - const worker = new Worker(__filename); - expect(worker).toBeDefined(); - }); -} else { - test("HTTP/2 server responds with file", done => { - const server = http2.createServer(); - server.on("stream", (stream, headers) => { - stream.respondWithFile(process.execPath); // Use a large-ish file. - }); - - const { clientSide, serverSide } = makeDuplexPair(); - server.emit("connection", serverSide); - - const client = http2.connect("http://localhost:80", { - createConnection: () => clientSide, - }); - - const req = client.request(); - - req.on("response", headers => { - expect(headers[":status"]).toBe(200); - }); - - req.on("data", () => { - process.exit(); - done(); - }); - - req.on("end", () => { - done.fail("Request should not end"); - }); - - req.end(); - }); -} - -//<#END_FILE: test-worker-terminate-http2-respond-with-file.js diff --git a/test/js/node/test/parallel/worker-unref-from-message-during-exit.test.js b/test/js/node/test/parallel/worker-unref-from-message-during-exit.test.js deleted file mode 100644 index 338155b933..0000000000 --- a/test/js/node/test/parallel/worker-unref-from-message-during-exit.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-worker-unref-from-message-during-exit.js -//#SHA1: 8555ad4d197dfc269beffcf6b2a3940df8a41659 -//----------------- -"use strict"; -const { Worker } = require("worker_threads"); - -// This used to crash because the `.unref()` was unexpected while the Worker -// was exiting. - -test("Worker unref from message during exit", async () => { - const w = new Worker( - ` -require('worker_threads').parentPort.postMessage({}); -`, - { eval: true }, - ); - - const messagePromise = new Promise(resolve => { - w.on("message", () => { - w.unref(); - resolve(); - }); - }); - - // Wait a bit so that the 'message' event is emitted while the Worker exits. - await Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); - - await expect(messagePromise).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-worker-unref-from-message-during-exit.js diff --git a/test/js/node/test/parallel/worker-workerdata-sharedarraybuffer.test.js b/test/js/node/test/parallel/worker-workerdata-sharedarraybuffer.test.js deleted file mode 100644 index 4ff64a3328..0000000000 --- a/test/js/node/test/parallel/worker-workerdata-sharedarraybuffer.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-worker-workerdata-sharedarraybuffer.js -//#SHA1: 2737167494699dbfe6986e4b879ecdeafd31a8a8 -//----------------- -// Flags: --expose-gc -"use strict"; - -const { Worker } = require("worker_threads"); - -test("SharedArrayBuffer can be passed via workerData and modified", async () => { - const sharedArrayBuffer = new SharedArrayBuffer(12); - const local = Buffer.from(sharedArrayBuffer); - - const w = new Worker( - ` - const { parentPort, workerData } = require('worker_threads'); - const local = Buffer.from(workerData.sharedArrayBuffer); - - parentPort.on('message', () => { - local.write('world!', 6); - parentPort.postMessage('written!'); - }); - `, - { - eval: true, - workerData: { sharedArrayBuffer }, - }, - ); - - const messagePromise = new Promise(resolve => { - w.on("message", resolve); - }); - - w.postMessage({}); - // This would be a race condition if the memory regions were overlapping - local.write("Hello "); - - await messagePromise; - - expect(local.toString()).toBe("Hello world!"); - - global.gc(); - await w.terminate(); -}); - -//<#END_FILE: test-worker-workerdata-sharedarraybuffer.js diff --git a/test/js/node/test/parallel/worker.test.js b/test/js/node/test/parallel/worker.test.js deleted file mode 100644 index 7328a65dec..0000000000 --- a/test/js/node/test/parallel/worker.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-worker.js -//#SHA1: 830c4e2ce132228fe7d49fd760271deed934db23 -//----------------- -"use strict"; - -const { Worker, isMainThread, parentPort } = require("worker_threads"); - -const kTestString = "Hello, world!"; - -if (isMainThread) { - test("Worker thread communication", done => { - const w = new Worker(__filename); - w.on("message", message => { - expect(message).toBe(kTestString); - done(); - }); - }); -} else { - setImmediate(() => { - process.nextTick(() => { - parentPort.postMessage(kTestString); - }); - }); -} - -//<#END_FILE: test-worker.js diff --git a/test/js/node/test/parallel/wrap-js-stream-read-stop.test.js b/test/js/node/test/parallel/wrap-js-stream-read-stop.test.js deleted file mode 100644 index 6efe3f5df1..0000000000 --- a/test/js/node/test/parallel/wrap-js-stream-read-stop.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-wrap-js-stream-read-stop.js -//#SHA1: 53e905da53ef7a130579672b04ddd6e65b7cb8d5 -//----------------- -"use strict"; - -const Stream = require("stream"); - -class FakeStream extends Stream { - constructor() { - super(); - this._paused = false; - } - - pause() { - this._paused = true; - } - - resume() { - this._paused = false; - } - - isPaused() { - return this._paused; - } -} - -class WrapStream { - constructor(stream) { - this.stream = stream; - this.stream.resume(); - } - - readStop() { - this.stream.pause(); - return 0; - } -} - -describe("WrapStream", () => { - let fakeStreamObj; - let wrappedStream; - - beforeEach(() => { - fakeStreamObj = new FakeStream(); - wrappedStream = new WrapStream(fakeStreamObj); - }); - - test("Resume by wrapped stream upon construction", () => { - expect(fakeStreamObj.isPaused()).toBe(false); - }); - - test("Pause and resume fakeStreamObj", () => { - fakeStreamObj.pause(); - expect(fakeStreamObj.isPaused()).toBe(true); - - fakeStreamObj.resume(); - expect(fakeStreamObj.isPaused()).toBe(false); - }); - - test("readStop method", () => { - expect(wrappedStream.readStop()).toBe(0); - expect(fakeStreamObj.isPaused()).toBe(true); - }); -}); - -//<#END_FILE: test-wrap-js-stream-read-stop.js diff --git a/test/js/node/test/parallel/zlib-brotli-16gb.test.js b/test/js/node/test/parallel/zlib-brotli-16gb.test.js deleted file mode 100644 index edefbaafe9..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-16gb.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-zlib-brotli-16GB.js -//#SHA1: 0c5e79f3713fdd0597dda7e531484a0ef3170578 -//----------------- -"use strict"; - -const { createBrotliDecompress } = require("node:zlib"); -const { getDefaultHighWaterMark } = require("stream"); - -// This tiny HEX string is a 16GB file. -// This test verifies that the stream actually stops. -const content = - "cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdf" + - "fe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8de" + - "fff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074e" + - "ffff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500ba" + - "f7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200d" + - "dfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180" + - "eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b0004" + - "0f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800" + - "a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c0" + - "0d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c16" + - "00e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0" + - "b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f011087" + - "0500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c" + - "30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4" + - "610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e" + - "2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300" + - "715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe098" + - "0382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04" + - "401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f0" + - "2200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f" + - "0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19" + - "f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff0" + - "4f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff" + - "82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3f" + - "fc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1" + - "ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bff3f"; - -test("the test", async () => { - const buf = Buffer.from(content, "hex"); - - const decoder = createBrotliDecompress(); - decoder.end(buf); - - const { promise, resolve, reject } = Promise.withResolvers(); - - setTimeout(() => { - try { - expect(decoder._readableState.buffer.length).toBe(getDefaultHighWaterMark() / (16 * 1024)); - resolve(); - } catch (e) { - reject(e); - } - }, 500); - await promise; -}); - -//#FILE: test-zlib-brotli-16GB.js -//----------------- diff --git a/test/js/node/test/parallel/zlib-brotli-flush.test.js b/test/js/node/test/parallel/zlib-brotli-flush.test.js deleted file mode 100644 index 77723ad78f..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-flush.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-zlib-brotli-flush.js -//#SHA1: b0a953be98db6dd674668bfd6cffa3e283144ad1 -//----------------- -"use strict"; -const zlib = require("zlib"); -const fs = require("fs"); -const path = require("path"); - -const fixturesPath = path.join(__dirname, "..", "fixtures"); -const file = fs.readFileSync(path.join(fixturesPath, "person.jpg")); -const chunkSize = 16; - -test("BrotliCompress flush should produce expected output", done => { - const deflater = new zlib.BrotliCompress(); - const chunk = file.slice(0, chunkSize); - const expectedFull = Buffer.from("iweA/9j/4AAQSkZJRgABAQEASA==", "base64"); - let actualFull; - - deflater.write(chunk, () => { - deflater.flush(() => { - const bufs = []; - let buf; - while ((buf = deflater.read()) !== null) { - bufs.push(buf); - } - actualFull = Buffer.concat(bufs); - expect(actualFull).toEqual(expectedFull); - done(); - }); - }); -}); - -//<#END_FILE: test-zlib-brotli-flush.js diff --git a/test/js/node/test/parallel/zlib-brotli-from-string.test.js b/test/js/node/test/parallel/zlib-brotli-from-string.test.js deleted file mode 100644 index fa861d967b..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-from-string.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-zlib-brotli-from-string.js -//#SHA1: bb4656c195e75f9d49e2bad9e7b1130f571fa68b -//----------------- -"use strict"; -// Test compressing and uncompressing a string with brotli - -const zlib = require("zlib"); - -const inputString = - "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + - "t. Morbi faucibus, purus at gravida dictum, libero arcu " + - "convallis lacus, in commodo libero metus eu nisi. Nullam" + - " commodo, neque nec porta placerat, nisi est fermentum a" + - "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + - "n non diam orci. Proin quis elit turpis. Suspendisse non" + - " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + - "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + - "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; -const compressedString = - "G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB" + - "K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7" + - "wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn" + - "f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1" + - "MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD" + - "mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu" + - "Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5" + - "2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE" + - "KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM="; - -test("brotli compress and decompress string", async () => { - const compressCallback = jest.fn(); - const decompressCallback = jest.fn(); - - await new Promise(resolve => { - zlib.brotliCompress(inputString, (err, buffer) => { - compressCallback(); - expect(inputString.length).toBeGreaterThan(buffer.length); - - zlib.brotliDecompress(buffer, (err, buffer) => { - decompressCallback(); - expect(buffer.toString()).toBe(inputString); - resolve(); - }); - }); - }); - - expect(compressCallback).toHaveBeenCalledTimes(1); - expect(decompressCallback).toHaveBeenCalledTimes(1); -}); - -test("brotli decompress base64 string", async () => { - const decompressCallback = jest.fn(); - - const buffer = Buffer.from(compressedString, "base64"); - await new Promise(resolve => { - zlib.brotliDecompress(buffer, (err, buffer) => { - decompressCallback(); - expect(buffer.toString()).toBe(inputString); - resolve(); - }); - }); - - expect(decompressCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-zlib-brotli-from-string.js diff --git a/test/js/node/test/parallel/zlib-brotli-kmaxlength-rangeerror.test.js b/test/js/node/test/parallel/zlib-brotli-kmaxlength-rangeerror.test.js deleted file mode 100644 index c3cbb9d19b..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-kmaxlength-rangeerror.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-zlib-brotli-kmaxlength-rangeerror.js -//#SHA1: f0d3ad25e8a844b31b7e14ab68a84182fd5f52b7 -//----------------- -"use strict"; - -const util = require("util"); - -// Change kMaxLength for zlib to trigger the error without having to allocate large Buffers. -const buffer = require("buffer"); -const oldkMaxLength = buffer.kMaxLength; -buffer.kMaxLength = 64; -const zlib = require("zlib"); -buffer.kMaxLength = oldkMaxLength; - -// Create a large input buffer -const encoded = Buffer.from("G38A+CXCIrFAIAM=", "base64"); - -test("brotliDecompress throws RangeError for large output (async)", async () => { - await expect(async () => util.promisify(zlib.brotliDecompress)(encoded)).toThrowWithCodeAsync( - RangeError, - "ERR_BUFFER_TOO_LARGE", - ); -}, 1000); - -test("brotliDecompressSync throws RangeError for large output", () => { - expect(() => zlib.brotliDecompressSync(encoded)).toThrowWithCode(RangeError, "ERR_BUFFER_TOO_LARGE"); -}); - -//<#END_FILE: test-zlib-brotli-kmaxlength-rangeerror.js diff --git a/test/js/node/test/parallel/zlib-brotli.test.js b/test/js/node/test/parallel/zlib-brotli.test.js deleted file mode 100644 index d61df3eae3..0000000000 --- a/test/js/node/test/parallel/zlib-brotli.test.js +++ /dev/null @@ -1,101 +0,0 @@ -//#FILE: test-zlib-brotli.js -//#SHA1: 53d893f351dd67279a8561e244183e38864c0c92 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const zlib = require("zlib"); - -// Test some brotli-specific properties of the brotli streams that can not -// be easily covered through expanding zlib-only tests. - -const sampleBuffer = fixtures.readSync("/pss-vectors.json"); - -test("Quality parameter at stream creation", () => { - const sizes = []; - for (let quality = zlib.constants.BROTLI_MIN_QUALITY; quality <= zlib.constants.BROTLI_MAX_QUALITY; quality++) { - const encoded = zlib.brotliCompressSync(sampleBuffer, { - params: { - [zlib.constants.BROTLI_PARAM_QUALITY]: quality, - }, - }); - sizes.push(encoded.length); - } - - // Increasing quality should roughly correspond to decreasing compressed size: - for (let i = 0; i < sizes.length - 1; i++) { - expect(sizes[i + 1]).toBeLessThanOrEqual(sizes[i] * 1.05); // 5 % margin of error. - } - expect(sizes[0]).toBeGreaterThan(sizes[sizes.length - 1]); -}); - -test("Setting out-of-bounds option values or keys", () => { - expect(() => { - zlib.createBrotliCompress({ - params: { - 10000: 0, - }, - }); - }).toThrow( - expect.objectContaining({ - code: "ERR_BROTLI_INVALID_PARAM", - name: "RangeError", - message: expect.any(String), - }), - ); - - // Test that accidentally using duplicate keys fails. - expect(() => { - zlib.createBrotliCompress({ - params: { - 0: 0, - "00": 0, - }, - }); - }).toThrow( - expect.objectContaining({ - code: "ERR_BROTLI_INVALID_PARAM", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => { - zlib.createBrotliCompress({ - params: { - // This is a boolean flag - [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: 42, - }, - }); - }).toThrow( - expect.objectContaining({ - code: "ERR_ZLIB_INITIALIZATION_FAILED", - name: "Error", - message: expect.any(String), - }), - ); -}); - -test("Options.flush range", () => { - expect(() => { - zlib.brotliCompressSync("", { flush: zlib.constants.Z_FINISH }); - }).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => { - zlib.brotliCompressSync("", { finishFlush: zlib.constants.Z_FINISH }); - }).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-brotli.js diff --git a/test/js/node/test/parallel/zlib-close-after-error.test.js b/test/js/node/test/parallel/zlib-close-after-error.test.js deleted file mode 100644 index f336f5a950..0000000000 --- a/test/js/node/test/parallel/zlib-close-after-error.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-zlib-close-after-error.js -//#SHA1: 1f561376d8af1a6b21f9f9abf6813a20cde33be6 -//----------------- -"use strict"; -// https://github.com/nodejs/node/issues/6034 - -const zlib = require("zlib"); - -test("zlib close after error", done => { - const decompress = zlib.createGunzip(15); - - decompress.on("error", err => { - expect(decompress._closed).toBe(true); - decompress.close(); - done(); - }); - - expect(decompress._closed).toBe(false); - decompress.write("something invalid"); -}); - -//<#END_FILE: test-zlib-close-after-error.js diff --git a/test/js/node/test/parallel/zlib-close-in-ondata.test.js b/test/js/node/test/parallel/zlib-close-in-ondata.test.js deleted file mode 100644 index e4449d57d4..0000000000 --- a/test/js/node/test/parallel/zlib-close-in-ondata.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-zlib-close-in-ondata.js -//#SHA1: 8218c0461dd0733882aaf37688e3b32b164e3535 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib stream closes in ondata event", done => { - const ts = zlib.createGzip(); - const buf = Buffer.alloc(1024 * 1024 * 20); - - ts.on( - "data", - jest.fn(() => { - ts.close(); - done(); - }), - ); - - ts.end(buf); -}); - -//<#END_FILE: test-zlib-close-in-ondata.js diff --git a/test/js/node/test/parallel/zlib-const.test.js b/test/js/node/test/parallel/zlib-const.test.js deleted file mode 100644 index a81535e446..0000000000 --- a/test/js/node/test/parallel/zlib-const.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-zlib-const.js -//#SHA1: d85ad9e395d5781dbe5bf05e5514104bd9503be8 -//----------------- -const zlib = require("zlib"); - -test("zlib constants and codes are immutable", () => { - // Test Z_OK constant - expect(zlib.constants.Z_OK).toBe(0); - zlib.constants.Z_OK = 1; - expect(zlib.constants.Z_OK).toBe(0); - - // Test Z_OK code - expect(zlib.codes.Z_OK).toBe(0); - zlib.codes.Z_OK = 1; - expect(zlib.codes.Z_OK).toBe(0); - zlib.codes = { Z_OK: 1 }; - expect(zlib.codes.Z_OK).toBe(0); - - // Test if zlib.codes is frozen - expect(Object.isFrozen(zlib.codes)).toBe(true); -}); - -//<#END_FILE: test-zlib-const.js diff --git a/test/js/node/test/parallel/zlib-convenience-methods.test.js b/test/js/node/test/parallel/zlib-convenience-methods.test.js deleted file mode 100644 index f1c853be23..0000000000 --- a/test/js/node/test/parallel/zlib-convenience-methods.test.js +++ /dev/null @@ -1,96 +0,0 @@ -//#FILE: test-zlib-convenience-methods.js -//#SHA1: e215a52650eaa95999dbb7d77f5b03376cdc673b -//----------------- -"use strict"; - -const zlib = require("zlib"); -const util = require("util"); -const { getBufferSources } = require("../common"); - -// Must be a multiple of 4 characters in total to test all ArrayBufferView -// types. -const expectStr = "blah".repeat(8); -const expectBuf = Buffer.from(expectStr); - -const opts = { - level: 9, - chunkSize: 1024, -}; - -const optsInfo = { - info: true, -}; - -const methodPairs = [ - ["gzip", "gunzip", "Gzip", "Gunzip"], - ["gzip", "unzip", "Gzip", "Unzip"], - ["deflate", "inflate", "Deflate", "Inflate"], - ["deflateRaw", "inflateRaw", "DeflateRaw", "InflateRaw"], - ["brotliCompress", "brotliDecompress", "BrotliCompress", "BrotliDecompress"], -]; - -const testCases = [ - ["string", expectStr], - ["Buffer", expectBuf], - ...getBufferSources(expectBuf).map(obj => [obj[Symbol.toStringTag], obj]), -]; - -describe("zlib convenience methods", () => { - describe.each(testCases)("%s input", (type, expected) => { - for (const [compress, decompress, CompressClass, DecompressClass] of methodPairs) { - describe(`${compress}/${decompress}`, async () => { - test("Async with options", async () => { - const c = await util.promisify(zlib[compress])(expected, opts); - const d = await util.promisify(zlib[decompress])(c, opts); - expect(d.toString()).toEqual(expectStr); - }); - - test("Async without options", async () => { - const c = await util.promisify(zlib[compress])(expected); - const d = await util.promisify(zlib[decompress])(c); - expect(d.toString()).toEqual(expectStr); - }); - - test("Async with info option", async () => { - const c = await util.promisify(zlib[compress])(expected, optsInfo); - expect(c.engine).toBeInstanceOf(zlib[CompressClass]); - const d = await util.promisify(zlib[decompress])(c.buffer, optsInfo); - expect(d.engine).toBeInstanceOf(zlib[DecompressClass]); - expect(d.buffer.toString()).toEqual(expectStr); - }); - - test("Sync with options", () => { - const c = zlib[compress + "Sync"](expected, opts); - const d = zlib[decompress + "Sync"](c, opts); - expect(d.toString()).toEqual(expectStr); - }); - - test("Sync without options", async () => { - const c = zlib[compress + "Sync"](expected); - const d = zlib[decompress + "Sync"](c); - expect(d.toString()).toEqual(expectStr); - }); - - test("Sync with info option", () => { - const c = zlib[compress + "Sync"](expected, optsInfo); - expect(c.engine).toBeInstanceOf(zlib[CompressClass]); - const d = zlib[decompress + "Sync"](c.buffer, optsInfo); - expect(d.engine).toBeInstanceOf(zlib[DecompressClass]); - expect(d.buffer.toString()).toEqual(expectStr); - }); - }); - } - }); - - test("throws error when callback is not provided", () => { - expect(() => zlib.gzip("abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "callback" argument must be of type function'), - }), - ); - }); -}); - -//<#END_FILE: test-zlib-convenience-methods.js diff --git a/test/js/node/test/parallel/zlib-crc32.test.js b/test/js/node/test/parallel/zlib-crc32.test.js deleted file mode 100644 index 5c1e51b63a..0000000000 --- a/test/js/node/test/parallel/zlib-crc32.test.js +++ /dev/null @@ -1,249 +0,0 @@ -//#FILE: test-zlib-crc32.js -//#SHA1: a46ac98c5c568eec892b62f9da1effc368081b07 -//----------------- -"use strict"; - -const zlib = require("zlib"); -const { Buffer } = require("buffer"); - -// The following test data comes from -// https://github.com/zlib-ng/zlib-ng/blob/5401b24/test/test_crc32.cc -// test_crc32.cc -- crc32 unit test -// Copyright (C) 2019-2021 IBM Corporation -// Authors: Rogerio Alves -// Matheus Castanho -// For conditions of distribution and use, see copyright notice in zlib.h -// -const tests = [ - [0x0, 0x0, 0, 0x0], - [0xffffffff, 0x0, 0, 0x0], - [0x0, 0x0, 255, 0x0] /* BZ 174799. */, - [0x0, 0x0, 256, 0x0], - [0x0, 0x0, 257, 0x0], - [0x0, 0x0, 32767, 0x0], - [0x0, 0x0, 32768, 0x0], - [0x0, 0x0, 32769, 0x0], - [0x0, "", 0, 0x0], - [0xffffffff, "", 0, 0xffffffff], - [0x0, "abacus", 6, 0xc3d7115b], - [0x0, "backlog", 7, 0x269205], - [0x0, "campfire", 8, 0x22a515f8], - [0x0, "delta", 5, 0x9643fed9], - [0x0, "executable", 10, 0xd68eda01], - [0x0, "file", 4, 0x8c9f3610], - [0x0, "greatest", 8, 0xc1abd6cd], - [0x0, "hello", 5, 0x3610a686], - [0x0, "inverter", 8, 0xc9e962c9], - [0x0, "jigsaw", 6, 0xce4e3f69], - [0x0, "karate", 6, 0x890be0e2], - [0x0, "landscape", 9, 0xc4e0330b], - [0x0, "machine", 7, 0x1505df84], - [0x0, "nanometer", 9, 0xd4e19f39], - [0x0, "oblivion", 8, 0xdae9de77], - [0x0, "panama", 6, 0x66b8979c], - [0x0, "quest", 5, 0x4317f817], - [0x0, "resource", 8, 0xbc91f416], - [0x0, "secret", 6, 0x5ca2e8e5], - [0x0, "test", 4, 0xd87f7e0c], - [0x0, "ultimate", 8, 0x3fc79b0b], - [0x0, "vector", 6, 0x1b6e485b], - [0x0, "walrus", 6, 0xbe769b97], - [0x0, "xeno", 4, 0xe7a06444], - [0x0, "yelling", 7, 0xfe3944e5], - [0x0, "zlib", 4, 0x73887d3a], - [0x0, "4BJD7PocN1VqX0jXVpWB", 20, 0xd487a5a1], - [0x0, "F1rPWI7XvDs6nAIRx41l", 20, 0x61a0132e], - [0x0, "ldhKlsVkPFOveXgkGtC2", 20, 0xdf02f76], - [0x0, "5KKnGOOrs8BvJ35iKTOS", 20, 0x579b2b0a], - [0x0, "0l1tw7GOcem06Ddu7yn4", 20, 0xf7d16e2d], - [0x0, "MCr47CjPIn9R1IvE1Tm5", 20, 0x731788f5], - [0x0, "UcixbzPKTIv0SvILHVdO", 20, 0x7112bb11], - [0x0, "dGnAyAhRQDsWw0ESou24", 20, 0xf32a0dac], - [0x0, "di0nvmY9UYMYDh0r45XT", 20, 0x625437bb], - [0x0, "2XKDwHfAhFsV0RhbqtvH", 20, 0x896930f9], - [0x0, "ZhrANFIiIvRnqClIVyeD", 20, 0x8579a37], - [0x0, "v7Q9ehzioTOVeDIZioT1", 20, 0x632aa8e0], - [0x0, "Yod5hEeKcYqyhfXbhxj2", 20, 0xc829af29], - [0x0, "GehSWY2ay4uUKhehXYb0", 20, 0x1b08b7e8], - [0x0, "kwytJmq6UqpflV8Y8GoE", 20, 0x4e33b192], - [0x0, "70684206568419061514", 20, 0x59a179f0], - [0x0, "42015093765128581010", 20, 0xcd1013d7], - [0x0, "88214814356148806939", 20, 0xab927546], - [0x0, "43472694284527343838", 20, 0x11f3b20c], - [0x0, "49769333513942933689", 20, 0xd562d4ca], - [0x0, "54979784887993251199", 20, 0x233395f7], - [0x0, "58360544869206793220", 20, 0x2d167fd5], - [0x0, "27347953487840714234", 20, 0x8b5108ba], - [0x0, "07650690295365319082", 20, 0xc46b3cd8], - [0x0, "42655507906821911703", 20, 0xc10b2662], - [0x0, "29977409200786225655", 20, 0xc9a0f9d2], - [0x0, "85181542907229116674", 20, 0x9341357b], - [0x0, "87963594337989416799", 20, 0xf0424937], - [0x0, "21395988329504168551", 20, 0xd7c4c31f], - [0x0, "51991013580943379423", 20, 0xf11edcc4], - [0x0, "*]+@!);({_$;}[_},?{?;(_?,=-][@", 30, 0x40795df4], - [0x0, "_@:_).&(#.[:[{[:)$++-($_;@[)}+", 30, 0xdd61a631], - [0x0, "&[!,[$_==}+.]@!;*(+},[;:)$;)-@", 30, 0xca907a99], - [0x0, "]{.[.+?+[[=;[?}_#&;[=)__$$:+=_", 30, 0xf652deac], - [0x0, "-%.)=/[@].:.(:,()$;=%@-$?]{%+%", 30, 0xaf39a5a9], - [0x0, "+]#$(@&.=:,*];/.!]%/{:){:@(;)$", 30, 0x6bebb4cf], - // eslint-disable-next-line no-template-curly-in-string - [0x0, ")-._.:?[&:.=+}(*$/=!.${;(=$@!}", 30, 0x76430bac], - [0x0, ":(_*&%/[[}+,?#$&*+#[([*-/#;%(]", 30, 0x6c80c388], - [0x0, "{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:", 30, 0xd54d977d], - [0x0, "_{$*,}(&,@.)):=!/%(&(,,-?$}}}!", 30, 0xe3966ad5], - [ - 0x0, - "e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL", - 100, - 0xe7c71db9, - ], - [ - 0x0, - "r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)", - 100, - 0xeaa52777, - ], - [ - 0x0, - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&", - 100, - 0xcd472048, - ], - [0x7a30360d, "abacus", 6, 0xf8655a84], - [0x6fd767ee, "backlog", 7, 0x1ed834b1], - [0xefeb7589, "campfire", 8, 0x686cfca], - [0x61cf7e6b, "delta", 5, 0x1554e4b1], - [0xdc712e2, "executable", 10, 0x761b4254], - [0xad23c7fd, "file", 4, 0x7abdd09b], - [0x85cb2317, "greatest", 8, 0x4ba91c6b], - [0x9eed31b0, "inverter", 8, 0xd5e78ba5], - [0xb94f34ca, "jigsaw", 6, 0x23649109], - [0xab058a2, "karate", 6, 0xc5591f41], - [0x5bff2b7a, "landscape", 9, 0xf10eb644], - [0x605c9a5f, "machine", 7, 0xbaa0a636], - [0x51bdeea5, "nanometer", 9, 0x6af89afb], - [0x85c21c79, "oblivion", 8, 0xecae222b], - [0x97216f56, "panama", 6, 0x47dffac4], - [0x18444af2, "quest", 5, 0x70c2fe36], - [0xbe6ce359, "resource", 8, 0x1471d925], - [0x843071f1, "secret", 6, 0x50c9a0db], - [0xf2480c60, "ultimate", 8, 0xf973daf8], - [0x2d2feb3d, "vector", 6, 0x344ac03d], - [0x7490310a, "walrus", 6, 0x6d1408ef], - [0x97d247d4, "xeno", 4, 0xe62670b5], - [0x93cf7599, "yelling", 7, 0x1b36da38], - [0x73c84278, "zlib", 4, 0x6432d127], - [0x228a87d1, "4BJD7PocN1VqX0jXVpWB", 20, 0x997107d0], - [0xa7a048d0, "F1rPWI7XvDs6nAIRx41l", 20, 0xdc567274], - [0x1f0ded40, "ldhKlsVkPFOveXgkGtC2", 20, 0xdcc63870], - [0xa804a62f, "5KKnGOOrs8BvJ35iKTOS", 20, 0x6926cffd], - [0x508fae6a, "0l1tw7GOcem06Ddu7yn4", 20, 0xb52b38bc], - [0xe5adaf4f, "MCr47CjPIn9R1IvE1Tm5", 20, 0xf83b8178], - [0x67136a40, "UcixbzPKTIv0SvILHVdO", 20, 0xc5213070], - [0xb00c4a10, "dGnAyAhRQDsWw0ESou24", 20, 0xbc7648b0], - [0x2e0c84b5, "di0nvmY9UYMYDh0r45XT", 20, 0xd8123a72], - [0x81238d44, "2XKDwHfAhFsV0RhbqtvH", 20, 0xd5ac5620], - [0xf853aa92, "ZhrANFIiIvRnqClIVyeD", 20, 0xceae099d], - [0x5a692325, "v7Q9ehzioTOVeDIZioT1", 20, 0xb07d2b24], - [0x3275b9f, "Yod5hEeKcYqyhfXbhxj2", 20, 0x24ce91df], - [0x38371feb, "GehSWY2ay4uUKhehXYb0", 20, 0x707b3b30], - [0xafc8bf62, "kwytJmq6UqpflV8Y8GoE", 20, 0x16abc6a9], - [0x9b07db73, "70684206568419061514", 20, 0xae1fb7b7], - [0xe75b214, "42015093765128581010", 20, 0xd4eecd2d], - [0x72d0fe6f, "88214814356148806939", 20, 0x4660ec7], - [0xf857a4b1, "43472694284527343838", 20, 0xfd8afdf7], - [0x54b8e14, "49769333513942933689", 20, 0xc6d1b5f2], - [0xd6aa5616, "54979784887993251199", 20, 0x32476461], - [0x11e63098, "58360544869206793220", 20, 0xd917cf1a], - [0xbe92385, "27347953487840714234", 20, 0x4ad14a12], - [0x49511de0, "07650690295365319082", 20, 0xe37b5c6c], - [0x3db13bc1, "42655507906821911703", 20, 0x7cc497f1], - [0xbb899bea, "29977409200786225655", 20, 0x99781bb2], - [0xf6cd9436, "85181542907229116674", 20, 0x132256a1], - [0x9109e6c3, "87963594337989416799", 20, 0xbfdb2c83], - [0x75770fc, "21395988329504168551", 20, 0x8d9d1e81], - [0x69b1d19b, "51991013580943379423", 20, 0x7b6d4404], - [0xc6132975, "*]+@!);({_$;}[_},?{?;(_?,=-][@", 30, 0x8619f010], - [0xd58cb00c, "_@:_).&(#.[:[{[:)$++-($_;@[)}+", 30, 0x15746ac3], - [0xb63b8caa, "&[!,[$_==}+.]@!;*(+},[;:)$;)-@", 30, 0xaccf812f], - [0x8a45a2b8, "]{.[.+?+[[=;[?}_#&;[=)__$$:+=_", 30, 0x78af45de], - [0xcbe95b78, "-%.)=/[@].:.(:,()$;=%@-$?]{%+%", 30, 0x25b06b59], - [0x4ef8a54b, "+]#$(@&.=:,*];/.!]%/{:){:@(;)$", 30, 0x4ba0d08f], - // eslint-disable-next-line no-template-curly-in-string - [0x76ad267a, ")-._.:?[&:.=+}(*$/=!.${;(=$@!}", 30, 0xe26b6aac], - [0x569e613c, ":(_*&%/[[}+,?#$&*+#[([*-/#;%(]", 30, 0x7e2b0a66], - [0x36aa61da, "{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:", 30, 0xb3430dc7], - [0xf67222df, "_{$*,}(&,@.)):=!/%(&(,,-?$}}}!", 30, 0x626c17a], - [ - 0x74b34fd3, - "e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL", - 100, - 0xccf98060, - ], - [ - 0x351fd770, - "r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)", - 100, - 0xd8b95312, - ], - [ - 0xc45aef77, - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&", - 100, - 0xbb1c9912, - ], - [ - 0xc45aef77, - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&", - 600, - 0x888afa5b, - ], -]; - -describe("zlib.crc32", () => { - test("crc32 test case", () => { - for (const [crc, data, len, expected] of tests) { - if (data === 0) { - return; - } - const buf = Buffer.from(data, "utf8"); - expect(buf.length).toBe(len); - expect(zlib.crc32(buf, crc)).toBe(expected); - expect(zlib.crc32(buf.toString(), crc)).toBe(expected); - if (crc === 0) { - expect(zlib.crc32(buf)).toBe(expected); - expect(zlib.crc32(buf.toString())).toBe(expected); - } - } - }); - - test("invalid input types", () => { - [undefined, null, true, 1, () => {}, {}].forEach(invalid => { - expect(() => zlib.crc32(invalid)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); - }); - - test("invalid crc types", () => { - [null, true, () => {}, {}].forEach(invalid => { - expect(() => zlib.crc32("test", invalid)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); - }); -}); - -//<#END_FILE: test-zlib-crc32.js diff --git a/test/js/node/test/parallel/zlib-create-raw.test.js b/test/js/node/test/parallel/zlib-create-raw.test.js deleted file mode 100644 index 4bd60d6d8e..0000000000 --- a/test/js/node/test/parallel/zlib-create-raw.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-zlib-create-raw.js -//#SHA1: 187539d5696ec6b7c567dfba0d1528c4b65d1e0a -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib.createInflateRaw returns an instance of InflateRaw", () => { - const inflateRaw = zlib.createInflateRaw(); - expect(inflateRaw).toBeInstanceOf(zlib.InflateRaw); -}); - -test("zlib.createDeflateRaw returns an instance of DeflateRaw", () => { - const deflateRaw = zlib.createDeflateRaw(); - expect(deflateRaw).toBeInstanceOf(zlib.DeflateRaw); -}); - -//<#END_FILE: test-zlib-create-raw.js diff --git a/test/js/node/test/parallel/zlib-deflate-constructors.test.js b/test/js/node/test/parallel/zlib-deflate-constructors.test.js deleted file mode 100644 index d9a0411de2..0000000000 --- a/test/js/node/test/parallel/zlib-deflate-constructors.test.js +++ /dev/null @@ -1,281 +0,0 @@ -//#FILE: test-zlib-deflate-constructors.js -//#SHA1: fdacb219f8fcceeeb4800473b37fca16fcf4c241 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -// Work with and without `new` keyword -test("Deflate constructor works with and without new keyword", () => { - expect(zlib.Deflate()).toBeInstanceOf(zlib.Deflate); - expect(new zlib.Deflate()).toBeInstanceOf(zlib.Deflate); -}); - -test("DeflateRaw constructor works with and without new keyword", () => { - expect(zlib.DeflateRaw()).toBeInstanceOf(zlib.DeflateRaw); - expect(new zlib.DeflateRaw()).toBeInstanceOf(zlib.DeflateRaw); -}); - -// Throws if `options.chunkSize` is invalid -test("Deflate constructor throws for invalid chunkSize", () => { - expect(() => new zlib.Deflate({ chunkSize: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ chunkSize: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ chunkSize: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Confirm that maximum chunk size cannot be exceeded because it is `Infinity`. -test("Z_MAX_CHUNK is Infinity", () => { - expect(zlib.constants.Z_MAX_CHUNK).toBe(Infinity); -}); - -// Throws if `options.windowBits` is invalid -test("Deflate constructor throws for invalid windowBits", () => { - expect(() => new zlib.Deflate({ windowBits: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ windowBits: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ windowBits: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ windowBits: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if `options.level` is invalid -test("Deflate constructor throws for invalid level", () => { - expect(() => new zlib.Deflate({ level: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ level: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ level: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ level: -2 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if `level` invalid in `Deflate.prototype.params()` -test("Deflate.prototype.params throws for invalid level", () => { - expect(() => new zlib.Deflate().params("test")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(-Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(-2)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if options.memLevel is invalid -test("Deflate constructor throws for invalid memLevel", () => { - expect(() => new zlib.Deflate({ memLevel: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ memLevel: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ memLevel: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ memLevel: -2 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Does not throw if opts.strategy is valid -test("Deflate constructor does not throw for valid strategy", () => { - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_FILTERED })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_HUFFMAN_ONLY })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_RLE })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_FIXED })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_DEFAULT_STRATEGY })).not.toThrow(); -}); - -// Throws if options.strategy is invalid -test("Deflate constructor throws for invalid strategy", () => { - expect(() => new zlib.Deflate({ strategy: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ strategy: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ strategy: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ strategy: -2 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws TypeError if `strategy` is invalid in `Deflate.prototype.params()` -test("Deflate.prototype.params throws for invalid strategy", () => { - expect(() => new zlib.Deflate().params(0, "test")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(0, -Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(0, Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(0, -2)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if opts.dictionary is not a Buffer -test("Deflate constructor throws if dictionary is not a Buffer", () => { - expect(() => new zlib.Deflate({ dictionary: "not a buffer" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-deflate-constructors.js diff --git a/test/js/node/test/parallel/zlib-deflate-raw-inherits.test.js b/test/js/node/test/parallel/zlib-deflate-raw-inherits.test.js deleted file mode 100644 index 1eb9243059..0000000000 --- a/test/js/node/test/parallel/zlib-deflate-raw-inherits.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-zlib-deflate-raw-inherits.js -//#SHA1: 9e1873864f4af27abf3a8a36a87edd2d036805d8 -//----------------- -"use strict"; - -const { DeflateRaw } = require("zlib"); -const { Readable } = require("stream"); - -// Validates that zlib.DeflateRaw can be inherited -// with Object.setPrototypeOf - -test("DeflateRaw can be inherited with Object.setPrototypeOf", done => { - function NotInitialized(options) { - DeflateRaw.call(this, options); - this.prop = true; - } - Object.setPrototypeOf(NotInitialized.prototype, DeflateRaw.prototype); - Object.setPrototypeOf(NotInitialized, DeflateRaw); - - const dest = new NotInitialized(); - - const read = new Readable({ - read() { - this.push(Buffer.from("a test string")); - this.push(null); - }, - }); - - read.pipe(dest); - dest.resume(); - - // We need to add an event listener to ensure the test completes - dest.on("finish", () => { - expect(dest.prop).toBe(true); - expect(dest instanceof DeflateRaw).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-zlib-deflate-raw-inherits.js diff --git a/test/js/node/test/parallel/zlib-destroy-pipe.test.js b/test/js/node/test/parallel/zlib-destroy-pipe.test.js deleted file mode 100644 index 90866882de..0000000000 --- a/test/js/node/test/parallel/zlib-destroy-pipe.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-zlib-destroy-pipe.js -//#SHA1: 55e5ddd18c87bc58f331f82caa482cd49f5de168 -//----------------- -"use strict"; - -const zlib = require("zlib"); -const { Writable } = require("stream"); - -test("verify that the zlib transform does not error in case it is destroyed with data still in flight", () => { - const ts = zlib.createGzip(); - - const ws = new Writable({ - write(chunk, enc, cb) { - setImmediate(cb); - ts.destroy(); - }, - }); - - const buf = Buffer.allocUnsafe(1024 * 1024 * 20); - ts.end(buf); - ts.pipe(ws); -}); - -//<#END_FILE: test-zlib-destroy-pipe.js diff --git a/test/js/node/test/parallel/zlib-destroy.test.js b/test/js/node/test/parallel/zlib-destroy.test.js deleted file mode 100644 index 0947ce2827..0000000000 --- a/test/js/node/test/parallel/zlib-destroy.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-zlib-destroy.js -//#SHA1: b28cfad7c9e73659c624238b74dcc38146c94203 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -// Verify that the zlib transform does clean up -// the handle when calling destroy. - -test("zlib transform cleans up handle on destroy", done => { - const ts = zlib.createGzip(); - ts.destroy(); - expect(ts._handle).toBeNull(); - - ts.on("close", () => { - ts.close(() => { - done(); - }); - }); -}); - -test("error is only emitted once", done => { - const decompress = zlib.createGunzip(15); - - let errorCount = 0; - decompress.on("error", err => { - errorCount++; - decompress.close(); - - // Ensure this callback is only called once - expect(errorCount).toBe(1); - done(); - }); - - decompress.write("something invalid"); - decompress.destroy(new Error("asd")); -}); - -//<#END_FILE: test-zlib-destroy.js diff --git a/test/js/node/test/parallel/zlib-dictionary-fail.test.js b/test/js/node/test/parallel/zlib-dictionary-fail.test.js deleted file mode 100644 index 4085281359..0000000000 --- a/test/js/node/test/parallel/zlib-dictionary-fail.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-zlib-dictionary-fail.js -//#SHA1: e9c6d383f9b0a202067a125c016f1ef3cd5be558 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -// String "test" encoded with dictionary "dict". -const input = Buffer.from([0x78, 0xbb, 0x04, 0x09, 0x01, 0xa5]); - -test("zlib.createInflate without dictionary", async () => { - const stream = zlib.createInflate(); - - await expect( - new Promise((resolve, reject) => { - stream.on("error", reject); - stream.write(input); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/Missing dictionary/), - }), - ); -}); - -test("zlib.createInflate with wrong dictionary", async () => { - const stream = zlib.createInflate({ dictionary: Buffer.from("fail") }); - - await expect( - new Promise((resolve, reject) => { - stream.on("error", reject); - stream.write(input); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/Bad dictionary/), - }), - ); -}); - -test("zlib.createInflateRaw with wrong dictionary", async () => { - const stream = zlib.createInflateRaw({ dictionary: Buffer.from("fail") }); - - await expect( - new Promise((resolve, reject) => { - stream.on("error", reject); - stream.write(input); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/(invalid|Operation-Ending-Supplemental Code is 0x12)/), - }), - ); -}); - -//<#END_FILE: test-zlib-dictionary-fail.js diff --git a/test/js/node/test/parallel/zlib-dictionary.test.js b/test/js/node/test/parallel/zlib-dictionary.test.js deleted file mode 100644 index e7b9fbc82a..0000000000 --- a/test/js/node/test/parallel/zlib-dictionary.test.js +++ /dev/null @@ -1,176 +0,0 @@ -//#FILE: test-zlib-dictionary.js -//#SHA1: b5fc5a33125dfeaa9965b0564a8196b056b95918 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -const spdyDict = Buffer.from( - [ - "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-", - "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi", - "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser", - "-agent10010120020120220320420520630030130230330430530630740040140240340440", - "5406407408409410411412413414415416417500501502503504505accept-rangesageeta", - "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic", - "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran", - "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati", - "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo", - "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe", - "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic", - "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1", - ".1statusversionurl\0", - ].join(""), -); - -const input = ["HTTP/1.1 200 Ok", "Server: node.js", "Content-Length: 0", ""].join("\r\n"); - -function basicDictionaryTest(spdyDict) { - return new Promise(resolve => { - let output = ""; - const deflate = zlib.createDeflate({ dictionary: spdyDict }); - const inflate = zlib.createInflate({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.end(); - }); -} - -function deflateResetDictionaryTest(spdyDict) { - return new Promise(resolve => { - let doneReset = false; - let output = ""; - const deflate = zlib.createDeflate({ dictionary: spdyDict }); - const inflate = zlib.createInflate({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - if (doneReset) inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.flush(() => { - deflate.reset(); - doneReset = true; - deflate.write(input); - deflate.end(); - }); - }); -} - -function rawDictionaryTest(spdyDict) { - return new Promise(resolve => { - let output = ""; - const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); - const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.end(); - }); -} - -function deflateRawResetDictionaryTest(spdyDict) { - return new Promise(resolve => { - let doneReset = false; - let output = ""; - const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); - const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - if (doneReset) inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.flush(() => { - deflate.reset(); - doneReset = true; - deflate.write(input); - deflate.end(); - }); - }); -} - -const dictionaries = [spdyDict, Buffer.from(spdyDict), new Uint8Array(spdyDict)]; - -describe("zlib dictionary tests", () => { - test.each(dictionaries)("basic dictionary test", async dict => { - await basicDictionaryTest(dict); - }); - - test.each(dictionaries)("deflate reset dictionary test", async dict => { - await deflateResetDictionaryTest(dict); - }); - - test.each(dictionaries)("raw dictionary test", async dict => { - await rawDictionaryTest(dict); - }); - - test.each(dictionaries)("deflate raw reset dictionary test", async dict => { - await deflateRawResetDictionaryTest(dict); - }); -}); - -//<#END_FILE: test-zlib-dictionary.js diff --git a/test/js/node/test/parallel/zlib-empty-buffer.test.js b/test/js/node/test/parallel/zlib-empty-buffer.test.js deleted file mode 100644 index 553415206f..0000000000 --- a/test/js/node/test/parallel/zlib-empty-buffer.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-zlib-empty-buffer.js -//#SHA1: 7a2e8687dcd6e4bd815aa22c2bbff08251d9d91a -//----------------- -"use strict"; -const zlib = require("zlib"); -const { inspect, promisify } = require("util"); -const emptyBuffer = Buffer.alloc(0); - -describe("zlib empty buffer", () => { - const testCases = [ - [zlib.deflateRawSync, zlib.inflateRawSync, "raw sync"], - [zlib.deflateSync, zlib.inflateSync, "deflate sync"], - [zlib.gzipSync, zlib.gunzipSync, "gzip sync"], - [zlib.brotliCompressSync, zlib.brotliDecompressSync, "br sync"], - [promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), "raw"], - [promisify(zlib.deflate), promisify(zlib.inflate), "deflate"], - [promisify(zlib.gzip), promisify(zlib.gunzip), "gzip"], - [promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), "br"], - ]; - - test.each(testCases)("compresses and decompresses empty buffer using %s", async (compress, decompress, method) => { - const compressed = await compress(emptyBuffer); - const decompressed = await decompress(compressed); - expect(decompressed).toStrictEqual(emptyBuffer); - }); -}); - -//<#END_FILE: test-zlib-empty-buffer.js diff --git a/test/js/node/test/parallel/zlib-failed-init.test.js b/test/js/node/test/parallel/zlib-failed-init.test.js deleted file mode 100644 index 1f47ce229d..0000000000 --- a/test/js/node/test/parallel/zlib-failed-init.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-zlib-failed-init.js -//#SHA1: a1c770e81c677ebefa0e5969e3441b28e63ab75a -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib createGzip with invalid chunkSize", () => { - expect(() => zlib.createGzip({ chunkSize: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -test("zlib createGzip with invalid windowBits", () => { - expect(() => zlib.createGzip({ windowBits: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -test("zlib createGzip with invalid memLevel", () => { - expect(() => zlib.createGzip({ memLevel: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -test("zlib createGzip with NaN level", () => { - const stream = zlib.createGzip({ level: NaN }); - expect(stream._level).toBe(zlib.constants.Z_DEFAULT_COMPRESSION); -}); - -test("zlib createGzip with NaN strategy", () => { - const stream = zlib.createGzip({ strategy: NaN }); - expect(stream._strategy).toBe(zlib.constants.Z_DEFAULT_STRATEGY); -}); - -//<#END_FILE: test-zlib-failed-init.js diff --git a/test/js/node/test/parallel/zlib-flush-drain-longblock.test.js b/test/js/node/test/parallel/zlib-flush-drain-longblock.test.js deleted file mode 100644 index 21f8a7d720..0000000000 --- a/test/js/node/test/parallel/zlib-flush-drain-longblock.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-zlib-flush-drain-longblock.js -//#SHA1: 95927f13fbb59e0a8a2a32c14d0443fc110bab6e -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib flush interacts properly with writableState.needDrain", done => { - const zipper = zlib.createGzip({ highWaterMark: 16384 }); - const unzipper = zlib.createGunzip(); - zipper.pipe(unzipper); - - zipper.write("A".repeat(17000)); - zipper.flush(); - - let received = 0; - let dataCallCount = 0; - - unzipper.on("data", d => { - received += d.length; - dataCallCount++; - }); - - unzipper.on("end", () => { - expect(received).toBe(17000); - expect(dataCallCount).toBeGreaterThanOrEqual(2); - done(); - }); - - // Properly end the streams to ensure all data is processed - zipper.end(); -}); - -//<#END_FILE: test-zlib-flush-drain-longblock.js diff --git a/test/js/node/test/parallel/zlib-flush-drain.test.js b/test/js/node/test/parallel/zlib-flush-drain.test.js deleted file mode 100644 index b4407e7cbd..0000000000 --- a/test/js/node/test/parallel/zlib-flush-drain.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-zlib-flush-drain.js -//#SHA1: 2f83bee63a56543c9824833e4fa7d8f5b33a373e -//----------------- -"use strict"; -const zlib = require("zlib"); - -const bigData = Buffer.alloc(10240, "x"); - -const opts = { - level: 0, - highWaterMark: 16, -}; - -let flushCount = 0; -let drainCount = 0; -let beforeFlush, afterFlush; - -test("zlib flush and drain behavior", done => { - const deflater = zlib.createDeflate(opts); - - // Shim deflater.flush so we can count times executed - const flush = deflater.flush; - deflater.flush = function (kind, callback) { - flushCount++; - flush.call(this, kind, callback); - }; - - deflater.write(bigData); - - const ws = deflater._writableState; - beforeFlush = ws.needDrain; - - deflater.on("data", () => {}); - - deflater.flush(function (err) { - afterFlush = ws.needDrain; - }); - - deflater.on("drain", function () { - drainCount++; - }); - - // Use setTimeout to ensure all asynchronous operations have completed - setTimeout(() => { - expect(beforeFlush).toBe(true); - expect(afterFlush).toBe(false); - expect(drainCount).toBe(1); - expect(flushCount).toBe(1); - done(); - }, 100); -}); - -//<#END_FILE: test-zlib-flush-drain.js diff --git a/test/js/node/test/parallel/zlib-flush-flags.test.js b/test/js/node/test/parallel/zlib-flush-flags.test.js deleted file mode 100644 index 306fe03791..0000000000 --- a/test/js/node/test/parallel/zlib-flush-flags.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-zlib-flush-flags.js -//#SHA1: 9fb4201163deb1a6dc1f74c5c7a2723ec1a9d342 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("createGzip with valid flush option", () => { - expect(() => zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH })).not.toThrow(); -}); - -test("createGzip with invalid flush option type", () => { - expect(() => zlib.createGzip({ flush: "foobar" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("createGzip with out of range flush option", () => { - expect(() => zlib.createGzip({ flush: 10000 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -test("createGzip with valid finishFlush option", () => { - expect(() => zlib.createGzip({ finishFlush: zlib.constants.Z_SYNC_FLUSH })).not.toThrow(); -}); - -test("createGzip with invalid finishFlush option type", () => { - expect(() => zlib.createGzip({ finishFlush: "foobar" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("createGzip with out of range finishFlush option", () => { - expect(() => zlib.createGzip({ finishFlush: 10000 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-flush-flags.js diff --git a/test/js/node/test/parallel/zlib-flush-write-sync-interleaved.test.js b/test/js/node/test/parallel/zlib-flush-write-sync-interleaved.test.js deleted file mode 100644 index 1661372b3d..0000000000 --- a/test/js/node/test/parallel/zlib-flush-write-sync-interleaved.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-zlib-flush-write-sync-interleaved.js -//#SHA1: 35bfe36486f112686943448a115a586035455ba7 -//----------------- -"use strict"; -const { createGzip, createGunzip, Z_PARTIAL_FLUSH } = require("zlib"); - -// Verify that .flush() behaves like .write() in terms of ordering, e.g. in -// a sequence like .write() + .flush() + .write() + .flush() each .flush() call -// only affects the data written before it. -// Refs: https://github.com/nodejs/node/issues/28478 - -test("zlib flush and write ordering", done => { - const compress = createGzip(); - const decompress = createGunzip(); - decompress.setEncoding("utf8"); - - const events = []; - const compressedChunks = []; - - for (const chunk of ["abc", "def", "ghi"]) { - compress.write(chunk, () => events.push({ written: chunk })); - compress.flush(Z_PARTIAL_FLUSH, () => { - events.push("flushed"); - const chunk = compress.read(); - if (chunk !== null) compressedChunks.push(chunk); - }); - } - - compress.end(() => { - events.push("compress end"); - writeToDecompress(); - }); - - function writeToDecompress() { - // Write the compressed chunks to a decompressor, one by one, in order to - // verify that the flushes actually worked. - const chunk = compressedChunks.shift(); - if (chunk === undefined) { - decompress.end(); - checkResults(); - return; - } - decompress.write(chunk, () => { - events.push({ read: decompress.read() }); - writeToDecompress(); - }); - } - - function checkResults() { - expect(events).toEqual([ - { written: "abc" }, - "flushed", - { written: "def" }, - "flushed", - { written: "ghi" }, - "flushed", - "compress end", - { read: "abc" }, - { read: "def" }, - { read: "ghi" }, - ]); - done(); - } -}); - -//<#END_FILE: test-zlib-flush-write-sync-interleaved.js diff --git a/test/js/node/test/parallel/zlib-flush.test.js b/test/js/node/test/parallel/zlib-flush.test.js deleted file mode 100644 index cfda6ffb6d..0000000000 --- a/test/js/node/test/parallel/zlib-flush.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-zlib-flush.js -//#SHA1: 61f325893c63c826c2a498bc52ef3401f9a5a542 -//----------------- -"use strict"; - -const zlib = require("node:zlib"); - -test("zlib flush", async () => { - const opts = { level: 0 }; - const deflater = zlib.createDeflate(opts); - const chunk = Buffer.from("/9j/4AAQSkZJRgABAQEASA==", "base64"); - const expectedNone = Buffer.from([0x78, 0x01]); - const blkhdr = Buffer.from([0x00, 0x10, 0x00, 0xef, 0xff]); - const adler32 = Buffer.from([0x00, 0x00, 0x00, 0xff, 0xff]); - const expectedFull = Buffer.concat([blkhdr, chunk, adler32]); - let actualNone; - let actualFull; - - await new Promise(resolve => { - deflater.write(chunk, function () { - deflater.flush(zlib.constants.Z_NO_FLUSH, function () { - actualNone = deflater.read(); - deflater.flush(function () { - const bufs = []; - let buf; - while ((buf = deflater.read()) !== null) bufs.push(buf); - actualFull = Buffer.concat(bufs); - resolve(); - }); - }); - }); - }); - - expect(actualNone).toEqual(expectedNone); - expect(actualFull).toEqual(expectedFull); -}); - -//<#END_FILE: test-zlib-flush.js diff --git a/test/js/node/test/parallel/zlib-from-concatenated-gzip.test.js b/test/js/node/test/parallel/zlib-from-concatenated-gzip.test.js deleted file mode 100644 index 1961f9e215..0000000000 --- a/test/js/node/test/parallel/zlib-from-concatenated-gzip.test.js +++ /dev/null @@ -1,99 +0,0 @@ -//#FILE: test-zlib-from-concatenated-gzip.js -//#SHA1: cf8f45097fb201dc583ee154b34585b6a0a0dc34 -//----------------- -"use strict"; -// Test unzipping a gzip file that contains multiple concatenated "members" - -const zlib = require("zlib"); -const fs = require("fs"); -const path = require("path"); - -const abc = "abc"; -const def = "def"; - -const abcEncoded = zlib.gzipSync(abc); -const defEncoded = zlib.gzipSync(def); - -const data = Buffer.concat([abcEncoded, defEncoded]); - -test("gunzipSync concatenated gzip members", () => { - expect(zlib.gunzipSync(data).toString()).toBe(abc + def); -}); - -test("gunzip concatenated gzip members", async () => { - const result = await new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - expect(result.toString()).toBe(abc + def); -}); - -test("unzip concatenated gzip members", async () => { - const result = await new Promise((resolve, reject) => { - zlib.unzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - expect(result.toString()).toBe(abc + def); -}); - -test("unzip concatenated deflate members", async () => { - const result = await new Promise((resolve, reject) => { - zlib.unzip(Buffer.concat([zlib.deflateSync("abc"), zlib.deflateSync("def")]), (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - expect(result.toString()).toBe(abc); -}); - -test("pseudo-multimember gzip file", async () => { - const pmmFileZlib = path.join(__dirname, "../fixtures/pseudo-multimember-gzip.z"); - const pmmFileGz = path.join(__dirname, "../fixtures/pseudo-multimember-gzip.gz"); - - const pmmExpected = zlib.inflateSync(fs.readFileSync(pmmFileZlib)); - const pmmResultBuffers = []; - - await new Promise((resolve, reject) => { - fs.createReadStream(pmmFileGz) - .pipe(zlib.createGunzip()) - .on("error", reject) - .on("data", data => pmmResultBuffers.push(data)) - .on("finish", resolve); - }); - - // Result should match original random garbage - expect(Buffer.concat(pmmResultBuffers)).toEqual(pmmExpected); -}); - -test("gzip member wrapping around input buffer boundary", async () => { - const offsets = [0, 1, 2, 3, 4, defEncoded.length]; - - for (const offset of offsets) { - const resultBuffers = []; - - await new Promise((resolve, reject) => { - const unzip = zlib - .createGunzip() - .on("error", reject) - .on("data", data => resultBuffers.push(data)) - .on("finish", resolve); - - // First write: write "abc" + the first bytes of "def" - unzip.write(Buffer.concat([abcEncoded, defEncoded.slice(0, offset)])); - - // Write remaining bytes of "def" - unzip.end(defEncoded.slice(offset)); - }); - - expect(Buffer.concat(resultBuffers).toString()).toBe( - "abcdef", - `result should match original input (offset = ${offset})`, - ); - } -}); - -//<#END_FILE: test-zlib-from-concatenated-gzip.js diff --git a/test/js/node/test/parallel/zlib-from-gzip-with-trailing-garbage.test.js b/test/js/node/test/parallel/zlib-from-gzip-with-trailing-garbage.test.js deleted file mode 100644 index 4b2371eabd..0000000000 --- a/test/js/node/test/parallel/zlib-from-gzip-with-trailing-garbage.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-zlib-from-gzip-with-trailing-garbage.js -//#SHA1: 8c3ebbede1912a48995aaada3c3e470d48bc01f3 -//----------------- -"use strict"; -const zlib = require("zlib"); - -describe("Unzipping a gzip file with trailing garbage", () => { - test("Should ignore trailing null-bytes", () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.alloc(10)]); - - expect(zlib.gunzipSync(data).toString()).toBe("abcdef"); - }); - - test("Should ignore trailing null-bytes (async)", async () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.alloc(10)]); - - const result = await new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - - expect(result.toString()).toBe("abcdef"); - }); - - test("Should throw error if trailing garbage looks like a gzip header", () => { - const data = Buffer.concat([ - zlib.gzipSync("abc"), - zlib.gzipSync("def"), - Buffer.from([0x1f, 0x8b, 0xff, 0xff]), - Buffer.alloc(10), - ]); - - expect(() => zlib.gunzipSync(data)).toThrow("unknown compression method"); - }); - - test("Should throw error if trailing garbage looks like a gzip header (async)", async () => { - const data = Buffer.concat([ - zlib.gzipSync("abc"), - zlib.gzipSync("def"), - Buffer.from([0x1f, 0x8b, 0xff, 0xff]), - Buffer.alloc(10), - ]); - - await expect( - new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }), - ).rejects.toThrow("unknown compression method"); - }); - - test("Should throw error if trailing junk is too short to be a gzip segment", () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.from([0x1f, 0x8b, 0xff, 0xff])]); - - expect(() => zlib.gunzipSync(data)).toThrow("unknown compression method"); - }); - - test("Should throw error if trailing junk is too short to be a gzip segment (async)", async () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.from([0x1f, 0x8b, 0xff, 0xff])]); - - await expect( - new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }), - ).rejects.toThrow("unknown compression method"); - }); -}); - -//<#END_FILE: test-zlib-from-gzip-with-trailing-garbage.js diff --git a/test/js/node/test/parallel/zlib-from-gzip.test.js b/test/js/node/test/parallel/zlib-from-gzip.test.js deleted file mode 100644 index c1406008f2..0000000000 --- a/test/js/node/test/parallel/zlib-from-gzip.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-zlib-from-gzip.js -//#SHA1: 44570a611316087d64497151e9ed570aca9060d5 -//----------------- -"use strict"; - -import { tmpdirSync } from "harness"; - -const assert = require("assert"); -const path = require("path"); -const zlib = require("zlib"); -const fixtures = require("../common/fixtures"); -const fs = require("fs"); - -test("test unzipping a file that was created with a non-node gzip lib, piped in as fast as possible", async () => { - const x = tmpdirSync(); - const gunzip = zlib.createGunzip(); - const fixture = fixtures.path("person.jpg.gz"); - const unzippedFixture = fixtures.path("person.jpg"); - const outputFile = path.resolve(x, "person.jpg"); - const expected = fs.readFileSync(unzippedFixture); - const inp = fs.createReadStream(fixture); - const out = fs.createWriteStream(outputFile); - - inp.pipe(gunzip).pipe(out); - - const { promise, resolve, reject } = Promise.withResolvers(); - - out.on("close", () => { - try { - const actual = fs.readFileSync(outputFile); - expect(actual.length).toBe(expected.length); - expect(actual).toEqual(expected); - resolve(); - } catch (e) { - reject(e); - } - }); - await promise; -}); - -//<#END_FILE: test-zlib-from-gzip.js diff --git a/test/js/node/test/parallel/zlib-from-string.test.js b/test/js/node/test/parallel/zlib-from-string.test.js deleted file mode 100644 index a9b8900d7f..0000000000 --- a/test/js/node/test/parallel/zlib-from-string.test.js +++ /dev/null @@ -1,121 +0,0 @@ -//#FILE: test-zlib-from-string.js -//#SHA1: 0514669607bbf01e20f41875fa716660ebfcf28b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Test compressing and uncompressing a string with zlib - -const zlib = require("zlib"); - -const inputString = - "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + - "t. Morbi faucibus, purus at gravida dictum, libero arcu " + - "convallis lacus, in commodo libero metus eu nisi. Nullam" + - " commodo, neque nec porta placerat, nisi est fermentum a" + - "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + - "n non diam orci. Proin quis elit turpis. Suspendisse non" + - " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + - "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + - "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; -const expectedBase64Deflate = - "eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmX" + - "AKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkD" + - "lKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6oc" + - "iBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoA" + - "bWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+" + - "RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKS" + - "qGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqq" + - "fKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOH" + - "aFE4RdpnGavKtrB5xzfO/Ll9"; -const expectedBase64Gzip = - "H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4" + - "96pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajN" + - "Pk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9Lq" + - "hyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBt" + - "aHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCw" + - "agLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+" + - "eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5" + - "mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2" + - "sHnHNzRtagj5AQAA"; - -test("deflate and inflate", async () => { - const buffer = await new Promise((resolve, reject) => { - zlib.deflate(inputString, (err, buffer) => { - if (err) reject(err); - else resolve(buffer); - }); - }); - - const inflated = await new Promise((resolve, reject) => { - zlib.inflate(buffer, (err, inflated) => { - if (err) reject(err); - else resolve(inflated); - }); - }); - - expect(inflated.toString()).toBe(inputString); -}); - -test("gzip and gunzip", async () => { - const buffer = await new Promise((resolve, reject) => { - zlib.gzip(inputString, (err, buffer) => { - if (err) reject(err); - else resolve(buffer); - }); - }); - - const gunzipped = await new Promise((resolve, reject) => { - zlib.gunzip(buffer, (err, gunzipped) => { - if (err) reject(err); - else resolve(gunzipped); - }); - }); - - expect(gunzipped.toString()).toBe(inputString); -}); - -test("unzip base64 deflate", async () => { - const buffer = Buffer.from(expectedBase64Deflate, "base64"); - const unzipped = await new Promise((resolve, reject) => { - zlib.unzip(buffer, (err, unzipped) => { - if (err) reject(err); - else resolve(unzipped); - }); - }); - - expect(unzipped.toString()).toBe(inputString); -}); - -test("unzip base64 gzip", async () => { - const buffer = Buffer.from(expectedBase64Gzip, "base64"); - const unzipped = await new Promise((resolve, reject) => { - zlib.unzip(buffer, (err, unzipped) => { - if (err) reject(err); - else resolve(unzipped); - }); - }); - - expect(unzipped.toString()).toBe(inputString); -}); - -//<#END_FILE: test-zlib-from-string.js diff --git a/test/js/node/test/parallel/zlib-invalid-arg-value-brotli-compress.test.js b/test/js/node/test/parallel/zlib-invalid-arg-value-brotli-compress.test.js deleted file mode 100644 index 4fbb56f1a0..0000000000 --- a/test/js/node/test/parallel/zlib-invalid-arg-value-brotli-compress.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-zlib-invalid-arg-value-brotli-compress.js -//#SHA1: fab88f892e21351603b5ae7227837d01149bdae6 -//----------------- -"use strict"; - -// This test ensures that the BrotliCompress function throws -// ERR_INVALID_ARG_TYPE when the values of the `params` key-value object are -// neither numbers nor booleans. - -const { BrotliCompress, constants } = require("zlib"); - -test("BrotliCompress throws ERR_INVALID_ARG_TYPE for invalid params value", () => { - const opts = { - params: { - [constants.BROTLI_PARAM_MODE]: "lol", - }, - }; - - expect(() => BrotliCompress(opts)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-invalid-arg-value-brotli-compress.js diff --git a/test/js/node/test/parallel/zlib-invalid-input-memory.test.js b/test/js/node/test/parallel/zlib-invalid-input-memory.test.js deleted file mode 100644 index 7193d0a531..0000000000 --- a/test/js/node/test/parallel/zlib-invalid-input-memory.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-zlib-invalid-input-memory.js -//#SHA1: 2607db89f2850bfbe75959ca2cd56e647b9eac78 -//----------------- -"use strict"; -const zlib = require("zlib"); -const onGC = require("../common/ongc"); -const common = require("../common"); - -const ongc = common.mustCall(); - -test.todo("zlib context with error can be garbage collected", () => { - const input = Buffer.from("foobar"); - const strm = zlib.createInflate(); - - strm.end(input); - - strm.once("error", err => { - expect(err).toBeTruthy(); - - setImmediate(() => { - global.gc(); - // Keep the event loop alive for seeing the async_hooks destroy hook we use for GC tracking... - // TODO(addaleax): This should maybe not be necessary? - setImmediate(() => {}); - }); - }); - onGC(strm, { ongc }); -}); - -//<#END_FILE: test-zlib-invalid-input-memory.js diff --git a/test/js/node/test/parallel/zlib-invalid-input.test.js b/test/js/node/test/parallel/zlib-invalid-input.test.js deleted file mode 100644 index aeb28f609d..0000000000 --- a/test/js/node/test/parallel/zlib-invalid-input.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-zlib-invalid-input.js -//#SHA1: b76d7dde545ea75c25000ee36864841cf73951e0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Test uncompressing invalid input - -const zlib = require("zlib"); - -const nonStringInputs = [1, true, { a: 1 }, ["a"]]; - -// zlib.Unzip classes need to get valid data, or else they'll throw. -const unzips = [zlib.Unzip(), zlib.Gunzip(), zlib.Inflate(), zlib.InflateRaw(), zlib.BrotliDecompress()]; - -test("zlib.gunzip throws TypeError for non-string inputs", () => { - nonStringInputs.forEach(input => { - expect(() => { - zlib.gunzip(input); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); -}); - -test("unzip classes emit error for invalid compressed data", done => { - let completedCount = 0; - - unzips.forEach((uz, i) => { - uz.on("error", err => { - expect(err).toBeTruthy(); - completedCount++; - if (completedCount === unzips.length) { - done(); - } - }); - - uz.on("end", () => { - throw new Error("end event should not be emitted"); - }); - - // This will trigger error event - uz.write("this is not valid compressed data."); - }); -}); - -//<#END_FILE: test-zlib-invalid-input.js diff --git a/test/js/node/test/parallel/zlib-kmaxlength-rangeerror.test.js b/test/js/node/test/parallel/zlib-kmaxlength-rangeerror.test.js deleted file mode 100644 index 9d61c6ba81..0000000000 --- a/test/js/node/test/parallel/zlib-kmaxlength-rangeerror.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-zlib-kmaxlength-rangeerror.js -//#SHA1: b6507dfb859656a2d5194153ddf45de79e0ef0ce -//----------------- -"use strict"; - -const util = require("util"); - -// Change kMaxLength for zlib to trigger the error without having to allocate large Buffers. -const buffer = require("buffer"); -const oldkMaxLength = buffer.kMaxLength; -buffer.kMaxLength = 64; -const zlib = require("zlib"); -buffer.kMaxLength = oldkMaxLength; - -const encoded = Buffer.from("H4sIAAAAAAAAA0tMHFgAAIw2K/GAAAAA", "base64"); - -describe("ensure that zlib throws a RangeError if the final buffer needs to be larger than kMaxLength and concatenation fails", () => { - test("sync", () => { - expect(() => zlib.gunzipSync(encoded)).toThrowWithCode(RangeError, "ERR_BUFFER_TOO_LARGE"); - }); - - test("async", async () => { - await expect(async () => util.promisify(zlib.gunzip)(encoded)).toThrowWithCodeAsync( - RangeError, - "ERR_BUFFER_TOO_LARGE", - ); - }); -}); - -//<#END_FILE: test-zlib-kmaxlength-rangeerror.js diff --git a/test/js/node/test/parallel/zlib-maxoutputlength.test.js b/test/js/node/test/parallel/zlib-maxoutputlength.test.js deleted file mode 100644 index 2223043725..0000000000 --- a/test/js/node/test/parallel/zlib-maxoutputlength.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-zlib-maxOutputLength.js -//#SHA1: 807eb08c901d7476140bcb35b519518450bef3e6 -//----------------- -"use strict"; -const zlib = require("zlib"); -const { promisify } = require("util"); - -const brotliDecompressAsync = promisify(zlib.brotliDecompress); - -const encoded = Buffer.from("G38A+CXCIrFAIAM=", "base64"); - -describe("zlib.brotliDecompress with maxOutputLength", () => { - test("Async: should throw ERR_BUFFER_TOO_LARGE when maxOutputLength is exceeded", async () => { - await expect(brotliDecompressAsync(encoded, { maxOutputLength: 64 })).rejects.toThrow( - expect.objectContaining({ - code: "ERR_BUFFER_TOO_LARGE", - message: expect.stringContaining("Cannot create a Buffer larger than 64 bytes"), - }), - ); - }); - - test("Sync: should throw ERR_BUFFER_TOO_LARGE when maxOutputLength is exceeded", () => { - expect(() => { - zlib.brotliDecompressSync(encoded, { maxOutputLength: 64 }); - }).toThrow( - expect.objectContaining({ - name: "RangeError", - code: "ERR_BUFFER_TOO_LARGE", - message: expect.stringContaining("Cannot create a Buffer larger than 64 bytes"), - }), - ); - }); - - test("Async: should not throw error when maxOutputLength is sufficient", async () => { - await brotliDecompressAsync(encoded, { maxOutputLength: 256 }); - }); - - test("Sync: should not throw error when maxOutputLength is sufficient", () => { - expect(() => { - zlib.brotliDecompressSync(encoded, { maxOutputLength: 256 }); - }).not.toThrow(); - }); -}); - -//<#END_FILE: test-zlib-maxOutputLength.js diff --git a/test/js/node/test/parallel/zlib-no-stream.test.js b/test/js/node/test/parallel/zlib-no-stream.test.js deleted file mode 100644 index 871f4686a2..0000000000 --- a/test/js/node/test/parallel/zlib-no-stream.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-zlib-no-stream.js -//#SHA1: 5755924e9363a20243c326747623e8e266f81625 -//----------------- -/* eslint-disable node-core/required-modules */ -/* eslint-disable node-core/require-common-first */ - -"use strict"; - -// We are not loading common because it will load the stream module, -// defeating the purpose of this test. - -const { gzipSync } = require("zlib"); - -// Avoid regressions such as https://github.com/nodejs/node/issues/36615 - -test("gzipSync should not throw", () => { - // This must not throw - expect(() => gzipSync("fooobar")).not.toThrow(); -}); - -//<#END_FILE: test-zlib-no-stream.js diff --git a/test/js/node/test/parallel/zlib-not-string-or-buffer.test.js b/test/js/node/test/parallel/zlib-not-string-or-buffer.test.js deleted file mode 100644 index 626430ec72..0000000000 --- a/test/js/node/test/parallel/zlib-not-string-or-buffer.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-zlib-not-string-or-buffer.js -//#SHA1: d07db97d9393df2ab9453800ae80f2921d93b6e2 -//----------------- -"use strict"; - -// Check the error condition testing for passing something other than a string -// or buffer. - -const zlib = require("zlib"); - -test("zlib.deflateSync throws for invalid input types", () => { - const invalidInputs = [undefined, null, true, false, 0, 1, [1, 2, 3], { foo: "bar" }]; - - invalidInputs.forEach(input => { - expect(() => zlib.deflateSync(input)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -//<#END_FILE: test-zlib-not-string-or-buffer.js diff --git a/test/js/node/test/parallel/zlib-object-write.test.js b/test/js/node/test/parallel/zlib-object-write.test.js deleted file mode 100644 index 63934f60b2..0000000000 --- a/test/js/node/test/parallel/zlib-object-write.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-zlib-object-write.js -//#SHA1: 8866194c1a944026a655101d66189f364b414ad7 -//----------------- -"use strict"; - -const { Gunzip } = require("zlib"); - -test("Gunzip in object mode throws on non-buffer write", () => { - const gunzip = new Gunzip({ objectMode: true }); - - // We use jest.fn() to create a mock function that we expect not to be called - const errorHandler = jest.fn(); - gunzip.on("error", errorHandler); - - expect(() => { - gunzip.write({}); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - // Verify that the error handler was not called - expect(errorHandler).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-zlib-object-write.js diff --git a/test/js/node/test/parallel/zlib-params.test.js b/test/js/node/test/parallel/zlib-params.test.js deleted file mode 100644 index 2c8063d78a..0000000000 --- a/test/js/node/test/parallel/zlib-params.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-zlib-params.js -//#SHA1: d7d1b0c78ae9b4b5df5a1057c18fc7f2ef735526 -//----------------- -"use strict"; -const zlib = require("zlib"); -const fs = require("fs"); -const path = require("path"); - -const fixturesPath = path.join(__dirname, "..", "fixtures"); -const file = fs.readFileSync(path.join(fixturesPath, "person.jpg")); -const chunkSize = 12 * 1024; -const opts = { level: 9, strategy: zlib.constants.Z_DEFAULT_STRATEGY }; - -test("zlib params change mid-stream", done => { - const deflater = zlib.createDeflate(opts); - - const chunk1 = file.slice(0, chunkSize); - const chunk2 = file.slice(chunkSize); - const blkhdr = Buffer.from([0x00, 0x5a, 0x82, 0xa5, 0x7d]); - const blkftr = Buffer.from("010000ffff7dac3072", "hex"); - const expected = Buffer.concat([blkhdr, chunk2, blkftr]); - const bufs = []; - - function read() { - let buf; - while ((buf = deflater.read()) !== null) { - bufs.push(buf); - } - } - - deflater.write(chunk1, () => { - deflater.params(0, zlib.constants.Z_DEFAULT_STRATEGY, () => { - while (deflater.read()); - - deflater.on("readable", read); - - deflater.end(chunk2); - }); - while (deflater.read()); - }); - - deflater.on("end", () => { - const actual = Buffer.concat(bufs); - expect(actual).toEqual(expected); - done(); - }); -}); - -//<#END_FILE: test-zlib-params.js diff --git a/test/js/node/test/parallel/zlib-premature-end.test.js b/test/js/node/test/parallel/zlib-premature-end.test.js deleted file mode 100644 index cb20184ac0..0000000000 --- a/test/js/node/test/parallel/zlib-premature-end.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-zlib-premature-end.js -//#SHA1: f44e08e4886032467eb9cf64412c7eda2b575a16 -//----------------- -"use strict"; -const zlib = require("zlib"); - -const input = "0123456789".repeat(4); - -const testCases = [ - [zlib.deflateRawSync, zlib.createInflateRaw], - [zlib.deflateSync, zlib.createInflate], - [zlib.brotliCompressSync, zlib.createBrotliDecompress], -]; - -const variants = [ - (stream, compressed, trailingData) => { - stream.end(compressed); - }, - (stream, compressed, trailingData) => { - stream.write(compressed); - stream.write(trailingData); - }, - (stream, compressed, trailingData) => { - stream.write(compressed); - stream.end(trailingData); - }, - (stream, compressed, trailingData) => { - stream.write(Buffer.concat([compressed, trailingData])); - }, - (stream, compressed, trailingData) => { - stream.end(Buffer.concat([compressed, trailingData])); - }, -]; - -describe("zlib premature end tests", () => { - testCases.forEach(([compress, decompressor]) => { - describe(`${compress.name} and ${decompressor.name}`, () => { - const compressed = compress(input); - const trailingData = Buffer.from("not valid compressed data"); - - variants.forEach((variant, index) => { - test(`variant ${index + 1}`, done => { - let output = ""; - const stream = decompressor(); - stream.setEncoding("utf8"); - stream.on("data", chunk => (output += chunk)); - stream.on("end", () => { - expect(output).toBe(input); - expect(stream.bytesWritten).toBe(compressed.length); - done(); - }); - variant(stream, compressed, trailingData); - }); - }); - }); - }); -}); - -//<#END_FILE: test-zlib-premature-end.js diff --git a/test/js/node/test/parallel/zlib-reset-before-write.test.js b/test/js/node/test/parallel/zlib-reset-before-write.test.js deleted file mode 100644 index fd997b8a51..0000000000 --- a/test/js/node/test/parallel/zlib-reset-before-write.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-zlib-reset-before-write.js -//#SHA1: 44561d35a5b7e4fc363d7dbde7ec6891af1f338a -//----------------- -"use strict"; -const zlib = require("zlib"); - -// Tests that zlib streams support .reset() and .params() -// before the first write. That is important to ensure that -// lazy init of zlib native library handles these cases. - -const testCases = [ - (z, cb) => { - z.reset(); - cb(); - }, - (z, cb) => z.params(0, zlib.constants.Z_DEFAULT_STRATEGY, cb), -]; - -testCases.forEach((fn, index) => { - test(`zlib stream supports ${index === 0 ? ".reset()" : ".params()"} before first write`, done => { - const deflate = zlib.createDeflate(); - const inflate = zlib.createInflate(); - - deflate.pipe(inflate); - - const output = []; - inflate - .on("error", err => { - expect(err).toBeFalsy(); - }) - .on("data", chunk => output.push(chunk)) - .on("end", () => { - expect(Buffer.concat(output).toString()).toBe("abc"); - done(); - }); - - fn(deflate, () => { - fn(inflate, () => { - deflate.write("abc"); - deflate.end(); - }); - }); - }); -}); - -//<#END_FILE: test-zlib-reset-before-write.js diff --git a/test/js/node/test/parallel/zlib-sync-no-event.test.js b/test/js/node/test/parallel/zlib-sync-no-event.test.js deleted file mode 100644 index 9e821acfe9..0000000000 --- a/test/js/node/test/parallel/zlib-sync-no-event.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-zlib-sync-no-event.js -//#SHA1: 382796f607eb25a85aa067e0dbc3d5103d321def -//----------------- -"use strict"; -const zlib = require("zlib"); - -const message = "Come on, Fhqwhgads."; -const buffer = Buffer.from(message); - -test("zlib sync compression and decompression without events", () => { - const zipper = new zlib.Gzip(); - const closeSpy = jest.fn(); - zipper.on("close", closeSpy); - - const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); - - const unzipper = new zlib.Gunzip(); - const unzipperCloseSpy = jest.fn(); - unzipper.on("close", unzipperCloseSpy); - - const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); - - expect(zipped).toEqual( - // prettier-ignore - Buffer.from([ 31, 139, 8, 0, 0, 0, 0, 0, 0, osbyte(), 115, 206, 207, 77, 85, 200, 207, 211, 81, 112, 203, 40, 44, 207, 72, 79, 76, 41, 214, 3, 0, 160, 120, 128, 220, 19, 0, 0, 0 ]), - ); - expect(unzipped.toString()).toEqual(message); - - expect(closeSpy).not.toHaveBeenCalled(); - expect(unzipperCloseSpy).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-zlib-sync-no-event.js - -function osbyte() { - if (process.platform === "darwin") return 19; - if (process.platform === "linux") return 3; - if (process.platform === "win32") return 10; -} diff --git a/test/js/node/test/parallel/zlib-truncated.test.js b/test/js/node/test/parallel/zlib-truncated.test.js deleted file mode 100644 index 703ce49c04..0000000000 --- a/test/js/node/test/parallel/zlib-truncated.test.js +++ /dev/null @@ -1,99 +0,0 @@ -//#FILE: test-zlib-truncated.js -//#SHA1: 79f9bcf3c52b3d0736ebe457652d579a856d1f7b -//----------------- -"use strict"; -// Tests zlib streams with truncated compressed input - -const zlib = require("zlib"); - -const inputString = - "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + - "t. Morbi faucibus, purus at gravida dictum, libero arcu " + - "convallis lacus, in commodo libero metus eu nisi. Nullam" + - " commodo, neque nec porta placerat, nisi est fermentum a" + - "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + - "n non diam orci. Proin quis elit turpis. Suspendisse non" + - " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + - "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + - "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; - -const errMessage = /unexpected end of file/; - -const methods = [ - { comp: "gzip", decomp: "gunzip", decompSync: "gunzipSync" }, - { comp: "gzip", decomp: "unzip", decompSync: "unzipSync" }, - { comp: "deflate", decomp: "inflate", decompSync: "inflateSync" }, - { comp: "deflateRaw", decomp: "inflateRaw", decompSync: "inflateRawSync" }, -]; - -methods.forEach(({ comp, decomp, decompSync }) => { - test(`Test ${comp} and ${decomp}`, async () => { - const compressed = await new Promise((resolve, reject) => { - zlib[comp](inputString, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - - const truncated = compressed.slice(0, compressed.length / 2); - const toUTF8 = buffer => buffer.toString("utf-8"); - - // sync sanity - const decompressed = zlib[decompSync](compressed); - expect(toUTF8(decompressed)).toBe(inputString); - - // async sanity - await new Promise((resolve, reject) => { - zlib[decomp](compressed, (err, result) => { - if (err) reject(err); - else { - expect(toUTF8(result)).toBe(inputString); - resolve(); - } - }); - }); - - // Sync truncated input test - expect(() => { - zlib[decompSync](truncated); - }).toThrow( - expect.objectContaining({ - message: expect.stringMatching(errMessage), - }), - ); - - // Async truncated input test - await expect( - new Promise((resolve, reject) => { - zlib[decomp](truncated, err => { - if (err) reject(err); - else resolve(); - }); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(errMessage), - }), - ); - - const syncFlushOpt = { finishFlush: zlib.constants.Z_SYNC_FLUSH }; - - // Sync truncated input test, finishFlush = Z_SYNC_FLUSH - const result = toUTF8(zlib[decompSync](truncated, syncFlushOpt)); - expect(result).toBe(inputString.slice(0, result.length)); - - // Async truncated input test, finishFlush = Z_SYNC_FLUSH - await new Promise((resolve, reject) => { - zlib[decomp](truncated, syncFlushOpt, (err, decompressed) => { - if (err) reject(err); - else { - const result = toUTF8(decompressed); - expect(result).toBe(inputString.slice(0, result.length)); - resolve(); - } - }); - }); - }); -}); - -//<#END_FILE: test-zlib-truncated.js diff --git a/test/js/node/test/parallel/zlib-unzip-one-byte-chunks.test.js b/test/js/node/test/parallel/zlib-unzip-one-byte-chunks.test.js deleted file mode 100644 index f4fa2a0795..0000000000 --- a/test/js/node/test/parallel/zlib-unzip-one-byte-chunks.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-zlib-unzip-one-byte-chunks.js -//#SHA1: 3c242140501ae0e8e9277c68696c231a04070018 -//----------------- -"use strict"; -const zlib = require("zlib"); - -test("zlib unzip one byte chunks", done => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def")]); - - const resultBuffers = []; - - const unzip = zlib - .createUnzip() - .on("error", err => { - expect(err).toBeFalsy(); - }) - .on("data", data => resultBuffers.push(data)) - .on("finish", () => { - const unzipped = Buffer.concat(resultBuffers).toString(); - expect(unzipped).toBe("abcdef"); - done(); - }); - - for (let i = 0; i < data.length; i++) { - // Write each single byte individually. - unzip.write(Buffer.from([data[i]])); - } - - unzip.end(); -}); - -//<#END_FILE: test-zlib-unzip-one-byte-chunks.js diff --git a/test/js/node/test/parallel/zlib-write-after-close.test.js b/test/js/node/test/parallel/zlib-write-after-close.test.js deleted file mode 100644 index a778b7731c..0000000000 --- a/test/js/node/test/parallel/zlib-write-after-close.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-zlib-write-after-close.js -//#SHA1: 5e09b22ace4e546969c9d31f686446adda15fbd2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const zlib = require("node:zlib"); - -test("zlib should not allow writing after close", async () => { - const closeCallback = jest.fn(); - - await new Promise(resolve => { - zlib.gzip("hello", function () { - const unzip = zlib.createGunzip(); - unzip.close(closeCallback); - unzip.write("asd", function (err) { - expect(err).toEqual( - expect.objectContaining({ - code: "ERR_STREAM_DESTROYED", - name: "Error", - message: "Cannot call write after a stream was destroyed", - }), - ); - resolve(); - }); - }); - }); - - expect(closeCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-zlib-write-after-close.js diff --git a/test/js/node/test/parallel/zlib-write-after-end.test.js b/test/js/node/test/parallel/zlib-write-after-end.test.js deleted file mode 100644 index c4e0586e65..0000000000 --- a/test/js/node/test/parallel/zlib-write-after-end.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-zlib-write-after-end.js -//#SHA1: 0d11ed6c9992b52c81a45bdb3d6fe0db4ab2681a -//----------------- -"use strict"; -const zlib = require("zlib"); - -// Regression test for https://github.com/nodejs/node/issues/30976 -// Writes to a stream should finish even after the readable side has been ended. - -test("zlib write after end", done => { - const data = zlib.deflateRawSync("Welcome"); - - const inflate = zlib.createInflateRaw(); - - inflate.resume(); - - const writeCallback = jest.fn(); - - inflate.write(data, writeCallback); - inflate.write(Buffer.from([0x00]), writeCallback); - inflate.write(Buffer.from([0x00]), writeCallback); - - inflate.flush(() => { - expect(writeCallback).toHaveBeenCalledTimes(3); - done(); - }); -}); - -//<#END_FILE: test-zlib-write-after-end.js diff --git a/test/js/node/test/parallel/zlib-write-after-flush.test.js b/test/js/node/test/parallel/zlib-write-after-flush.test.js deleted file mode 100644 index 3bdcfb8d4f..0000000000 --- a/test/js/node/test/parallel/zlib-write-after-flush.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-zlib-write-after-flush.js -//#SHA1: 2cd2c91ba7a105560cdf6831ece0e95174aac860 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -const compressionMethods = [ - [zlib.createGzip, zlib.createGunzip], - [zlib.createBrotliCompress, zlib.createBrotliDecompress], -]; - -compressionMethods.forEach(([createCompress, createDecompress]) => { - test(`${createCompress.name} and ${createDecompress.name}`, done => { - const gzip = createCompress(); - const gunz = createDecompress(); - - gzip.pipe(gunz); - - let output = ""; - const input = "A line of data\n"; - gunz.setEncoding("utf8"); - gunz.on("data", c => (output += c)); - gunz.on("end", () => { - expect(output).toBe(input); - done(); - }); - - // Make sure that flush/write doesn't trigger an assert failure - gzip.flush(); - gzip.write(input); - gzip.end(); - gunz.read(0); - }); -}); - -//<#END_FILE: test-zlib-write-after-flush.js diff --git a/test/js/node/test/parallel/zlib-zero-byte.test.js b/test/js/node/test/parallel/zlib-zero-byte.test.js deleted file mode 100644 index 762ee875f2..0000000000 --- a/test/js/node/test/parallel/zlib-zero-byte.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-zlib-zero-byte.js -//#SHA1: 54539c28619fb98230547ba0929ddad146f15bc5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -for (const Compressor of [zlib.Gzip, zlib.BrotliCompress]) { - test(`${Compressor.name} compresses zero-byte input`, async () => { - const gz = Compressor(); - const emptyBuffer = Buffer.alloc(0); - let received = 0; - - gz.on("data", c => { - received += c.length; - }); - - const endPromise = new Promise(resolve => { - gz.on("end", resolve); - }); - - const finishPromise = new Promise(resolve => { - gz.on("finish", resolve); - }); - - gz.write(emptyBuffer); - gz.end(); - - await Promise.all([endPromise, finishPromise]); - - const expected = Compressor === zlib.Gzip ? 20 : 1; - expect(received).toBe(expected); - }); -} - -//<#END_FILE: test-zlib-zero-byte.js diff --git a/test/js/node/test/parallel/zlib-zero-windowbits.test.js b/test/js/node/test/parallel/zlib-zero-windowbits.test.js deleted file mode 100644 index 32f4225a30..0000000000 --- a/test/js/node/test/parallel/zlib-zero-windowbits.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-zlib-zero-windowBits.js -//#SHA1: 3f1f031d2f5ab37f2ea2a963d6de0e2ececa9d33 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -// windowBits is a special case in zlib. On the compression side, 0 is invalid. -// On the decompression side, it indicates that zlib should use the value from -// the header of the compressed stream. -test("windowBits 0 for decompression", () => { - const inflate = zlib.createInflate({ windowBits: 0 }); - expect(inflate).toBeInstanceOf(zlib.Inflate); - - const gunzip = zlib.createGunzip({ windowBits: 0 }); - expect(gunzip).toBeInstanceOf(zlib.Gunzip); - - const unzip = zlib.createUnzip({ windowBits: 0 }); - expect(unzip).toBeInstanceOf(zlib.Unzip); -}); - -test("windowBits 0 for compression throws error", () => { - expect(() => zlib.createGzip({ windowBits: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-zero-windowBits.js