Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename FileInfo time fields and represent them as Date objects #4932

Merged
merged 1 commit into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cli/js/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1316,15 +1316,15 @@ declare namespace Deno {
/** The last modification time of the file. This corresponds to the `mtime`
* field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This
* may not be available on all platforms. */
modified: number | null;
mtime: Date | null;
/** The last access time of the file. This corresponds to the `atime`
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
* be available on all platforms. */
accessed: number | null;
atime: Date | null;
/** The creation time of the file. This corresponds to the `birthtime`
* field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may not
* be available on all platforms. */
created: number | null;
* field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may
* not be available on all platforms. */
birthtime: Date | null;
/** ID of the device containing the file.
*
* _Linux/Mac OS only._ */
Expand Down
18 changes: 9 additions & 9 deletions cli/js/ops/fs/stat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { build } from "../../build.ts";

export interface FileInfo {
size: number;
modified: number | null;
accessed: number | null;
created: number | null;
mtime: Date | null;
atime: Date | null;
birthtime: Date | null;
dev: number | null;
ino: number | null;
mode: number | null;
Expand All @@ -26,9 +26,9 @@ export interface StatResponse {
isDirectory: boolean;
isSymlink: boolean;
size: number;
modified: number;
accessed: number;
created: number;
mtime: number | null;
atime: number | null;
birthtime: number | null;
// Null for stat(), but exists for readdir().
name: string | null;
// Unix only members
Expand All @@ -51,9 +51,9 @@ export function parseFileInfo(response: StatResponse): FileInfo {
isDirectory: response.isDirectory,
isSymlink: response.isSymlink,
size: response.size,
modified: response.modified ? response.modified : null,
accessed: response.accessed ? response.accessed : null,
created: response.created ? response.created : null,
mtime: response.mtime != null ? new Date(response.mtime) : null,
atime: response.atime != null ? new Date(response.atime) : null,
birthtime: response.birthtime != null ? new Date(response.birthtime) : null,
// Only non-null if on Unix
dev: isUnix ? response.dev : null,
ino: isUnix ? response.ino : null,
Expand Down
81 changes: 51 additions & 30 deletions cli/js/tests/stat_test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { unitTest, assert, assertEquals } from "./test_util.ts";

// TODO Add tests for modified, accessed, and created fields once there is a way
// to create temp files.
unitTest({ perms: { read: true } }, function statSyncSuccess(): void {
const packageInfo = Deno.statSync("README.md");
assert(packageInfo.isFile);
assert(!packageInfo.isSymlink);

const modulesInfo = Deno.statSync("cli/tests/symlink_to_subdir");
assert(modulesInfo.isDirectory);
assert(!modulesInfo.isSymlink);

const testsInfo = Deno.statSync("cli/tests");
assert(testsInfo.isDirectory);
assert(!testsInfo.isSymlink);
});
unitTest(
{ perms: { read: true, write: true } },
function statSyncSuccess(): void {
const packageInfo = Deno.statSync("README.md");
assert(packageInfo.isFile);
assert(!packageInfo.isSymlink);

const modulesInfo = Deno.statSync("cli/tests/symlink_to_subdir");
assert(modulesInfo.isDirectory);
assert(!modulesInfo.isSymlink);

const testsInfo = Deno.statSync("cli/tests");
assert(testsInfo.isDirectory);
assert(!testsInfo.isSymlink);

const tempFile = Deno.makeTempFileSync();
const tempInfo = Deno.statSync(tempFile);
const now = Date.now();
assert(tempInfo.atime !== null && now - tempInfo.atime.valueOf() < 1000);
assert(tempInfo.mtime !== null && now - tempInfo.mtime.valueOf() < 1000);
assert(
tempInfo.birthtime === null || now - tempInfo.birthtime.valueOf() < 1000
);
}
);

unitTest({ perms: { read: false } }, function statSyncPerm(): void {
let caughtError = false;
Expand Down Expand Up @@ -83,21 +93,32 @@ unitTest({ perms: { read: true } }, function lstatSyncNotFound(): void {
assertEquals(badInfo, undefined);
});

unitTest({ perms: { read: true } }, async function statSuccess(): Promise<
void
> {
const packageInfo = await Deno.stat("README.md");
assert(packageInfo.isFile);
assert(!packageInfo.isSymlink);

const modulesInfo = await Deno.stat("cli/tests/symlink_to_subdir");
assert(modulesInfo.isDirectory);
assert(!modulesInfo.isSymlink);

const testsInfo = await Deno.stat("cli/tests");
assert(testsInfo.isDirectory);
assert(!testsInfo.isSymlink);
});
unitTest(
{ perms: { read: true, write: true } },
async function statSuccess(): Promise<void> {
const packageInfo = await Deno.stat("README.md");
assert(packageInfo.isFile);
assert(!packageInfo.isSymlink);

const modulesInfo = await Deno.stat("cli/tests/symlink_to_subdir");
assert(modulesInfo.isDirectory);
assert(!modulesInfo.isSymlink);

const testsInfo = await Deno.stat("cli/tests");
assert(testsInfo.isDirectory);
assert(!testsInfo.isSymlink);

const tempFile = await Deno.makeTempFile();
const tempInfo = await Deno.stat(tempFile);
const now = Date.now();
assert(tempInfo.atime !== null && now - tempInfo.atime.valueOf() < 1000);
assert(tempInfo.mtime !== null && now - tempInfo.mtime.valueOf() < 1000);

assert(
tempInfo.birthtime === null || now - tempInfo.birthtime.valueOf() < 1000
);
}
);

unitTest({ perms: { read: false } }, async function statPerm(): Promise<void> {
let caughtError = false;
Expand Down
68 changes: 27 additions & 41 deletions cli/js/tests/utime_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { unitTest, assert } from "./test_util.ts";
// Allow 10 second difference.
// Note this might not be enough for FAT (but we are not testing on such fs).
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function assertFuzzyTimestampEquals(t1: any, t2: number): void {
assert(typeof t1 === "number");
assert(Math.abs(t1 - t2) < 10);
function assertFuzzyTimestampEquals(t1: Date | null, t2: Date): void {
assert(t1 instanceof Date);
assert(Math.abs(t1.valueOf() - t2.valueOf()) < 10_000);
}

unitTest(
Expand All @@ -23,8 +23,8 @@ unitTest(
Deno.utimeSync(filename, atime, mtime);

const fileInfo = Deno.statSync(filename);
assertFuzzyTimestampEquals(fileInfo.accessed, atime);
assertFuzzyTimestampEquals(fileInfo.modified, mtime);
assertFuzzyTimestampEquals(fileInfo.atime, new Date(atime * 1000));
assertFuzzyTimestampEquals(fileInfo.mtime, new Date(mtime * 1000));
}
);

Expand All @@ -38,8 +38,8 @@ unitTest(
Deno.utimeSync(testDir, atime, mtime);

const dirInfo = Deno.statSync(testDir);
assertFuzzyTimestampEquals(dirInfo.accessed, atime);
assertFuzzyTimestampEquals(dirInfo.modified, mtime);
assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000));
assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000));
}
);

Expand All @@ -48,13 +48,13 @@ unitTest(
function utimeSyncDateSuccess(): void {
const testDir = Deno.makeTempDirSync();

const atime = 1000;
const mtime = 50000;
Deno.utimeSync(testDir, new Date(atime * 1000), new Date(mtime * 1000));
const atime = new Date(1000_000);
const mtime = new Date(50000_000);
Deno.utimeSync(testDir, atime, mtime);

const dirInfo = Deno.statSync(testDir);
assertFuzzyTimestampEquals(dirInfo.accessed, atime);
assertFuzzyTimestampEquals(dirInfo.modified, mtime);
assertFuzzyTimestampEquals(dirInfo.atime, atime);
assertFuzzyTimestampEquals(dirInfo.mtime, mtime);
}
);

Expand All @@ -71,15 +71,8 @@ unitTest(
Deno.utimeSync(filename, atime, mtime);

const fileInfo = Deno.statSync(filename);
// atime and mtime must be scaled by a factor of 1000 to be recorded in seconds
assertFuzzyTimestampEquals(
fileInfo.accessed,
Math.trunc(atime.valueOf() / 1000)
);
assertFuzzyTimestampEquals(
fileInfo.modified,
Math.trunc(mtime.valueOf() / 1000)
);
assertFuzzyTimestampEquals(fileInfo.atime, atime);
assertFuzzyTimestampEquals(fileInfo.mtime, mtime);
}
);

Expand All @@ -95,8 +88,8 @@ unitTest(
Deno.utimeSync(testDir, atime, mtime);

const dirInfo = Deno.statSync(testDir);
assertFuzzyTimestampEquals(dirInfo.accessed, atime);
assertFuzzyTimestampEquals(dirInfo.modified, mtime);
assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000));
assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000));
}
);

