summaryrefslogtreecommitdiff
path: root/tests/unit_node/process_test.ts
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2024-02-10 13:22:13 -0700
committerGitHub <noreply@github.com>2024-02-10 20:22:13 +0000
commitf5e46c9bf2f50d66a953fa133161fc829cecff06 (patch)
tree8faf2f5831c1c7b11d842cd9908d141082c869a5 /tests/unit_node/process_test.ts
parentd2477f780630a812bfd65e3987b70c0d309385bb (diff)
chore: move cli/tests/ -> tests/ (#22369)
This looks like a massive PR, but it's only a move from cli/tests -> tests, and updates of relative paths for files. This is the first step towards aggregate all of the integration test files under tests/, which will lead to a set of integration tests that can run without the CLI binary being built. While we could leave these tests under `cli`, it would require us to keep a more complex directory structure for the various test runners. In addition, we have a lot of complexity to ignore various test files in the `cli` project itself (cargo publish exclusion rules, autotests = false, etc). And finally, the `tests/` folder will eventually house the `test_ffi`, `test_napi` and other testing code, reducing the size of the root repo directory. For easier review, the extremely large and noisy "move" is in the first commit (with no changes -- just a move), while the remainder of the changes to actual files is in the second commit.
Diffstat (limited to 'tests/unit_node/process_test.ts')
-rw-r--r--tests/unit_node/process_test.ts986
1 files changed, 986 insertions, 0 deletions
diff --git a/tests/unit_node/process_test.ts b/tests/unit_node/process_test.ts
new file mode 100644
index 000000000..4f4703d35
--- /dev/null
+++ b/tests/unit_node/process_test.ts
@@ -0,0 +1,986 @@
+// deno-lint-ignore-file no-undef
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+import process, { argv, env } from "node:process";
+import { Readable } from "node:stream";
+import { once } from "node:events";
+import {
+ assert,
+ assertEquals,
+ assertFalse,
+ assertObjectMatch,
+ assertStrictEquals,
+ assertThrows,
+} from "@test_util/std/assert/mod.ts";
+import { stripColor } from "@test_util/std/fmt/colors.ts";
+import * as path from "@test_util/std/path/mod.ts";
+import { delay } from "@test_util/std/async/delay.ts";
+
+const testDir = new URL(".", import.meta.url);
+
+Deno.test({
+ name: "process.cwd and process.chdir success",
+ fn() {
+ assertEquals(process.cwd(), Deno.cwd());
+
+ const currentDir = Deno.cwd();
+
+ const tempDir = Deno.makeTempDirSync();
+ process.chdir(tempDir);
+ assertEquals(
+ Deno.realPathSync(process.cwd()),
+ Deno.realPathSync(tempDir),
+ );
+
+ process.chdir(currentDir);
+ },
+});
+
+Deno.test({
+ name: "process.chdir failure",
+ fn() {
+ assertThrows(
+ () => {
+ process.chdir("non-existent-directory-name");
+ },
+ Deno.errors.NotFound,
+ "file",
+ // On every OS Deno returns: "No such file" except for Windows, where it's:
+ // "The system cannot find the file specified. (os error 2)" so "file" is
+ // the only common string here.
+ );
+ },
+});
+
+Deno.test({
+ name: "process.version",
+ fn() {
+ assertEquals(typeof process, "object");
+ assertEquals(typeof process.version, "string");
+ assertEquals(typeof process.versions, "object");
+ assertEquals(typeof process.versions.node, "string");
+ assertEquals(typeof process.versions.v8, "string");
+ assertEquals(typeof process.versions.uv, "string");
+ assertEquals(typeof process.versions.zlib, "string");
+ assertEquals(typeof process.versions.brotli, "string");
+ assertEquals(typeof process.versions.ares, "string");
+ assertEquals(typeof process.versions.modules, "string");
+ assertEquals(typeof process.versions.nghttp2, "string");
+ assertEquals(typeof process.versions.napi, "string");
+ assertEquals(typeof process.versions.llhttp, "string");
+ assertEquals(typeof process.versions.openssl, "string");
+ assertEquals(typeof process.versions.cldr, "string");
+ assertEquals(typeof process.versions.icu, "string");
+ assertEquals(typeof process.versions.tz, "string");
+ assertEquals(typeof process.versions.unicode, "string");
+ // These two are not present in `process.versions` in Node, but we
+ // add them anyway
+ assertEquals(typeof process.versions.deno, "string");
+ assertEquals(typeof process.versions.typescript, "string");
+ },
+});
+
+Deno.test({
+ name: "process.platform",
+ fn() {
+ assertEquals(typeof process.platform, "string");
+ },
+});
+
+Deno.test({
+ name: "process.mainModule",
+ fn() {
+ assertEquals(process.mainModule, undefined);
+ // Check that it is writable
+ // @ts-ignore these are deprecated now
+ process.mainModule = "foo";
+ // @ts-ignore these are deprecated now
+ assertEquals(process.mainModule, "foo");
+ },
+});
+
+Deno.test({
+ name: "process.arch",
+ fn() {
+ assertEquals(typeof process.arch, "string");
+ if (Deno.build.arch == "x86_64") {
+ assertEquals(process.arch, "x64");
+ } else if (Deno.build.arch == "aarch64") {
+ assertEquals(process.arch, "arm64");
+ } else {
+ throw new Error("unreachable");
+ }
+ },
+});
+
+Deno.test({
+ name: "process.pid",
+ fn() {
+ assertEquals(typeof process.pid, "number");
+ assertEquals(process.pid, Deno.pid);
+ },
+});
+
+Deno.test({
+ name: "process.ppid",
+ fn() {
+ assertEquals(typeof process.ppid, "number");
+ assertEquals(process.ppid, Deno.ppid);
+ },
+});
+
+Deno.test({
+ name: "process.on",
+ async fn() {
+ assertEquals(typeof process.on, "function");
+
+ let triggered = false;
+ process.on("exit", () => {
+ triggered = true;
+ });
+ // @ts-ignore fix the type here
+ process.emit("exit");
+ assert(triggered);
+
+ const cwd = path.dirname(path.fromFileUrl(import.meta.url));
+
+ const command = new Deno.Command(Deno.execPath(), {
+ args: [
+ "run",
+ "--quiet",
+ "--unstable",
+ "./testdata/process_exit.ts",
+ ],
+ cwd,
+ });
+ const { stdout } = await command.output();
+
+ const decoder = new TextDecoder();
+ assertEquals(stripColor(decoder.decode(stdout).trim()), "1\n2");
+ },
+});
+
+Deno.test({
+ name: "process.on signal",
+ ignore: Deno.build.os == "windows",
+ async fn() {
+ const process = new Deno.Command(Deno.execPath(), {
+ args: [
+ "eval",
+ `
+ import process from "node:process";
+ setInterval(() => {}, 1000);
+ process.on("SIGINT", () => {
+ console.log("foo");
+ });
+ `,
+ ],
+ stdout: "piped",
+ stderr: "null",
+ }).spawn();
+ await delay(500);
+ for (const _ of Array(3)) {
+ process.kill("SIGINT");
+ await delay(20);
+ }
+ await delay(20);
+ process.kill("SIGTERM");
+ const output = await process.output();
+ assertEquals(new TextDecoder().decode(output.stdout), "foo\nfoo\nfoo\n");
+ },
+});
+
+Deno.test({
+ name: "process.off signal",
+ ignore: Deno.build.os == "windows",
+ async fn() {
+ const process = new Deno.Command(Deno.execPath(), {
+ args: [
+ "eval",
+ `
+ import process from "node:process";
+ setInterval(() => {}, 1000);
+ const listener = () => {
+ console.log("foo");
+ process.off("SIGINT")
+ };
+ process.on("SIGINT", listener);
+ `,
+ ],
+ stdout: "piped",
+ stderr: "null",
+ }).spawn();
+ await delay(500);
+ for (const _ of Array(3)) {
+ try {
+ process.kill("SIGINT");
+ } catch { /* should die after the first one */ }
+ await delay(20);
+ }
+ await delay(20);
+ try {
+ process.kill("SIGTERM");
+ } catch { /* should be dead, avoid hanging just in case */ }
+ const output = await process.output();
+ assertEquals(new TextDecoder().decode(output.stdout), "foo\n");
+ },
+});
+
+Deno.test({
+ name: "process.on SIGBREAK doesn't throw",
+ fn() {
+ const listener = () => {};
+ process.on("SIGBREAK", listener);
+ process.off("SIGBREAK", listener);
+ },
+});
+
+Deno.test({
+ name: "process.on SIGTERM doesn't throw on windows",
+ ignore: Deno.build.os !== "windows",
+ fn() {
+ const listener = () => {};
+ process.on("SIGTERM", listener);
+ process.off("SIGTERM", listener);
+ },
+});
+
+Deno.test({
+ name: "process.argv",
+ fn() {
+ assert(Array.isArray(argv));
+ assert(Array.isArray(process.argv));
+ assert(
+ process.argv[0].match(/[^/\\]*deno[^/\\]*$/),
+ "deno included in the file name of argv[0]",
+ );
+ assertEquals(
+ process.argv[1],
+ path.fromFileUrl(Deno.mainModule),
+ );
+ // argv supports array methods.
+ assert(Array.isArray(process.argv.slice(2)));
+ assertEquals(process.argv.indexOf(Deno.execPath()), 0);
+ assertEquals(process.argv.indexOf(path.fromFileUrl(Deno.mainModule)), 1);
+ },
+});
+
+Deno.test({
+ name: "process.argv0",
+ fn() {
+ assertEquals(typeof process.argv0, "string");
+ assert(
+ process.argv0.match(/[^/\\]*deno[^/\\]*$/),
+ "deno included in the file name of argv[0]",
+ );
+ // Setting should be a noop
+ process.argv0 = "foobar";
+ assert(
+ process.argv0.match(/[^/\\]*deno[^/\\]*$/),
+ "deno included in the file name of argv[0]",
+ );
+ },
+});
+
+Deno.test({
+ name: "process.execArgv",
+ fn() {
+ assert(Array.isArray(process.execArgv));
+ assert(process.execArgv.length == 0);
+ // execArgv supports array methods.
+ assert(Array.isArray(process.argv.slice(0)));
+ assertEquals(process.argv.indexOf("foo"), -1);
+ },
+});
+
+Deno.test({
+ name: "process.env",
+ fn() {
+ Deno.env.set("HELLO", "WORLD");
+
+ assertObjectMatch(process.env, Deno.env.toObject());
+
+ assertEquals(typeof (process.env.HELLO), "string");
+ assertEquals(process.env.HELLO, "WORLD");
+
+ assertEquals(typeof env.HELLO, "string");
+ assertEquals(env.HELLO, "WORLD");
+
+ assert(Object.getOwnPropertyNames(process.env).includes("HELLO"));
+ assert(Object.keys(process.env).includes("HELLO"));
+
+ assert(Object.prototype.hasOwnProperty.call(process.env, "HELLO"));
+ assert(
+ !Object.prototype.hasOwnProperty.call(
+ process.env,
+ "SURELY_NON_EXISTENT_VAR",
+ ),
+ );
+
+ // deno-lint-ignore no-prototype-builtins
+ assert(process.env.hasOwnProperty("HELLO"));
+ assert("HELLO" in process.env);
+ assert(Object.keys(process.env.valueOf()).includes("HELLO"));
+
+ assertEquals(process.env.toString(), "[object Object]");
+ assertEquals(process.env.toLocaleString(), "[object Object]");
+
+ // should not error when assigning false to an env var
+ process.env.HELLO = false as unknown as string;
+ assertEquals(process.env.HELLO, "false");
+ process.env.HELLO = "WORLD";
+ assertEquals(process.env.HELLO, "WORLD");
+ },
+});
+
+Deno.test({
+ name: "process.env requires scoped env permission",
+ permissions: { env: ["FOO"] },
+ fn() {
+ Deno.env.set("FOO", "1");
+ assert("FOO" in process.env);
+ assertFalse("BAR" in process.env);
+ assert(Object.hasOwn(process.env, "FOO"));
+ assertFalse(Object.hasOwn(process.env, "BAR"));
+ },
+});
+
+Deno.test({
+ name: "process.env doesn't throw with invalid env var names",
+ fn() {
+ assertEquals(process.env[""], undefined);
+ assertEquals(process.env["\0"], undefined);
+ assertEquals(process.env["=c:"], undefined);
+ assertFalse(Object.hasOwn(process.env, ""));
+ assertFalse(Object.hasOwn(process.env, "\0"));
+ assertFalse(Object.hasOwn(process.env, "=c:"));
+ assertFalse("" in process.env);
+ assertFalse("\0" in process.env);
+ assertFalse("=c:" in process.env);
+ },
+});
+
+Deno.test({
+ name: "process.stdin",
+ fn() {
+ assertEquals(process.stdin.fd, Deno.stdin.rid);
+ assertEquals(process.stdin.isTTY, Deno.stdin.isTerminal());
+ },
+});
+
+Deno.test({
+ name: "process.stdin readable with a TTY",
+ // TODO(PolarETech): Run this test even in non tty environment
+ ignore: !Deno.stdin.isTerminal(),
+ // stdin resource is present before the test starts.
+ sanitizeResources: false,
+ async fn() {
+ const { promise, resolve } = Promise.withResolvers<void>();
+ const expected = ["foo", "bar", null, "end"];
+ const data: (string | null)[] = [];
+
+ process.stdin.setEncoding("utf8");
+ process.stdin.on("readable", () => {
+ data.push(process.stdin.read());
+ });
+ process.stdin.on("end", () => {
+ data.push("end");
+ });
+
+ process.stdin.push("foo");
+ process.nextTick(() => {
+ process.stdin.push("bar");
+ process.nextTick(() => {
+ process.stdin.push(null);
+ resolve();
+ });
+ });
+
+ await promise;
+ assertEquals(process.stdin.readableHighWaterMark, 0);
+ assertEquals(data, expected);
+ },
+});
+
+Deno.test({
+ name: "process.stdin readable with piping a file",
+ async fn() {
+ const expected = ["65536", "foo", "bar", "null", "end"];
+ const scriptPath = "./testdata/process_stdin.ts";
+ const filePath = "./testdata/process_stdin_dummy.txt";
+
+ const shell = Deno.build.os === "windows" ? "cmd.exe" : "/bin/sh";
+ const cmd = `"${Deno.execPath()}" run ${scriptPath} < ${filePath}`;
+ const args = Deno.build.os === "windows" ? ["/d", "/c", cmd] : ["-c", cmd];
+
+ const p = new Deno.Command(shell, {
+ args,
+ stdin: "null",
+ stdout: "piped",
+ stderr: "null",
+ windowsRawArguments: true,
+ cwd: testDir,
+ });
+
+ const { stdout } = await p.output();
+ const data = new TextDecoder().decode(stdout).trim().split("\n");
+ assertEquals(data, expected);
+ },
+});
+
+Deno.test({
+ name: "process.stdin readable with piping a stream",
+ async fn() {
+ const expected = ["16384", "foo", "bar", "null", "end"];
+ const scriptPath = "./testdata/process_stdin.ts";
+
+ const command = new Deno.Command(Deno.execPath(), {
+ args: ["run", scriptPath],
+ stdin: "piped",
+ stdout: "piped",
+ stderr: "null",
+ cwd: testDir,
+ });
+ const child = command.spawn();
+
+ const writer = await child.stdin.getWriter();
+ writer.ready
+ .then(() => writer.write(new TextEncoder().encode("foo\nbar")))
+ .then(() => writer.releaseLock())
+ .then(() => child.stdin.close());
+
+ const { stdout } = await child.output();
+ const data = new TextDecoder().decode(stdout).trim().split("\n");
+ assertEquals(data, expected);
+ },
+});
+
+Deno.test({
+ name: "process.stdin readable with piping a socket",
+ ignore: Deno.build.os === "windows",
+ async fn() {
+ const expected = ["16384", "foo", "bar", "null", "end"];
+ const scriptPath = "./testdata/process_stdin.ts";
+
+ const listener = Deno.listen({ hostname: "127.0.0.1", port: 9000 });
+ listener.accept().then(async (conn) => {
+ await conn.write(new TextEncoder().encode("foo\nbar"));
+ conn.close();
+ listener.close();
+ });
+
+ const shell = "/bin/bash";
+ const cmd =
+ `"${Deno.execPath()}" run ${scriptPath} < /dev/tcp/127.0.0.1/9000`;
+ const args = ["-c", cmd];
+
+ const p = new Deno.Command(shell, {
+ args,
+ stdin: "null",
+ stdout: "piped",
+ stderr: "null",
+ cwd: testDir,
+ });
+
+ const { stdout } = await p.output();
+ const data = new TextDecoder().decode(stdout).trim().split("\n");
+ assertEquals(data, expected);
+ },
+});
+
+Deno.test({
+ name: "process.stdin readable with null",
+ async fn() {
+ const expected = ["65536", "null", "end"];
+ const scriptPath = "./testdata/process_stdin.ts";
+
+ const command = new Deno.Command(Deno.execPath(), {
+ args: ["run", scriptPath],
+ stdin: "null",
+ stdout: "piped",
+ stderr: "null",
+ cwd: testDir,
+ });
+
+ const { stdout } = await command.output();
+ const data = new TextDecoder().decode(stdout).trim().split("\n");
+ assertEquals(data, expected);
+ },
+});
+
+// TODO(kt3k): Enable this test case. 'readable' event handler in
+// `process_stdin.ts` doesn't work now
+Deno.test({
+ name: "process.stdin readable with unsuitable stdin",
+ ignore: true,
+ // // TODO(PolarETech): Prepare a similar test that can be run on Windows
+ // ignore: Deno.build.os === "windows",
+ async fn() {
+ const expected = ["16384", "null", "end"];
+ const scriptPath = "./testdata/process_stdin.ts";
+ const directoryPath = "./testdata/";
+
+ const shell = "/bin/bash";
+ const cmd = `"${Deno.execPath()}" run ${scriptPath} < ${directoryPath}`;
+ const args = ["-c", cmd];
+
+ const p = new Deno.Command(shell, {
+ args,
+ stdin: "null",
+ stdout: "piped",
+ stderr: "null",
+ windowsRawArguments: true,
+ cwd: testDir,
+ });
+
+ const { stdout } = await p.output();
+ const data = new TextDecoder().decode(stdout).trim().split("\n");
+ assertEquals(data, expected);
+ },
+});
+
+Deno.test({
+ name: "process.stdout",
+ fn() {
+ assertEquals(process.stdout.fd, Deno.stdout.rid);
+ const isTTY = Deno.stdout.isTerminal();
+ assertEquals(process.stdout.isTTY, isTTY);
+ const consoleSize = isTTY ? Deno.consoleSize() : undefined;
+ assertEquals(process.stdout.columns, consoleSize?.columns);
+ assertEquals(process.stdout.rows, consoleSize?.rows);
+ assertEquals(
+ `${process.stdout.getWindowSize()}`,
+ `${consoleSize && [consoleSize.columns, consoleSize.rows]}`,
+ );
+
+ if (isTTY) {
+ assertStrictEquals(process.stdout.cursorTo(1, 2, () => {}), true);
+ assertStrictEquals(process.stdout.moveCursor(3, 4, () => {}), true);
+ assertStrictEquals(process.stdout.clearLine(1, () => {}), true);
+ assertStrictEquals(process.stdout.clearScreenDown(() => {}), true);
+ } else {
+ assertStrictEquals(process.stdout.cursorTo, undefined);
+ assertStrictEquals(process.stdout.moveCursor, undefined);
+ assertStrictEquals(process.stdout.clearLine, undefined);
+ assertStrictEquals(process.stdout.clearScreenDown, undefined);
+ }
+ },
+});
+
+Deno.test({
+ name: "process.stderr",
+ fn() {
+ assertEquals(process.stderr.fd, Deno.stderr.rid);
+ const isTTY = Deno.stderr.isTerminal();
+ assertEquals(process.stderr.isTTY, isTTY);
+ const consoleSize = isTTY ? Deno.consoleSize() : undefined;
+ assertEquals(process.stderr.columns, consoleSize?.columns);
+ assertEquals(process.stderr.rows, consoleSize?.rows);
+ assertEquals(
+ `${process.stderr.getWindowSize()}`,
+ `${consoleSize && [consoleSize.columns, consoleSize.rows]}`,
+ );
+
+ if (isTTY) {
+ assertStrictEquals(process.stderr.cursorTo(1, 2, () => {}), true);
+ assertStrictEquals(process.stderr.moveCursor(3, 4, () => {}), true);
+ assertStrictEquals(process.stderr.clearLine(1, () => {}), true);
+ assertStrictEquals(process.stderr.clearScreenDown(() => {}), true);
+ } else {
+ assertStrictEquals(process.stderr.cursorTo, undefined);
+ assertStrictEquals(process.stderr.moveCursor, undefined);
+ assertStrictEquals(process.stderr.clearLine, undefined);
+ assertStrictEquals(process.stderr.clearScreenDown, undefined);
+ }
+ },
+});
+
+Deno.test({
+ name: "process.nextTick",
+ async fn() {
+ let withoutArguments = false;
+ process.nextTick(() => {
+ withoutArguments = true;
+ });
+
+ const expected = 12;
+ let result;
+ process.nextTick((x: number) => {
+ result = x;
+ }, 12);
+
+ await delay(10);
+ assert(withoutArguments);
+ assertEquals(result, expected);
+ },
+});
+
+Deno.test({
+ name: "process.hrtime",
+ // TODO(kt3k): Enable this test
+ ignore: true,
+ fn() {
+ const [sec0, nano0] = process.hrtime();
+ // seconds and nano seconds are positive integers.
+ assert(sec0 > 0);
+ assert(Number.isInteger(sec0));
+ assert(nano0 > 0);
+ assert(Number.isInteger(nano0));
+
+ const [sec1, nano1] = process.hrtime();
+ // the later call returns bigger value
+ assert(sec1 >= sec0);
+ assert(nano1 > nano0);
+
+ const [sec2, nano2] = process.hrtime([sec1, nano1]);
+ // the difference of the 2 calls is a small positive value.
+ assertEquals(sec2, 0);
+ assert(nano2 > 0);
+ },
+});
+
+Deno.test({
+ name: "process.hrtime.bigint",
+ fn() {
+ const time = process.hrtime.bigint();
+ assertEquals(typeof time, "bigint");
+ assert(time > 0n);
+ },
+});
+
+Deno.test("process.on, process.off, process.removeListener doesn't throw on unimplemented events", () => {
+ const events = [
+ "beforeExit",
+ "disconnect",
+ "message",
+ "multipleResolves",
+ "rejectionHandled",
+ "uncaughtException",
+ "uncaughtExceptionMonitor",
+ "unhandledRejection",
+ "worker",
+ ];
+ const handler = () => {};
+ events.forEach((ev) => {
+ process.on(ev, handler);
+ assertEquals(process.listenerCount(ev), 1);
+ process.off(ev, handler);
+ assertEquals(process.listenerCount(ev), 0);
+ process.on(ev, handler);
+ assertEquals(process.listenerCount(ev), 1);
+ process.removeListener(ev, handler);
+ assertEquals(process.listenerCount(ev), 0);
+ });
+});
+
+Deno.test("process.memoryUsage()", () => {
+ const mem = process.memoryUsage();
+ assert(typeof mem.rss === "number");
+ assert(typeof mem.heapTotal === "number");
+ assert(typeof mem.heapUsed === "number");
+ assert(typeof mem.external === "number");
+ assert(typeof mem.arrayBuffers === "number");
+ assertEquals(mem.arrayBuffers, 0);
+});
+
+Deno.test("process.memoryUsage.rss()", () => {
+ const rss = process.memoryUsage.rss();
+ assert(typeof rss === "number");
+});
+
+Deno.test("process.exitCode", () => {
+ assertEquals(process.exitCode, undefined);
+ process.exitCode = 127;
+ assertEquals(process.exitCode, 127);
+ // deno-lint-ignore no-explicit-any
+ (process.exitCode as any) = "asdf";
+ // deno-lint-ignore no-explicit-any
+ assertEquals(process.exitCode as any, "asdf");
+ // deno-lint-ignore no-explicit-any
+ (process.exitCode as any) = "10";
+ process.exitCode = undefined; // reset
+});
+
+async function exitCodeTest(codeText: string, expectedExitCode: number) {
+ const command = new Deno.Command(Deno.execPath(), {
+ args: [
+ "eval",
+ codeText,
+ ],
+ cwd: testDir,
+ });
+ const { code } = await command.output();
+ assertEquals(code, expectedExitCode);
+}
+
+Deno.test("process.exitCode in should change exit code", async () => {
+ await exitCodeTest(
+ "import process from 'node:process'; process.exitCode = 127;",
+ 127,
+ );
+ await exitCodeTest(
+ "import process from 'node:process'; process.exitCode = 2.5;",
+ 2,
+ );
+ await exitCodeTest(
+ "import process from 'node:process'; process.exitCode = '10';",
+ 10,
+ );
+ await exitCodeTest(
+ "import process from 'node:process'; process.exitCode = '0x10';",
+ 16,
+ );
+ await exitCodeTest(
+ "import process from 'node:process'; process.exitCode = NaN;",
+ 0,
+ );
+});
+
+Deno.test("Deno.exit should override process exit", async () => {
+ await exitCodeTest(
+ "import process from 'node:process'; process.exitCode = 10; Deno.exit(12);",
+ 12,
+ );
+});
+
+Deno.test("process.config", () => {
+ assert(process.config !== undefined);
+ assert(process.config.target_defaults !== undefined);
+ assert(process.config.variables !== undefined);
+});
+
+Deno.test("process._exiting", () => {
+ // @ts-ignore fix the type here
+ assert(process._exiting === false);
+});
+
+Deno.test("process.execPath", () => {
+ assertEquals(process.execPath, process.argv[0]);
+});
+
+Deno.test("process.execPath is writable", () => {
+ // pnpm writes to process.execPath
+ // https://github.com/pnpm/pnpm/blob/67d8b65d2e8da1df3725034b8c5b1fcf3af4ad81/packages/config/src/index.ts#L175
+ const originalExecPath = process.execPath;
+ try {
+ process.execPath = "/path/to/node";
+ assertEquals(process.execPath, "/path/to/node");
+ } finally {
+ process.execPath = originalExecPath;
+ }
+});
+
+Deno.test("process.getgid", () => {
+ if (Deno.build.os === "windows") {
+ assertEquals(process.getgid, undefined);
+ } else {
+ assertEquals(process.getgid?.(), Deno.gid());
+ }
+});
+
+Deno.test("process.getuid", () => {
+ if (Deno.build.os === "windows") {
+ assertEquals(process.getuid, undefined);
+ } else {
+ assertEquals(process.getuid?.(), Deno.uid());
+ }
+});
+
+Deno.test("process.geteuid", () => {
+ if (Deno.build.os === "windows") {
+ assertEquals(process.geteuid, undefined);
+ } else {
+ assert(typeof process.geteuid?.() === "number");
+ }
+});
+
+Deno.test({
+ name: "process.exit",
+ async fn() {
+ const command = new Deno.Command(Deno.execPath(), {
+ args: [
+ "run",
+ "--quiet",
+ "--unstable",
+ "./testdata/process_exit2.ts",
+ ],
+ cwd: testDir,
+ });
+ const { stdout } = await command.output();
+
+ const decoder = new TextDecoder();
+ assertEquals(stripColor(decoder.decode(stdout).trim()), "exit");
+ },
+});
+
+Deno.test({
+ name: "process.reallyExit",
+ async fn() {
+ const command = new Deno.Command(Deno.execPath(), {
+ args: [
+ "run",
+ "--quiet",
+ "--unstable",
+ "./testdata/process_really_exit.ts",
+ ],
+ cwd: testDir,
+ });
+ const { stdout } = await command.output();
+
+ const decoder = new TextDecoder();
+ assertEquals(stripColor(decoder.decode(stdout).trim()), "really exited");
+ },
+});
+
+Deno.test({
+ name: "process.stdout isn't closed when source stream ended",
+ async fn() {
+ const source = Readable.from(["foo", "bar"]);
+
+ source.pipe(process.stdout);
+ await once(source, "end");
+
+ // Wait a bit to ensure that streaming is completely finished.
+ await delay(10);
+
+ // This checks if the rid 1 is still valid.
+ assert(typeof process.stdout.isTTY === "boolean");
+ },
+});
+
+Deno.test({
+ name: "process.title",
+ fn() {
+ assertEquals(process.title, "deno");
+ // Verify that setting the value has no effect.
+ process.title = "foo";
+ assertEquals(process.title, "deno");
+ },
+});
+
+Deno.test({
+ name: "process.argv[1] in Worker",
+ async fn() {
+ const worker = new Worker(
+ `data:text/javascript,import process from "node:process";console.log(process.argv[1]);`,
+ { type: "module" },
+ );
+ await delay(10);
+ worker.terminate();
+ },
+});
+
+Deno.test({
+ name: "process.binding('uv').errname",
+ ignore: Deno.build.os === "windows",
+ fn() {
+ // @ts-ignore: untyped internal binding, not actually supposed to be
+ // used by userland modules in Node.js
+ const uv = process.binding("uv");
+ assert(uv.errname);
+ assert(typeof uv.errname === "function");
+ assertEquals(uv.errname(-1), "EPERM");
+ },
+});
+
+Deno.test({
+ name: "process.report",
+ fn() {
+ // The process.report is marked as possibly undefined in node 18 typings
+ if (!process.report) throw "No process report";
+
+ assert(typeof process.report.directory === "string");
+ assert(typeof process.report.filename === "string");
+ assert(typeof process.report.getReport === "function");
+ assert(typeof process.report.reportOnFatalError === "boolean");
+ assert(typeof process.report.reportOnSignal === "boolean");
+ assert(typeof process.report.reportOnUncaughtException === "boolean");
+ assert(typeof process.report.signal === "string");
+ assert(typeof process.report.writeReport === "function");
+ },
+});
+
+Deno.test({
+ name: "process.report.writeReport unimplemented result",
+ fn() {
+ // The process.report is marked as possibly undefined in node 18 typings
+ if (!process.report) throw "No process report";
+
+ assertEquals(process.report.writeReport(), "");
+ },
+});
+
+Deno.test({
+ name: "process.report.getReport result",
+ fn() {
+ // The process.report is marked as possibly undefined in node 18 typings
+ if (!process.report) throw "No process report";
+
+ // deno-lint-ignore no-explicit-any
+ const result = process.report.getReport() as any;
+
+ // test and remove dynamic parts
+ assert(typeof result.header.filename === "string");
+ delete result.header.filename;
+ assert(typeof result.header.dumpEventTime === "object");
+ delete result.header.dumpEventTime;
+ assert(typeof result.header.dumpEventTimeStamp === "number");
+ delete result.header.dumpEventTimeStamp;
+ assert(typeof result.header.processId === "number");
+ delete result.header.processId;
+ assert(typeof result.header.cwd === "string");
+ delete result.header.cwd;
+ assert(typeof result.header.nodejsVersion === "string");
+ assert(result.header.nodejsVersion.startsWith("v"));
+ delete result.header.nodejsVersion;
+ assert(typeof result.header.arch === "string");
+ delete result.header.arch;
+ assert(typeof result.header.platform === "string");
+ delete result.header.platform;
+ assert(typeof result.header.componentVersions === "object");
+ delete result.header.componentVersions;
+ assert(typeof result.header.osName === "string");
+ delete result.header.osName;
+ assert(typeof result.header.osMachine === "string");
+ delete result.header.osMachine;
+ assert(Array.isArray(result.header.cpus));
+ delete result.header.cpus;
+ assert(typeof result.header.networkInterfaces === "object");
+ delete result.header.networkInterfaces;
+ assert(typeof result.header.host === "string");
+ delete result.header.host;
+
+ // test hardcoded part
+ assertEquals(result, {
+ header: {
+ reportVersion: 3,
+ event: "JavaScript API",
+ trigger: "GetReport",
+ threadId: 0,
+ commandLine: ["node"],
+ glibcVersionRuntime: "2.38",
+ glibcVersionCompiler: "2.38",
+ wordSize: 64,
+ release: {
+ name: "node",
+ headersUrl:
+ "https://nodejs.org/download/release/v21.2.0/node-v21.2.0-headers.tar.gz",
+ sourceUrl:
+ "https://nodejs.org/download/release/v21.2.0/node-v21.2.0.tar.gz",
+ },
+ osRelease: undefined,
+ osVersion: undefined,
+ },
+ javascriptStack: undefined,
+ javascriptHeap: undefined,
+ nativeStack: undefined,
+ resourceUsage: undefined,
+ uvthreadResourceUsage: undefined,
+ libuv: undefined,
+ workers: [],
+ environmentVariables: undefined,
+ userLimits: undefined,
+ sharedObjects: undefined,
+ });
+ },
+});