mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
fix(node): Implement os.userInfo
properly, add missing toPrimitive
(#24702)
Fixes the implementation of `os.userInfo`, and adds a missing `toPrimitive` for `tmpdir`. This allows us to enable the corresponding node_compat test.
This commit is contained in:
parent
6d44952d4d
commit
6c6bbeb974
9 changed files with 469 additions and 54 deletions
|
@ -348,7 +348,7 @@ deno_core::extension!(deno_node,
|
||||||
ops::http2::op_http2_send_response,
|
ops::http2::op_http2_send_response,
|
||||||
ops::os::op_node_os_get_priority<P>,
|
ops::os::op_node_os_get_priority<P>,
|
||||||
ops::os::op_node_os_set_priority<P>,
|
ops::os::op_node_os_set_priority<P>,
|
||||||
ops::os::op_node_os_username<P>,
|
ops::os::op_node_os_user_info<P>,
|
||||||
ops::os::op_geteuid<P>,
|
ops::os::op_geteuid<P>,
|
||||||
ops::os::op_getegid<P>,
|
ops::os::op_getegid<P>,
|
||||||
ops::os::op_cpus<P>,
|
ops::os::op_cpus<P>,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
use crate::NodePermissions;
|
use crate::NodePermissions;
|
||||||
use deno_core::op2;
|
use deno_core::op2;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
|
@ -15,6 +17,8 @@ pub enum OsError {
|
||||||
Permission(deno_core::error::AnyError),
|
Permission(deno_core::error::AnyError),
|
||||||
#[error("Failed to get cpu info")]
|
#[error("Failed to get cpu info")]
|
||||||
FailedToGetCpuInfo,
|
FailedToGetCpuInfo,
|
||||||
|
#[error("Failed to get user info")]
|
||||||
|
FailedToGetUserInfo(#[source] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
|
@ -54,20 +58,162 @@ where
|
||||||
priority::set_priority(pid, priority).map_err(OsError::Priority)
|
priority::set_priority(pid, priority).map_err(OsError::Priority)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct UserInfo {
|
||||||
|
username: String,
|
||||||
|
homedir: String,
|
||||||
|
shell: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn get_user_info(uid: u32) -> Result<UserInfo, OsError> {
|
||||||
|
use std::ffi::CStr;
|
||||||
|
let mut pw: MaybeUninit<libc::passwd> = MaybeUninit::uninit();
|
||||||
|
let mut result: *mut libc::passwd = std::ptr::null_mut();
|
||||||
|
// SAFETY: libc call, no invariants
|
||||||
|
let max_buf_size = unsafe { libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) };
|
||||||
|
let buf_size = if max_buf_size < 0 {
|
||||||
|
// from the man page
|
||||||
|
16_384
|
||||||
|
} else {
|
||||||
|
max_buf_size as usize
|
||||||
|
};
|
||||||
|
let mut buf = {
|
||||||
|
let mut b = Vec::<MaybeUninit<libc::c_char>>::with_capacity(buf_size);
|
||||||
|
// SAFETY: MaybeUninit has no initialization invariants, and len == cap
|
||||||
|
unsafe {
|
||||||
|
b.set_len(buf_size);
|
||||||
|
}
|
||||||
|
b
|
||||||
|
};
|
||||||
|
// SAFETY: libc call, args are correct
|
||||||
|
let s = unsafe {
|
||||||
|
libc::getpwuid_r(
|
||||||
|
uid,
|
||||||
|
pw.as_mut_ptr(),
|
||||||
|
buf.as_mut_ptr().cast(),
|
||||||
|
buf_size,
|
||||||
|
std::ptr::addr_of_mut!(result),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if result.is_null() {
|
||||||
|
if s != 0 {
|
||||||
|
return Err(
|
||||||
|
OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Err(OsError::FailedToGetUserInfo(std::io::Error::from(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SAFETY: pw was initialized by the call to `getpwuid_r` above
|
||||||
|
let pw = unsafe { pw.assume_init() };
|
||||||
|
// SAFETY: initialized above, pw alive until end of function, nul terminated
|
||||||
|
let username = unsafe { CStr::from_ptr(pw.pw_name) };
|
||||||
|
// SAFETY: initialized above, pw alive until end of function, nul terminated
|
||||||
|
let homedir = unsafe { CStr::from_ptr(pw.pw_dir) };
|
||||||
|
// SAFETY: initialized above, pw alive until end of function, nul terminated
|
||||||
|
let shell = unsafe { CStr::from_ptr(pw.pw_shell) };
|
||||||
|
Ok(UserInfo {
|
||||||
|
username: username.to_string_lossy().into_owned(),
|
||||||
|
homedir: homedir.to_string_lossy().into_owned(),
|
||||||
|
shell: Some(shell.to_string_lossy().into_owned()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn get_user_info(_uid: u32) -> Result<UserInfo, OsError> {
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
|
||||||
|
use windows_sys::Win32::Foundation::CloseHandle;
|
||||||
|
use windows_sys::Win32::Foundation::GetLastError;
|
||||||
|
use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
|
||||||
|
use windows_sys::Win32::Foundation::HANDLE;
|
||||||
|
use windows_sys::Win32::System::Threading::GetCurrentProcess;
|
||||||
|
use windows_sys::Win32::System::Threading::OpenProcessToken;
|
||||||
|
use windows_sys::Win32::UI::Shell::GetUserProfileDirectoryW;
|
||||||
|
struct Handle(HANDLE);
|
||||||
|
impl Drop for Handle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: win32 call
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut token: MaybeUninit<HANDLE> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
// Get a handle to the current process
|
||||||
|
// SAFETY: win32 call
|
||||||
|
unsafe {
|
||||||
|
if OpenProcessToken(
|
||||||
|
GetCurrentProcess(),
|
||||||
|
windows_sys::Win32::Security::TOKEN_READ,
|
||||||
|
token.as_mut_ptr(),
|
||||||
|
) == 0
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: initialized by call above
|
||||||
|
let token = Handle(unsafe { token.assume_init() });
|
||||||
|
|
||||||
|
let mut bufsize = 0;
|
||||||
|
// get the size for the homedir buf (it'll end up in `bufsize`)
|
||||||
|
// SAFETY: win32 call
|
||||||
|
unsafe {
|
||||||
|
GetUserProfileDirectoryW(token.0, std::ptr::null_mut(), &mut bufsize);
|
||||||
|
let err = GetLastError();
|
||||||
|
if err != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return Err(OsError::FailedToGetUserInfo(
|
||||||
|
std::io::Error::from_raw_os_error(err as i32),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut path = vec![0; bufsize as usize];
|
||||||
|
// Actually get the homedir
|
||||||
|
// SAFETY: path is `bufsize` elements
|
||||||
|
unsafe {
|
||||||
|
if GetUserProfileDirectoryW(token.0, path.as_mut_ptr(), &mut bufsize) == 0 {
|
||||||
|
return Err(
|
||||||
|
OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove trailing nul
|
||||||
|
path.pop();
|
||||||
|
let homedir_wide = OsString::from_wide(&path);
|
||||||
|
let homedir = homedir_wide.to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
Ok(UserInfo {
|
||||||
|
username: deno_whoami::username(),
|
||||||
|
homedir,
|
||||||
|
shell: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
#[string]
|
#[serde]
|
||||||
pub fn op_node_os_username<P>(
|
pub fn op_node_os_user_info<P>(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
) -> Result<String, deno_core::error::AnyError>
|
#[smi] uid: u32,
|
||||||
|
) -> Result<UserInfo, OsError>
|
||||||
where
|
where
|
||||||
P: NodePermissions + 'static,
|
P: NodePermissions + 'static,
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
let permissions = state.borrow_mut::<P>();
|
let permissions = state.borrow_mut::<P>();
|
||||||
permissions.check_sys("username", "node:os.userInfo()")?;
|
permissions
|
||||||
|
.check_sys("userInfo", "node:os.userInfo()")
|
||||||
|
.map_err(OsError::Permission)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(deno_whoami::username())
|
get_user_info(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
|
|
|
@ -2558,19 +2558,6 @@ export class ERR_FS_RMDIR_ENOTDIR extends NodeSystemError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ERR_OS_NO_HOMEDIR extends NodeSystemError {
|
|
||||||
constructor() {
|
|
||||||
const code = isWindows ? "ENOENT" : "ENOTDIR";
|
|
||||||
const ctx: NodeSystemErrorCtx = {
|
|
||||||
message: "not a directory",
|
|
||||||
syscall: "home",
|
|
||||||
code,
|
|
||||||
errno: isWindows ? osConstants.errno.ENOENT : osConstants.errno.ENOTDIR,
|
|
||||||
};
|
|
||||||
super(code, ctx, "Path is not a directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ERR_HTTP_SOCKET_ASSIGNED extends NodeError {
|
export class ERR_HTTP_SOCKET_ASSIGNED extends NodeError {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(
|
super(
|
||||||
|
|
|
@ -28,16 +28,17 @@ import {
|
||||||
op_homedir,
|
op_homedir,
|
||||||
op_node_os_get_priority,
|
op_node_os_get_priority,
|
||||||
op_node_os_set_priority,
|
op_node_os_set_priority,
|
||||||
op_node_os_username,
|
op_node_os_user_info,
|
||||||
} from "ext:core/ops";
|
} from "ext:core/ops";
|
||||||
|
|
||||||
import { validateIntegerRange } from "ext:deno_node/_utils.ts";
|
import { validateIntegerRange } from "ext:deno_node/_utils.ts";
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
import { isWindows } from "ext:deno_node/_util/os.ts";
|
import { isWindows } from "ext:deno_node/_util/os.ts";
|
||||||
import { ERR_OS_NO_HOMEDIR } from "ext:deno_node/internal/errors.ts";
|
|
||||||
import { os } from "ext:deno_node/internal_binding/constants.ts";
|
import { os } from "ext:deno_node/internal_binding/constants.ts";
|
||||||
import { osUptime } from "ext:runtime/30_os.js";
|
import { osUptime } from "ext:runtime/30_os.js";
|
||||||
import { Buffer } from "ext:deno_node/internal/buffer.mjs";
|
import { Buffer } from "ext:deno_node/internal/buffer.mjs";
|
||||||
|
import { primordials } from "ext:core/mod.js";
|
||||||
|
const { StringPrototypeEndsWith, StringPrototypeSlice } = primordials;
|
||||||
|
|
||||||
export const constants = os;
|
export const constants = os;
|
||||||
|
|
||||||
|
@ -136,6 +137,8 @@ export function arch(): string {
|
||||||
(uptime as any)[Symbol.toPrimitive] = (): number => uptime();
|
(uptime as any)[Symbol.toPrimitive] = (): number => uptime();
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
(machine as any)[Symbol.toPrimitive] = (): string => machine();
|
(machine as any)[Symbol.toPrimitive] = (): string => machine();
|
||||||
|
// deno-lint-ignore no-explicit-any
|
||||||
|
(tmpdir as any)[Symbol.toPrimitive] = (): string | null => tmpdir();
|
||||||
|
|
||||||
export function cpus(): CPUCoreInfo[] {
|
export function cpus(): CPUCoreInfo[] {
|
||||||
return op_cpus();
|
return op_cpus();
|
||||||
|
@ -268,26 +271,27 @@ export function setPriority(pid: number, priority?: number) {
|
||||||
export function tmpdir(): string | null {
|
export function tmpdir(): string | null {
|
||||||
/* This follows the node js implementation, but has a few
|
/* This follows the node js implementation, but has a few
|
||||||
differences:
|
differences:
|
||||||
* On windows, if none of the environment variables are defined,
|
* We use a plain Deno.env.get, instead of safeGetenv,
|
||||||
we return null.
|
|
||||||
* On unix we use a plain Deno.env.get, instead of safeGetenv,
|
|
||||||
which special cases setuid binaries.
|
which special cases setuid binaries.
|
||||||
* Node removes a single trailing / or \, we remove all.
|
|
||||||
*/
|
*/
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
const temp = Deno.env.get("TEMP") || Deno.env.get("TMP");
|
let temp = Deno.env.get("TEMP") || Deno.env.get("TMP") ||
|
||||||
if (temp) {
|
(Deno.env.get("SystemRoot") || Deno.env.get("windir")) + "\\temp";
|
||||||
return temp.replace(/(?<!:)[/\\]*$/, "");
|
if (
|
||||||
|
temp.length > 1 && StringPrototypeEndsWith(temp, "\\") &&
|
||||||
|
!StringPrototypeEndsWith(temp, ":\\")
|
||||||
|
) {
|
||||||
|
temp = StringPrototypeSlice(temp, 0, -1);
|
||||||
}
|
}
|
||||||
const base = Deno.env.get("SYSTEMROOT") || Deno.env.get("WINDIR");
|
|
||||||
if (base) {
|
return temp;
|
||||||
return base + "\\temp";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} else { // !isWindows
|
} else { // !isWindows
|
||||||
const temp = Deno.env.get("TMPDIR") || Deno.env.get("TMP") ||
|
let temp = Deno.env.get("TMPDIR") || Deno.env.get("TMP") ||
|
||||||
Deno.env.get("TEMP") || "/tmp";
|
Deno.env.get("TEMP") || "/tmp";
|
||||||
return temp.replace(/(?<!^)\/*$/, "");
|
if (temp.length > 1 && StringPrototypeEndsWith(temp, "/")) {
|
||||||
|
temp = StringPrototypeSlice(temp, 0, -1);
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +324,6 @@ export function uptime(): number {
|
||||||
return osUptime();
|
return osUptime();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Not yet implemented */
|
|
||||||
export function userInfo(
|
export function userInfo(
|
||||||
options: UserInfoOptions = { encoding: "utf-8" },
|
options: UserInfoOptions = { encoding: "utf-8" },
|
||||||
): UserInfo {
|
): UserInfo {
|
||||||
|
@ -331,20 +334,10 @@ export function userInfo(
|
||||||
uid = -1;
|
uid = -1;
|
||||||
gid = -1;
|
gid = -1;
|
||||||
}
|
}
|
||||||
|
let { username, homedir, shell } = op_node_os_user_info(uid);
|
||||||
// TODO(@crowlKats): figure out how to do this correctly:
|
|
||||||
// The value of homedir returned by os.userInfo() is provided by the operating system.
|
|
||||||
// This differs from the result of os.homedir(), which queries environment
|
|
||||||
// variables for the home directory before falling back to the operating system response.
|
|
||||||
let _homedir = homedir();
|
|
||||||
if (!_homedir) {
|
|
||||||
throw new ERR_OS_NO_HOMEDIR();
|
|
||||||
}
|
|
||||||
let shell = isWindows ? null : (Deno.env.get("SHELL") || null);
|
|
||||||
let username = op_node_os_username();
|
|
||||||
|
|
||||||
if (options?.encoding === "buffer") {
|
if (options?.encoding === "buffer") {
|
||||||
_homedir = _homedir ? Buffer.from(_homedir) : _homedir;
|
homedir = homedir ? Buffer.from(homedir) : homedir;
|
||||||
shell = shell ? Buffer.from(shell) : shell;
|
shell = shell ? Buffer.from(shell) : shell;
|
||||||
username = Buffer.from(username);
|
username = Buffer.from(username);
|
||||||
}
|
}
|
||||||
|
@ -352,7 +345,7 @@ export function userInfo(
|
||||||
return {
|
return {
|
||||||
uid,
|
uid,
|
||||||
gid,
|
gid,
|
||||||
homedir: _homedir,
|
homedir,
|
||||||
shell,
|
shell,
|
||||||
username,
|
username,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1086,6 +1086,7 @@ mod node {
|
||||||
},
|
},
|
||||||
OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"),
|
OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"),
|
||||||
OsError::FailedToGetCpuInfo => "TypeError",
|
OsError::FailedToGetCpuInfo => "TypeError",
|
||||||
|
OsError::FailedToGetUserInfo(e) => get_io_error_class(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1368,8 +1368,12 @@ impl SysDescriptor {
|
||||||
match kind.as_str() {
|
match kind.as_str() {
|
||||||
"hostname" | "osRelease" | "osUptime" | "loadavg"
|
"hostname" | "osRelease" | "osUptime" | "loadavg"
|
||||||
| "networkInterfaces" | "systemMemoryInfo" | "uid" | "gid" | "cpus"
|
| "networkInterfaces" | "systemMemoryInfo" | "uid" | "gid" | "cpus"
|
||||||
| "homedir" | "getegid" | "username" | "statfs" | "getPriority"
|
| "homedir" | "getegid" | "statfs" | "getPriority" | "setPriority"
|
||||||
| "setPriority" => Ok(Self(kind)),
|
| "userInfo" => Ok(Self(kind)),
|
||||||
|
|
||||||
|
// the underlying permission check changed to `userInfo` to better match the API,
|
||||||
|
// alias this to avoid breaking existing projects with `--allow-sys=username`
|
||||||
|
"username" => Ok(Self("userInfo".into())),
|
||||||
_ => Err(type_error(format!("unknown system info kind \"{kind}\""))),
|
_ => Err(type_error(format!("unknown system info kind \"{kind}\""))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,6 @@
|
||||||
"test-net-server-try-ports.js",
|
"test-net-server-try-ports.js",
|
||||||
"test-net-socket-timeout.js",
|
"test-net-socket-timeout.js",
|
||||||
"test-net-write-arguments.js",
|
"test-net-write-arguments.js",
|
||||||
// TODO(nathanwhit): Disable os.userInfo is slightly incorrect
|
|
||||||
// "test-os.js",
|
|
||||||
"test-path-resolve.js",
|
"test-path-resolve.js",
|
||||||
"test-querystring.js",
|
"test-querystring.js",
|
||||||
"test-readline-interface.js",
|
"test-readline-interface.js",
|
||||||
|
@ -448,6 +446,7 @@
|
||||||
"test-next-tick-when-exiting.js",
|
"test-next-tick-when-exiting.js",
|
||||||
"test-next-tick.js",
|
"test-next-tick.js",
|
||||||
"test-nodeeventtarget.js",
|
"test-nodeeventtarget.js",
|
||||||
|
"test-os.js",
|
||||||
"test-outgoing-message-destroy.js",
|
"test-outgoing-message-destroy.js",
|
||||||
"test-outgoing-message-pipe.js",
|
"test-outgoing-message-pipe.js",
|
||||||
"test-parse-args.mjs",
|
"test-parse-args.mjs",
|
||||||
|
|
|
@ -1878,7 +1878,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
|
||||||
- [parallel/test-os-homedir-no-envvar.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os-homedir-no-envvar.js)
|
- [parallel/test-os-homedir-no-envvar.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os-homedir-no-envvar.js)
|
||||||
- [parallel/test-os-process-priority.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os-process-priority.js)
|
- [parallel/test-os-process-priority.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os-process-priority.js)
|
||||||
- [parallel/test-os-userinfo-handles-getter-errors.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os-userinfo-handles-getter-errors.js)
|
- [parallel/test-os-userinfo-handles-getter-errors.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os-userinfo-handles-getter-errors.js)
|
||||||
- [parallel/test-os.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-os.js)
|
|
||||||
- [parallel/test-path-posix-relative-on-windows.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-path-posix-relative-on-windows.js)
|
- [parallel/test-path-posix-relative-on-windows.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-path-posix-relative-on-windows.js)
|
||||||
- [parallel/test-pending-deprecation.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-pending-deprecation.js)
|
- [parallel/test-pending-deprecation.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-pending-deprecation.js)
|
||||||
- [parallel/test-perf-gc-crash.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-perf-gc-crash.js)
|
- [parallel/test-perf-gc-crash.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-perf-gc-crash.js)
|
||||||
|
|
286
tests/node_compat/test/parallel/test-os.js
Normal file
286
tests/node_compat/test/parallel/test-os.js
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
// deno-fmt-ignore-file
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
|
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||||
|
// Taken from Node 18.12.1
|
||||||
|
// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
|
||||||
|
|
||||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||||
|
// following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included
|
||||||
|
// in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const { inspect } = require('util');
|
||||||
|
|
||||||
|
const is = {
|
||||||
|
number: (value, key) => {
|
||||||
|
assert(!Number.isNaN(value), `${key} should not be NaN`);
|
||||||
|
assert.strictEqual(typeof value, 'number');
|
||||||
|
},
|
||||||
|
string: (value) => { assert.strictEqual(typeof value, 'string'); },
|
||||||
|
array: (value) => { assert.ok(Array.isArray(value)); },
|
||||||
|
object: (value) => {
|
||||||
|
assert.strictEqual(typeof value, 'object');
|
||||||
|
assert.notStrictEqual(value, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.env.TMPDIR = '/tmpdir';
|
||||||
|
process.env.TMP = '/tmp';
|
||||||
|
process.env.TEMP = '/temp';
|
||||||
|
if (common.isWindows) {
|
||||||
|
assert.strictEqual(os.tmpdir(), '/temp');
|
||||||
|
process.env.TEMP = '';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/tmp');
|
||||||
|
process.env.TMP = '';
|
||||||
|
const expected = `${process.env.SystemRoot || process.env.windir}\\temp`;
|
||||||
|
assert.strictEqual(os.tmpdir(), expected);
|
||||||
|
process.env.TEMP = '\\temp\\';
|
||||||
|
assert.strictEqual(os.tmpdir(), '\\temp');
|
||||||
|
process.env.TEMP = '\\tmpdir/';
|
||||||
|
assert.strictEqual(os.tmpdir(), '\\tmpdir/');
|
||||||
|
process.env.TEMP = '\\';
|
||||||
|
assert.strictEqual(os.tmpdir(), '\\');
|
||||||
|
process.env.TEMP = 'C:\\';
|
||||||
|
assert.strictEqual(os.tmpdir(), 'C:\\');
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(os.tmpdir(), '/tmpdir');
|
||||||
|
process.env.TMPDIR = '';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/tmp');
|
||||||
|
process.env.TMP = '';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/temp');
|
||||||
|
process.env.TEMP = '';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/tmp');
|
||||||
|
process.env.TMPDIR = '/tmpdir/';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/tmpdir');
|
||||||
|
process.env.TMPDIR = '/tmpdir\\';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/tmpdir\\');
|
||||||
|
process.env.TMPDIR = '/';
|
||||||
|
assert.strictEqual(os.tmpdir(), '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
const endianness = os.endianness();
|
||||||
|
is.string(endianness);
|
||||||
|
assert.match(endianness, /[BL]E/);
|
||||||
|
|
||||||
|
const hostname = os.hostname();
|
||||||
|
is.string(hostname);
|
||||||
|
assert.ok(hostname.length > 0);
|
||||||
|
|
||||||
|
// IBMi process priority is different.
|
||||||
|
if (!common.isIBMi) {
|
||||||
|
const DUMMY_PRIORITY = 10;
|
||||||
|
os.setPriority(DUMMY_PRIORITY);
|
||||||
|
const priority = os.getPriority();
|
||||||
|
is.number(priority);
|
||||||
|
assert.strictEqual(priority, DUMMY_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// On IBMi, os.uptime() returns 'undefined'
|
||||||
|
if (!common.isIBMi) {
|
||||||
|
const uptime = os.uptime();
|
||||||
|
is.number(uptime);
|
||||||
|
assert.ok(uptime > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpus = os.cpus();
|
||||||
|
is.array(cpus);
|
||||||
|
assert.ok(cpus.length > 0);
|
||||||
|
for (const cpu of cpus) {
|
||||||
|
assert.strictEqual(typeof cpu.model, 'string');
|
||||||
|
assert.strictEqual(typeof cpu.speed, 'number');
|
||||||
|
assert.strictEqual(typeof cpu.times.user, 'number');
|
||||||
|
assert.strictEqual(typeof cpu.times.nice, 'number');
|
||||||
|
assert.strictEqual(typeof cpu.times.sys, 'number');
|
||||||
|
assert.strictEqual(typeof cpu.times.idle, 'number');
|
||||||
|
assert.strictEqual(typeof cpu.times.irq, 'number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = os.type();
|
||||||
|
is.string(type);
|
||||||
|
assert.ok(type.length > 0);
|
||||||
|
|
||||||
|
const release = os.release();
|
||||||
|
is.string(release);
|
||||||
|
assert.ok(release.length > 0);
|
||||||
|
// TODO: Check format on more than just AIX
|
||||||
|
if (common.isAIX)
|
||||||
|
assert.match(release, /^\d+\.\d+$/);
|
||||||
|
|
||||||
|
const platform = os.platform();
|
||||||
|
is.string(platform);
|
||||||
|
assert.ok(platform.length > 0);
|
||||||
|
|
||||||
|
const arch = os.arch();
|
||||||
|
is.string(arch);
|
||||||
|
assert.ok(arch.length > 0);
|
||||||
|
|
||||||
|
if (!common.isSunOS) {
|
||||||
|
// not implemented yet
|
||||||
|
assert.ok(os.loadavg().length > 0);
|
||||||
|
assert.ok(os.freemem() > 0);
|
||||||
|
assert.ok(os.totalmem() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const interfaces = os.networkInterfaces();
|
||||||
|
switch (platform) {
|
||||||
|
case 'linux': {
|
||||||
|
const filter = (e) =>
|
||||||
|
e.address === '127.0.0.1' &&
|
||||||
|
e.netmask === '255.0.0.0';
|
||||||
|
|
||||||
|
const actual = interfaces.lo.filter(filter);
|
||||||
|
const expected = [{
|
||||||
|
address: '127.0.0.1',
|
||||||
|
netmask: '255.0.0.0',
|
||||||
|
family: 'IPv4',
|
||||||
|
mac: '00:00:00:00:00:00',
|
||||||
|
internal: true,
|
||||||
|
cidr: '127.0.0.1/8'
|
||||||
|
}];
|
||||||
|
assert.deepStrictEqual(actual, expected);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'win32': {
|
||||||
|
const filter = (e) =>
|
||||||
|
e.address === '127.0.0.1';
|
||||||
|
|
||||||
|
const actual = interfaces['Loopback Pseudo-Interface 1'].filter(filter);
|
||||||
|
const expected = [{
|
||||||
|
address: '127.0.0.1',
|
||||||
|
netmask: '255.0.0.0',
|
||||||
|
family: 'IPv4',
|
||||||
|
mac: '00:00:00:00:00:00',
|
||||||
|
internal: true,
|
||||||
|
cidr: '127.0.0.1/8'
|
||||||
|
}];
|
||||||
|
assert.deepStrictEqual(actual, expected);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const netmaskToCIDRSuffixMap = new Map(Object.entries({
|
||||||
|
'255.0.0.0': 8,
|
||||||
|
'255.255.255.0': 24,
|
||||||
|
'ffff:ffff:ffff:ffff::': 64,
|
||||||
|
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128
|
||||||
|
}));
|
||||||
|
|
||||||
|
Object.values(interfaces)
|
||||||
|
.flat(Infinity)
|
||||||
|
.map((v) => ({ v, mask: netmaskToCIDRSuffixMap.get(v.netmask) }))
|
||||||
|
.forEach(({ v, mask }) => {
|
||||||
|
assert.ok('cidr' in v, `"cidr" prop not found in ${inspect(v)}`);
|
||||||
|
if (mask) {
|
||||||
|
assert.strictEqual(v.cidr, `${v.address}/${mask}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const EOL = os.EOL;
|
||||||
|
if (common.isWindows) {
|
||||||
|
assert.strictEqual(EOL, '\r\n');
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(EOL, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
const home = os.homedir();
|
||||||
|
is.string(home);
|
||||||
|
assert.ok(home.includes(path.sep));
|
||||||
|
|
||||||
|
const version = os.version();
|
||||||
|
assert.strictEqual(typeof version, 'string');
|
||||||
|
assert(version);
|
||||||
|
|
||||||
|
if (common.isWindows && process.env.USERPROFILE) {
|
||||||
|
assert.strictEqual(home, process.env.USERPROFILE);
|
||||||
|
delete process.env.USERPROFILE;
|
||||||
|
assert.ok(os.homedir().includes(path.sep));
|
||||||
|
process.env.USERPROFILE = home;
|
||||||
|
} else if (!common.isWindows && process.env.HOME) {
|
||||||
|
assert.strictEqual(home, process.env.HOME);
|
||||||
|
delete process.env.HOME;
|
||||||
|
assert.ok(os.homedir().includes(path.sep));
|
||||||
|
process.env.HOME = home;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pwd = os.userInfo();
|
||||||
|
is.object(pwd);
|
||||||
|
const pwdBuf = os.userInfo({ encoding: 'buffer' });
|
||||||
|
|
||||||
|
if (common.isWindows) {
|
||||||
|
assert.strictEqual(pwd.uid, -1);
|
||||||
|
assert.strictEqual(pwd.gid, -1);
|
||||||
|
assert.strictEqual(pwd.shell, null);
|
||||||
|
assert.strictEqual(pwdBuf.uid, -1);
|
||||||
|
assert.strictEqual(pwdBuf.gid, -1);
|
||||||
|
assert.strictEqual(pwdBuf.shell, null);
|
||||||
|
} else {
|
||||||
|
is.number(pwd.uid);
|
||||||
|
is.number(pwd.gid);
|
||||||
|
assert.strictEqual(typeof pwd.shell, 'string');
|
||||||
|
// It's possible for /etc/passwd to leave the user's shell blank.
|
||||||
|
if (pwd.shell.length > 0) {
|
||||||
|
assert(pwd.shell.includes(path.sep));
|
||||||
|
}
|
||||||
|
assert.strictEqual(pwd.uid, pwdBuf.uid);
|
||||||
|
assert.strictEqual(pwd.gid, pwdBuf.gid);
|
||||||
|
assert.strictEqual(pwd.shell, pwdBuf.shell.toString('utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
is.string(pwd.username);
|
||||||
|
assert.ok(pwd.homedir.includes(path.sep));
|
||||||
|
assert.strictEqual(pwd.username, pwdBuf.username.toString('utf8'));
|
||||||
|
assert.strictEqual(pwd.homedir, pwdBuf.homedir.toString('utf8'));
|
||||||
|
|
||||||
|
assert.strictEqual(`${os.hostname}`, os.hostname());
|
||||||
|
assert.strictEqual(`${os.homedir}`, os.homedir());
|
||||||
|
assert.strictEqual(`${os.release}`, os.release());
|
||||||
|
assert.strictEqual(`${os.type}`, os.type());
|
||||||
|
assert.strictEqual(`${os.endianness}`, os.endianness());
|
||||||
|
assert.strictEqual(`${os.tmpdir}`, os.tmpdir());
|
||||||
|
assert.strictEqual(`${os.arch}`, os.arch());
|
||||||
|
assert.strictEqual(`${os.platform}`, os.platform());
|
||||||
|
assert.strictEqual(`${os.version}`, os.version());
|
||||||
|
assert.strictEqual(`${os.machine}`, os.machine());
|
||||||
|
assert.strictEqual(+os.totalmem, os.totalmem());
|
||||||
|
|
||||||
|
// Assert that the following values are coercible to numbers.
|
||||||
|
// On IBMi, os.uptime() returns 'undefined'
|
||||||
|
if (!common.isIBMi) {
|
||||||
|
is.number(+os.uptime, 'uptime');
|
||||||
|
is.number(os.uptime(), 'uptime');
|
||||||
|
}
|
||||||
|
|
||||||
|
is.number(+os.availableParallelism, 'availableParallelism');
|
||||||
|
is.number(os.availableParallelism(), 'availableParallelism');
|
||||||
|
is.number(+os.freemem, 'freemem');
|
||||||
|
is.number(os.freemem(), 'freemem');
|
||||||
|
|
||||||
|
const devNull = os.devNull;
|
||||||
|
if (common.isWindows) {
|
||||||
|
assert.strictEqual(devNull, '\\\\.\\nul');
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(devNull, '/dev/null');
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.ok(os.availableParallelism() > 0);
|
Loading…
Reference in a new issue