Expand Down Expand Up @@ -148,8 +141,8 @@ unitTest(
await Deno.utime(filename, atime, mtime);

const fileInfo = Deno.statSync(filename);
assertFuzzyTimestampEquals(fileInfo.accessed, atime);
assertFuzzyTimestampEquals(fileInfo.modified, mtime);
assertFuzzyTimestampEquals(fileInfo.atime, new Date(atime * 1000));
assertFuzzyTimestampEquals(fileInfo.mtime, new Date(mtime * 1000));
}
);

Expand All @@ -163,8 +156,8 @@ unitTest(
await Deno.utime(testDir, atime, mtime);

const dirInfo = Deno.statSync(testDir);
assertFuzzyTimestampEquals(dirInfo.accessed, atime);
assertFuzzyTimestampEquals(dirInfo.modified, mtime);
assertFuzzyTimestampEquals(dirInfo.atime, new Date(atime * 1000));
assertFuzzyTimestampEquals(dirInfo.mtime, new Date(mtime * 1000));
}
);

Expand All @@ -173,13 +166,13 @@ unitTest(
async function utimeDateSuccess(): Promise<void> {
const testDir = Deno.makeTempDirSync();

const atime = 1000;
const mtime = 50000;
await Deno.utime(testDir, new Date(atime * 1000), new Date(mtime * 1000));
const atime = new Date(100_000);
const mtime = new Date(5000_000);
await Deno.utime(testDir, atime, mtime);

const dirInfo = Deno.statSync(testDir);
assertFuzzyTimestampEquals(dirInfo.accessed, atime);
assertFuzzyTimestampEquals(dirInfo.modified, mtime);
assertFuzzyTimestampEquals(dirInfo.atime, atime);
assertFuzzyTimestampEquals(dirInfo.mtime, mtime);
}
);

