// Copyright 2018-2026 the Deno authors. MIT license.

// deno-lint-ignore-file no-undef no-console

import process, {
  arch as importedArch,
  argv,
  argv0 as importedArgv0,
  cpuUsage as importedCpuUsage,
  env,
  execArgv as importedExecArgv,
  execPath as importedExecPath,
  getActiveResourcesInfo as importedGetActiveResourcesInfo,
  getegid,
  geteuid,
  getgid,
  getuid,
  pid as importedPid,
  platform as importedPlatform,
  report as importedReport,
  setegid,
  seteuid,
  setgid,
  setuid,
} from "node:process";

import { Readable } from "node:stream";
import { once } from "node:events";
import {
  assert,
  assertEquals,
  assertFalse,
  assertMatch,
  assertObjectMatch,
  assertStrictEquals,
  assertThrows,
  fail,
} from "@std/assert";
import { stripAnsiCode } from "@std/fmt/colors";
import * as path from "@std/path";
import { delay } from "@std/async/delay";
import { stub } from "@std/testing/mock";
import { execSync } from "node:child_process";
import * as fs from "node:fs";
import * as net from "node:net";
import nodeAssert from "node:assert";

const testDir = new URL(".", import.meta.url);

const processWithActiveResources = process as typeof process & {
  _getActiveHandles(): unknown[];
  _getActiveRequests(): unknown[];
};

function getGroupNameFromSystem(gid: number): string {
  const stdout = execSync(`grep ":${gid}:" /etc/group`).toString();
  const groupInfo = stdout.trim().split(":")[0];
  return groupInfo;
}

function getUserNameFromSystem(uid: number): string {
  const stdout = execSync(`id -un ${uid}`).toString();
  const name = stdout.trim();
  return name;
}

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() {
    // process.chdir now wraps Deno errors into Node-shaped uv errors with
    // syscall/path/dest, so a missing directory throws a plain Error with
    // code "ENOENT" rather than Deno.errors.NotFound.
    const err = assertThrows(
      () => {
        process.chdir("non-existent-directory-name");
      },
      Error,
      "ENOENT",
    ) as Error & { code?: string; syscall?: string; dest?: string };
    assertEquals(err.code, "ENOENT");
    assertEquals(err.syscall, "chdir");
    assertEquals(err.dest, "non-existent-directory-name");
  },
});

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");
    // Must match the NAPI_VERSION in ext/napi/js_native_api.rs
    assertEquals(process.versions.napi, "10");
    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() {
    const expectedOs = Deno.build.os == "windows" ? "win32" : Deno.build.os;
    assertEquals(typeof process.platform, "string");
    assertEquals(process.platform, expectedOs);
    assertEquals(typeof importedPlatform, "string");
    assertEquals(importedPlatform, expectedOs);
  },
});

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() {
    function testValue(arch: string) {
      if (Deno.build.arch == "x86_64") {
        assertEquals(arch, "x64");
      } else if (Deno.build.arch == "aarch64") {
        assertEquals(arch, "arm64");
      } else {
        throw new Error("unreachable");
      }
    }

    assertEquals(typeof process.arch, "string");
    testValue(process.arch);
    assertEquals(typeof importedArch, "string");
    testValue(importedArch);
  },
});

Deno.test({
  name: "process.pid",
  fn() {
    assertEquals(typeof process.pid, "number");
    assertEquals(process.pid, Deno.pid);
    assertEquals(typeof importedPid, "number");
    assertEquals(importedPid, 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",
        "./testdata/process_exit.ts",
      ],
      cwd,
    });
    const { stdout } = await command.output();

    const decoder = new TextDecoder();
    assertEquals(stripAnsiCode(decoder.decode(stdout).trim()), "1\n2");
  },
});

Deno.test({
  // Regression test for https://github.com/denoland/deno/issues/24646: a
  // spurious `ERR_MULTIPLE_CALLBACK` ("Callback called multiple times") was
  // thrown from `onwrite` while doing many synchronous writes to
  // `process.stdout`/`process.stderr` (e.g. commander.js help output under
  // Jest). The full reproduction needs a test runner that evaluates files in
  // separate module realms, but this at least exercises the stdio write path
  // with the same empty-write / `isTTY` toggling pattern in a piped child.
  name: "process.stdout/stderr survive many synchronous writes (#24646)",
  async fn() {
    const code = `
      import process from "node:process";
      for (let i = 0; i < 1000; i++) {
        process.stdout.isTTY = i % 2 === 0;
        process.stderr.isTTY = i % 2 === 0;
        process.stdout.write("");
        process.stderr.write("");
        process.stdout.write("Usage: prog [options]\\n");
        process.stderr.write("error: bad option\\n");
      }
      console.error("DONE_OK");
    `;
    const command = new Deno.Command(Deno.execPath(), {
      args: ["eval", "--quiet", code],
      stdout: "piped",
      stderr: "piped",
    });
    const { code: exitCode, stderr } = await command.output();
    const err = new TextDecoder().decode(stderr);
    assert(
      !err.includes("Callback called multiple times"),
      "unexpected ERR_MULTIPLE_CALLBACK:\n" + err,
    );
    assert(err.includes("DONE_OK"), "child did not finish cleanly:\n" + err);
    assertEquals(exitCode, 0);
  },
});

