diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index 843c7db557..13da0a729d 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -349,4 +349,29 @@ impl FileSystem for DenoCompileFileSystem { .utime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) .await } + + fn lutime_sync( + &self, + path: &Path, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + self.error_if_in_vfs(path)?; + RealFs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) + } + async fn lutime_async( + &self, + path: PathBuf, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + self.error_if_in_vfs(&path)?; + RealFs + .lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) + .await + } } diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index 183e51e505..767ed11f4f 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -473,8 +473,8 @@ function toUnixTimeFromEpoch(value) { ]; } - const seconds = value; - const nanoseconds = 0; + const seconds = MathTrunc(value); + const nanoseconds = MathTrunc((value * 1e3) - (seconds * 1e3)) * 1e6; return [ seconds, diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs index 153327ff64..e2babf40aa 100644 --- a/ext/fs/in_memory_fs.rs +++ b/ext/fs/in_memory_fs.rs @@ -350,6 +350,27 @@ impl FileSystem for InMemoryFs { self.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) } + fn lutime_sync( + &self, + _path: &Path, + _atime_secs: i64, + _atime_nanos: u32, + _mtime_secs: i64, + _mtime_nanos: u32, + ) -> FsResult<()> { + Err(FsError::NotSupported) + } + async fn lutime_async( + &self, + path: PathBuf, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + self.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) + } + fn write_file_sync( &self, path: &Path, diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 6e3c359bbc..6036f82284 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -221,6 +221,23 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync { mtime_nanos: u32, ) -> FsResult<()>; + fn lutime_sync( + &self, + path: &Path, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()>; + async fn lutime_async( + &self, + path: PathBuf, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()>; + fn write_file_sync( &self, path: &Path, diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 054f5c9c4b..79f66cc4bf 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -274,6 +274,35 @@ impl FileSystem for RealFs { .await? } + fn lutime_sync( + &self, + path: &Path, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); + let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); + filetime::set_symlink_file_times(path, atime, mtime).map_err(Into::into) + } + + async fn lutime_async( + &self, + path: PathBuf, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); + let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); + spawn_blocking(move || { + filetime::set_symlink_file_times(path, atime, mtime).map_err(Into::into) + }) + .await? + } + fn write_file_sync( &self, path: &Path, @@ -927,9 +956,14 @@ fn open_with_access_check( }; (*access_check)(true, &path, &options)?; - // For windows - #[allow(unused_mut)] let mut opts: fs::OpenOptions = open_options(options); + #[cfg(windows)] + { + // allow opening directories + use std::os::windows::fs::OpenOptionsExt; + opts.custom_flags(winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS); + } + #[cfg(unix)] { // Don't follow symlinks on open -- we must always pass fully-resolved files @@ -943,7 +977,15 @@ fn open_with_access_check( Ok(opts.open(&path)?) } else { - let opts = open_options(options); + // for unix + #[allow(unused_mut)] + let mut opts = open_options(options); + #[cfg(windows)] + { + // allow opening directories + use std::os::windows::fs::OpenOptionsExt; + opts.custom_flags(winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS); + } Ok(opts.open(path)?) } } diff --git a/ext/node/lib.rs b/ext/node/lib.rs index ff979a8ba5..01c464df13 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -315,6 +315,8 @@ deno_core::extension!(deno_node, ops::fs::op_node_fs_exists_sync

, ops::fs::op_node_cp_sync

, ops::fs::op_node_cp

, + ops::fs::op_node_lutimes_sync

, + ops::fs::op_node_lutimes

, ops::fs::op_node_statfs

, ops::winerror::op_node_sys_to_uv_error, ops::v8::op_v8_cached_data_version_tag, @@ -426,6 +428,7 @@ deno_core::extension!(deno_node, "_fs/_fs_futimes.ts", "_fs/_fs_link.ts", "_fs/_fs_lstat.ts", + "_fs/_fs_lutimes.ts", "_fs/_fs_mkdir.ts", "_fs/_fs_mkdtemp.ts", "_fs/_fs_open.ts", diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index cfc7606560..304a6c2539 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -222,3 +222,54 @@ where Err(anyhow!("Unsupported platform.")) } } + +#[op2(fast)] +pub fn op_node_lutimes_sync

