nodejs/test/parallel/test-fs-stat-bigint.js

248 lines
7.2 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const promiseFs = require('fs').promises;
const tmpdir = require('../common/tmpdir');
const { isDate } = require('util').types;
const { inspect } = require('util');
tmpdir.refresh();
let testIndex = 0;
function getFilename() {
const filename = tmpdir.resolve(`test-file-${++testIndex}`);
fs.writeFileSync(filename, 'test');
return filename;
}
function verifyStats(bigintStats, numStats, allowableDelta) {
// allowableDelta: It's possible that the file stats are updated between the
// two stat() calls so allow for a small difference.
for (const key of Object.keys(numStats)) {
const val = numStats[key];
if (isDate(val)) {
const time = val.getTime();
const time2 = bigintStats[key].getTime();
assert(
time - time2 <= allowableDelta,
`difference of ${key}.getTime() should <= ${allowableDelta}.\n` +
`Number version ${time}, BigInt version ${time2}n`);
} else if (key === 'mode') {
assert.strictEqual(bigintStats[key], BigInt(val));
assert.strictEqual(
bigintStats.isBlockDevice(),
numStats.isBlockDevice()
);
assert.strictEqual(
bigintStats.isCharacterDevice(),
numStats.isCharacterDevice()
);
assert.strictEqual(
bigintStats.isDirectory(),
numStats.isDirectory()
);
assert.strictEqual(
bigintStats.isFIFO(),
numStats.isFIFO()
);
assert.strictEqual(
bigintStats.isFile(),
numStats.isFile()
);
assert.strictEqual(
bigintStats.isSocket(),
numStats.isSocket()
);
assert.strictEqual(
bigintStats.isSymbolicLink(),
numStats.isSymbolicLink()
);
} else if (key.endsWith('Ms')) {
const nsKey = key.replace('Ms', 'Ns');
const msFromBigInt = bigintStats[key];
const nsFromBigInt = bigintStats[nsKey];
const msFromBigIntNs = Number(nsFromBigInt / (10n ** 6n));
const msFromNum = numStats[key];
assert(
msFromNum - Number(msFromBigInt) <= allowableDelta,
`Number version ${key} = ${msFromNum}, ` +
`BigInt version ${key} = ${msFromBigInt}n, ` +
`Allowable delta = ${allowableDelta}`);
assert(
msFromNum - Number(msFromBigIntNs) <= allowableDelta,
`Number version ${key} = ${msFromNum}, ` +
`BigInt version ${nsKey} = ${nsFromBigInt}n` +
` = ${msFromBigIntNs}ms, Allowable delta = ${allowableDelta}`);
} else if (Number.isSafeInteger(val)) {
assert.strictEqual(
bigintStats[key], BigInt(val),
`${inspect(bigintStats[key])} !== ${inspect(BigInt(val))}\n` +
`key=${key}, val=${val}`
);
} else {
assert(
Number(bigintStats[key]) - val < 1,
`${key} is not a safe integer, difference should < 1.\n` +
`Number version ${val}, BigInt version ${bigintStats[key]}n`);
}
}
}
const runSyncTest = (func, arg) => {
const startTime = process.hrtime.bigint();
const bigintStats = func(arg, common.mustNotMutateObjectDeep({ bigint: true }));
const numStats = func(arg);
const endTime = process.hrtime.bigint();
const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6);
verifyStats(bigintStats, numStats, allowableDelta);
};
{
const filename = getFilename();
runSyncTest(fs.statSync, filename);
}
if (!common.isWindows) {
const filename = getFilename();
const link = `${filename}-link`;
fs.symlinkSync(filename, link);
runSyncTest(fs.lstatSync, link);
}
{
const filename = getFilename();
const fd = fs.openSync(filename, 'r');
runSyncTest(fs.fstatSync, fd);
fs.closeSync(fd);
}
lib: add throws option to fs.f/l/statSync For consumers that aren't interested in *why* a `statSync` call failed, allocating and throwing an exception is an unnecessary expense. This PR adds an option that will cause it to return `undefined` in such cases instead. As a motivating example, the JavaScript & TypeScript language service shared between Visual Studio and Visual Studio Code is stuck with synchronous file IO for architectural and backward-compatibility reasons. It frequently needs to speculatively check for the existence of files and directories that may not exist (and cares about file vs directory, so `existsSync` is insufficient), but ignores file system entries it can't access, regardless of the reason. Benchmarking the language service is difficult because it's so hard to get good coverage of both code bases and user behaviors, but, as a representative metric, we measured batch compilation of a few hundred popular projects (by star count) from GitHub and found that, on average, we saved about 1-2% of total compilation time. We speculate that the savings could be even more significant in interactive (language service or watch mode) scenarios, where the same (non-existent) files need to be polled over and over again. It's not a huge improvement, but it's a very small change and it will affect a lot of users (and CI runs). For reference, our measurements were against `v12.x` (3637a061a at the time) on an Ubuntu Server desktop with an SSD. PR-URL: https://github.com/nodejs/node/pull/33716 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
2020-06-03 01:18:58 +00:00
{
assert.throws(
() => fs.statSync('does_not_exist'),
{ code: 'ENOENT' });
assert.strictEqual(
fs.statSync('does_not_exist', common.mustNotMutateObjectDeep({ throwIfNoEntry: false })),
lib: add throws option to fs.f/l/statSync For consumers that aren't interested in *why* a `statSync` call failed, allocating and throwing an exception is an unnecessary expense. This PR adds an option that will cause it to return `undefined` in such cases instead. As a motivating example, the JavaScript & TypeScript language service shared between Visual Studio and Visual Studio Code is stuck with synchronous file IO for architectural and backward-compatibility reasons. It frequently needs to speculatively check for the existence of files and directories that may not exist (and cares about file vs directory, so `existsSync` is insufficient), but ignores file system entries it can't access, regardless of the reason. Benchmarking the language service is difficult because it's so hard to get good coverage of both code bases and user behaviors, but, as a representative metric, we measured batch compilation of a few hundred popular projects (by star count) from GitHub and found that, on average, we saved about 1-2% of total compilation time. We speculate that the savings could be even more significant in interactive (language service or watch mode) scenarios, where the same (non-existent) files need to be polled over and over again. It's not a huge improvement, but it's a very small change and it will affect a lot of users (and CI runs). For reference, our measurements were against `v12.x` (3637a061a at the time) on an Ubuntu Server desktop with an SSD. PR-URL: https://github.com/nodejs/node/pull/33716 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
2020-06-03 01:18:58 +00:00
undefined);
}
{
assert.throws(
() => fs.lstatSync('does_not_exist'),
{ code: 'ENOENT' });
assert.strictEqual(
fs.lstatSync('does_not_exist', common.mustNotMutateObjectDeep({ throwIfNoEntry: false })),
lib: add throws option to fs.f/l/statSync For consumers that aren't interested in *why* a `statSync` call failed, allocating and throwing an exception is an unnecessary expense. This PR adds an option that will cause it to return `undefined` in such cases instead. As a motivating example, the JavaScript & TypeScript language service shared between Visual Studio and Visual Studio Code is stuck with synchronous file IO for architectural and backward-compatibility reasons. It frequently needs to speculatively check for the existence of files and directories that may not exist (and cares about file vs directory, so `existsSync` is insufficient), but ignores file system entries it can't access, regardless of the reason. Benchmarking the language service is difficult because it's so hard to get good coverage of both code bases and user behaviors, but, as a representative metric, we measured batch compilation of a few hundred popular projects (by star count) from GitHub and found that, on average, we saved about 1-2% of total compilation time. We speculate that the savings could be even more significant in interactive (language service or watch mode) scenarios, where the same (non-existent) files need to be polled over and over again. It's not a huge improvement, but it's a very small change and it will affect a lot of users (and CI runs). For reference, our measurements were against `v12.x` (3637a061a at the time) on an Ubuntu Server desktop with an SSD. PR-URL: https://github.com/nodejs/node/pull/33716 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
2020-06-03 01:18:58 +00:00
undefined);
}
{
assert.throws(
() => fs.fstatSync(9999),
{ code: 'EBADF' });
assert.throws(
() => fs.fstatSync(9999, common.mustNotMutateObjectDeep({ throwIfNoEntry: false })),
lib: add throws option to fs.f/l/statSync For consumers that aren't interested in *why* a `statSync` call failed, allocating and throwing an exception is an unnecessary expense. This PR adds an option that will cause it to return `undefined` in such cases instead. As a motivating example, the JavaScript & TypeScript language service shared between Visual Studio and Visual Studio Code is stuck with synchronous file IO for architectural and backward-compatibility reasons. It frequently needs to speculatively check for the existence of files and directories that may not exist (and cares about file vs directory, so `existsSync` is insufficient), but ignores file system entries it can't access, regardless of the reason. Benchmarking the language service is difficult because it's so hard to get good coverage of both code bases and user behaviors, but, as a representative metric, we measured batch compilation of a few hundred popular projects (by star count) from GitHub and found that, on average, we saved about 1-2% of total compilation time. We speculate that the savings could be even more significant in interactive (language service or watch mode) scenarios, where the same (non-existent) files need to be polled over and over again. It's not a huge improvement, but it's a very small change and it will affect a lot of users (and CI runs). For reference, our measurements were against `v12.x` (3637a061a at the time) on an Ubuntu Server desktop with an SSD. PR-URL: https://github.com/nodejs/node/pull/33716 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
2020-06-03 01:18:58 +00:00
{ code: 'EBADF' });
}
const runCallbackTest = (func, arg, done) => {
const startTime = process.hrtime.bigint();
func(arg, common.mustNotMutateObjectDeep({ bigint: true }), common.mustCall((err, bigintStats) => {
func(arg, common.mustCall((err, numStats) => {
const endTime = process.hrtime.bigint();
const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6);
verifyStats(bigintStats, numStats, allowableDelta);
if (done) {
done();
}
}));
}));
};
{
const filename = getFilename();
runCallbackTest(fs.stat, filename);
}
if (!common.isWindows) {
const filename = getFilename();
const link = `${filename}-link`;
fs.symlinkSync(filename, link);
runCallbackTest(fs.lstat, link);
}
{
const filename = getFilename();
const fd = fs.openSync(filename, 'r');
runCallbackTest(fs.fstat, fd, () => { fs.closeSync(fd); });
}
const runPromiseTest = async (func, arg) => {
const startTime = process.hrtime.bigint();
const bigintStats = await func(arg, common.mustNotMutateObjectDeep({ bigint: true }));
const numStats = await func(arg);
const endTime = process.hrtime.bigint();
const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6);
verifyStats(bigintStats, numStats, allowableDelta);
};
{
const filename = getFilename();
runPromiseTest(promiseFs.stat, filename);
}
if (!common.isWindows) {
const filename = getFilename();
const link = `${filename}-link`;
fs.symlinkSync(filename, link);
runPromiseTest(promiseFs.lstat, link);
}
(async function() {
const filename = getFilename();
const handle = await promiseFs.open(filename, 'r');
const startTime = process.hrtime.bigint();
const bigintStats = await handle.stat(common.mustNotMutateObjectDeep({ bigint: true }));
const numStats = await handle.stat();
const endTime = process.hrtime.bigint();
const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6);
verifyStats(bigintStats, numStats, allowableDelta);
await handle.close();
})().then(common.mustCall());
{
// These two tests have an equivalent in ./test-fs-stat.js
// BigIntStats Date properties can be set before reading them
fs.stat(__filename, { bigint: true }, common.mustSucceed((s) => {
s.atime = 2;
s.mtime = 3;
s.ctime = 4;
s.birthtime = 5;
assert.strictEqual(s.atime, 2);
assert.strictEqual(s.mtime, 3);
assert.strictEqual(s.ctime, 4);
assert.strictEqual(s.birthtime, 5);
}));
// BigIntStats Date properties can be set after reading them
fs.stat(__filename, { bigint: true }, common.mustSucceed((s) => {
// eslint-disable-next-line no-unused-expressions
s.atime, s.mtime, s.ctime, s.birthtime;
s.atime = 2;
s.mtime = 3;
s.ctime = 4;
s.birthtime = 5;
assert.strictEqual(s.atime, 2);
assert.strictEqual(s.mtime, 3);
assert.strictEqual(s.ctime, 4);
assert.strictEqual(s.birthtime, 5);
}));
}