Deno.test({
  name: "process.on signal",
  ignore: Deno.build.os == "windows",
  async fn() {
    let wait = "";
    const testTimeout = setTimeout(
      () => fail("Test timed out waiting for " + wait),
      10_000,
    );
    try {
      const process = new Deno.Command(Deno.execPath(), {
        args: [
          "eval",
          `
          import process from "node:process";
          setInterval(() => {}, 1000);
          process.on("SIGINT", () => {
            console.log("foo");
          });
          console.log("ready");
          `,
        ],
        stdout: "piped",
        stderr: "null",
      }).spawn();
      let output = "";
      process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
        new WritableStream({
          write(chunk) {
            console.log("chunk:", chunk);
            output += chunk;
          },
        }),
      );
      wait = "ready";
      while (!output.includes("ready\n")) {
        await delay(10);
      }
      for (let i = 0; i < 3; i++) {
        output = "";
        process.kill("SIGINT");
        wait = "foo " + i;
        while (!output.includes("foo\n")) {
          await delay(10);
        }
      }
      process.kill("SIGTERM");
      await process.status;
    } finally {
      clearTimeout(testTimeout);
    }
  },
});

Deno.test(
  { permissions: { run: true, read: true } },
  async function processKill() {
    const p = new Deno.Command(Deno.execPath(), {
      args: ["eval", "setTimeout(() => {}, 10000)"],
    }).spawn();

    // kill with signal 0 should keep the process alive in linux (true means no error happened)
    // windows ignore signals
    assertEquals(process.kill(p.pid, 0), true);
    process.kill(p.pid);
    await p.status;
  },
);

Deno.test({
  name: "process.off signal",
  ignore: Deno.build.os == "windows",
  async fn() {
    const testTimeout = setTimeout(() => fail("Test timed out"), 10_000);
    try {
      const process = new Deno.Command(Deno.execPath(), {
        args: [
          "eval",
          `
          import process from "node:process";
          setInterval(() => {}, 1000);
          const listener = () => {
            process.off("SIGINT", listener);
            console.log("foo");
          };
          process.on("SIGINT", listener);
          console.log("ready");
          `,
        ],
        stdout: "piped",
        stderr: "null",
      }).spawn();
      let output = "";
      process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
        new WritableStream({
          write(chunk) {
            console.log("chunk:", chunk);
            output += chunk;
          },
        }),
      );
      while (!output.includes("ready\n")) {
        await delay(10);
      }
      output = "";
      process.kill("SIGINT");
      while (!output.includes("foo\n")) {
        await delay(10);
      }
      process.kill("SIGINT");
      await process.status;
    } finally {
      clearTimeout(testTimeout);
    }
  },
});

// Only supported on Windows (but won't throw anywhere)
Deno.test({
  name: "process.on SIGBREAK doesn't throw",
  fn() {
    const listener = () => {};
    process.on("SIGBREAK", listener);
    process.off("SIGBREAK", listener);
  },
});

// Not supported on Windows (but won't throw anywhere)
Deno.test({
  name: "process.on SIGTERM doesn't throw",
  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);
    // argv[0] should be the executable path (same as process.execPath), this is Node.js behavior
    assertEquals(process.argv[0], Deno.execPath());
  },
});

Deno.test({
  name: "process.argv0",
  async fn() {
    const { stdout } = await new Deno.Command(Deno.execPath(), {
      args: [
        "eval",
        `import process from "node:process";console.log(process.argv0);`,
      ],
      stdout: "piped",
      stderr: "null",
    }).output();
    assertEquals(new TextDecoder().decode(stdout).trim(), Deno.execPath());

    assertEquals(typeof process.argv0, "string");
    assert(
      process.argv0.match(/[^/\\]*deno[^/\\]*$/),
      "deno included in the file name of argv[0]",
    );
    assertEquals(typeof importedArgv0, "string");
    assert(
      importedArgv0.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() {
    assert(Object.prototype.hasOwnProperty.call(process, "env"));

    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",
      ),
    );

    Object.defineProperty(process.env, "HELLO", {
      value: "OTHER_WORLD",
      configurable: true,
      writable: true,
      enumerable: true,
    });
    assertEquals(process.env.HELLO, "OTHER_WORLD");

    // 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");

    delete process.env.HELLO;
    assertEquals(process.env.HELLO, undefined);
  },
});

