From 870e8056c14f26b8a77df22aee43c54a3b58c88f Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Mon, 29 Apr 2024 16:59:42 -0700 Subject: [PATCH 01/23] Add `Deno.exit.code` support --- cli/tests/unit/os_test.ts | 51 +++++++++++++++++++++++++++++++++++++++ runtime/js/30_os.js | 17 +++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 cli/tests/unit/os_test.ts diff --git a/cli/tests/unit/os_test.ts b/cli/tests/unit/os_test.ts new file mode 100644 index 0000000000000..ea9e499e360c9 --- /dev/null +++ b/cli/tests/unit/os_test.ts @@ -0,0 +1,51 @@ +// This file contains unit tests for the Deno.exit.code API +import { assertEquals, assertThrows } from "../../testing/asserts.ts"; + +Deno.test({ + name: "Deno.exit.code getter and setter", + fn() { + // Initial value is 0 + assertEquals(Deno.exit.code, 0); + + // Set a new value + Deno.exit.code = 5; + assertEquals(Deno.exit.code, 5); + + // Reset to initial value + Deno.exit.code = 0; + assertEquals(Deno.exit.code, 0); + + // Throws on non-number values + assertThrows(() => { + // @ts-expect-error Testing for runtime error + Deno.exit.code = "not a number"; + }, TypeError, "Exit code must be a number."); + }, +}); + +Deno.test({ + name: "Setting Deno.exit.code does not cause an immediate exit", + fn() { + let exited = false; + const originalExit = Deno.exit; + Deno.exit = () => { + exited = true; + }; + + Deno.exit.code = 1; + assertEquals(exited, false); + + Deno.exit = originalExit; + }, +}); + +Deno.test({ + name: "Retrieving the set exit code before process termination", + fn() { + Deno.exit.code = 42; + assertEquals(Deno.exit.code, 42); + + // Reset to initial value + Deno.exit.code = 0; + }, +}); diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index 8948cf1ad0264..46fefee922a76 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -94,6 +94,23 @@ function exit(code) { throw new Error("Code not reachable"); } +let _exitCode = 0; +Object.defineProperty(exit, "code", { + get() { + return _exitCode; + }, + set(value) { + const code = parseInt(value) || undefined; + if (typeof code !== "number") { + throw new TypeError("Exit code must be a number."); + } + _exitCode = code; + op_set_exit_code(code); + }, + enumerable: true, + configurable: true, +}); + function setEnv(key, value) { op_set_env(key, value); } From 0798c5fc30ecfa0a991f370866c026a68558d826 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Mon, 29 Apr 2024 17:11:29 -0700 Subject: [PATCH 02/23] Update lib.deno.ns.d.ts --- cli/tsc/dts/lib.deno.ns.d.ts | 55 ++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index f9b61dc66f05c..01165660c87a7 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1452,19 +1452,56 @@ declare namespace Deno { fn: (b: BenchContext) => void | Promise, ): void; - /** Exit the Deno process with optional exit code. + /** An interface containing methods to iteract with the process exit state. * - * If no exit code is supplied then Deno will exit with return code of `0`. - * - * In worker contexts this is an alias to `self.close();`. - * - * ```ts - * Deno.exit(5); - * ``` + * @category Runtime Environment + */ + export interface Exit { + /** Exit the Deno process with optional exit code. + * + * If no exit code is supplied then Deno will exit with return code of `0`. + * + * In worker contexts this is an alias to `self.close();`. + * + * ```ts + * Deno.exit(5); + * ``` + * + * @category Runtime Environment + */ + (code?: number): never; + + /** Get the Deno process exit code. + * + * If no exit code has been supplied, then Deno will assume a return code of `0`. + * + * ```ts + * console.log(Deno.exit.code); //-> 0 + * ``` + * + * @category Runtime Environment + */ + get code(): number; + + /** Set the Deno process exit code. + * + * A number or non-NaN string must be provided, otherwise a TypeError will be thrown. + * + * ```ts + * Deno.exit.code = 1; + * console.log(Deno.exit.code); //-> 1 + * ``` + * + * @category Runtime Environment + */ + set code(value: number | string); + } + + /** An interface containing methods to iteract with the process exit state. * * @category Runtime Environment */ - export function exit(code?: number): never; + export const exit: Exit; /** An interface containing methods to interact with the process environment * variables. From 9b7c5fbb4ba55642842a708567946bf0948f2256 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Mon, 29 Apr 2024 17:15:53 -0700 Subject: [PATCH 03/23] fix: use `_exitCode` as `Deno.exit` fallback value --- runtime/js/30_os.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index 46fefee922a76..f1bdeee1c581f 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -70,12 +70,13 @@ function setExitHandler(fn) { exitHandler = fn; } +let _exitCode = 0; function exit(code) { // Set exit code first so unload event listeners can override it. if (typeof code === "number") { op_set_exit_code(code); } else { - code = 0; + code = _exitCode; } // Dispatches `unload` only when it's not dispatched yet. @@ -94,13 +95,12 @@ function exit(code) { throw new Error("Code not reachable"); } -let _exitCode = 0; Object.defineProperty(exit, "code", { get() { return _exitCode; }, set(value) { - const code = parseInt(value) || undefined; + const code = parseInt(value, 10) || undefined; if (typeof code !== "number") { throw new TypeError("Exit code must be a number."); } From 9cc8188333a3182bf4b6a4a25dffcb35994d66f5 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Mon, 29 Apr 2024 17:35:51 -0700 Subject: [PATCH 04/23] chore: apply style format to test file --- cli/tests/unit/os_test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cli/tests/unit/os_test.ts b/cli/tests/unit/os_test.ts index ea9e499e360c9..873a7c215db30 100644 --- a/cli/tests/unit/os_test.ts +++ b/cli/tests/unit/os_test.ts @@ -16,10 +16,14 @@ Deno.test({ assertEquals(Deno.exit.code, 0); // Throws on non-number values - assertThrows(() => { - // @ts-expect-error Testing for runtime error - Deno.exit.code = "not a number"; - }, TypeError, "Exit code must be a number."); + assertThrows( + () => { + // @ts-expect-error Testing for runtime error + Deno.exit.code = "not a number"; + }, + TypeError, + "Exit code must be a number.", + ); }, }); From f81d833bebf8c18e70c397c45a798ffe88be8d34 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Mon, 29 Apr 2024 19:04:07 -0700 Subject: [PATCH 05/23] chore: apply fmt to `lib.deno.ns.d.ts` file --- cli/tsc/dts/lib.deno.ns.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 01165660c87a7..010fbe46967d8 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1482,7 +1482,7 @@ declare namespace Deno { * @category Runtime Environment */ get code(): number; - + /** Set the Deno process exit code. * * A number or non-NaN string must be provided, otherwise a TypeError will be thrown. From f5b6c8313368d269295a769205bd975c012bccbf Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Tue, 30 Apr 2024 09:54:02 -0700 Subject: [PATCH 06/23] fix(lint): use primordials --- runtime/js/30_os.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index f1bdeee1c581f..f23bdf257f2da 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -21,7 +21,10 @@ import { const { Error, FunctionPrototypeBind, + ObjectDefineProperty, + NumberParseInt, SymbolFor, + TypeError, } = primordials; import { Event, EventTarget } from "ext:deno_web/02_event.js"; @@ -95,12 +98,12 @@ function exit(code) { throw new Error("Code not reachable"); } -Object.defineProperty(exit, "code", { +ObjectDefineProperty(exit, "code", { get() { return _exitCode; }, set(value) { - const code = parseInt(value, 10) || undefined; + const code = NumberParseInt(value, 10) || undefined; if (typeof code !== "number") { throw new TypeError("Exit code must be a number."); } From 2435c7515298c337441404a90ae87e2887838721 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Tue, 30 Apr 2024 09:54:14 -0700 Subject: [PATCH 07/23] chore: add addl tests --- cli/tests/unit/os_test.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/cli/tests/unit/os_test.ts b/cli/tests/unit/os_test.ts index 873a7c215db30..b86ba13d83972 100644 --- a/cli/tests/unit/os_test.ts +++ b/cli/tests/unit/os_test.ts @@ -43,6 +43,42 @@ Deno.test({ }, }); +Deno.test({ + name: "Running Deno.exit(value) overrides Deno.exit.code", + fn() { + let args: unknown[] | undefined; + + const originalExit = Deno.exit; + Deno.exit = (...x) => { + args = x; + }; + + Deno.exit.code = 42; + Deno.exit(0); + + assertEquals(args, [0]); + Deno.exit = originalExit; + }, +}); + +Deno.test({ + name: "Running Deno.exit() uses Deno.exit.code as fallback", + fn() { + let args: unknown[] | undefined; + + const originalExit = Deno.exit; + Deno.exit = (...x) => { + args = x; + }; + + Deno.exit.code = 42; + Deno.exit(); + + assertEquals(args, [42]); + Deno.exit = originalExit; + }, +}); + Deno.test({ name: "Retrieving the set exit code before process termination", fn() { From 581abccd1053b61776660fa73c4d302cb9b54242 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Tue, 7 May 2024 20:01:45 -0700 Subject: [PATCH 08/23] chore: revert to getter/setter & incorporate node process --- cli/tests/unit/os_test.ts | 145 +++++++++++++++++----------------- cli/tsc/dts/lib.deno.ns.d.ts | 42 ++++------ ext/node/polyfills/process.ts | 33 ++++---- runtime/js/30_os.js | 30 ++++--- runtime/js/90_deno_ns.js | 6 ++ 5 files changed, 127 insertions(+), 129 deletions(-) diff --git a/cli/tests/unit/os_test.ts b/cli/tests/unit/os_test.ts index b86ba13d83972..af6ef219a2cf0 100644 --- a/cli/tests/unit/os_test.ts +++ b/cli/tests/unit/os_test.ts @@ -1,91 +1,94 @@ -// This file contains unit tests for the Deno.exit.code API +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + import { assertEquals, assertThrows } from "../../testing/asserts.ts"; -Deno.test({ - name: "Deno.exit.code getter and setter", - fn() { - // Initial value is 0 - assertEquals(Deno.exit.code, 0); - - // Set a new value - Deno.exit.code = 5; - assertEquals(Deno.exit.code, 5); - - // Reset to initial value - Deno.exit.code = 0; - assertEquals(Deno.exit.code, 0); - - // Throws on non-number values - assertThrows( - () => { - // @ts-expect-error Testing for runtime error - Deno.exit.code = "not a number"; - }, - TypeError, - "Exit code must be a number.", - ); - }, +Deno.test("Deno.exitCode getter and setter", () => { + // Initial value is 0 + assertEquals(Deno.exitCode, 0); + + // Set a new value + Deno.exitCode = 5; + assertEquals(Deno.exitCode, 5); + + // Reset to initial value + Deno.exitCode = 0; + assertEquals(Deno.exitCode, 0); }); -Deno.test({ - name: "Setting Deno.exit.code does not cause an immediate exit", - fn() { - let exited = false; - const originalExit = Deno.exit; - Deno.exit = () => { - exited = true; - }; +Deno.test("Setting Deno.exitCode to NaN throws TypeError", () => { + // @ts-expect-error; + Deno.exitCode = "123"; + assertEquals(Deno.exitCode, 123); + + // Reset + Deno.exitCode = 0; + assertEquals(Deno.exitCode, 0); + + // Throws on non-number values + assertThrows( + () => { + // @ts-expect-error Testing for runtime error + Deno.exitCode = "not a number"; + }, + TypeError, + "Exit code must be a number.", + ); +}); + +Deno.test("Setting Deno.exitCode does not cause an immediate exit", () => { + let exited = false; + const originalExit = Deno.exit; + + // @ts-expect-error; read-only + Deno.exit = () => { + exited = true; + }; - Deno.exit.code = 1; - assertEquals(exited, false); + Deno.exitCode = 1; + assertEquals(exited, false); - Deno.exit = originalExit; - }, + // @ts-expect-error; read-only + Deno.exit = originalExit; }); -Deno.test({ - name: "Running Deno.exit(value) overrides Deno.exit.code", - fn() { - let args: unknown[] | undefined; +Deno.test("Running Deno.exit(value) overrides Deno.exitCode", () => { + let args: unknown[] | undefined; - const originalExit = Deno.exit; - Deno.exit = (...x) => { - args = x; - }; + const originalExit = Deno.exit; + // @ts-expect-error; read-only + Deno.exit = (...x) => { + args = x; + }; - Deno.exit.code = 42; - Deno.exit(0); + Deno.exitCode = 42; + Deno.exit(0); - assertEquals(args, [0]); - Deno.exit = originalExit; - }, + assertEquals(args, [0]); + // @ts-expect-error; read-only + Deno.exit = originalExit; }); -Deno.test({ - name: "Running Deno.exit() uses Deno.exit.code as fallback", - fn() { - let args: unknown[] | undefined; +Deno.test("Running Deno.exit() uses Deno.exitCode as fallback", () => { + let args: unknown[] | undefined; - const originalExit = Deno.exit; - Deno.exit = (...x) => { - args = x; - }; + const originalExit = Deno.exit; + // @ts-expect-error; read-only + Deno.exit = (...x) => { + args = x; + }; - Deno.exit.code = 42; - Deno.exit(); + Deno.exitCode = 42; + Deno.exit(); - assertEquals(args, [42]); - Deno.exit = originalExit; - }, + assertEquals(args, [42]); + // @ts-expect-error; read-only + Deno.exit = originalExit; }); -Deno.test({ - name: "Retrieving the set exit code before process termination", - fn() { - Deno.exit.code = 42; - assertEquals(Deno.exit.code, 42); +Deno.test("Retrieving the set exit code before process termination", () => { + Deno.exitCode = 42; + assertEquals(Deno.exitCode, 42); - // Reset to initial value - Deno.exit.code = 0; - }, + // Reset to initial value + Deno.exitCode = 0; }); diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index a2a0165be8aeb..688fbb5b67110 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1470,31 +1470,6 @@ declare namespace Deno { * @category Runtime Environment */ (code?: number): never; - - /** Get the Deno process exit code. - * - * If no exit code has been supplied, then Deno will assume a return code of `0`. - * - * ```ts - * console.log(Deno.exit.code); //-> 0 - * ``` - * - * @category Runtime Environment - */ - get code(): number; - - /** Set the Deno process exit code. - * - * A number or non-NaN string must be provided, otherwise a TypeError will be thrown. - * - * ```ts - * Deno.exit.code = 1; - * console.log(Deno.exit.code); //-> 1 - * ``` - * - * @category Runtime Environment - */ - set code(value: number | string); } /** An interface containing methods to iteract with the process exit state. @@ -1503,6 +1478,23 @@ declare namespace Deno { */ export const exit: Exit; + /** The exit code for the Deno process. + * + * If no exit code has been supplied, then Deno will assume a return code of `0`. + * + * When setting an exit code value, a number or non-NaN string must be provided, + * otherwise a TypeError will be thrown. + * + * ```ts + * console.log(Deno.exitCode); //-> 0 + * Deno.exitCode = 1; + * console.log(Deno.exitCode); //-> 1 + * ``` + * + * @category Runtime Environment + */ + export var exitCode: number; + /** An interface containing methods to interact with the process environment * variables. * diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index b24bf6a8e4b9e..5f9af37e2f775 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -10,7 +10,6 @@ import { op_geteuid, op_node_process_kill, op_process_abort, - op_set_exit_code, } from "ext:core/ops"; import { warnNotImplemented } from "ext:deno_node/_utils.ts"; @@ -74,17 +73,16 @@ const notImplementedEvents = [ ]; export const argv: string[] = ["", ""]; -let globalProcessExitCode: number | undefined = undefined; + +// In Node, `process.exitCode` is initially `undefined` until set. +// And retains any value as long as it's nullish or number-ish. +let ProcessExitCode: undefined | null | string | number; /** https://nodejs.org/api/process.html#process_process_exit_code */ export const exit = (code?: number | string) => { if (code || code === 0) { - if (typeof code === "string") { - const parsedCode = parseInt(code); - globalProcessExitCode = isNaN(parsedCode) ? undefined : parsedCode; - } else { - globalProcessExitCode = code; - } + // @ts-expect-error; Deno.exitCode setter parses string + Deno.exitCode = code; } if (!process._exiting) { @@ -92,10 +90,12 @@ export const exit = (code?: number | string) => { // FIXME(bartlomieju): this is wrong, we won't be using syscall to exit // and thus the `unload` event will not be emitted to properly trigger "emit" // event on `process`. - process.emit("exit", process.exitCode || 0); + process.emit("exit", ProcessExitCode || Deno.exitCode); } - process.reallyExit(process.exitCode || 0); + // Any valid thing `process.exitCode` set is already held in Deno.exitCode. + // At this point, we don't have to pass around Node's raw/string exit value. + process.reallyExit(Deno.exitCode); }; /** https://nodejs.org/api/process.html#processumaskmask */ @@ -401,15 +401,14 @@ class Process extends EventEmitter { /** https://nodejs.org/api/process.html#processexitcode_1 */ get exitCode() { - return globalProcessExitCode; + return ProcessExitCode; } - set exitCode(code: number | undefined) { - globalProcessExitCode = code; - code = parseInt(code) || 0; - if (!isNaN(code)) { - op_set_exit_code(code); - } + set exitCode(code: number | string | null | undefined) { + // @ts-expect-error; Deno.exitCode parses string + if (code != null) Deno.exitCode = code; + // invalid string would have thrown + ProcessExitCode = code; } // Typed as any to avoid importing "module" module for types diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index f23bdf257f2da..8a535f70f5e9c 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -21,7 +21,6 @@ import { const { Error, FunctionPrototypeBind, - ObjectDefineProperty, NumberParseInt, SymbolFor, TypeError, @@ -98,21 +97,18 @@ function exit(code) { throw new Error("Code not reachable"); } -ObjectDefineProperty(exit, "code", { - get() { - return _exitCode; - }, - set(value) { - const code = NumberParseInt(value, 10) || undefined; - if (typeof code !== "number") { - throw new TypeError("Exit code must be a number."); - } - _exitCode = code; - op_set_exit_code(code); - }, - enumerable: true, - configurable: true, -}); +function getExitCode() { + return _exitCode; +} + +function setExitCode(value) { + const code = NumberParseInt(value, 10) || undefined; + if (typeof code !== "number") { + throw new TypeError("Exit code must be a number."); + } + _exitCode = code; + op_set_exit_code(code); +} function setEnv(key, value) { op_set_env(key, value); @@ -146,12 +142,14 @@ export { env, execPath, exit, + getExitCode, gid, hostname, loadavg, networkInterfaces, osRelease, osUptime, + setExitCode, setExitHandler, systemMemoryInfo, uid, diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 02ac7b60202a2..aabe20c18a357 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -117,6 +117,12 @@ const denoNs = { inspect: console.inspect, env: os.env, exit: os.exit, + get exitCode() { + return os.getExitCode(); + }, + set exitCode(value) { + os.setExitCode(value); + }, execPath: os.execPath, Buffer: buffer.Buffer, readAll: buffer.readAll, From fa4fdc0867cc621efa366c617bab6b3995ca4fd7 Mon Sep 17 00:00:00 2001 From: Luke Edwards Date: Tue, 7 May 2024 20:18:35 -0700 Subject: [PATCH 09/23] fix: revert `Deno.exit` dts changes --- cli/tsc/dts/lib.deno.ns.d.ts | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 688fbb5b67110..977b9b7bdf1dc 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1452,31 +1452,19 @@ declare namespace Deno { fn: (b: BenchContext) => void | Promise, ): void; - /** An interface containing methods to iteract with the process exit state. + /** Exit the Deno process with optional exit code. * - * @category Runtime - */ - export interface Exit { - /** Exit the Deno process with optional exit code. - * - * If no exit code is supplied then Deno will exit with return code of `0`. - * - * In worker contexts this is an alias to `self.close();`. - * - * ```ts - * Deno.exit(5); - * ``` - * - * @category Runtime Environment - */ - (code?: number): never; - } - - /** An interface containing methods to iteract with the process exit state. + * If no exit code is supplied then Deno will exit with return code of `0`. + * + * In worker contexts this is an alias to `self.close();`. + * + * ```ts + * Deno.exit(5); + * ``` * * @category Runtime Environment */ - export const exit: Exit; + export function exit(code?: number): never; /** The exit code for the Deno process. * From 9b30d688533d90e5a5876c4176e1e6348b701f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 14 May 2024 00:10:15 +0200 Subject: [PATCH 10/23] updates to match Node 21.5.0 --- ext/node/polyfills/process.ts | 17 +++++++++++------ runtime/js/30_os.js | 7 +++---- runtime/ops/os/mod.rs | 10 +++++++++- tests/unit_node/process_test.ts | 10 +++++----- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 87b5de333beaa..b26cbf0aab1a2 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -48,6 +48,7 @@ import { } from "ext:deno_node/_next_tick.ts"; import { isWindows } from "ext:deno_node/_util/os.ts"; import * as io from "ext:deno_io/12_io.js"; +import * as denoOs from "ext:runtime/30_os.js"; export let argv0 = ""; @@ -81,21 +82,21 @@ let ProcessExitCode: undefined | null | string | number; /** https://nodejs.org/api/process.html#process_process_exit_code */ export const exit = (code?: number | string) => { if (code || code === 0) { - // @ts-expect-error; Deno.exitCode setter parses string - Deno.exitCode = code; + denoOs.setExitCode(code); } + const exitCode = ProcessExitCode || denoOs.getExitCode(); if (!process._exiting) { process._exiting = true; // FIXME(bartlomieju): this is wrong, we won't be using syscall to exit // and thus the `unload` event will not be emitted to properly trigger "emit" // event on `process`. - process.emit("exit", ProcessExitCode || Deno.exitCode); + process.emit("exit", exitCode); } // Any valid thing `process.exitCode` set is already held in Deno.exitCode. // At this point, we don't have to pass around Node's raw/string exit value. - process.reallyExit(Deno.exitCode); + process.reallyExit(exitCode); }; /** https://nodejs.org/api/process.html#processumaskmask */ @@ -405,8 +406,12 @@ class Process extends EventEmitter { } set exitCode(code: number | string | null | undefined) { - // @ts-expect-error; Deno.exitCode parses string - if (code != null) Deno.exitCode = code; + if (code != null) { + // This is looser than `denoOs.setExitCode` which requires exit code + // to be decimal or string of a decimal, but Node accept eg. 0x10. + code = parseInt(code) || 0; + denoOs.setExitCode(code); + } // invalid string would have thrown ProcessExitCode = code; } diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index 8a535f70f5e9c..26404beba83af 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -7,6 +7,7 @@ import { op_exec_path, op_exit, op_get_env, + op_get_exit_code, op_gid, op_hostname, op_loadavg, @@ -72,13 +73,12 @@ function setExitHandler(fn) { exitHandler = fn; } -let _exitCode = 0; function exit(code) { // Set exit code first so unload event listeners can override it. if (typeof code === "number") { op_set_exit_code(code); } else { - code = _exitCode; + code = op_get_exit_code(); } // Dispatches `unload` only when it's not dispatched yet. @@ -98,7 +98,7 @@ function exit(code) { } function getExitCode() { - return _exitCode; + return op_get_exit_code(); } function setExitCode(value) { @@ -106,7 +106,6 @@ function setExitCode(value) { if (typeof code !== "number") { throw new TypeError("Exit code must be a number."); } - _exitCode = code; op_set_exit_code(code); } diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index 7a71590baa9ef..a30051e84f3cb 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -32,6 +32,7 @@ deno_core::extension!( op_os_uptime, op_set_env, op_set_exit_code, + op_get_exit_code, op_system_memory_info, op_uid, op_runtime_memory_usage, @@ -60,12 +61,13 @@ deno_core::extension!( op_os_uptime, op_set_env, op_set_exit_code, + op_get_exit_code, op_system_memory_info, op_uid, op_runtime_memory_usage, ], middleware = |op| match op.name { - "op_exit" | "op_set_exit_code" => + "op_exit" | "op_set_exit_code" | "op_get_exit_code" => op.with_implementation_from(&deno_core::op_void_sync()), _ => op, }, @@ -164,6 +166,12 @@ fn op_set_exit_code(state: &mut OpState, #[smi] code: i32) { state.borrow_mut::().set(code); } +#[op2(fast)] +#[smi] +fn op_get_exit_code(state: &mut OpState) { + state.borrow_mut::().get(); +} + #[op2(fast)] fn op_exit(state: &mut OpState) { let code = state.borrow::().get(); diff --git a/tests/unit_node/process_test.ts b/tests/unit_node/process_test.ts index 15ad052bfcccd..da1b488df77eb 100644 --- a/tests/unit_node/process_test.ts +++ b/tests/unit_node/process_test.ts @@ -781,10 +781,10 @@ Deno.test("process.exitCode", () => { assertEquals(process.exitCode, undefined); process.exitCode = 127; assertEquals(process.exitCode, 127); - // deno-lint-ignore no-explicit-any - (process.exitCode as any) = "asdf"; - // deno-lint-ignore no-explicit-any - assertEquals(process.exitCode as any, "asdf"); + 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 @@ -821,7 +821,7 @@ Deno.test("process.exitCode in should change exit code", async () => { ); await exitCodeTest( "import process from 'node:process'; process.exitCode = NaN;", - 0, + 1, ); }); From 21930ef4fedf9e12a2aa92ed94c07116506b2b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 23 May 2024 02:14:17 +0200 Subject: [PATCH 11/23] improve error a bit --- ext/node/polyfills/process.ts | 2 +- runtime/js/30_os.js | 4 +++- .../test/.tmp.318/test-stdin-from-file-spawn-cmd | 1 + .../test/.tmp.318/test-stdin-from-file-spawn.js | 16 ++++++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd create mode 100644 tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 8f1ffac368680..09c9f7017a21a 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -437,7 +437,7 @@ Object.defineProperty(Process.prototype, "exitCode", { return ProcessExitCode; }, set(code: number | string | null | undefined) { - if (code != null) { + if (!!code) { // This is looser than `denoOs.setExitCode` which requires exit code // to be decimal or string of a decimal, but Node accept eg. 0x10. code = parseInt(code) || 0; diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index 26404beba83af..8205a0e9bcbbf 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -104,7 +104,9 @@ function getExitCode() { function setExitCode(value) { const code = NumberParseInt(value, 10) || undefined; if (typeof code !== "number") { - throw new TypeError("Exit code must be a number."); + throw new TypeError( + `Exit code must be a number, got: ${code} (${typeof code}).`, + ); } op_set_exit_code(code); } diff --git a/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd b/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd new file mode 100644 index 0000000000000..cfd72d1f2740f --- /dev/null +++ b/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd @@ -0,0 +1 @@ +echo hello \ No newline at end of file diff --git a/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js b/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js new file mode 100644 index 0000000000000..e7b469b4f8eaf --- /dev/null +++ b/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js @@ -0,0 +1,16 @@ + +'use strict'; +const { spawn } = require('child_process'); +// Reference the object to invoke the getter +process.stdin; +setTimeout(() => { + let ok = false; + const child = spawn(process.env.SHELL || '/bin/sh', + [], { stdio: ['inherit', 'pipe'] }); + child.stdout.on('data', () => { + ok = true; + }); + child.on('close', () => { + process.exit(ok ? 0 : -1); + }); +}, 100); From 974c06bdba5a7837d22c384ce65f46b0194ef621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 23 May 2024 13:58:18 +0200 Subject: [PATCH 12/23] lint --- ext/node/polyfills/process.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 09c9f7017a21a..0417144dc9aab 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -437,7 +437,7 @@ Object.defineProperty(Process.prototype, "exitCode", { return ProcessExitCode; }, set(code: number | string | null | undefined) { - if (!!code) { + if (code) { // This is looser than `denoOs.setExitCode` which requires exit code // to be decimal or string of a decimal, but Node accept eg. 0x10. code = parseInt(code) || 0; From fb2ad14e0ba9f01f9acb857cce0a65bf2b6133a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 23 May 2024 14:03:04 +0200 Subject: [PATCH 13/23] remove stray files --- .../test/.tmp.318/test-stdin-from-file-spawn-cmd | 1 - .../test/.tmp.318/test-stdin-from-file-spawn.js | 16 ---------------- 2 files changed, 17 deletions(-) delete mode 100644 tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd delete mode 100644 tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js diff --git a/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd b/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd deleted file mode 100644 index cfd72d1f2740f..0000000000000 --- a/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn-cmd +++ /dev/null @@ -1 +0,0 @@ -echo hello \ No newline at end of file diff --git a/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js b/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js deleted file mode 100644 index e7b469b4f8eaf..0000000000000 --- a/tests/node_compat/test/.tmp.318/test-stdin-from-file-spawn.js +++ /dev/null @@ -1,16 +0,0 @@ - -'use strict'; -const { spawn } = require('child_process'); -// Reference the object to invoke the getter -process.stdin; -setTimeout(() => { - let ok = false; - const child = spawn(process.env.SHELL || '/bin/sh', - [], { stdio: ['inherit', 'pipe'] }); - child.stdout.on('data', () => { - ok = true; - }); - child.on('close', () => { - process.exit(ok ? 0 : -1); - }); -}, 100); From 35c78d469dd56eac537f339ce67640cef2626908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 23 May 2024 16:53:05 +0200 Subject: [PATCH 14/23] maybe fix? --- ext/node/polyfills/process.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 0417144dc9aab..b31bea6e2c6cd 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -83,6 +83,8 @@ let ProcessExitCode: undefined | null | string | number; export const exit = (code?: number | string) => { if (code || code === 0) { denoOs.setExitCode(code); + } else if (Number.isNaN(code)) { + denoOs.setExitCode(1); } const exitCode = ProcessExitCode || denoOs.getExitCode(); @@ -437,11 +439,14 @@ Object.defineProperty(Process.prototype, "exitCode", { return ProcessExitCode; }, set(code: number | string | null | undefined) { - if (code) { + if (!!code) { // This is looser than `denoOs.setExitCode` which requires exit code // to be decimal or string of a decimal, but Node accept eg. 0x10. code = parseInt(code) || 0; denoOs.setExitCode(code); + } else if (Number.isNaN(code)) { + code = 1; + denoOs.setExitCode(code); } // invalid string would have thrown ProcessExitCode = code; From 54831aecfe8d802c7ff6582ae540de33188a6250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 23 May 2024 18:30:30 +0200 Subject: [PATCH 15/23] Try again --- ext/node/polyfills/process.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index b31bea6e2c6cd..9e05888d3d416 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -439,7 +439,7 @@ Object.defineProperty(Process.prototype, "exitCode", { return ProcessExitCode; }, set(code: number | string | null | undefined) { - if (!!code) { + if (code) { // This is looser than `denoOs.setExitCode` which requires exit code // to be decimal or string of a decimal, but Node accept eg. 0x10. code = parseInt(code) || 0; From 7fb4d91653e39ec47776ceb36398dedd472c5b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 29 May 2024 00:23:50 +0200 Subject: [PATCH 16/23] node tests finally passing, will Deno tests pass too? --- ext/node/polyfills/process.ts | 6 +++--- runtime/js/30_os.js | 2 +- runtime/ops/os/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 9e05888d3d416..4b7a3c7d4daa4 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -87,18 +87,18 @@ export const exit = (code?: number | string) => { denoOs.setExitCode(1); } - const exitCode = ProcessExitCode || denoOs.getExitCode(); + ProcessExitCode = denoOs.getExitCode(); if (!process._exiting) { process._exiting = true; // FIXME(bartlomieju): this is wrong, we won't be using syscall to exit // and thus the `unload` event will not be emitted to properly trigger "emit" // event on `process`. - process.emit("exit", exitCode); + process.emit("exit", ProcessExitCode); } // Any valid thing `process.exitCode` set is already held in Deno.exitCode. // At this point, we don't have to pass around Node's raw/string exit value. - process.reallyExit(exitCode); + process.reallyExit(ProcessExitCode); }; /** https://nodejs.org/api/process.html#processumaskmask */ diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js index 8205a0e9bcbbf..866fad2878a7c 100644 --- a/runtime/js/30_os.js +++ b/runtime/js/30_os.js @@ -102,7 +102,7 @@ function getExitCode() { } function setExitCode(value) { - const code = NumberParseInt(value, 10) || undefined; + const code = NumberParseInt(value, 10); if (typeof code !== "number") { throw new TypeError( `Exit code must be a number, got: ${code} (${typeof code}).`, diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index a30051e84f3cb..f6f55f68fa5b0 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -168,8 +168,8 @@ fn op_set_exit_code(state: &mut OpState, #[smi] code: i32) { #[op2(fast)] #[smi] -fn op_get_exit_code(state: &mut OpState) { - state.borrow_mut::().get(); +fn op_get_exit_code(state: &mut OpState) -> i32 { + state.borrow_mut::().get() } #[op2(fast)] From 6cf53f57e460892d63ab905a02da859738873a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 29 May 2024 00:31:12 +0200 Subject: [PATCH 17/23] update type declarations --- cli/tsc/dts/lib.deno.ns.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index aabce3e41bdb0..cf8e4ba0532f6 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1462,7 +1462,7 @@ declare namespace Deno { * Deno.exit(5); * ``` * - * @category Runtime Environment + * @category Runtime */ export function exit(code?: number): never; @@ -1479,7 +1479,7 @@ declare namespace Deno { * console.log(Deno.exitCode); //-> 1 * ``` * - * @category Runtime Environment + * @category Runtime */ export var exitCode: number; From ba9a252bcba990465be1a5a19dd8134fae4d7e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 29 May 2024 00:37:42 +0200 Subject: [PATCH 18/23] add spec test --- tests/specs/run/exit_code/__test__.jsonc | 5 +++++ tests/specs/run/exit_code/main.js | 7 +++++++ tests/specs/run/exit_code/main.out | 1 + 3 files changed, 13 insertions(+) create mode 100644 tests/specs/run/exit_code/__test__.jsonc create mode 100644 tests/specs/run/exit_code/main.js create mode 100644 tests/specs/run/exit_code/main.out diff --git a/tests/specs/run/exit_code/__test__.jsonc b/tests/specs/run/exit_code/__test__.jsonc new file mode 100644 index 0000000000000..977f8b0e37f9a --- /dev/null +++ b/tests/specs/run/exit_code/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "run main.js", + "exitCode": 42, + "output": "main.out" +} diff --git a/tests/specs/run/exit_code/main.js b/tests/specs/run/exit_code/main.js new file mode 100644 index 0000000000000..1ba1f06c4fdec --- /dev/null +++ b/tests/specs/run/exit_code/main.js @@ -0,0 +1,7 @@ +if (Deno.exitCode != 0) { + throw new Error("boom!"); +} + +Deno.exitCode = 42; + +console.log("Deno.exitCode", Deno.exitCode); diff --git a/tests/specs/run/exit_code/main.out b/tests/specs/run/exit_code/main.out new file mode 100644 index 0000000000000..25f2de2d5afac --- /dev/null +++ b/tests/specs/run/exit_code/main.out @@ -0,0 +1 @@ +Deno.exitCode 42 From eb03dff210df440f3e7ea9fda11b433d107780c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 29 May 2024 01:03:33 +0200 Subject: [PATCH 19/23] move to 99_main, need to fix exit sanitizer --- runtime/js/90_deno_ns.js | 6 ------ runtime/js/99_main.js | 8 ++++++++ tests/specs/test/exit_code/__test__.jsonc | 5 +++++ tests/specs/test/exit_code/main.js | 3 +++ tests/specs/test/exit_code/main.out | 1 + 5 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 tests/specs/test/exit_code/__test__.jsonc create mode 100644 tests/specs/test/exit_code/main.js create mode 100644 tests/specs/test/exit_code/main.out diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 77384e2ef3cb8..2e13976a7141d 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -117,12 +117,6 @@ const denoNs = { inspect: console.inspect, env: os.env, exit: os.exit, - get exitCode() { - return os.getExitCode(); - }, - set exitCode(value) { - os.setExitCode(value); - }, execPath: os.execPath, Buffer: buffer.Buffer, readAll: buffer.readAll, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 3fa9fc41b6107..4f949b214636f 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -674,6 +674,14 @@ ObjectDefineProperties(finalDenoNs, { return internals.future ? undefined : customInspect; }, }, + exitCode: { + get() { + return os.getExitCode(); + }, + set(value) { + os.setExitCode(value); + }, + }, }); const { diff --git a/tests/specs/test/exit_code/__test__.jsonc b/tests/specs/test/exit_code/__test__.jsonc new file mode 100644 index 0000000000000..a5b2910681663 --- /dev/null +++ b/tests/specs/test/exit_code/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "test main.js", + "exitCode": 0, + "output": "main.out" +} diff --git a/tests/specs/test/exit_code/main.js b/tests/specs/test/exit_code/main.js new file mode 100644 index 0000000000000..768bb4165e278 --- /dev/null +++ b/tests/specs/test/exit_code/main.js @@ -0,0 +1,3 @@ +Deno.test("Deno.exitCode", () => { + Deno.exitCode = 42; +}); diff --git a/tests/specs/test/exit_code/main.out b/tests/specs/test/exit_code/main.out new file mode 100644 index 0000000000000..25f2de2d5afac --- /dev/null +++ b/tests/specs/test/exit_code/main.out @@ -0,0 +1 @@ +Deno.exitCode 42 From f228e1d8ae3d7b2baa337f02fa9d43d6818a7b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 29 May 2024 01:57:23 +0200 Subject: [PATCH 20/23] fix test --- ext/node/polyfills/process.ts | 36 ++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 4b7a3c7d4daa4..9a28137af96e8 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -439,16 +439,38 @@ Object.defineProperty(Process.prototype, "exitCode", { return ProcessExitCode; }, set(code: number | string | null | undefined) { - if (code) { + let parsedCode; + + if (typeof code === "number") { + if (Number.isNaN(code)) { + parsedCode = 1; + denoOs.setExitCode(parsedCode); + ProcessExitCode = parsedCode; + return; + } + // This is looser than `denoOs.setExitCode` which requires exit code // to be decimal or string of a decimal, but Node accept eg. 0x10. - code = parseInt(code) || 0; - denoOs.setExitCode(code); - } else if (Number.isNaN(code)) { - code = 1; - denoOs.setExitCode(code); + parsedCode = parseInt(code); + denoOs.setExitCode(parsedCode); + ProcessExitCode = parsedCode; + return; } - // invalid string would have thrown + + if (typeof code === "string") { + parsedCode = parseInt(code); + if (Number.isNaN(parsedCode)) { + throw new TypeError( + `The "code" argument must be of type number. Received type ${typeof code} (${code})`, + ); + } + denoOs.setExitCode(parsedCode); + ProcessExitCode = parsedCode; + return; + } + + // TODO(bartlomieju): hope for the best here. This should be further tightened. + denoOs.setExitCode(code); ProcessExitCode = code; }, }); From 6c2f23456c7b130dcb6f13dac57d79ddde417544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 30 May 2024 00:18:37 +0200 Subject: [PATCH 21/23] exit sanitizer --- cli/js/40_test.js | 14 +++++++++++++- tests/specs/test/exit_code/__test__.jsonc | 2 +- tests/specs/test/exit_code/main.out | 18 +++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/cli/js/40_test.js b/cli/js/40_test.js index 2877bfa9b5415..6a1737e9b5333 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -28,6 +28,10 @@ const { import { setExitHandler } from "ext:runtime/30_os.js"; +// Capture `Deno` global so that users deleting or mangling it, won't +// have impact on our sanitizers. +const DenoNs = globalThis.Deno; + /** * @typedef {{ * id: number, @@ -101,7 +105,15 @@ function assertExit(fn, isTest) { try { const innerResult = await fn(...new SafeArrayIterator(params)); - if (innerResult) return innerResult; + const exitCode = DenoNs.exitCode; + if (exitCode !== 0) { + throw new Error( + `${isTest ? "Test case" : "Bench"} set the exit code to ${exitCode}.`, + ); + } + if (innerResult) { + return innerResult; + } } finally { setExitHandler(null); } diff --git a/tests/specs/test/exit_code/__test__.jsonc b/tests/specs/test/exit_code/__test__.jsonc index a5b2910681663..37d678453b197 100644 --- a/tests/specs/test/exit_code/__test__.jsonc +++ b/tests/specs/test/exit_code/__test__.jsonc @@ -1,5 +1,5 @@ { "args": "test main.js", - "exitCode": 0, + "exitCode": 1, "output": "main.out" } diff --git a/tests/specs/test/exit_code/main.out b/tests/specs/test/exit_code/main.out index 25f2de2d5afac..355ed0564bca4 100644 --- a/tests/specs/test/exit_code/main.out +++ b/tests/specs/test/exit_code/main.out @@ -1 +1,17 @@ -Deno.exitCode 42 +running 1 test from ./main.js +Deno.exitCode ... FAILED (0ms) + + ERRORS + +Deno.exitCode => ./main.js:1:6 +error: Error: Test case set the exit code to 42. + at exitSanitizer (ext:cli/40_test.js:110:15) + at async outerWrapped (ext:cli/40_test.js:129:14) + + FAILURES + +Deno.exitCode => ./main.js:1:6 + +FAILED | 0 passed | 1 failed ([WILDCARD]ms) + +error: Test failed From 0b91e1988d14c4abfe8bc5f2baef7b768c36147a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 30 May 2024 00:39:30 +0200 Subject: [PATCH 22/23] Wording --- cli/js/40_test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/js/40_test.js b/cli/js/40_test.js index 6a1737e9b5333..0a42b5ecd5fa4 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -108,7 +108,9 @@ function assertExit(fn, isTest) { const exitCode = DenoNs.exitCode; if (exitCode !== 0) { throw new Error( - `${isTest ? "Test case" : "Bench"} set the exit code to ${exitCode}.`, + `${ + isTest ? "Test case" : "Bench" + } finishes with exit code set to ${exitCode}.`, ); } if (innerResult) { From 393f7b939dc6f5b57a807f8cc121ea7e8b525b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 30 May 2024 00:47:55 +0200 Subject: [PATCH 23/23] update tests and working --- cli/js/40_test.js | 5 ++++- tests/specs/test/exit_code/main.out | 10 ++++----- tests/specs/test/exit_code2/__test__.jsonc | 5 +++++ tests/specs/test/exit_code2/main.js | 7 ++++++ tests/specs/test/exit_code2/main.out | 25 ++++++++++++++++++++++ tests/specs/test/exit_code3/__test__.jsonc | 5 +++++ tests/specs/test/exit_code3/main.js | 6 ++++++ tests/specs/test/exit_code3/main.out | 18 ++++++++++++++++ 8 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 tests/specs/test/exit_code2/__test__.jsonc create mode 100644 tests/specs/test/exit_code2/main.js create mode 100644 tests/specs/test/exit_code2/main.out create mode 100644 tests/specs/test/exit_code3/__test__.jsonc create mode 100644 tests/specs/test/exit_code3/main.js create mode 100644 tests/specs/test/exit_code3/main.out diff --git a/cli/js/40_test.js b/cli/js/40_test.js index 0a42b5ecd5fa4..5a081e2175c0e 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -107,10 +107,13 @@ function assertExit(fn, isTest) { const innerResult = await fn(...new SafeArrayIterator(params)); const exitCode = DenoNs.exitCode; if (exitCode !== 0) { + // Reset the code to allow other tests to run... + DenoNs.exitCode = 0; + // ...and fail the current test. throw new Error( `${ isTest ? "Test case" : "Bench" - } finishes with exit code set to ${exitCode}.`, + } finished with exit code set to ${exitCode}.`, ); } if (innerResult) { diff --git a/tests/specs/test/exit_code/main.out b/tests/specs/test/exit_code/main.out index 355ed0564bca4..2562695a025be 100644 --- a/tests/specs/test/exit_code/main.out +++ b/tests/specs/test/exit_code/main.out @@ -1,17 +1,17 @@ running 1 test from ./main.js -Deno.exitCode ... FAILED (0ms) +Deno.exitCode ... FAILED ([WILDCARD]) ERRORS Deno.exitCode => ./main.js:1:6 -error: Error: Test case set the exit code to 42. - at exitSanitizer (ext:cli/40_test.js:110:15) - at async outerWrapped (ext:cli/40_test.js:129:14) +error: Error: Test case finished with exit code set to 42. + at exitSanitizer (ext:cli/40_test.js:113:15) + at async outerWrapped (ext:cli/40_test.js:134:14) FAILURES Deno.exitCode => ./main.js:1:6 -FAILED | 0 passed | 1 failed ([WILDCARD]ms) +FAILED | 0 passed | 1 failed ([WILDCARD]) error: Test failed diff --git a/tests/specs/test/exit_code2/__test__.jsonc b/tests/specs/test/exit_code2/__test__.jsonc new file mode 100644 index 0000000000000..37d678453b197 --- /dev/null +++ b/tests/specs/test/exit_code2/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "test main.js", + "exitCode": 1, + "output": "main.out" +} diff --git a/tests/specs/test/exit_code2/main.js b/tests/specs/test/exit_code2/main.js new file mode 100644 index 0000000000000..2e6398a580617 --- /dev/null +++ b/tests/specs/test/exit_code2/main.js @@ -0,0 +1,7 @@ +Deno.test("Deno.exitCode", () => { + Deno.exitCode = 5; + throw new Error(""); +}); + +Deno.test("success", () => { +}); diff --git a/tests/specs/test/exit_code2/main.out b/tests/specs/test/exit_code2/main.out new file mode 100644 index 0000000000000..adc9cb577525e --- /dev/null +++ b/tests/specs/test/exit_code2/main.out @@ -0,0 +1,25 @@ +running 2 tests from ./main.js +Deno.exitCode ... FAILED ([WILDCARD]) +success ... FAILED ([WILDCARD]) + + ERRORS + +Deno.exitCode => ./main.js:1:6 +error: Error + throw new Error(""); + ^ + at [WILDCARD]/exit_code2/main.js:3:9 + +success => ./main.js:6:6 +error: Error: Test case finished with exit code set to 5. + at exitSanitizer (ext:cli/40_test.js:113:15) + at async outerWrapped (ext:cli/40_test.js:134:14) + + FAILURES + +Deno.exitCode => ./main.js:1:6 +success => ./main.js:6:6 + +FAILED | 0 passed | 2 failed ([WILDCARD]) + +error: Test failed diff --git a/tests/specs/test/exit_code3/__test__.jsonc b/tests/specs/test/exit_code3/__test__.jsonc new file mode 100644 index 0000000000000..37d678453b197 --- /dev/null +++ b/tests/specs/test/exit_code3/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "test main.js", + "exitCode": 1, + "output": "main.out" +} diff --git a/tests/specs/test/exit_code3/main.js b/tests/specs/test/exit_code3/main.js new file mode 100644 index 0000000000000..a859afbaef911 --- /dev/null +++ b/tests/specs/test/exit_code3/main.js @@ -0,0 +1,6 @@ +Deno.test("Deno.exitCode", () => { + Deno.exitCode = 42; +}); + +Deno.test("success", () => { +}); diff --git a/tests/specs/test/exit_code3/main.out b/tests/specs/test/exit_code3/main.out new file mode 100644 index 0000000000000..6e333bf4257ee --- /dev/null +++ b/tests/specs/test/exit_code3/main.out @@ -0,0 +1,18 @@ +running 2 tests from ./main.js +Deno.exitCode ... FAILED ([WILDCARD]) +success ... ok ([WILDCARD]) + + ERRORS + +Deno.exitCode => ./main.js:1:6 +error: Error: Test case finished with exit code set to 42. + at exitSanitizer (ext:cli/40_test.js:113:15) + at async outerWrapped (ext:cli/40_test.js:134:14) + + FAILURES + +Deno.exitCode => ./main.js:1:6 + +FAILED | 1 passed | 1 failed ([WILDCARD]) + +error: Test failed