( + state: &mut OpState, + #[string] path: &str, + #[number] atime_secs: i64, + #[smi] atime_nanos: u32, + #[number] mtime_secs: i64, + #[smi] mtime_nanos: u32, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = Path::new(path); + + state + .borrow_mut::

() + .check_write_with_api_name(path, Some("node:fs.lutimes"))?; + + let fs = state.borrow::(); + fs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?; + Ok(()) +} + +#[op2(async)] +pub async fn op_node_lutimes

( + state: Rc>, + #[string] path: String, + #[number] atime_secs: i64, + #[smi] atime_nanos: u32, + #[number] mtime_secs: i64, + #[smi] mtime_nanos: u32, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = PathBuf::from(path); + + let fs = { + let mut state = state.borrow_mut(); + state + .borrow_mut::

() + .check_write_with_api_name(&path, Some("node:fs.lutimesSync"))?; + state.borrow::().clone() + }; + + fs.lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) + .await?; + + Ok(()) +} diff --git a/ext/node/polyfills/_fs/_fs_futimes.ts b/ext/node/polyfills/_fs/_fs_futimes.ts index cc4e35b0b3..98cd1066c3 100644 --- a/ext/node/polyfills/_fs/_fs_futimes.ts +++ b/ext/node/polyfills/_fs/_fs_futimes.ts @@ -5,6 +5,9 @@ import type { CallbackWithError } from "ext:deno_node/_fs/_fs_common.ts"; import { FsFile } from "ext:deno_fs/30_fs.js"; +import { validateInteger } from "ext:deno_node/internal/validators.mjs"; +import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; +import { toUnixTimestamp } from "ext:deno_node/internal/fs/utils.mjs"; function getValidTime( time: number | string | Date, @@ -23,7 +26,7 @@ function getValidTime( ); } - return time; + return toUnixTimestamp(time); } export function futimes( @@ -35,6 +38,11 @@ export function futimes( if (!callback) { throw new Deno.errors.InvalidData("No callback function supplied"); } + if (typeof fd !== "number") { + throw new ERR_INVALID_ARG_TYPE("fd", "number", fd); + } + + validateInteger(fd, "fd", 0, 2147483647); atime = getValidTime(atime, "atime"); mtime = getValidTime(mtime, "mtime"); @@ -51,6 +59,12 @@ export function futimesSync( atime: number | string | Date, mtime: number | string | Date, ) { + if (typeof fd !== "number") { + throw new ERR_INVALID_ARG_TYPE("fd", "number", fd); + } + + validateInteger(fd, "fd", 0, 2147483647); + atime = getValidTime(atime, "atime"); mtime = getValidTime(mtime, "mtime"); diff --git a/ext/node/polyfills/_fs/_fs_lutimes.ts b/ext/node/polyfills/_fs/_fs_lutimes.ts new file mode 100644 index 0000000000..2475c57149 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_lutimes.ts @@ -0,0 +1,85 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file prefer-primordials + +import type { CallbackWithError } from "ext:deno_node/_fs/_fs_common.ts"; +import { type Buffer } from "node:buffer"; +import { primordials } from "ext:core/mod.js"; +import { op_node_lutimes, op_node_lutimes_sync } from "ext:core/ops"; +import { promisify } from "ext:deno_node/internal/util.mjs"; +import { + getValidatedPath, + toUnixTimestamp, +} from "ext:deno_node/internal/fs/utils.mjs"; + +const { MathTrunc } = primordials; + +type TimeLike = number | string | Date; +type PathLike = string | Buffer | URL; + +function getValidUnixTime( + value: TimeLike, + name: string, +): [number, number] { + if (typeof value === "string") { + value = Number(value); + } + + if ( + typeof value === "number" && + (Number.isNaN(value) || !Number.isFinite(value)) + ) { + throw new Deno.errors.InvalidData( + `invalid ${name}, must not be infinity or NaN`, + ); + } + + const unixSeconds = toUnixTimestamp(value); + + const seconds = MathTrunc(unixSeconds); + const nanoseconds = MathTrunc((unixSeconds * 1e3) - (seconds * 1e3)) * 1e6; + + return [ + seconds, + nanoseconds, + ]; +} + +export function lutimes( + path: PathLike, + atime: TimeLike, + mtime: TimeLike, + callback: CallbackWithError, +): void { + if (!callback) { + throw new Error("No callback function supplied"); + } + const [atimeSecs, atimeNanos] = getValidUnixTime(atime, "atime"); + const [mtimeSecs, mtimeNanos] = getValidUnixTime(mtime, "mtime"); + + path = getValidatedPath(path).toString(); + + op_node_lutimes(path, atimeSecs, atimeNanos, mtimeSecs, mtimeNanos).then( + () => callback(null), + callback, + ); +} + +export function lutimesSync( + path: PathLike, + atime: TimeLike, + mtime: TimeLike, +): void { + const { 0: atimeSecs, 1: atimeNanos } = getValidUnixTime(atime, "atime"); + const { 0: mtimeSecs, 1: mtimeNanos } = getValidUnixTime(mtime, "mtime"); + + path = getValidatedPath(path).toString(); + + op_node_lutimes_sync(path, atimeSecs, atimeNanos, mtimeSecs, mtimeNanos); +} + +export const lutimesPromise = promisify(lutimes) as ( + path: PathLike, + atime: TimeLike, + mtime: TimeLike, +) => Promise; diff --git a/ext/node/polyfills/_fs/_fs_utimes.ts b/ext/node/polyfills/_fs/_fs_utimes.ts index 1d0e5c1fff..3fff4a4623 100644 --- a/ext/node/polyfills/_fs/_fs_utimes.ts +++ b/ext/node/polyfills/_fs/_fs_utimes.ts @@ -4,13 +4,16 @@ // deno-lint-ignore-file prefer-primordials import type { CallbackWithError } from "ext:deno_node/_fs/_fs_common.ts"; -import { pathFromURL } from "ext:deno_web/00_infra.js"; import { promisify } from "ext:deno_node/internal/util.mjs"; +import { + getValidatedPath, + toUnixTimestamp, +} from "ext:deno_node/internal/fs/utils.mjs"; function getValidTime( time: number | string | Date, name: string, -): number | Date { +): number { if (typeof time === "string") { time = Number(time); } @@ -24,7 +27,7 @@ function getValidTime( ); } - return time; + return toUnixTimestamp(time); } export function utimes( @@ -33,7 +36,7 @@ export function utimes( mtime: number | string | Date, callback: CallbackWithError, ) { - path = path instanceof URL ? pathFromURL(path) : path; + path = getValidatedPath(path).toString(); if (!callback) { throw new Deno.errors.InvalidData("No callback function supplied"); @@ -56,7 +59,7 @@ export function utimesSync( atime: number | string | Date, mtime: number | string | Date, ) { - path = path instanceof URL ? pathFromURL(path) : path; + path = getValidatedPath(path).toString(); atime = getValidTime(atime, "atime"); mtime = getValidTime(mtime, "mtime"); diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index f788f72b5f..6f0c53e4d2 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -29,6 +29,11 @@ import { ftruncate, ftruncateSync } from "ext:deno_node/_fs/_fs_ftruncate.ts"; import { futimes, futimesSync } from "ext:deno_node/_fs/_fs_futimes.ts"; import { link, linkPromise, linkSync } from "ext:deno_node/_fs/_fs_link.ts"; import { lstat, lstatPromise, lstatSync } from "ext:deno_node/_fs/_fs_lstat.ts"; +import { + lutimes, + lutimesPromise, + lutimesSync, +} from "ext:deno_node/_fs/_fs_lutimes.ts"; import { mkdir, mkdirPromise, mkdirSync } from "ext:deno_node/_fs/_fs_mkdir.ts"; import { mkdtemp, @@ -123,6 +128,7 @@ import { ReadStream, WriteStream, } from "ext:deno_node/internal/fs/streams.mjs"; +import { toUnixTimestamp as _toUnixTimestamp } from "ext:deno_node/internal/fs/utils.mjs"; const { F_OK, @@ -170,7 +176,7 @@ const promises = { // lchown: promisify(lchown), chown: chownPromise, utimes: utimesPromise, - // lutimes = promisify(lutimes), + lutimes: lutimesPromise, realpath: realpathPromise, mkdtemp: mkdtempPromise, writeFile: writeFilePromise, @@ -216,6 +222,8 @@ export default { linkSync, lstat, lstatSync, + lutimes, + lutimesSync, mkdir, mkdirSync, mkdtemp, @@ -284,9 +292,13 @@ export default { WriteStream, writeSync, X_OK, + // For tests + _toUnixTimestamp, }; export { + // For tests + _toUnixTimestamp, access, accessSync, appendFile, @@ -323,6 +335,8 @@ export { linkSync, lstat, lstatSync, + lutimes, + lutimesSync, mkdir, mkdirSync, mkdtemp, diff --git a/ext/node/polyfills/internal/fs/utils.mjs b/ext/node/polyfills/internal/fs/utils.mjs index ec379ed99a..21c5892ce2 100644 --- a/ext/node/polyfills/internal/fs/utils.mjs +++ b/ext/node/polyfills/internal/fs/utils.mjs @@ -5,6 +5,8 @@ "use strict"; +import { primordials } from "ext:core/mod.js"; +const { DatePrototypeGetTime } = primordials; import { Buffer } from "node:buffer"; import { ERR_FS_EISDIR, @@ -698,7 +700,7 @@ export function toUnixTimestamp(time, name = "time") { } if (isDate(time)) { // Convert to 123.456 UNIX timestamp - return Date.getTime(time) / 1000; + return DatePrototypeGetTime(time) / 1000; } throw new ERR_INVALID_ARG_TYPE(name, ["Date", "Time in seconds"], time); } diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc index 8321173df0..5c0c854a88 100644 --- a/tests/node_compat/config.jsonc +++ b/tests/node_compat/config.jsonc @@ -359,6 +359,7 @@ "test-fs-rmdir-recursive-warns-on-file.js", "test-fs-rmdir-recursive.js", "test-fs-rmdir-type-check.js", + "test-fs-utimes.js", "test-fs-watchfile.js", "test-fs-write-buffer.js", "test-fs-write-file-buffer.js", diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md index 571e3ed66b..a36ad1b634 100644 --- a/tests/node_compat/runner/TODO.md +++ b/tests/node_compat/runner/TODO.md @@ -899,7 +899,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co - [parallel/test-fs-util-validateoffsetlength.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-util-validateoffsetlength.js) - [parallel/test-fs-utils-get-dirents.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-utils-get-dirents.js) - [parallel/test-fs-utimes-y2K38.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-utimes-y2K38.js) -- [parallel/test-fs-utimes.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-utimes.js) - [parallel/test-fs-watch-abort-signal.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-watch-abort-signal.js) - [parallel/test-fs-watch-close-when-destroyed.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-watch-close-when-destroyed.js) - [parallel/test-fs-watch-encoding.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-watch-encoding.js) diff --git a/tests/node_compat/test.ts b/tests/node_compat/test.ts index 939fdf52a7..077abc2290 100644 --- a/tests/node_compat/test.ts +++ b/tests/node_compat/test.ts @@ -86,6 +86,7 @@ async function runTest(t: Deno.TestContext, path: string): Promise { //"--unsafely-ignore-certificate-errors", "--unstable-unsafe-proto", "--unstable-bare-node-builtins", + "--unstable-fs", "--v8-flags=" + v8Flags.join(), ]; if (usesNodeTest) { diff --git a/tests/node_compat/test/parallel/test-fs-utimes.js b/tests/node_compat/test/parallel/test-fs-utimes.js new file mode 100644 index 0000000000..f24e27f367 --- /dev/null +++ b/tests/node_compat/test/parallel/test-fs-utimes.js @@ -0,0 +1,218 @@ +// 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 util = require('util'); +const fs = require('fs'); +const url = require('url'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const lpath = `${tmpdir.path}/symlink`; +fs.symlinkSync('unoent-entry', lpath); + +function stat_resource(resource, statSync = fs.statSync) { + if (typeof resource === 'string') { + return statSync(resource); + } + const stats = fs.fstatSync(resource); + // Ensure mtime has been written to disk + // except for directories on AIX where it cannot be synced + if ((common.isAIX || common.isIBMi) && stats.isDirectory()) + return stats; + fs.fsyncSync(resource); + return fs.fstatSync(resource); +} + +function check_mtime(resource, mtime, statSync) { + mtime = fs._toUnixTimestamp(mtime); + const stats = stat_resource(resource, statSync); + const real_mtime = fs._toUnixTimestamp(stats.mtime); + return mtime - real_mtime; +} + +function expect_errno(syscall, resource, err, errno) { + assert( + err && (err.code === errno || err.code === 'ENOSYS'), + `FAILED: expect_errno ${util.inspect(arguments)}` + ); +} + +function expect_ok(syscall, resource, err, atime, mtime, statSync) { + const mtime_diff = check_mtime(resource, mtime, statSync); + assert( + // Check up to single-second precision. + // Sub-second precision is OS and fs dependant. + !err && (mtime_diff < 2) || err && err.code === 'ENOSYS', + `FAILED: expect_ok ${util.inspect(arguments)} + check_mtime: ${mtime_diff}` + ); +} + +const stats = fs.statSync(tmpdir.path); + +const asPath = (path) => path; +const asUrl = (path) => url.pathToFileURL(path); + +const cases = [ + [asPath, new Date('1982-09-10 13:37')], + [asPath, new Date()], + [asPath, 123456.789], + [asPath, stats.mtime], + [asPath, '123456', -1], + [asPath, new Date('2017-04-08T17:59:38.008Z')], + [asUrl, new Date()], +]; + +runTests(cases.values()); + +function runTests(iter) { + const { value, done } = iter.next(); + if (done) return; + + // Support easy setting same or different atime / mtime values. + const [pathType, atime, mtime = atime] = value; + + let fd; + // + // test async code paths + // + fs.utimes(pathType(tmpdir.path), atime, mtime, common.mustCall((err) => { + expect_ok('utimes', tmpdir.path, err, atime, mtime); + + fs.lutimes(pathType(lpath), atime, mtime, common.mustCall((err) => { + expect_ok('lutimes', lpath, err, atime, mtime, fs.lstatSync); + + fs.utimes(pathType('foobarbaz'), atime, mtime, common.mustCall((err) => { + expect_errno('utimes', 'foobarbaz', err, 'ENOENT'); + + // don't close this fd + if (common.isWindows) { + fd = fs.openSync(tmpdir.path, 'r+'); + } else { + fd = fs.openSync(tmpdir.path, 'r'); + } + + fs.futimes(fd, atime, mtime, common.mustCall((err) => { + expect_ok('futimes', fd, err, atime, mtime); + + syncTests(); + + setImmediate(common.mustCall(runTests), iter); + })); + })); + })); + })); + + // + // test synchronized code paths, these functions throw on failure + // + function syncTests() { + fs.utimesSync(pathType(tmpdir.path), atime, mtime); + expect_ok('utimesSync', tmpdir.path, undefined, atime, mtime); + + fs.lutimesSync(pathType(lpath), atime, mtime); + expect_ok('lutimesSync', lpath, undefined, atime, mtime, fs.lstatSync); + + // Some systems don't have futimes + // if there's an error, it should be ENOSYS + try { + fs.futimesSync(fd, atime, mtime); + expect_ok('futimesSync', fd, undefined, atime, mtime); + } catch (ex) { + expect_errno('futimesSync', fd, ex, 'ENOSYS'); + } + + let err; + try { + fs.utimesSync(pathType('foobarbaz'), atime, mtime); + } catch (ex) { + err = ex; + } + expect_errno('utimesSync', 'foobarbaz', err, 'ENOENT'); + + err = undefined; + } +} + +const expectTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; +// utimes-only error cases +{ + assert.throws( + () => fs.utimes(0, new Date(), new Date(), common.mustNotCall()), + expectTypeError + ); + assert.throws( + () => fs.utimesSync(0, new Date(), new Date()), + expectTypeError + ); +} + +// shared error cases +[false, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.utimes(i, new Date(), new Date(), common.mustNotCall()), + expectTypeError + ); + assert.throws( + () => fs.utimesSync(i, new Date(), new Date()), + expectTypeError + ); + assert.throws( + () => fs.futimes(i, new Date(), new Date(), common.mustNotCall()), + expectTypeError + ); + assert.throws( + () => fs.futimesSync(i, new Date(), new Date()), + expectTypeError + ); +}); + +const expectRangeError = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "fd" is out of range. ' + + 'It must be >= 0 && <= 2147483647. Received -1' +}; +// futimes-only error cases +{ + assert.throws( + () => fs.futimes(-1, new Date(), new Date(), common.mustNotCall()), + expectRangeError + ); + assert.throws( + () => fs.futimesSync(-1, new Date(), new Date()), + expectRangeError + ); +} diff --git a/tests/unit/read_file_test.ts b/tests/unit/read_file_test.ts index 1b7f2a5513..562bf99698 100644 --- a/tests/unit/read_file_test.ts +++ b/tests/unit/read_file_test.ts @@ -172,11 +172,7 @@ Deno.test( try { await Deno.readFile("tests/testdata/assets/"); } catch (e) { - if (Deno.build.os === "windows") { - assertEquals(e.code, "EPERM"); - } else { - assertEquals(e.code, "EISDIR"); - } + assertEquals(e.code, "EISDIR"); } }, );