// #30701
Deno.test({
  name: "process.env handles falsy values correctly",
  fn() {
    const key = "TEST_ENV_VAR_EMPTY_STRING";
    Deno.env.set(key, "");

    assertEquals(process.env[key], "");
    assertEquals(Object.keys(process.env).includes(key), true);
    assert(key in process.env);
    assert(Object.hasOwn(process.env, key));
  },
});

Deno.test({
  name: "process.env requires scoped env permission",
  permissions: { env: ["FOO"] },
  fn() {
    Deno.env.set("FOO", "1");
    assert("FOO" in process.env);
    assertThrows(() => {
      process.env.BAR;
    }, Deno.errors.NotCapable);
    assert(Object.hasOwn(process.env, "FOO"));
  },
});

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.env: checking symbol in env should not require permission",
  permissions: "none",
  fn() {
    const symbol = Symbol.for("67");
    Reflect.has(globalThis.process.env, symbol);
  },
});

Deno.test({
  name: "process.stdin",
  fn() {
    // @ts-ignore `Deno.stdin.rid` was soft-removed in Deno 2.
    assertEquals(process.stdin.fd, Deno.stdin.rid);
    const isTTY = Deno.stdin.isTerminal();
    assertEquals(process.stdin.isTTY, isTTY);

    // Allows overwriting `process.stdin.isTTY` (mirrors stdout/stderr from #26130)
    const original = process.stdin.isTTY;
    try {
      // @ts-ignore isTTY is defined as readonly in types but we allow setting it
      process.stdin.isTTY = !isTTY;
      assertEquals(process.stdin.isTTY, !isTTY);
    } finally {
      // @ts-ignore isTTY is defined as readonly in types but we allow setting it
      process.stdin.isTTY = original;
    }
  },
});

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 = [
      Deno.build.os == "windows" ? "16384" : "65536",
      "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 = ["65536", "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 = ["65536", "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() {
    // @ts-ignore `Deno.stdout.rid` was soft-removed in Deno 2.
    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);
    assert([1, 4, 8, 24].includes(process.stdout.getColorDepth()));
    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);
    }

    // Allows overwriting `process.stdout.isTTY`
    // https://github.com/denoland/deno/issues/26123
    const original = process.stdout.isTTY;
    try {
      process.stdout.isTTY = !isTTY;
      assertEquals(process.stdout.isTTY, !isTTY);
    } finally {
      process.stdout.isTTY = original;
    }
  },
});

