From 0f40ee7ff4880357958d73186e8b54c20ee73a8f Mon Sep 17 00:00:00 2001 From: Jake Champion Date: Tue, 1 Apr 2025 12:48:09 +0100 Subject: [PATCH] fix(ext/node): support the optional `previousValue` parameter for process.cpuUsage() (#28550) --- ext/node/polyfills/process.ts | 57 ++++++++++++++++++++++++++++++--- tests/unit_node/process_test.ts | 48 +++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index cbfee4f9f0..9e50b8f18d 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -4,7 +4,7 @@ // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials -import { core, internals } from "ext:core/mod.js"; +import { core, internals, primordials } from "ext:core/mod.js"; import { initializeDebugEnv } from "ext:deno_node/internal/util/debuglog.ts"; import { op_getegid, @@ -17,9 +17,14 @@ import { warnNotImplemented } from "ext:deno_node/_utils.ts"; import { EventEmitter } from "node:events"; import Module, { getBuiltinModule } from "node:module"; import { report } from "ext:deno_node/internal/process/report.ts"; -import { validateString } from "ext:deno_node/internal/validators.mjs"; +import { + validateNumber, + validateObject, + validateString, +} from "ext:deno_node/internal/validators.mjs"; import { ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE_RANGE, ERR_OUT_OF_RANGE, ERR_UNKNOWN_SIGNAL, errnoException, @@ -79,6 +84,8 @@ import type { BindingName } from "ext:deno_node/internal_binding/mod.ts"; import { buildAllowedFlags } from "ext:deno_node/internal/process/per_thread.mjs"; import { setProcess } from "ext:deno_node/_events.mjs"; +const { NumberMAX_SAFE_INTEGER } = primordials; + const notImplementedEvents = [ "multipleResolves", "worker", @@ -144,6 +151,48 @@ function addReadOnlyProcessAlias( } } +interface CpuUsage { + user: number; + system: number; +} + +// Ensure that a previously passed in value is valid. Currently, the native +// implementation always returns numbers <= Number.MAX_SAFE_INTEGER. +function previousCpuUsageValueIsValid(num) { + return typeof num === "number" && num >= 0 && num <= NumberMAX_SAFE_INTEGER; +} + +export function cpuUsage(previousValue?: CpuUsage): CpuUsage { + const cpuValues = Deno.cpuUsage(previousValue); + + if (previousValue) { + if (!previousCpuUsageValueIsValid(previousValue.user)) { + validateObject(previousValue, "previousValue"); + + validateNumber(previousValue.user, "previousValue.user"); + throw new ERR_INVALID_ARG_VALUE_RANGE( + "previousValue.user", + previousValue.user, + ); + } + + if (!previousCpuUsageValueIsValid(previousValue.system)) { + validateNumber(previousValue.system, "previousValue.system"); + throw new ERR_INVALID_ARG_VALUE_RANGE( + "previousValue.system", + previousValue.system, + ); + } + + return { + user: cpuValues.user - previousValue.user, + system: cpuValues.system - previousValue.system, + }; + } + + return cpuValues; +} + function createWarningObject( warning: string, type: string, @@ -574,9 +623,7 @@ process.config = { }, }; -process.cpuUsage = function () { - return Deno.cpuUsage(); -}; +process.cpuUsage = cpuUsage; /** https://nodejs.org/api/process.html#process_process_cwd */ process.cwd = cwd; diff --git a/tests/unit_node/process_test.ts b/tests/unit_node/process_test.ts index fe6f43b3e5..b9985e660a 100644 --- a/tests/unit_node/process_test.ts +++ b/tests/unit_node/process_test.ts @@ -6,6 +6,7 @@ import process, { arch as importedArch, argv, argv0 as importedArgv0, + cpuUsage as importedCpuUsage, env, execArgv as importedExecArgv, execPath as importedExecPath, @@ -1145,9 +1146,56 @@ Deno.test(function importedExecPathTest() { }); 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.stdout.columns writable", () => {