mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 08:39:09 -05:00
test: add unit tests from std/node (#17794)
Adds two test files: "cli/tests/unit_node/process_test.ts" and "cli/tests/unit_node/child_process_test.ts" --------- Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
This commit is contained in:
parent
848e2c0d57
commit
4c2380af5c
26 changed files with 1471 additions and 80 deletions
|
@ -70,6 +70,8 @@ mod js_unit_tests;
|
|||
mod lint;
|
||||
#[path = "lsp_tests.rs"]
|
||||
mod lsp;
|
||||
#[path = "node_unit_tests.rs"]
|
||||
mod node_unit_tests;
|
||||
#[path = "npm_tests.rs"]
|
||||
mod npm;
|
||||
#[path = "repl_tests.rs"]
|
||||
|
|
21
cli/tests/integration/node_unit_tests.rs
Normal file
21
cli/tests/integration/node_unit_tests.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use test_util as util;
|
||||
|
||||
#[test]
|
||||
fn node_unit_tests() {
|
||||
let _g = util::http_server();
|
||||
|
||||
let mut deno = util::deno_cmd()
|
||||
.current_dir(util::root_path())
|
||||
.arg("test")
|
||||
.arg("--unstable")
|
||||
.arg("-A")
|
||||
.arg(util::tests_path().join("unit_node"))
|
||||
.spawn()
|
||||
.expect("failed to spawn script");
|
||||
|
||||
let status = deno.wait().expect("failed to wait for the child process");
|
||||
assert_eq!(Some(0), status.code());
|
||||
assert!(status.success());
|
||||
}
|
577
cli/tests/unit_node/child_process_test.ts
Normal file
577
cli/tests/unit_node/child_process_test.ts
Normal file
|
@ -0,0 +1,577 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import CP from "node:child_process";
|
||||
import { Buffer } from "node:buffer";
|
||||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
assertExists,
|
||||
assertNotStrictEquals,
|
||||
assertStrictEquals,
|
||||
assertStringIncludes,
|
||||
} from "../../../test_util/std/testing/asserts.ts";
|
||||
import { Deferred, deferred } from "../../../test_util/std/async/deferred.ts";
|
||||
import * as path from "../../../test_util/std/path/mod.ts";
|
||||
|
||||
const { spawn, execFile, execFileSync, ChildProcess } = CP;
|
||||
|
||||
function withTimeout<T>(timeoutInMS: number): Deferred<T> {
|
||||
const promise = deferred<T>();
|
||||
const timer = setTimeout(() => {
|
||||
promise.reject("Timeout");
|
||||
}, timeoutInMS);
|
||||
promise.then(() => {
|
||||
clearTimeout(timer);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
// TODO(uki00a): Once Node.js's `parallel/test-child-process-spawn-error.js` works, this test case should be removed.
|
||||
Deno.test("[node/child_process spawn] The 'error' event is emitted when no binary is found", async () => {
|
||||
const promise = withTimeout(1000);
|
||||
const childProcess = spawn("no-such-cmd");
|
||||
childProcess.on("error", (_err: Error) => {
|
||||
// TODO(@bartlomieju) Assert an error message.
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
Deno.test("[node/child_process spawn] The 'exit' event is emitted with an exit code after the child process ends", async () => {
|
||||
const promise = withTimeout(3000);
|
||||
const childProcess = spawn(Deno.execPath(), ["--help"], {
|
||||
env: { NO_COLOR: "true" },
|
||||
});
|
||||
try {
|
||||
let exitCode = null;
|
||||
childProcess.on("exit", (code: number) => {
|
||||
promise.resolve();
|
||||
exitCode = code;
|
||||
});
|
||||
await promise;
|
||||
assertStrictEquals(exitCode, 0);
|
||||
assertStrictEquals(childProcess.exitCode, exitCode);
|
||||
} finally {
|
||||
childProcess.kill();
|
||||
childProcess.stdout?.destroy();
|
||||
childProcess.stderr?.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("[node/child_process disconnect] the method exists", async () => {
|
||||
const promise = withTimeout(1000);
|
||||
const childProcess = spawn(Deno.execPath(), ["--help"], {
|
||||
env: { NO_COLOR: "true" },
|
||||
});
|
||||
try {
|
||||
childProcess.disconnect();
|
||||
childProcess.on("exit", () => {
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
} finally {
|
||||
childProcess.kill();
|
||||
childProcess.stdout?.destroy();
|
||||
childProcess.stderr?.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process spawn] Verify that stdin and stdout work",
|
||||
fn: async () => {
|
||||
const promise = withTimeout(3000);
|
||||
const childProcess = spawn(Deno.execPath(), ["fmt", "-"], {
|
||||
env: { NO_COLOR: "true" },
|
||||
stdio: ["pipe", "pipe"],
|
||||
});
|
||||
try {
|
||||
assert(childProcess.stdin, "stdin should be defined");
|
||||
assert(childProcess.stdout, "stdout should be defined");
|
||||
let data = "";
|
||||
childProcess.stdout.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
childProcess.stdin.write(" console.log('hello')", "utf-8");
|
||||
childProcess.stdin.end();
|
||||
childProcess.on("close", () => {
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
assertStrictEquals(data, `console.log("hello");\n`);
|
||||
} finally {
|
||||
childProcess.kill();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process spawn] stdin and stdout with binary data",
|
||||
fn: async () => {
|
||||
const promise = withTimeout(10000);
|
||||
const p = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"./testdata/binary_stdio.js",
|
||||
);
|
||||
const childProcess = spawn(Deno.execPath(), ["run", p], {
|
||||
env: { NO_COLOR: "true" },
|
||||
stdio: ["pipe", "pipe"],
|
||||
});
|
||||
try {
|
||||
assert(childProcess.stdin, "stdin should be defined");
|
||||
assert(childProcess.stdout, "stdout should be defined");
|
||||
let data: Buffer;
|
||||
childProcess.stdout.on("data", (chunk) => {
|
||||
data = chunk;
|
||||
});
|
||||
const buffer = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
childProcess.stdin.write(buffer);
|
||||
childProcess.stdin.end();
|
||||
childProcess.on("close", () => {
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
assertEquals(new Uint8Array(data!), buffer);
|
||||
} finally {
|
||||
childProcess.kill();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
async function spawnAndGetEnvValue(
|
||||
inputValue: string | number | boolean,
|
||||
): Promise<string> {
|
||||
const promise = withTimeout<string>(3000);
|
||||
const env = spawn(
|
||||
`"${Deno.execPath()}" eval -p "Deno.env.toObject().BAZ"`,
|
||||
{
|
||||
env: { BAZ: String(inputValue), NO_COLOR: "true" },
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
try {
|
||||
let envOutput = "";
|
||||
|
||||
assert(env.stdout);
|
||||
env.on("error", (err: Error) => promise.reject(err));
|
||||
env.stdout.on("data", (data) => {
|
||||
envOutput += data;
|
||||
});
|
||||
env.on("close", () => {
|
||||
promise.resolve(envOutput.trim());
|
||||
});
|
||||
return await promise;
|
||||
} finally {
|
||||
env.kill();
|
||||
}
|
||||
}
|
||||
|
||||
Deno.test({
|
||||
ignore: Deno.build.os === "windows",
|
||||
name:
|
||||
"[node/child_process spawn] Verify that environment values can be numbers",
|
||||
async fn() {
|
||||
const envOutputValue = await spawnAndGetEnvValue(42);
|
||||
assertStrictEquals(envOutputValue, "42");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
ignore: Deno.build.os === "windows",
|
||||
name:
|
||||
"[node/child_process spawn] Verify that environment values can be booleans",
|
||||
async fn() {
|
||||
const envOutputValue = await spawnAndGetEnvValue(false);
|
||||
assertStrictEquals(envOutputValue, "false");
|
||||
},
|
||||
});
|
||||
|
||||
/* Start of ported part */ 3;
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Ported from Node 15.5.1
|
||||
|
||||
// TODO(uki00a): Remove this case once Node's `parallel/test-child-process-spawn-event.js` works.
|
||||
Deno.test("[child_process spawn] 'spawn' event", async () => {
|
||||
const timeout = withTimeout(3000);
|
||||
const subprocess = spawn(Deno.execPath(), ["eval", "console.log('ok')"]);
|
||||
|
||||
let didSpawn = false;
|
||||
subprocess.on("spawn", function () {
|
||||
didSpawn = true;
|
||||
});
|
||||
|
||||
function mustNotBeCalled() {
|
||||
timeout.reject(new Error("function should not have been called"));
|
||||
}
|
||||
|
||||
const promises = [] as Promise<void>[];
|
||||
function mustBeCalledAfterSpawn() {
|
||||
const promise = deferred<void>();
|
||||
promises.push(promise);
|
||||
return () => {
|
||||
if (didSpawn) {
|
||||
promise.resolve();
|
||||
} else {
|
||||
promise.reject(
|
||||
new Error("function should be called after the 'spawn' event"),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
subprocess.on("error", mustNotBeCalled);
|
||||
subprocess.stdout!.on("data", mustBeCalledAfterSpawn());
|
||||
subprocess.stdout!.on("end", mustBeCalledAfterSpawn());
|
||||
subprocess.stdout!.on("close", mustBeCalledAfterSpawn());
|
||||
subprocess.stderr!.on("data", mustNotBeCalled);
|
||||
subprocess.stderr!.on("end", mustBeCalledAfterSpawn());
|
||||
subprocess.stderr!.on("close", mustBeCalledAfterSpawn());
|
||||
subprocess.on("exit", mustBeCalledAfterSpawn());
|
||||
subprocess.on("close", mustBeCalledAfterSpawn());
|
||||
|
||||
try {
|
||||
await Promise.race([Promise.all(promises), timeout]);
|
||||
timeout.resolve();
|
||||
} finally {
|
||||
subprocess.kill();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(uki00a): Remove this case once Node's `parallel/test-child-process-spawn-shell.js` works.
|
||||
Deno.test("[child_process spawn] Verify that a shell is executed", async () => {
|
||||
const promise = withTimeout(3000);
|
||||
const doesNotExist = spawn("does-not-exist", { shell: true });
|
||||
try {
|
||||
assertNotStrictEquals(doesNotExist.spawnfile, "does-not-exist");
|
||||
doesNotExist.on("error", () => {
|
||||
promise.reject("The 'error' event must not be emitted.");
|
||||
});
|
||||
doesNotExist.on("exit", (code: number, signal: null) => {
|
||||
assertStrictEquals(signal, null);
|
||||
|
||||
if (Deno.build.os === "windows") {
|
||||
assertStrictEquals(code, 1); // Exit code of cmd.exe
|
||||
} else {
|
||||
assertStrictEquals(code, 127); // Exit code of /bin/sh });
|
||||
}
|
||||
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
} finally {
|
||||
doesNotExist.kill();
|
||||
doesNotExist.stdout?.destroy();
|
||||
doesNotExist.stderr?.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(uki00a): Remove this case once Node's `parallel/test-child-process-spawn-shell.js` works.
|
||||
Deno.test({
|
||||
ignore: Deno.build.os === "windows",
|
||||
name: "[node/child_process spawn] Verify that passing arguments works",
|
||||
async fn() {
|
||||
const promise = withTimeout(3000);
|
||||
const echo = spawn("echo", ["foo"], {
|
||||
shell: true,
|
||||
});
|
||||
let echoOutput = "";
|
||||
|
||||
try {
|
||||
assertStrictEquals(
|
||||
echo.spawnargs[echo.spawnargs.length - 1].replace(/"/g, ""),
|
||||
"echo foo",
|
||||
);
|
||||
assert(echo.stdout);
|
||||
echo.stdout.on("data", (data) => {
|
||||
echoOutput += data;
|
||||
});
|
||||
echo.on("close", () => {
|
||||
assertStrictEquals(echoOutput.trim(), "foo");
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
} finally {
|
||||
echo.kill();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// TODO(uki00a): Remove this case once Node's `parallel/test-child-process-spawn-shell.js` works.
|
||||
Deno.test({
|
||||
ignore: Deno.build.os === "windows",
|
||||
name: "[node/child_process spawn] Verity that shell features can be used",
|
||||
async fn() {
|
||||
const promise = withTimeout(3000);
|
||||
const cmd = "echo bar | cat";
|
||||
const command = spawn(cmd, {
|
||||
shell: true,
|
||||
});
|
||||
try {
|
||||
let commandOutput = "";
|
||||
|
||||
assert(command.stdout);
|
||||
command.stdout.on("data", (data) => {
|
||||
commandOutput += data;
|
||||
});
|
||||
|
||||
command.on("close", () => {
|
||||
assertStrictEquals(commandOutput.trim(), "bar");
|
||||
promise.resolve();
|
||||
});
|
||||
|
||||
await promise;
|
||||
} finally {
|
||||
command.kill();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// TODO(uki00a): Remove this case once Node's `parallel/test-child-process-spawn-shell.js` works.
|
||||
Deno.test({
|
||||
ignore: Deno.build.os === "windows",
|
||||
name:
|
||||
"[node/child_process spawn] Verity that environment is properly inherited",
|
||||
async fn() {
|
||||
const promise = withTimeout(3000);
|
||||
const env = spawn(
|
||||
`"${Deno.execPath()}" eval -p "Deno.env.toObject().BAZ"`,
|
||||
{
|
||||
env: { BAZ: "buzz", NO_COLOR: "true" },
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
try {
|
||||
let envOutput = "";
|
||||
|
||||
assert(env.stdout);
|
||||
env.on("error", (err: Error) => promise.reject(err));
|
||||
env.stdout.on("data", (data) => {
|
||||
envOutput += data;
|
||||
});
|
||||
env.on("close", () => {
|
||||
assertStrictEquals(envOutput.trim(), "buzz");
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
} finally {
|
||||
env.kill();
|
||||
}
|
||||
},
|
||||
});
|
||||
/* End of ported part */
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process execFile] Get stdout as a string",
|
||||
async fn() {
|
||||
let child: unknown;
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"./testdata/exec_file_text_output.js",
|
||||
);
|
||||
const promise = new Promise<string | null>((resolve, reject) => {
|
||||
child = execFile(Deno.execPath(), ["run", script], (err, stdout) => {
|
||||
if (err) reject(err);
|
||||
else if (stdout) resolve(stdout as string);
|
||||
else resolve(null);
|
||||
});
|
||||
});
|
||||
try {
|
||||
const stdout = await promise;
|
||||
assertEquals(stdout, "Hello World!\n");
|
||||
} finally {
|
||||
if (child instanceof ChildProcess) {
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process execFile] Get stdout as a buffer",
|
||||
async fn() {
|
||||
let child: unknown;
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"./testdata/exec_file_text_output.js",
|
||||
);
|
||||
const promise = new Promise<Buffer | null>((resolve, reject) => {
|
||||
child = execFile(
|
||||
Deno.execPath(),
|
||||
["run", script],
|
||||
{ encoding: "buffer" },
|
||||
(err, stdout) => {
|
||||
if (err) reject(err);
|
||||
else if (stdout) resolve(stdout as Buffer);
|
||||
else resolve(null);
|
||||
},
|
||||
);
|
||||
});
|
||||
try {
|
||||
const stdout = await promise;
|
||||
assert(Buffer.isBuffer(stdout));
|
||||
assertEquals(stdout.toString("utf8"), "Hello World!\n");
|
||||
} finally {
|
||||
if (child instanceof ChildProcess) {
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process execFile] Get stderr",
|
||||
async fn() {
|
||||
let child: unknown;
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"./testdata/exec_file_text_error.js",
|
||||
);
|
||||
const promise = new Promise<
|
||||
{ err: Error | null; stderr?: string | Buffer }
|
||||
>((resolve) => {
|
||||
child = execFile(Deno.execPath(), ["run", script], (err, _, stderr) => {
|
||||
resolve({ err, stderr });
|
||||
});
|
||||
});
|
||||
try {
|
||||
const { err, stderr } = await promise;
|
||||
if (child instanceof ChildProcess) {
|
||||
assertEquals(child.exitCode, 1);
|
||||
assertEquals(stderr, "yikes!\n");
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
if (child instanceof ChildProcess) {
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process execFile] Exceed given maxBuffer limit",
|
||||
async fn() {
|
||||
let child: unknown;
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"./testdata/exec_file_text_error.js",
|
||||
);
|
||||
const promise = new Promise<
|
||||
{ err: Error | null; stderr?: string | Buffer }
|
||||
>((resolve) => {
|
||||
child = execFile(Deno.execPath(), ["run", script], {
|
||||
encoding: "buffer",
|
||||
maxBuffer: 3,
|
||||
}, (err, _, stderr) => {
|
||||
resolve({ err, stderr });
|
||||
});
|
||||
});
|
||||
try {
|
||||
const { err, stderr } = await promise;
|
||||
if (child instanceof ChildProcess) {
|
||||
assert(err);
|
||||
assertEquals(
|
||||
// deno-lint-ignore no-explicit-any
|
||||
(err as any).code,
|
||||
"ERR_CHILD_PROCESS_STDIO_MAXBUFFER",
|
||||
);
|
||||
assertEquals(err.message, "stderr maxBuffer length exceeded");
|
||||
assertEquals((stderr as Buffer).toString("utf8"), "yik");
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
if (child instanceof ChildProcess) {
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process] ChildProcess.kill()",
|
||||
async fn() {
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"./testdata/infinite_loop.js",
|
||||
);
|
||||
const childProcess = spawn(Deno.execPath(), ["run", script]);
|
||||
const p = withTimeout(3000);
|
||||
childProcess.on("exit", () => p.resolve());
|
||||
childProcess.kill("SIGKILL");
|
||||
await p;
|
||||
assert(childProcess.killed);
|
||||
assertEquals(childProcess.signalCode, "SIGKILL");
|
||||
assertExists(childProcess.exitCode);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process] ChildProcess.unref()",
|
||||
async fn() {
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"testdata",
|
||||
"child_process_unref.js",
|
||||
);
|
||||
const childProcess = spawn(Deno.execPath(), [
|
||||
"run",
|
||||
"-A",
|
||||
"--unstable",
|
||||
script,
|
||||
]);
|
||||
const p = deferred();
|
||||
childProcess.on("exit", () => p.resolve());
|
||||
await p;
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process] child_process.fork",
|
||||
async fn() {
|
||||
const testdataDir = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"testdata",
|
||||
);
|
||||
const script = path.join(
|
||||
testdataDir,
|
||||
"node_modules",
|
||||
"foo",
|
||||
"index.js",
|
||||
);
|
||||
const p = deferred();
|
||||
const cp = CP.fork(script, [], { cwd: testdataDir, stdio: "pipe" });
|
||||
let output = "";
|
||||
cp.on("close", () => p.resolve());
|
||||
cp.stdout?.on("data", (data) => {
|
||||
output += data;
|
||||
});
|
||||
await p;
|
||||
assertEquals(output, "foo\ntrue\ntrue\ntrue\n");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test("[node/child_process execFileSync] 'inherit' stdout and stderr", () => {
|
||||
execFileSync(Deno.execPath(), ["--help"], { stdio: "inherit" });
|
||||
});
|
||||
|
||||
Deno.test(
|
||||
"[node/child_process spawn] supports windowsVerbatimArguments option",
|
||||
{ ignore: Deno.build.os !== "windows" },
|
||||
async () => {
|
||||
const cmdFinished = deferred();
|
||||
let output = "";
|
||||
const cp = spawn("cmd", ["/d", "/s", "/c", '"deno ^"--version^""'], {
|
||||
stdio: "pipe",
|
||||
windowsVerbatimArguments: true,
|
||||
});
|
||||
cp.on("close", () => cmdFinished.resolve());
|
||||
cp.stdout?.on("data", (data) => {
|
||||
output += data;
|
||||
});
|
||||
await cmdFinished;
|
||||
assertStringIncludes(output, "deno");
|
||||
assertStringIncludes(output, "v8");
|
||||
assertStringIncludes(output, "typescript");
|
||||
},
|
||||
);
|
713
cli/tests/unit_node/process_test.ts
Normal file
713
cli/tests/unit_node/process_test.ts
Normal file
|
@ -0,0 +1,713 @@
|
|||
// deno-lint-ignore-file no-undef
|
||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import process, { argv, env } from "node:process";
|
||||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
assertFalse,
|
||||
assertObjectMatch,
|
||||
assertStrictEquals,
|
||||
assertThrows,
|
||||
} from "../../../test_util/std/testing/asserts.ts";
|
||||
import { stripColor } from "../../../test_util/std/fmt/colors.ts";
|
||||
import { deferred } from "../../../test_util/std/async/deferred.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.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 promise = deferred();
|
||||
let c = 0;
|
||||
const listener = () => {
|
||||
c += 1;
|
||||
};
|
||||
process.on("SIGINT", listener);
|
||||
setTimeout(async () => {
|
||||
// Sends SIGINT 3 times.
|
||||
for (const _ of Array(3)) {
|
||||
await delay(20);
|
||||
Deno.kill(Deno.pid, "SIGINT");
|
||||
}
|
||||
await delay(20);
|
||||
Deno.removeSignalListener("SIGINT", listener);
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
assertEquals(c, 3);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "process.off signal",
|
||||
ignore: Deno.build.os == "windows",
|
||||
async fn() {
|
||||
const promise = deferred();
|
||||
let c = 0;
|
||||
const listener = () => {
|
||||
c += 1;
|
||||
process.off("SIGINT", listener);
|
||||
};
|
||||
process.on("SIGINT", listener);
|
||||
setTimeout(async () => {
|
||||
// Sends SIGINT 3 times.
|
||||
for (const _ of Array(3)) {
|
||||
await delay(20);
|
||||
Deno.kill(Deno.pid, "SIGINT");
|
||||
}
|
||||
await delay(20);
|
||||
promise.resolve();
|
||||
});
|
||||
await promise;
|
||||
assertEquals(c, 1);
|
||||
},
|
||||
});
|
||||
|
||||
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.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.isatty(Deno.stdin.rid));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "process.stdin readable with a TTY",
|
||||
// TODO(PolarETech): Run this test even in non tty environment
|
||||
ignore: !Deno.isatty(Deno.stdin.rid),
|
||||
async fn() {
|
||||
const promise = deferred();
|
||||
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);
|
||||
promise.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.isatty(Deno.stdout.rid);
|
||||
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.isatty(Deno.stderr.rid);
|
||||
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", () => {
|
||||
assert(process.exitCode === undefined);
|
||||
process.exitCode = 127;
|
||||
assert(process.exitCode === 127);
|
||||
});
|
||||
|
||||
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({
|
||||
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");
|
||||
},
|
||||
});
|
11
cli/tests/unit_node/testdata/binary_stdio.js
vendored
Normal file
11
cli/tests/unit_node/testdata/binary_stdio.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
const buffer = new Uint8Array(10);
|
||||
const nread = await Deno.stdin.read(buffer);
|
||||
|
||||
if (nread != 10) {
|
||||
throw new Error("Too little data read");
|
||||
}
|
||||
|
||||
const nwritten = await Deno.stdout.write(buffer);
|
||||
if (nwritten != 10) {
|
||||
throw new Error("Too little data written");
|
||||
}
|
9
cli/tests/unit_node/testdata/child_process_unref.js
vendored
Normal file
9
cli/tests/unit_node/testdata/child_process_unref.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
import cp from "node:child_process";
|
||||
import * as path from "node:path";
|
||||
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"infinite_loop.js",
|
||||
);
|
||||
const childProcess = cp.spawn(Deno.execPath(), ["run", script]);
|
||||
childProcess.unref();
|
2
cli/tests/unit_node/testdata/exec_file_text_error.js
vendored
Normal file
2
cli/tests/unit_node/testdata/exec_file_text_error.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
console.error("yikes!");
|
||||
Deno.exit(1);
|
1
cli/tests/unit_node/testdata/exec_file_text_output.js
vendored
Normal file
1
cli/tests/unit_node/testdata/exec_file_text_output.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
console.log("Hello World!");
|
3
cli/tests/unit_node/testdata/infinite_loop.js
vendored
Normal file
3
cli/tests/unit_node/testdata/infinite_loop.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
while (true) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
4
cli/tests/unit_node/testdata/node_modules/foo/index.js
generated
vendored
Normal file
4
cli/tests/unit_node/testdata/node_modules/foo/index.js
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
console.log("foo");
|
||||
console.log(typeof require === "function");
|
||||
console.log(typeof module === "object");
|
||||
console.log(typeof exports === "object");
|
3
cli/tests/unit_node/testdata/node_modules/foo/package.json
generated
vendored
Normal file
3
cli/tests/unit_node/testdata/node_modules/foo/package.json
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "foo"
|
||||
}
|
19
cli/tests/unit_node/testdata/process_exit.ts
vendored
Normal file
19
cli/tests/unit_node/testdata/process_exit.ts
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
import process from "node:process";
|
||||
|
||||
//deno-lint-ignore no-undef
|
||||
process.on("exit", () => {
|
||||
console.log(1);
|
||||
});
|
||||
|
||||
function unexpected() {
|
||||
console.log(null);
|
||||
}
|
||||
//deno-lint-ignore no-undef
|
||||
process.on("exit", unexpected);
|
||||
//deno-lint-ignore no-undef
|
||||
process.removeListener("exit", unexpected);
|
||||
|
||||
//deno-lint-ignore no-undef
|
||||
process.on("exit", () => {
|
||||
console.log(2);
|
||||
});
|
4
cli/tests/unit_node/testdata/process_exit2.ts
vendored
Normal file
4
cli/tests/unit_node/testdata/process_exit2.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
import process from "node:process";
|
||||
|
||||
process.on("exit", () => console.log("exit"));
|
||||
process.exit();
|
11
cli/tests/unit_node/testdata/process_stdin.ts
vendored
Normal file
11
cli/tests/unit_node/testdata/process_stdin.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
import process from "node:process";
|
||||
|
||||
console.log(process.stdin.readableHighWaterMark);
|
||||
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("readable", () => {
|
||||
console.log(process.stdin.read());
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
console.log("end");
|
||||
});
|
2
cli/tests/unit_node/testdata/process_stdin_dummy.txt
vendored
Normal file
2
cli/tests/unit_node/testdata/process_stdin_dummy.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
foo
|
||||
bar
|
|
@ -841,14 +841,7 @@ impl JsRuntime {
|
|||
)
|
||||
.await?;
|
||||
let receiver = runtime.mod_evaluate(id);
|
||||
poll_fn(|cx| {
|
||||
let r = runtime.poll_event_loop(cx, false);
|
||||
// TODO(bartlomieju): some code in readable-stream polyfill in `ext/node`
|
||||
// is calling `nextTick()` during snapshotting, which causes infinite loop
|
||||
runtime.state.borrow_mut().has_tick_scheduled = false;
|
||||
r
|
||||
})
|
||||
.await?;
|
||||
runtime.run_event_loop(false).await?;
|
||||
receiver.await?
|
||||
})
|
||||
.with_context(|| format!("Couldn't execute '{}'", file_source.specifier))
|
||||
|
|
|
@ -110,7 +110,7 @@ function initialize(nodeModules, nodeGlobalThisName) {
|
|||
value: nodeGlobalThis,
|
||||
});
|
||||
// FIXME(bartlomieju): not nice to depend on `Deno` namespace here
|
||||
internals.__bootstrapNodeProcess(Deno.args);
|
||||
internals.__bootstrapNodeProcess(Deno.args, Deno.version);
|
||||
}
|
||||
|
||||
internals.node = {
|
||||
|
|
|
@ -11,6 +11,11 @@ interface Tock {
|
|||
args: Array<unknown>;
|
||||
}
|
||||
|
||||
let nextTickEnabled = false;
|
||||
export function enableNextTick() {
|
||||
nextTickEnabled = true;
|
||||
}
|
||||
|
||||
const queue = new FixedQueue();
|
||||
|
||||
export function processTicksAndRejections() {
|
||||
|
@ -71,8 +76,6 @@ export function runNextTicks() {
|
|||
// return;
|
||||
if (!core.hasTickScheduled()) {
|
||||
core.runMicrotasks();
|
||||
}
|
||||
if (!core.hasTickScheduled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -93,6 +96,12 @@ export function nextTick<T extends Array<unknown>>(
|
|||
callback: (...args: T) => void,
|
||||
...args: T
|
||||
) {
|
||||
// If we're snapshotting we don't want to push nextTick to be run. We'll
|
||||
// enable next ticks in "__bootstrapNodeProcess()";
|
||||
if (!nextTickEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateFunction(callback, "callback");
|
||||
|
||||
if (_exiting) {
|
||||
|
|
|
@ -126,5 +126,8 @@ export const versions = {
|
|||
unicode: "14.0",
|
||||
ngtcp2: "0.8.1",
|
||||
nghttp3: "0.7.0",
|
||||
...Deno.version,
|
||||
// Will be filled when calling "__bootstrapNodeProcess()",
|
||||
deno: "",
|
||||
v8: "",
|
||||
typescript: "",
|
||||
};
|
||||
|
|
|
@ -9,13 +9,12 @@ import {
|
|||
moveCursor,
|
||||
} from "internal:deno_node/polyfills/internal/readline/callbacks.mjs";
|
||||
import { Duplex, Readable, Writable } from "internal:deno_node/polyfills/stream.ts";
|
||||
import { stdio } from "internal:deno_node/polyfills/_process/stdio.mjs";
|
||||
import { isWindows } from "internal:deno_node/polyfills/_util/os.ts";
|
||||
import { fs as fsConstants } from "internal:deno_node/polyfills/internal_binding/constants.ts";
|
||||
import * as files from "internal:runtime/js/40_files.js";
|
||||
|
||||
// https://github.com/nodejs/node/blob/00738314828074243c9a52a228ab4c68b04259ef/lib/internal/bootstrap/switches/is_main_thread.js#L41
|
||||
function createWritableStdioStream(writer, name) {
|
||||
export function createWritableStdioStream(writer, name) {
|
||||
const stream = new Writable({
|
||||
write(buf, enc, cb) {
|
||||
if (!writer) {
|
||||
|
@ -92,18 +91,6 @@ function createWritableStdioStream(writer, name) {
|
|||
return stream;
|
||||
}
|
||||
|
||||
/** https://nodejs.org/api/process.html#process_process_stderr */
|
||||
export const stderr = stdio.stderr = createWritableStdioStream(
|
||||
files.stderr,
|
||||
"stderr",
|
||||
);
|
||||
|
||||
/** https://nodejs.org/api/process.html#process_process_stdout */
|
||||
export const stdout = stdio.stdout = createWritableStdioStream(
|
||||
files.stdout,
|
||||
"stdout",
|
||||
);
|
||||
|
||||
// TODO(PolarETech): This function should be replaced by
|
||||
// `guessHandleType()` in "../internal_binding/util.ts".
|
||||
// https://github.com/nodejs/node/blob/v18.12.1/src/node_util.cc#L257
|
||||
|
@ -162,9 +149,10 @@ const _read = function (size) {
|
|||
|
||||
/** https://nodejs.org/api/process.html#process_process_stdin */
|
||||
// https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L189
|
||||
export const stdin = stdio.stdin = (() => {
|
||||
/** Create process.stdin */
|
||||
export const initStdin = () => {
|
||||
const fd = files.stdin?.rid;
|
||||
let _stdin;
|
||||
let stdin;
|
||||
const stdinType = _guessStdinType(fd);
|
||||
|
||||
switch (stdinType) {
|
||||
|
@ -173,7 +161,7 @@ export const stdin = stdio.stdin = (() => {
|
|||
// use `Readable` instead.
|
||||
// https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L200
|
||||
// https://github.com/nodejs/node/blob/v18.12.1/lib/internal/fs/streams.js#L148
|
||||
_stdin = new Readable({
|
||||
stdin = new Readable({
|
||||
highWaterMark: 64 * 1024,
|
||||
autoDestroy: false,
|
||||
read: _read,
|
||||
|
@ -197,7 +185,7 @@ export const stdin = stdio.stdin = (() => {
|
|||
// 2. Creating a net.Socket() from a fd is not currently supported.
|
||||
// https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L206
|
||||
// https://github.com/nodejs/node/blob/v18.12.1/lib/net.js#L329
|
||||
_stdin = new Duplex({
|
||||
stdin = new Duplex({
|
||||
readable: stdinType === "TTY" ? undefined : true,
|
||||
writable: stdinType === "TTY" ? undefined : false,
|
||||
readableHighWaterMark: stdinType === "TTY" ? 0 : undefined,
|
||||
|
@ -210,39 +198,41 @@ export const stdin = stdio.stdin = (() => {
|
|||
|
||||
if (stdinType !== "TTY") {
|
||||
// Make sure the stdin can't be `.end()`-ed
|
||||
_stdin._writableState.ended = true;
|
||||
stdin._writableState.ended = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Provide a dummy contentless input for e.g. non-console
|
||||
// Windows applications.
|
||||
_stdin = new Readable({ read() {} });
|
||||
_stdin.push(null);
|
||||
stdin = new Readable({ read() {} });
|
||||
stdin.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
return _stdin;
|
||||
})();
|
||||
stdin.on("close", () => files.stdin?.close());
|
||||
stdin.fd = files.stdin?.rid ?? -1;
|
||||
Object.defineProperty(stdin, "isTTY", {
|
||||
stdin.on("close", () => files.stdin?.close());
|
||||
stdin.fd = files.stdin?.rid ?? -1;
|
||||
Object.defineProperty(stdin, "isTTY", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return Deno.isatty?.(Deno.stdin.rid);
|
||||
},
|
||||
});
|
||||
stdin._isRawMode = false;
|
||||
stdin.setRawMode = (enable) => {
|
||||
});
|
||||
stdin._isRawMode = false;
|
||||
stdin.setRawMode = (enable) => {
|
||||
files.stdin?.setRaw?.(enable);
|
||||
stdin._isRawMode = enable;
|
||||
return stdin;
|
||||
};
|
||||
Object.defineProperty(stdin, "isRaw", {
|
||||
};
|
||||
Object.defineProperty(stdin, "isRaw", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return stdin._isRawMode;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return stdin;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import promises from "internal:deno_node/polyfills/readline/promises.ts";
|
|||
import { validateAbortSignal } from "internal:deno_node/polyfills/internal/validators.mjs";
|
||||
import { promisify } from "internal:deno_node/polyfills/internal/util.mjs";
|
||||
import { AbortError } from "internal:deno_node/polyfills/internal/errors.ts";
|
||||
import { process } from "internal:deno_node/polyfills/process.ts";
|
||||
import process from "internal:deno_node/polyfills/process.ts";
|
||||
|
||||
import {
|
||||
Interface as _Interface,
|
||||
|
|
|
@ -40,7 +40,7 @@ import {
|
|||
promisify,
|
||||
} from "internal:deno_node/polyfills/util.ts";
|
||||
import { createDeferredPromise } from "internal:deno_node/polyfills/internal/util.mjs";
|
||||
import { process } from "internal:deno_node/polyfills/process.ts";
|
||||
import process from "internal:deno_node/polyfills/process.ts";
|
||||
import { Buffer } from "internal:deno_node/polyfills/buffer.ts";
|
||||
import {
|
||||
convertToValidSignal,
|
||||
|
|
|
@ -29,15 +29,17 @@ import {
|
|||
import { _exiting } from "internal:deno_node/polyfills/_process/exiting.ts";
|
||||
export { _nextTick as nextTick, chdir, cwd, env, version, versions };
|
||||
import {
|
||||
stderr as stderr_,
|
||||
stdin as stdin_,
|
||||
stdout as stdout_,
|
||||
createWritableStdioStream,
|
||||
initStdin,
|
||||
} from "internal:deno_node/polyfills/_process/streams.mjs";
|
||||
import { stdio } from "internal:deno_node/polyfills/_process/stdio.mjs";
|
||||
import {
|
||||
enableNextTick,
|
||||
processTicksAndRejections,
|
||||
runNextTicks,
|
||||
} from "internal:deno_node/polyfills/_next_tick.ts";
|
||||
import { isWindows } from "internal:deno_node/polyfills/_util/os.ts";
|
||||
import * as files from "internal:runtime/js/40_files.js";
|
||||
|
||||
// TODO(kt3k): This should be set at start up time
|
||||
export let arch = "";
|
||||
|
@ -50,11 +52,11 @@ export let pid = 0;
|
|||
|
||||
// TODO(kt3k): Give better types to stdio objects
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const stderr = stderr_ as any;
|
||||
let stderr = null as any;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const stdin = stdin_ as any;
|
||||
let stdin = null as any;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const stdout = stdout_ as any;
|
||||
let stdout = null as any;
|
||||
|
||||
export { stderr, stdin, stdout };
|
||||
import { getBinding } from "internal:deno_node/polyfills/internal_binding/mod.ts";
|
||||
|
@ -663,13 +665,10 @@ class Process extends EventEmitter {
|
|||
noDeprecation = false;
|
||||
}
|
||||
|
||||
// TODO(kt3k): Do the below at start up time.
|
||||
/*
|
||||
if (Deno.build.os === "windows") {
|
||||
if (isWindows) {
|
||||
delete Process.prototype.getgid;
|
||||
delete Process.prototype.getuid;
|
||||
}
|
||||
*/
|
||||
|
||||
/** https://nodejs.org/api/process.html#process_process */
|
||||
const process = new Process();
|
||||
|
@ -689,13 +688,21 @@ export const removeAllListeners = process.removeAllListeners;
|
|||
|
||||
// Should be called only once, in `runtime/js/99_main.js` when the runtime is
|
||||
// bootstrapped.
|
||||
internals.__bootstrapNodeProcess = function (args: string[]) {
|
||||
internals.__bootstrapNodeProcess = function (
|
||||
args: string[],
|
||||
denoVersions: Record<string, string>,
|
||||
) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
argv[i + 2] = args[i];
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(denoVersions)) {
|
||||
versions[key] = value;
|
||||
}
|
||||
|
||||
core.setNextTickCallback(processTicksAndRejections);
|
||||
core.setMacrotaskCallback(runNextTicks);
|
||||
enableNextTick();
|
||||
|
||||
// TODO(bartlomieju): this is buggy, see https://github.com/denoland/deno/issues/16928
|
||||
// We should use a specialized API in 99_main.js instead
|
||||
|
@ -740,12 +747,22 @@ internals.__bootstrapNodeProcess = function (args: string[]) {
|
|||
}
|
||||
});
|
||||
|
||||
// Initializes stdin
|
||||
stdin = stdio.stdin = process.stdin = initStdin();
|
||||
|
||||
/** https://nodejs.org/api/process.html#process_process_stderr */
|
||||
stderr = stdio.stderr = process.stderr = createWritableStdioStream(
|
||||
files.stderr,
|
||||
"stderr",
|
||||
);
|
||||
|
||||
/** https://nodejs.org/api/process.html#process_process_stdout */
|
||||
stdout = stdio.stdout = process.stdout = createWritableStdioStream(
|
||||
files.stdout,
|
||||
"stdout",
|
||||
);
|
||||
|
||||
delete internals.__bootstrapNodeProcess;
|
||||
};
|
||||
|
||||
export default process;
|
||||
|
||||
//TODO(Soremwar)
|
||||
//Remove on 1.0
|
||||
//Kept for backwards compatibility with std
|
||||
export { process };
|
||||
|
|
|
@ -7,9 +7,10 @@ import {
|
|||
import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs";
|
||||
import { promisify } from "internal:deno_node/polyfills/internal/util.mjs";
|
||||
export { setUnrefTimeout } from "internal:deno_node/polyfills/internal/timers.mjs";
|
||||
import * as timers from "internal:deno_web/02_timers.js";
|
||||
|
||||
const clearTimeout_ = globalThis.clearTimeout;
|
||||
const clearInterval_ = globalThis.clearInterval;
|
||||
const clearTimeout_ = timers.clearTimeout;
|
||||
const clearInterval_ = timers.clearInterval;
|
||||
|
||||
export function setTimeout(
|
||||
callback: (...args: unknown[]) => void,
|
||||
|
|
|
@ -515,11 +515,6 @@ function bootstrapMainRuntime(runtimeOptions) {
|
|||
ObjectDefineProperty(globalThis, "Deno", util.readOnly(finalDenoNs));
|
||||
|
||||
util.log("args", runtimeOptions.args);
|
||||
|
||||
// FIXME(bartlomieju): this should be a helper function that is placed in
|
||||
// "internals" namespace
|
||||
// Initialize Node polyfills
|
||||
// internals.__bootstrapNodeProcess();
|
||||
}
|
||||
|
||||
function bootstrapWorkerRuntime(
|
||||
|
|
|
@ -29,6 +29,7 @@ export async function checkCopyright() {
|
|||
":!:cli/tsc/compiler.d.ts",
|
||||
":!:test_util/wpt/**",
|
||||
":!:cli/tools/init/templates/**",
|
||||
":!:cli/tests/unit_node/testdata/**",
|
||||
|
||||
// rust
|
||||
"*.rs",
|
||||
|
|
Loading…
Reference in a new issue