Deno.test({
  name: "process.stderr",
  fn() {
    // @ts-ignore `Deno.stderr.rid` was soft-removed in Deno 2.
    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.getActiveResourcesInfo reports active timers",
  fn() {
    const countTimeouts = () =>
      process.getActiveResourcesInfo().filter((type) => type === "Timeout")
        .length;
    const before = countTimeouts();
    const timer = setTimeout(() => {}, 1000);
    try {
      assertEquals(countTimeouts(), before + 1);
      assertEquals(importedGetActiveResourcesInfo(), [
        ...process.getActiveResourcesInfo(),
      ]);
    } finally {
      clearTimeout(timer);
    }
  },
});

Deno.test({
  name: "process._getActiveRequests reports pending fs requests",
  async fn() {
    const tempFile = Deno.makeTempFileSync();
    try {
      const before = processWithActiveResources._getActiveRequests().length;
      const promises = Array.from(
        { length: 12 },
        () =>
          new Promise<number>((resolve, reject) => {
            fs.open(tempFile, "r", (err, fd) => {
              if (err) {
                reject(err);
              } else {
                resolve(fd);
              }
            });
          }),
      );

      assertEquals(
        processWithActiveResources._getActiveRequests().length,
        before + 12,
      );

      const fds = await Promise.all(promises);
      for (const fd of fds) {
        fs.closeSync(fd);
      }
      assertEquals(
        processWithActiveResources._getActiveRequests().length,
        before,
      );
    } finally {
      Deno.removeSync(tempFile);
    }
  },
});

Deno.test({
  name: "process._getActiveHandles reports active net handles",
  async fn() {
    let acceptedSocket: net.Socket | undefined;
    const server = net.createServer((socket) => {
      acceptedSocket = socket;
    });
    await new Promise<void>((resolve) => server.listen(0, resolve));

    const serverConnection = once(server, "connection");
    const clientSocket = net.connect(
      (server.address() as net.AddressInfo).port,
    );
    try {
      await Promise.all([once(clientSocket, "connect"), serverConnection]);

      const handles = processWithActiveResources._getActiveHandles();
      assert(handles.includes(server));
      assert(handles.includes(clientSocket));
      assert(acceptedSocket);
      assert(handles.includes(acceptedSocket));
    } finally {
      clientSocket.destroy();
      acceptedSocket?.destroy();
      await new Promise<void>((resolve) => server.close(() => resolve()));
    }
  },
});

Deno.test({
  name:
    "process.getActiveResourcesInfo reports net handles with Node wrap names",
  async fn() {
    const countOf = (type: string) =>
      process.getActiveResourcesInfo().filter((t) => t === type).length;

    const beforeServer = countOf("TCPServerWrap");
    const beforeSocket = countOf("TCPSocketWrap");

    let acceptedSocket: net.Socket | undefined;
    const server = net.createServer((socket) => {
      acceptedSocket = socket;
    });
    await new Promise<void>((resolve) => server.listen(0, resolve));

    const serverConnection = once(server, "connection");
    const clientSocket = net.connect(
      (server.address() as net.AddressInfo).port,
    );
    try {
      await Promise.all([once(clientSocket, "connect"), serverConnection]);

      assertEquals(countOf("TCPServerWrap"), beforeServer + 1);
      // The client socket plus the server-accepted socket.
      assertEquals(countOf("TCPSocketWrap"), beforeSocket + 2);
    } finally {
      clientSocket.destroy();
      acceptedSocket?.destroy();
      await new Promise<void>((resolve) => server.close(() => resolve()));
    }
  },
});

Deno.test({
  name: "process.getActiveResourcesInfo reports fs requests as FSReqCallback",
  async fn() {
    const tempFile = Deno.makeTempFileSync();
    const countOf = () =>
      process.getActiveResourcesInfo().filter((t) => t === "FSReqCallback")
        .length;
    try {
      const before = countOf();
      const promises = Array.from(
        { length: 5 },
        () =>
          new Promise<number>((resolve, reject) => {
            fs.open(tempFile, "r", (err, fd) => {
              if (err) reject(err);
              else resolve(fd);
            });
          }),
      );

      assertEquals(countOf(), before + 5);

      const fds = await Promise.all(promises);
      for (const fd of fds) fs.closeSync(fd);
      assertEquals(countOf(), before);
    } finally {
      Deno.removeSync(tempFile);
    }
  },
});

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",
    "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);
  assertThrows(() => {
    // deno-lint-ignore no-explicit-any
    (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 = '10';",
    10,
  );
  await exitCodeTest(
    "import process from 'node:process'; process.exitCode = '0x10';",
    16,
  );
});

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);
  assertEquals(process.config.variables.host_arch, process.arch);
});

Deno.test("process._exiting", () => {
  // @ts-ignore fix the type here
  assert(process._exiting === false);
});

Deno.test("process.execPath", () => {
  assertEquals(typeof process.execPath, "string");
  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(geteuid);
    assert(typeof process.geteuid?.() === "number");
  }
});

Deno.test({
  name: "process.exit",
  async fn() {
    const command = new Deno.Command(Deno.execPath(), {
      args: [
        "run",
        "--quiet",
        "./testdata/process_exit2.ts",
      ],
      cwd: testDir,
    });
    const { stdout } = await command.output();

    const decoder = new TextDecoder();
    assertEquals(stripAnsiCode(decoder.decode(stdout).trim()), "exit");
  },
});

Deno.test({
  name: "process.reallyExit",
  async fn() {
    const command = new Deno.Command(Deno.execPath(), {
      args: [
        "run",
        "--quiet",
        "./testdata/process_really_exit.ts",
      ],
      cwd: testDir,
    });
    const { stdout } = await command.output();

    const decoder = new TextDecoder();
    assertEquals(stripAnsiCode(decoder.decode(stdout).trim()), "really exited");
  },
});