Expand All @@ -197,15 +190,8 @@ unitTest(
await Deno.utime(filename, atime, mtime);

const fileInfo = Deno.statSync(filename);
// The dates must be scaled by a factored of 1000 to make them seconds
assertFuzzyTimestampEquals(
fileInfo.accessed,
Math.trunc(atime.valueOf() / 1000)
);
assertFuzzyTimestampEquals(
fileInfo.modified,
Math.trunc(mtime.valueOf() / 1000)
);
assertFuzzyTimestampEquals(fileInfo.atime, atime);
assertFuzzyTimestampEquals(fileInfo.mtime, mtime);
}
);

Expand Down
31 changes: 19 additions & 12 deletions cli/ops/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use deno_core::ZeroCopyBuf;
use futures::future::FutureExt;
use std::convert::From;
use std::env::{current_dir, set_current_dir, temp_dir};
use std::io;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use std::time::UNIX_EPOCH;

use rand::{thread_rng, Rng};
Expand Down Expand Up @@ -441,14 +443,19 @@ fn op_copy_file(
})
}

macro_rules! to_seconds {
($time:expr) => {{
// Unwrap is safe here as if the file is before the unix epoch
// something is very wrong.
$time
.and_then(|t| Ok(t.duration_since(UNIX_EPOCH).unwrap().as_secs()))
.unwrap_or(0)
}};
fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> serde_json::Value {
match maybe_time {
Ok(time) => {
let msec = time
.duration_since(UNIX_EPOCH)
.map(|t| t.as_secs_f64() * 1000f64)
.unwrap_or_else(|err| err.duration().as_secs_f64() * -1000f64);
serde_json::Number::from_f64(msec)
.map(serde_json::Value::Number)
.unwrap_or(serde_json::Value::Null)
}
Err(_) => serde_json::Value::Null,
}
}

#[inline(always)]
Expand Down Expand Up @@ -477,10 +484,10 @@ fn get_stat_json(
"isDirectory": metadata.is_dir(),
"isSymlink": metadata.file_type().is_symlink(),
"size": metadata.len(),
// In seconds. Available on both Unix or Windows.
"modified":to_seconds!(metadata.modified()),
"accessed":to_seconds!(metadata.accessed()),
"created":to_seconds!(metadata.created()),
// In milliseconds, like JavaScript. Available on both Unix or Windows.
"mtime": to_msec(metadata.modified()),
"atime": to_msec(metadata.accessed()),
"birthtime": to_msec(metadata.created()),
// Following are only valid under Unix.
"dev": usm!(dev),
"ino": usm!(ino),
Expand Down
7 changes: 3 additions & 4 deletions std/archive/tar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,9 @@ export class Tar {

const mode =
opts.fileMode || (info && info.mode) || parseInt("777", 8) & 0xfff,
mtime =
opts.mtime ||
(info && info.modified) ||
Math.floor(new Date().getTime() / 1000),
mtime = Math.floor(
opts.mtime ?? (info?.mtime ?? new Date()).valueOf() / 1000
),
uid = opts.uid || 0,
gid = opts.gid || 0;
if (typeof opts.owner === "string" && opts.owner.length >= 32) {
Expand Down
Loading