Deno.test({
  name: "process._rawDebug",
  async fn() {
    const command = new Deno.Command(Deno.execPath(), {
      args: [
        "run",
        "--quiet",
        "./testdata/process_raw_debug.ts",
      ],
      cwd: testDir,
    });
    const { stdout, stderr } = await command.output();

    assertEquals(stdout.length, 0);
    const decoder = new TextDecoder();
    assertEquals(
      stripAnsiCode(decoder.decode(stderr).trim()),
      "this should go to stderr { a: 1, b: [ 'a', 2 ] }",
    );
  },
});

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() {
    // Default process.title should be the execPath (matches Node.js behavior)
    assertEquals(process.title, process.execPath);
    // Setting process.title should work
    const original = process.title;
    process.title = "foo";
    assertEquals(process.title, "foo");
    // Restore
    process.title = original;
  },
});

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.binding('uv').getErrorMessage",
  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.getErrorMessage);
    assert(typeof uv.getErrorMessage === "function");
    assertEquals(uv.getErrorMessage(-1), "operation not permitted");
  },
});

Deno.test({
  name: "process.binding('uv').getErrorMap",
  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.getErrorMap);
    assert(typeof uv.getErrorMap === "function");
    const errorMap = uv.getErrorMap();
    assert(errorMap instanceof Map);
    // errorMap maps error code (number) to [name, message]
    assertEquals(errorMap.get(-1), ["EPERM", "operation not permitted"]);
  },
});

Deno.test({
  name: "process.binding('uv').getCodeMap",
  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.getCodeMap);
    assert(typeof uv.getCodeMap === "function");
    const codeMap = uv.getCodeMap();
    assert(codeMap instanceof Map);
    // codeMap maps error name (string) to error code (number)
    assertEquals(codeMap.get("EPERM"), -1);
  },
});

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;

    // glibc fields only exist on Linux+glibc builds; on musl Linux and
    // non-Linux platforms the keys must be absent so libc-flavor detection
    // works (denoland/deno#33948).
    const isGlibc = Deno.build.os === "linux" && Deno.build.env === "gnu";
    if (isGlibc) {
      assertEquals(result.header.glibcVersionRuntime, "2.38");
      assertEquals(result.header.glibcVersionCompiler, "2.38");
      delete result.header.glibcVersionRuntime;
      delete result.header.glibcVersionCompiler;
    } else {
      assert(
        !("glibcVersionRuntime" in result.header),
        "glibcVersionRuntime must not be present on non-glibc platforms",
      );
      assert(
        !("glibcVersionCompiler" in result.header),
        "glibcVersionCompiler must not be present on non-glibc platforms",
      );
    }

    // test hardcoded part
    assertEquals(result, {
      header: {
        reportVersion: 3,
        event: "JavaScript API",
        trigger: "GetReport",
        threadId: 0,
        commandLine: ["node"],
        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,
    });
  },
});

// Regression test for https://github.com/denoland/deno/issues/35072
Deno.test({
  name: "named export { report } from node:process",
  fn() {
    // `import { report } from 'node:process'` must work as a named export
    assert(importedReport !== undefined);
    // The named export must be the same object reference as process.report
    assert(importedReport === process.report);
    assert(typeof importedReport.getReport === "function");
    assert(typeof importedReport.writeReport === "function");
    assert(typeof importedReport.directory === "string");
    assert(typeof importedReport.filename === "string");
  },
});

Deno.test({
  name: "process.setSourceMapsEnabled",
  fn() {
    // @ts-ignore: setSourceMapsEnabled is not available in the types yet.
    process.setSourceMapsEnabled(false); // noop
    // @ts-ignore: setSourceMapsEnabled is not available in the types yet.
    process.setSourceMapsEnabled(true); // noop
  },
});

Deno.test({
  name: "process.sourceMapsEnabled",
  fn() {
    // @ts-ignore: not available in the types yet.
    assertEquals(process.sourceMapsEnabled, true);
  },
});

// Regression test for https://github.com/denoland/deno/issues/23761
Deno.test({
  name: "process.uptime without this",
  fn() {
    const v = (0, process.uptime)();
    assert(v >= 0);
  },
});

// Test for https://github.com/denoland/deno/issues/23863
Deno.test({
  name: "instantiate process constructor without 'new' keyword",
  fn() {
    // This would throw
    process.constructor.call({});
  },
});

// Test for https://github.com/denoland/deno/issues/22892
Deno.test("process.listeners - include SIG* events", () => {
  const listener = () => console.log("SIGINT");
  process.on("SIGINT", listener);
  assertEquals(process.listeners("SIGINT").length, 1);

  const listener2 = () => console.log("SIGINT");
  process.prependListener("SIGINT", listener2);
  assertEquals(process.listeners("SIGINT").length, 2);

  process.off("SIGINT", listener);
  assertEquals(process.listeners("SIGINT").length, 1);
  process.off("SIGINT", listener2);
  assertEquals(process.listeners("SIGINT").length, 0);
});

Deno.test(function processVersionsOwnProperty() {
  assert(Object.prototype.hasOwnProperty.call(process, "versions"));
});

Deno.test(function importedExecArgvTest() {
  assert(Array.isArray(importedExecArgv));
});

Deno.test(function importedExecPathTest() {
  assertEquals(typeof importedExecPath, "string");
  assertEquals(importedExecPath, Deno.execPath());
});

Deno.test("process.cpuUsage()", () => {
  assert(process.cpuUsage.length === 1);
  const cpuUsage = process.cpuUsage();
  assert(typeof cpuUsage.user === "number");
  assert(typeof cpuUsage.system === "number");
  const a = process.cpuUsage();
  const b = process.cpuUsage(a);
  assert(a.user > b.user);
  assert(a.system > b.system);

  assertThrows(
    () => {
      // @ts-ignore TS2322
      process.cpuUsage({});
    },
    TypeError,
  );

  assertThrows(
    () => {
      // @ts-ignore TS2322
      process.cpuUsage({ user: "1", system: 2 });
    },
    TypeError,
  );
  assertThrows(
    () => {
      // @ts-ignore TS2322
      process.cpuUsage({ user: 1, system: "2" });
    },
    TypeError,
  );

  for (const invalidNumber of [-1, -Infinity, Infinity, NaN]) {
    assertThrows(
      () => {
        process.cpuUsage({ user: invalidNumber, system: 2 });
      },
      RangeError,
    );
    assertThrows(
      () => {
        process.cpuUsage({ user: 2, system: invalidNumber });
      },
      RangeError,
    );
  }
});

Deno.test("importedCpuUsage", () => {
  assert(importedCpuUsage === process.cpuUsage);
});

Deno.test("process.resourceUsage()", () => {
  const usage = process.resourceUsage();
  // All 16 fields documented at
  // https://nodejs.org/api/process.html#processresourceusage must be present
  // and numeric.
  for (
    const field of [
      "userCPUTime",
      "systemCPUTime",
      "maxRSS",
      "sharedMemorySize",
      "unsharedDataSize",
      "unsharedStackSize",
      "minorPageFault",
      "majorPageFault",
      "swappedOut",
      "fsRead",
      "fsWrite",
      "ipcSent",
      "ipcReceived",
      "signalsCount",
      "voluntaryContextSwitches",
      "involuntaryContextSwitches",
    ] as const
  ) {
    assert(typeof usage[field] === "number", `${field} should be a number`);
    assert(usage[field] >= 0, `${field} should be non-negative`);
  }
  // CPU time should be monotonically non-decreasing.
  const later = process.resourceUsage();
  assert(later.userCPUTime >= usage.userCPUTime);
  assert(later.systemCPUTime >= usage.systemCPUTime);
});

Deno.test("importedResourceUsage", async () => {
  const { resourceUsage } = await import("node:process");
  assert(resourceUsage === process.resourceUsage);
});

Deno.test({
  name:
    "process.constrainedMemory requires systemMemoryInfo permission on Linux and Android",
  ignore: Deno.build.os !== "linux" && Deno.build.os !== "android",
  permissions: { sys: false },
  fn() {
    assertThrows(
      () => process.constrainedMemory(),
      Deno.errors.NotCapable,
      'Requires sys access to "systemMemoryInfo"',
    );
  },
});

Deno.test({
  name: "process.constrainedMemory works with sys permission",
  ignore: Deno.build.os !== "linux" && Deno.build.os !== "android",
  permissions: { sys: true },
  fn() {
    assert(typeof process.constrainedMemory() === "number");
  },
});

Deno.test({
  name:
    "process.constrainedMemory returns 0 without sys permission outside Linux and Android",
  ignore: Deno.build.os === "linux" || Deno.build.os === "android",
  permissions: { sys: false },
  fn() {
    assertEquals(process.constrainedMemory(), 0);
  },
});

Deno.test("process.stdout.columns writable", () => {
  process.stdout.columns = 80;
  assertEquals(process.stdout.columns, 80);
});

Deno.test("getBuiltinModule", () => {
  assert(process.getBuiltinModule("fs"));
  assert(process.getBuiltinModule("node:fs"));
  assertEquals(process.getBuiltinModule("something"), undefined);
});

Deno.test("process.emitWarning() prints to stderr", async () => {
  using writeStub = stub(process.stderr, "write", () => true);
  const { promise, resolve } = Promise.withResolvers<void>();
  process.on("warning", () => resolve());

  process.emitWarning("This is a warning", {
    code: "TEST0001",
    detail: "This is some additional information",
  });

  await promise;

  const arg = writeStub.calls[0].args[0];
  assert(typeof arg === "string");
  assertMatch(
    arg,
    /\(node:\d+\) \[TEST0001\] Warning: This is a warning\nThis is some additional information\n/,
  );
});

Deno.test("process.emitWarning() does not print to stderr when it is deprecation warning and noDeprecation is set to true", async () => {
  using writeStub = stub(process.stderr, "write", () => true);

  // deno-lint-ignore no-explicit-any
  (process as any).noDeprecation = true; // Set noDeprecation to true
  process.emitWarning("This is a deprecation warning", {
    code: "TEST0002",
    detail: "This is some additional information",
    type: "DeprecationWarning",
  });

  await delay(10);

  assertEquals(writeStub.calls.length, 0);
  // deno-lint-ignore no-explicit-any
  (process as any).noDeprecation = false; // Reset noDeprecation
});

Deno.test("process.moduleLoadList", () => {
  // deno-lint-ignore no-explicit-any
  const moduleLoadList = (process as any).moduleLoadList;
  assert(Array.isArray(moduleLoadList));
  assertEquals(moduleLoadList.length, 0);
});

Deno.test({
  name: "process.setegid()",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    const originalEgid = getegid!();
    const groupName = getGroupNameFromSystem(originalEgid);

    // only assert that it doesn't throw
    setegid!(originalEgid);
    setegid!(groupName);
  },
});

Deno.test({
  name: "process.setegid() throws on invalid group",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    assertThrows(
      () => {
        setegid!("67?!");
      },
      "Group identifier does not exist: 67?!",
    );
  },
});

Deno.test({
  name: "process.setegid() should be undefined on unsupported platforms",
  ignore: Deno.build.os !== "windows" && Deno.build.os !== "android",
  fn() {
    assertEquals(process.setegid, undefined);
  },
});

Deno.test({
  name: "process.seteuid()",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    const originalEuid = geteuid!();
    const userName = getUserNameFromSystem(originalEuid);

    // calling with original euid to ensure it doesn't throw
    seteuid!(originalEuid);
    seteuid!(userName);
  },
});

Deno.test({
  name: "process.seteuid() throws on invalid user",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    assertThrows(
      () => {
        seteuid!("67?!");
      },
      "User identifier does not exist: 67?!",
    );
  },
});

Deno.test({
  name: "process.seteuid() should be undefined on unsupported platforms",
  ignore: Deno.build.os !== "windows" && Deno.build.os !== "android",
  fn() {
    assertEquals(process.seteuid, undefined);
  },
});

Deno.test({
  name: "process.setgid()",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    const originalGid = getgid!();
    const groupName = getGroupNameFromSystem(originalGid);

    // Calling with original gid to ensure it doesn't throw
    setgid!(originalGid);
    setgid!(groupName);
  },
});

Deno.test({
  name: "process.setgid() throws on invalid group",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    assertThrows(
      () => {
        setgid!("67?!");
      },
      "Group identifier does not exist: 67?!",
    );
  },
});

Deno.test({
  name: "process.setgid() should be undefined on unsupported platforms",
  ignore: Deno.build.os !== "windows" && Deno.build.os !== "android",
  fn() {
    assertEquals(process.setgid, undefined);
  },
});

Deno.test({
  name: "process.setuid()",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    const originalUid = getuid!();
    const userName = getUserNameFromSystem(originalUid);

    // Calling with original uid to ensure it doesn't throw
    setuid!(originalUid);
    setuid!(userName);
  },
});

Deno.test({
  name: "process.setuid() throws on invalid user",
  ignore: Deno.build.os === "windows" || Deno.build.os === "android",
  fn() {
    assertThrows(
      () => {
        setuid!("67?!");
      },
      "User identifier does not exist: 67?!",
    );
  },
});

Deno.test({
  name: "process.setuid() should be undefined on unsupported platforms",
  ignore: Deno.build.os !== "windows" && Deno.build.os !== "android",
  fn() {
    assertEquals(process.setuid, undefined);
  },
});

Deno.test({
  name: "process.loadEnvFile()",
  async fn() {
    const dirPath = Deno.makeTempDirSync();
    const envFilePath = path.join(dirPath, "envfile.env");
    const envContent = "FOO=foo\nBAR=bar\nBAZ=baz";
    Deno.writeTextFileSync(envFilePath, envContent);

    const code = `
    import assert from "node:assert";
    import process from "node:process";
    process.loadEnvFile(Deno.args[0]);

    assert.strictEqual(process.env.FOO, "foo");
    assert.strictEqual(process.env.BAR, "bar");
    assert.strictEqual(process.env.BAZ, "baz");
    `;

    const command = new Deno.Command(Deno.execPath(), {
      args: ["eval", code, envFilePath],
      cwd: testDir,
      stderr: "piped",
      stdout: "piped",
    });
    const { code: exitCode, stderr } = await command.output();
    const decoder = new TextDecoder();
    const stderrStr = decoder.decode(stderr).trim();
    if (exitCode !== 0) {
      console.error("Error output:", stderrStr);
    }

    Deno.removeSync(dirPath, { recursive: true });
  },
});

Deno.test({
  name: "process.loadEnvFile() with buffer path",
  async fn() {
    const dirPath = Deno.makeTempDirSync();
    const envFilePath = path.join(dirPath, "envfile.env");
    const envContent = "FOO=foo\nBAR=bar\nBAZ=baz";
    Deno.writeTextFileSync(envFilePath, envContent);

    const code = `
    import assert from "node:assert";
    import process from "node:process";
    process.loadEnvFile(Buffer.from(Deno.args[0]));

    assert.strictEqual(process.env.FOO, "foo");
    assert.strictEqual(process.env.BAR, "bar");
    assert.strictEqual(process.env.BAZ, "baz");
    `;

    const command = new Deno.Command(Deno.execPath(), {
      args: ["eval", code, envFilePath],
      cwd: testDir,
      stderr: "piped",
      stdout: "piped",
    });
    const { code: exitCode, stderr } = await command.output();
    const decoder = new TextDecoder();
    const stderrStr = decoder.decode(stderr).trim();
    if (exitCode !== 0) {
      console.error("Error output:", stderrStr);
    }

    Deno.removeSync(dirPath, { recursive: true });
  },
});

Deno.test({
  name: "process.loadEnvFile() with non-existent file",
  fn() {
    const dirPath = Deno.makeTempDirSync();
    const envFilePath = path.join(dirPath, "envfile.env");

    nodeAssert.throws(
      () => process.loadEnvFile(envFilePath),
      {
        code: "ENOENT",
        syscall: "open",
        path: envFilePath,
      },
    );

    Deno.removeSync(dirPath, { recursive: true });
  },
});

Deno.test({
  name: "process.loadEnvFile() trim null characters in env values",
  async fn() {
    const dirPath = Deno.makeTempDirSync();
    const envFilePath = path.join(dirPath, "envfile.env");
    const envContent = "FOO=f\0oo\nBAR=bar\n";
    Deno.writeTextFileSync(envFilePath, envContent);

    const code = `
    import assert from "node:assert";
    import process from "node:process";
    process.loadEnvFile(Deno.args[0]);

    assert.strictEqual(process.env.FOO, "f");
    assert.strictEqual(process.env.BAR, "bar");
    `;

    const command = new Deno.Command(Deno.execPath(), {
      args: ["eval", code, envFilePath],
      cwd: testDir,
      stderr: "piped",
      stdout: "piped",
    });
    const { code: exitCode, stderr } = await command.output();
    const decoder = new TextDecoder();
    const stderrStr = decoder.decode(stderr).trim();
    if (exitCode !== 0) {
      console.error("Error output:", stderrStr);
    }

    Deno.removeSync(dirPath, { recursive: true });
  },
});

Deno.test({
  name: "process.loadEnvFile() does not throw on invalid UTF-8 encoding",
  fn() {
    const dirPath = Deno.makeTempDirSync();
    const envFilePath = path.join(dirPath, "envfile.env");
    const contentArray = new Uint8Array([0xff, 0xfe, 0xfd]);
    Deno.writeFileSync(envFilePath, contentArray);
    // should load fine as in Node.js
    process.loadEnvFile(envFilePath);
    Deno.removeSync(dirPath, { recursive: true });
  },
});

// Regression test: signal handlers receive the signal name as first argument
Deno.test({
  name: "[node/process] signal handlers receive signal name",
  ignore: Deno.build.os === "windows",
  async fn() {
    const received: string[] = [];
    const handler = (signal: string) => {
      received.push(signal);
    };
    process.once("SIGUSR1", handler);
    process.kill(process.pid, "SIGUSR1");
    // Signal delivery is async; wait for it
    await new Promise((resolve) => setTimeout(resolve, 100));
    assertEquals(received, ["SIGUSR1"]);
  },
});
