mirror of
https://github.com/oven-sh/bun
synced 2026-02-04 07:58:54 +00:00
Compare commits
5 Commits
dylan/pyth
...
dylan/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adf83f3634 | ||
|
|
f283cda399 | ||
|
|
5a3d273259 | ||
|
|
b192ee2152 | ||
|
|
920890ab80 |
@@ -127,9 +127,6 @@ std::optional<BufferEncodingType> parseEnumeration2(JSGlobalObject& lexicalGloba
|
||||
case 'h':
|
||||
case 'H':
|
||||
// hex
|
||||
if (encoding[1] == 'e')
|
||||
if (encoding[2] == 'x' && encoding[3] == '\0')
|
||||
return BufferEncodingType::hex;
|
||||
if (WTF::equalIgnoringASCIICase(encoding, "hex"_s))
|
||||
return BufferEncodingType::hex;
|
||||
break;
|
||||
|
||||
@@ -255,6 +255,14 @@ pub const Async = struct {
|
||||
const args_: Arguments.Writev = task.args;
|
||||
const fd = args_.fd.impl().uv();
|
||||
const bufs = args_.buffers.buffers.items;
|
||||
|
||||
// https://github.com/nodejs/node/blob/35742a2d0b3ac1a1eff5ab333b5ae2fef009f00b/lib/fs.js#L953
|
||||
if (bufs.len == 0) {
|
||||
task.result = Maybe(Return.Writev){ .result = .{ .bytes_written = 0 } };
|
||||
task.globalObject.bunVM().eventLoop().enqueueTask(JSC.Task.init(task));
|
||||
return task.promise.value();
|
||||
}
|
||||
|
||||
const pos: i64 = args_.position orelse -1;
|
||||
|
||||
var sum: u64 = 0;
|
||||
|
||||
@@ -763,6 +763,12 @@ function ReadStream(this: typeof ReadStream, pathOrFd, options) {
|
||||
options = { encoding: options };
|
||||
}
|
||||
|
||||
if (options.encoding && options.encoding !== "buffer") {
|
||||
if (!Buffer.isEncoding(options.encoding)) {
|
||||
throw $ERR_INVALID_ARG_VALUE("encoding", options.encoding, "is invalid encoding");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isObject(options) && !$isCallable(options)) {
|
||||
throw new TypeError("Expected options to be an object or a string");
|
||||
}
|
||||
@@ -1070,6 +1076,16 @@ var WriteStreamClass = (WriteStream = function WriteStream(path, options = defau
|
||||
throw new TypeError("Expected options to be an object");
|
||||
}
|
||||
|
||||
if (typeof options === "string") {
|
||||
options = { encoding: options };
|
||||
}
|
||||
|
||||
if (options.encoding && options.encoding !== "buffer") {
|
||||
if (!Buffer.isEncoding(options.encoding)) {
|
||||
throw $ERR_INVALID_ARG_VALUE("encoding", options.encoding, "is invalid encoding");
|
||||
}
|
||||
}
|
||||
|
||||
var {
|
||||
fs = defaultWriteStreamOptions.fs,
|
||||
start = defaultWriteStreamOptions.start,
|
||||
|
||||
238
test/js/node/test/parallel/test-fs-access.js
Normal file
238
test/js/node/test/parallel/test-fs-access.js
Normal file
@@ -0,0 +1,238 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
// This tests that fs.access and fs.accessSync works as expected
|
||||
// and the errors thrown from these APIs include the desired properties
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.isWindows && process.getuid() === 0)
|
||||
common.skip('as this test should not be run as `root`');
|
||||
|
||||
if (common.isIBMi)
|
||||
common.skip('IBMi has a different access permission mechanism');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const { UV_ENOENT } = internalBinding('uv');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const doesNotExist = tmpdir.resolve('__this_should_not_exist');
|
||||
const readOnlyFile = tmpdir.resolve('read_only_file');
|
||||
const readWriteFile = tmpdir.resolve('read_write_file');
|
||||
|
||||
function createFileWithPerms(file, mode) {
|
||||
fs.writeFileSync(file, '');
|
||||
fs.chmodSync(file, mode);
|
||||
}
|
||||
|
||||
tmpdir.refresh();
|
||||
createFileWithPerms(readOnlyFile, 0o444);
|
||||
createFileWithPerms(readWriteFile, 0o666);
|
||||
|
||||
// On non-Windows supported platforms, fs.access(readOnlyFile, W_OK, ...)
|
||||
// always succeeds if node runs as the super user, which is sometimes the
|
||||
// case for tests running on our continuous testing platform agents.
|
||||
//
|
||||
// In this case, this test tries to change its process user id to a
|
||||
// non-superuser user so that the test that checks for write access to a
|
||||
// read-only file can be more meaningful.
|
||||
//
|
||||
// The change of user id is done after creating the fixtures files for the same
|
||||
// reason: the test may be run as the superuser within a directory in which
|
||||
// only the superuser can create files, and thus it may need superuser
|
||||
// privileges to create them.
|
||||
//
|
||||
// There's not really any point in resetting the process' user id to 0 after
|
||||
// changing it to 'nobody', since in the case that the test runs without
|
||||
// superuser privilege, it is not possible to change its process user id to
|
||||
// superuser.
|
||||
//
|
||||
// It can prevent the test from removing files created before the change of user
|
||||
// id, but that's fine. In this case, it is the responsibility of the
|
||||
// continuous integration platform to take care of that.
|
||||
let hasWriteAccessForReadonlyFile = false;
|
||||
if (!common.isWindows && process.getuid() === 0) {
|
||||
hasWriteAccessForReadonlyFile = true;
|
||||
try {
|
||||
process.setuid('nobody');
|
||||
hasWriteAccessForReadonlyFile = false;
|
||||
} catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
}
|
||||
|
||||
assert.strictEqual(typeof fs.constants.F_OK, 'number');
|
||||
assert.strictEqual(typeof fs.constants.R_OK, 'number');
|
||||
assert.strictEqual(typeof fs.constants.W_OK, 'number');
|
||||
assert.strictEqual(typeof fs.constants.X_OK, 'number');
|
||||
|
||||
const throwNextTick = (e) => { process.nextTick(() => { throw e; }); };
|
||||
|
||||
fs.access(__filename, common.mustCall(function(...args) {
|
||||
assert.deepStrictEqual(args, [null]);
|
||||
}));
|
||||
fs.promises.access(__filename)
|
||||
.then(common.mustCall())
|
||||
.catch(throwNextTick);
|
||||
fs.access(__filename, fs.constants.R_OK, common.mustCall(function(...args) {
|
||||
assert.deepStrictEqual(args, [null]);
|
||||
}));
|
||||
fs.promises.access(__filename, fs.constants.R_OK)
|
||||
.then(common.mustCall())
|
||||
.catch(throwNextTick);
|
||||
fs.access(readOnlyFile, fs.constants.R_OK, common.mustCall(function(...args) {
|
||||
assert.deepStrictEqual(args, [null]);
|
||||
}));
|
||||
fs.promises.access(readOnlyFile, fs.constants.R_OK)
|
||||
.then(common.mustCall())
|
||||
.catch(throwNextTick);
|
||||
|
||||
{
|
||||
const expectedError = (err) => {
|
||||
assert.notStrictEqual(err, null);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.path, doesNotExist);
|
||||
};
|
||||
const expectedErrorPromise = (err) => {
|
||||
expectedError(err);
|
||||
assert.match(err.stack, /at async Object\.access/);
|
||||
};
|
||||
fs.access(doesNotExist, common.mustCall(expectedError));
|
||||
fs.promises.access(doesNotExist)
|
||||
.then(common.mustNotCall(), common.mustCall(expectedErrorPromise))
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
{
|
||||
function expectedError(err) {
|
||||
assert.strictEqual(this, undefined);
|
||||
if (hasWriteAccessForReadonlyFile) {
|
||||
assert.ifError(err);
|
||||
} else {
|
||||
assert.notStrictEqual(err, null);
|
||||
assert.strictEqual(err.path, readOnlyFile);
|
||||
}
|
||||
}
|
||||
fs.access(readOnlyFile, fs.constants.W_OK, common.mustCall(expectedError));
|
||||
fs.promises.access(readOnlyFile, fs.constants.W_OK)
|
||||
.then(common.mustNotCall(), common.mustCall(expectedError))
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
{
|
||||
const expectedError = (err) => {
|
||||
assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE');
|
||||
assert.ok(err instanceof TypeError);
|
||||
return true;
|
||||
};
|
||||
assert.throws(
|
||||
() => { fs.access(100, fs.constants.F_OK, common.mustNotCall()); },
|
||||
expectedError
|
||||
);
|
||||
|
||||
fs.promises.access(100, fs.constants.F_OK)
|
||||
.then(common.mustNotCall(), common.mustCall(expectedError))
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.access(__filename, fs.constants.F_OK);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.access(__filename, fs.constants.F_OK, common.mustNotMutateObjectDeep({}));
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
|
||||
// Regular access should not throw.
|
||||
fs.accessSync(__filename);
|
||||
const mode = fs.constants.R_OK | fs.constants.W_OK;
|
||||
fs.accessSync(readWriteFile, mode);
|
||||
|
||||
// Invalid modes should throw.
|
||||
[
|
||||
false,
|
||||
1n,
|
||||
{ [Symbol.toPrimitive]() { return fs.constants.R_OK; } },
|
||||
[1],
|
||||
'r',
|
||||
].forEach((mode, i) => {
|
||||
console.log(mode, i);
|
||||
assert.throws(
|
||||
() => fs.access(readWriteFile, mode, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.accessSync(readWriteFile, mode),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Out of range modes should throw
|
||||
[
|
||||
-1,
|
||||
8,
|
||||
Infinity,
|
||||
NaN,
|
||||
].forEach((mode, i) => {
|
||||
console.log(mode, i);
|
||||
assert.throws(
|
||||
() => fs.access(readWriteFile, mode, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.accessSync(readWriteFile, mode),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => { fs.accessSync(doesNotExist); },
|
||||
(err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.path, doesNotExist);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, access '${doesNotExist}'`
|
||||
);
|
||||
assert.strictEqual(err.constructor, Error);
|
||||
assert.strictEqual(err.syscall, 'access');
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => { fs.accessSync(Buffer.from(doesNotExist)); },
|
||||
(err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.path, doesNotExist);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, access '${doesNotExist}'`
|
||||
);
|
||||
assert.strictEqual(err.constructor, Error);
|
||||
assert.strictEqual(err.syscall, 'access');
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
114
test/js/node/test/parallel/test-fs-append-file-flush.js
Normal file
114
test/js/node/test/parallel/test-fs-append-file-flush.js
Normal file
@@ -0,0 +1,114 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const fsp = require('node:fs/promises');
|
||||
const test = require('node:test');
|
||||
const data = 'foo';
|
||||
let cnt = 0;
|
||||
|
||||
function nextFile() {
|
||||
return tmpdir.resolve(`${cnt++}.out`);
|
||||
}
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
test('synchronous version', async (t) => {
|
||||
await t.test('validation', (t) => {
|
||||
for (const v of ['true', '', 0, 1, [], {}, Symbol()]) {
|
||||
assert.throws(() => {
|
||||
fs.appendFileSync(nextFile(), data, { flush: v });
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('performs flush', (t) => {
|
||||
const spy = t.mock.method(fs, 'fsyncSync');
|
||||
const file = nextFile();
|
||||
fs.appendFileSync(file, data, { flush: true });
|
||||
const calls = spy.mock.calls;
|
||||
assert.strictEqual(calls.length, 1);
|
||||
assert.strictEqual(calls[0].result, undefined);
|
||||
assert.strictEqual(calls[0].error, undefined);
|
||||
assert.strictEqual(calls[0].arguments.length, 1);
|
||||
assert.strictEqual(typeof calls[0].arguments[0], 'number');
|
||||
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
|
||||
});
|
||||
|
||||
await t.test('does not perform flush', (t) => {
|
||||
const spy = t.mock.method(fs, 'fsyncSync');
|
||||
|
||||
for (const v of [undefined, null, false]) {
|
||||
const file = nextFile();
|
||||
fs.appendFileSync(file, data, { flush: v });
|
||||
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
|
||||
}
|
||||
|
||||
assert.strictEqual(spy.mock.calls.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
test('callback version', async (t) => {
|
||||
await t.test('validation', (t) => {
|
||||
for (const v of ['true', '', 0, 1, [], {}, Symbol()]) {
|
||||
assert.throws(() => {
|
||||
fs.appendFileSync(nextFile(), data, { flush: v });
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('performs flush', (t, done) => {
|
||||
const spy = t.mock.method(fs, 'fsync');
|
||||
const file = nextFile();
|
||||
fs.appendFile(file, data, { flush: true }, common.mustSucceed(() => {
|
||||
const calls = spy.mock.calls;
|
||||
assert.strictEqual(calls.length, 1);
|
||||
assert.strictEqual(calls[0].result, undefined);
|
||||
assert.strictEqual(calls[0].error, undefined);
|
||||
assert.strictEqual(calls[0].arguments.length, 2);
|
||||
assert.strictEqual(typeof calls[0].arguments[0], 'number');
|
||||
assert.strictEqual(typeof calls[0].arguments[1], 'function');
|
||||
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
await t.test('does not perform flush', (t, done) => {
|
||||
const values = [undefined, null, false];
|
||||
const spy = t.mock.method(fs, 'fsync');
|
||||
let cnt = 0;
|
||||
|
||||
for (const v of values) {
|
||||
const file = nextFile();
|
||||
|
||||
fs.appendFile(file, data, { flush: v }, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.readFileSync(file, 'utf8'), data);
|
||||
cnt++;
|
||||
|
||||
if (cnt === values.length) {
|
||||
assert.strictEqual(spy.mock.calls.length, 0);
|
||||
done();
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('promise based version', async (t) => {
|
||||
await t.test('validation', async (t) => {
|
||||
for (const v of ['true', '', 0, 1, [], {}, Symbol()]) {
|
||||
await assert.rejects(() => {
|
||||
return fsp.appendFile(nextFile(), data, { flush: v });
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('success path', async (t) => {
|
||||
for (const v of [undefined, null, false, true]) {
|
||||
const file = nextFile();
|
||||
await fsp.appendFile(file, data, { flush: v });
|
||||
assert.strictEqual(await fsp.readFile(file, 'utf8'), data);
|
||||
}
|
||||
});
|
||||
});
|
||||
102
test/js/node/test/parallel/test-fs-append-file-sync.js
Normal file
102
test/js/node/test/parallel/test-fs-append-file-sync.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
const currentFileData = 'ABCD';
|
||||
const m = 0o600;
|
||||
const num = 220;
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const data = fixtures.utf8TestText;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
// Test that empty file will be created and have content added.
|
||||
const filename = tmpdir.resolve('append-sync.txt');
|
||||
|
||||
fs.appendFileSync(filename, data);
|
||||
|
||||
const fileData = fs.readFileSync(filename);
|
||||
|
||||
assert.strictEqual(Buffer.byteLength(data), fileData.length);
|
||||
|
||||
// Test that appends data to a non empty file.
|
||||
const filename2 = tmpdir.resolve('append-sync2.txt');
|
||||
fs.writeFileSync(filename2, currentFileData);
|
||||
|
||||
fs.appendFileSync(filename2, data);
|
||||
|
||||
const fileData2 = fs.readFileSync(filename2);
|
||||
|
||||
assert.strictEqual(Buffer.byteLength(data) + currentFileData.length,
|
||||
fileData2.length);
|
||||
|
||||
// Test that appendFileSync accepts buffers.
|
||||
const filename3 = tmpdir.resolve('append-sync3.txt');
|
||||
fs.writeFileSync(filename3, currentFileData);
|
||||
|
||||
const buf = Buffer.from(data, 'utf8');
|
||||
fs.appendFileSync(filename3, buf);
|
||||
|
||||
const fileData3 = fs.readFileSync(filename3);
|
||||
|
||||
assert.strictEqual(buf.length + currentFileData.length, fileData3.length);
|
||||
|
||||
const filename4 = tmpdir.resolve('append-sync4.txt');
|
||||
fs.writeFileSync(filename4, currentFileData, common.mustNotMutateObjectDeep({ mode: m }));
|
||||
|
||||
[
|
||||
true, false, 0, 1, Infinity, () => {}, {}, [], undefined, null,
|
||||
].forEach((value) => {
|
||||
assert.throws(
|
||||
() => fs.appendFileSync(filename4, value, common.mustNotMutateObjectDeep({ mode: m })),
|
||||
{ message: /data/, code: 'ERR_INVALID_ARG_TYPE' }
|
||||
);
|
||||
});
|
||||
fs.appendFileSync(filename4, `${num}`, common.mustNotMutateObjectDeep({ mode: m }));
|
||||
|
||||
// Windows permissions aren't Unix.
|
||||
if (!common.isWindows) {
|
||||
const st = fs.statSync(filename4);
|
||||
assert.strictEqual(st.mode & 0o700, m);
|
||||
}
|
||||
|
||||
const fileData4 = fs.readFileSync(filename4);
|
||||
|
||||
assert.strictEqual(Buffer.byteLength(String(num)) + currentFileData.length,
|
||||
fileData4.length);
|
||||
|
||||
// Test that appendFile accepts file descriptors.
|
||||
const filename5 = tmpdir.resolve('append-sync5.txt');
|
||||
fs.writeFileSync(filename5, currentFileData);
|
||||
|
||||
const filename5fd = fs.openSync(filename5, 'a+', 0o600);
|
||||
fs.appendFileSync(filename5fd, data);
|
||||
fs.closeSync(filename5fd);
|
||||
|
||||
const fileData5 = fs.readFileSync(filename5);
|
||||
|
||||
assert.strictEqual(Buffer.byteLength(data) + currentFileData.length,
|
||||
fileData5.length);
|
||||
187
test/js/node/test/parallel/test-fs-append-file.js
Normal file
187
test/js/node/test/parallel/test-fs-append-file.js
Normal file
@@ -0,0 +1,187 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const currentFileData = 'ABCD';
|
||||
const fixtures = require('../common/fixtures');
|
||||
const s = fixtures.utf8TestText;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
const throwNextTick = (e) => { process.nextTick(() => { throw e; }); };
|
||||
|
||||
// Test that empty file will be created and have content added (callback API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append.txt');
|
||||
|
||||
fs.appendFile(filename, s, common.mustSucceed(() => {
|
||||
fs.readFile(filename, common.mustSucceed((buffer) => {
|
||||
assert.strictEqual(Buffer.byteLength(s), buffer.length);
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
// Test that empty file will be created and have content added (promise API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-promise.txt');
|
||||
|
||||
fs.promises.appendFile(filename, s)
|
||||
.then(common.mustCall(() => fs.promises.readFile(filename)))
|
||||
.then((buffer) => {
|
||||
assert.strictEqual(Buffer.byteLength(s), buffer.length);
|
||||
})
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
// Test that appends data to a non-empty file (callback API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-non-empty.txt');
|
||||
fs.writeFileSync(filename, currentFileData);
|
||||
|
||||
fs.appendFile(filename, s, common.mustSucceed(() => {
|
||||
fs.readFile(filename, common.mustSucceed((buffer) => {
|
||||
assert.strictEqual(Buffer.byteLength(s) + currentFileData.length,
|
||||
buffer.length);
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
// Test that appends data to a non-empty file (promise API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-non-empty-promise.txt');
|
||||
fs.writeFileSync(filename, currentFileData);
|
||||
|
||||
fs.promises.appendFile(filename, s)
|
||||
.then(common.mustCall(() => fs.promises.readFile(filename)))
|
||||
.then((buffer) => {
|
||||
assert.strictEqual(Buffer.byteLength(s) + currentFileData.length,
|
||||
buffer.length);
|
||||
})
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
// Test that appendFile accepts buffers (callback API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-buffer.txt');
|
||||
fs.writeFileSync(filename, currentFileData);
|
||||
|
||||
const buf = Buffer.from(s, 'utf8');
|
||||
|
||||
fs.appendFile(filename, buf, common.mustSucceed(() => {
|
||||
fs.readFile(filename, common.mustSucceed((buffer) => {
|
||||
assert.strictEqual(buf.length + currentFileData.length, buffer.length);
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
// Test that appendFile accepts buffers (promises API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-buffer-promises.txt');
|
||||
fs.writeFileSync(filename, currentFileData);
|
||||
|
||||
const buf = Buffer.from(s, 'utf8');
|
||||
|
||||
fs.promises.appendFile(filename, buf)
|
||||
.then(common.mustCall(() => fs.promises.readFile(filename)))
|
||||
.then((buffer) => {
|
||||
assert.strictEqual(buf.length + currentFileData.length, buffer.length);
|
||||
})
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
// Test that appendFile does not accept invalid data type (callback API).
|
||||
[false, 5, {}, null, undefined].forEach(async (data) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: /"data"|"buffer"/
|
||||
};
|
||||
const filename = tmpdir.resolve('append-invalid-data.txt');
|
||||
|
||||
assert.throws(
|
||||
() => fs.appendFile(filename, data, common.mustNotCall()),
|
||||
errObj
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.appendFileSync(filename, data),
|
||||
errObj
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
fs.promises.appendFile(filename, data),
|
||||
errObj
|
||||
);
|
||||
// The filename shouldn't exist if throwing error.
|
||||
assert.throws(
|
||||
() => fs.statSync(filename),
|
||||
{
|
||||
code: 'ENOENT',
|
||||
message: /no such file or directory/
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Test that appendFile accepts file descriptors (callback API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-descriptors.txt');
|
||||
fs.writeFileSync(filename, currentFileData);
|
||||
|
||||
fs.open(filename, 'a+', common.mustSucceed((fd) => {
|
||||
fs.appendFile(fd, s, common.mustSucceed(() => {
|
||||
fs.close(fd, common.mustSucceed(() => {
|
||||
fs.readFile(filename, common.mustSucceed((buffer) => {
|
||||
assert.strictEqual(Buffer.byteLength(s) + currentFileData.length,
|
||||
buffer.length);
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
// Test that appendFile accepts file descriptors (promises API).
|
||||
{
|
||||
const filename = tmpdir.resolve('append-descriptors-promises.txt');
|
||||
fs.writeFileSync(filename, currentFileData);
|
||||
|
||||
let fd;
|
||||
fs.promises.open(filename, 'a+')
|
||||
.then(common.mustCall((fileDescriptor) => {
|
||||
fd = fileDescriptor;
|
||||
return fs.promises.appendFile(fd, s);
|
||||
}))
|
||||
.then(common.mustCall(() => fd.close()))
|
||||
.then(common.mustCall(() => fs.promises.readFile(filename)))
|
||||
.then(common.mustCall((buffer) => {
|
||||
assert.strictEqual(Buffer.byteLength(s) + currentFileData.length,
|
||||
buffer.length);
|
||||
}))
|
||||
.catch(throwNextTick);
|
||||
}
|
||||
|
||||
assert.throws(
|
||||
() => fs.appendFile(tmpdir.resolve('append6.txt'), console.log),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||
80
test/js/node/test/parallel/test-fs-assert-encoding-error.js
Normal file
80
test/js/node/test/parallel/test-fs-assert-encoding-error.js
Normal file
@@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const testPath = tmpdir.resolve('assert-encoding-error');
|
||||
const options = 'test';
|
||||
const expectedError = {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
name: 'TypeError',
|
||||
};
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readFile(testPath, options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readFileSync(testPath, options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readdir(testPath, options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readdirSync(testPath, options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readlink(testPath, options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readlinkSync(testPath, options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.writeFile(testPath, 'data', options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.writeFileSync(testPath, 'data', options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.appendFile(testPath, 'data', options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.appendFileSync(testPath, 'data', options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.watch(testPath, options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.realpath(testPath, options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.realpathSync(testPath, options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.mkdtemp(testPath, options, common.mustNotCall());
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.mkdtempSync(testPath, options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.ReadStream(testPath, options);
|
||||
}, expectedError);
|
||||
|
||||
assert.throws(() => {
|
||||
fs.WriteStream(testPath, options);
|
||||
}, expectedError);
|
||||
43
test/js/node/test/parallel/test-fs-buffer.js
Normal file
43
test/js/node/test/parallel/test-fs-buffer.js
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
fs.access(Buffer.from(tmpdir.path), common.mustSucceed());
|
||||
|
||||
const buf = Buffer.from(tmpdir.resolve('a.txt'));
|
||||
fs.open(buf, 'w+', common.mustSucceed((fd) => {
|
||||
assert(fd);
|
||||
fs.close(fd, common.mustSucceed());
|
||||
}));
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.accessSync(true);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "path" argument must be of type string or an instance of ' +
|
||||
'Buffer or URL. Received type boolean (true)'
|
||||
}
|
||||
);
|
||||
|
||||
const dir = Buffer.from(fixtures.fixturesDir);
|
||||
fs.readdir(dir, 'hex', common.mustSucceed((hexList) => {
|
||||
fs.readdir(dir, common.mustSucceed((stringList) => {
|
||||
stringList.forEach((val, idx) => {
|
||||
const fromHexList = Buffer.from(hexList[idx], 'hex').toString();
|
||||
assert.strictEqual(
|
||||
fromHexList,
|
||||
val,
|
||||
`expected ${val}, got ${fromHexList} by hex decoding ${hexList[idx]}`
|
||||
);
|
||||
});
|
||||
}));
|
||||
}));
|
||||
89
test/js/node/test/parallel/test-fs-chmod-mask.js
Normal file
89
test/js/node/test/parallel/test-fs-chmod-mask.js
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
// This tests that the lower bits of mode > 0o777 still works in fs APIs.
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
let mode;
|
||||
// On Windows chmod is only able to manipulate write permission
|
||||
if (common.isWindows) {
|
||||
mode = 0o444; // read-only
|
||||
} else {
|
||||
mode = 0o777;
|
||||
}
|
||||
|
||||
const maskToIgnore = 0o10000;
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
function test(mode, asString) {
|
||||
const suffix = asString ? 'str' : 'num';
|
||||
const input = asString ?
|
||||
(mode | maskToIgnore).toString(8) : (mode | maskToIgnore);
|
||||
|
||||
{
|
||||
const file = tmpdir.resolve(`chmod-async-${suffix}.txt`);
|
||||
fs.writeFileSync(file, 'test', 'utf-8');
|
||||
|
||||
fs.chmod(file, input, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.statSync(file).mode & 0o777, mode);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file = tmpdir.resolve(`chmodSync-${suffix}.txt`);
|
||||
fs.writeFileSync(file, 'test', 'utf-8');
|
||||
|
||||
fs.chmodSync(file, input);
|
||||
assert.strictEqual(fs.statSync(file).mode & 0o777, mode);
|
||||
}
|
||||
|
||||
{
|
||||
const file = tmpdir.resolve(`fchmod-async-${suffix}.txt`);
|
||||
fs.writeFileSync(file, 'test', 'utf-8');
|
||||
fs.open(file, 'w', common.mustSucceed((fd) => {
|
||||
fs.fchmod(fd, input, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode);
|
||||
fs.close(fd, assert.ifError);
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file = tmpdir.resolve(`fchmodSync-${suffix}.txt`);
|
||||
fs.writeFileSync(file, 'test', 'utf-8');
|
||||
const fd = fs.openSync(file, 'w');
|
||||
|
||||
fs.fchmodSync(fd, input);
|
||||
assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode);
|
||||
|
||||
fs.close(fd, assert.ifError);
|
||||
}
|
||||
|
||||
if (fs.lchmod) {
|
||||
const link = tmpdir.resolve(`lchmod-src-${suffix}`);
|
||||
const file = tmpdir.resolve(`lchmod-dest-${suffix}`);
|
||||
fs.writeFileSync(file, 'test', 'utf-8');
|
||||
fs.symlinkSync(file, link);
|
||||
|
||||
fs.lchmod(link, input, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode);
|
||||
}));
|
||||
}
|
||||
|
||||
if (fs.lchmodSync) {
|
||||
const link = tmpdir.resolve(`lchmodSync-src-${suffix}`);
|
||||
const file = tmpdir.resolve(`lchmodSync-dest-${suffix}`);
|
||||
fs.writeFileSync(file, 'test', 'utf-8');
|
||||
fs.symlinkSync(file, link);
|
||||
|
||||
fs.lchmodSync(link, input);
|
||||
assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode);
|
||||
}
|
||||
}
|
||||
|
||||
test(mode, true);
|
||||
test(mode, false);
|
||||
152
test/js/node/test/parallel/test-fs-chmod.js
Normal file
152
test/js/node/test/parallel/test-fs-chmod.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
let mode_async;
|
||||
let mode_sync;
|
||||
|
||||
// Need to hijack fs.open/close to make sure that things
|
||||
// get closed once they're opened.
|
||||
fs._open = fs.open;
|
||||
fs._openSync = fs.openSync;
|
||||
fs.open = open;
|
||||
fs.openSync = openSync;
|
||||
fs._close = fs.close;
|
||||
fs._closeSync = fs.closeSync;
|
||||
fs.close = close;
|
||||
fs.closeSync = closeSync;
|
||||
|
||||
let openCount = 0;
|
||||
|
||||
function open() {
|
||||
openCount++;
|
||||
return fs._open.apply(fs, arguments);
|
||||
}
|
||||
|
||||
function openSync() {
|
||||
openCount++;
|
||||
return fs._openSync.apply(fs, arguments);
|
||||
}
|
||||
|
||||
function close() {
|
||||
openCount--;
|
||||
return fs._close.apply(fs, arguments);
|
||||
}
|
||||
|
||||
function closeSync() {
|
||||
openCount--;
|
||||
return fs._closeSync.apply(fs, arguments);
|
||||
}
|
||||
|
||||
|
||||
// On Windows chmod is only able to manipulate write permission
|
||||
if (common.isWindows) {
|
||||
mode_async = 0o400; // read-only
|
||||
mode_sync = 0o600; // read-write
|
||||
} else {
|
||||
mode_async = 0o777;
|
||||
mode_sync = 0o644;
|
||||
}
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
const file1 = tmpdir.resolve('a.js');
|
||||
const file2 = tmpdir.resolve('a1.js');
|
||||
|
||||
// Create file1.
|
||||
fs.closeSync(fs.openSync(file1, 'w'));
|
||||
|
||||
fs.chmod(file1, mode_async.toString(8), common.mustSucceed(() => {
|
||||
if (common.isWindows) {
|
||||
assert.ok((fs.statSync(file1).mode & 0o777) & mode_async);
|
||||
} else {
|
||||
assert.strictEqual(fs.statSync(file1).mode & 0o777, mode_async);
|
||||
}
|
||||
|
||||
fs.chmodSync(file1, mode_sync);
|
||||
if (common.isWindows) {
|
||||
assert.ok((fs.statSync(file1).mode & 0o777) & mode_sync);
|
||||
} else {
|
||||
assert.strictEqual(fs.statSync(file1).mode & 0o777, mode_sync);
|
||||
}
|
||||
}));
|
||||
|
||||
fs.open(file2, 'w', common.mustSucceed((fd) => {
|
||||
fs.fchmod(fd, mode_async.toString(8), common.mustSucceed(() => {
|
||||
if (common.isWindows) {
|
||||
assert.ok((fs.fstatSync(fd).mode & 0o777) & mode_async);
|
||||
} else {
|
||||
assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode_async);
|
||||
}
|
||||
|
||||
assert.throws(
|
||||
() => fs.fchmod(fd, {}),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
);
|
||||
|
||||
fs.fchmodSync(fd, mode_sync);
|
||||
if (common.isWindows) {
|
||||
assert.ok((fs.fstatSync(fd).mode & 0o777) & mode_sync);
|
||||
} else {
|
||||
assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode_sync);
|
||||
}
|
||||
|
||||
fs.close(fd, assert.ifError);
|
||||
}));
|
||||
}));
|
||||
|
||||
// lchmod
|
||||
if (fs.lchmod) {
|
||||
const link = tmpdir.resolve('symbolic-link');
|
||||
|
||||
fs.symlinkSync(file2, link);
|
||||
|
||||
fs.lchmod(link, mode_async, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode_async);
|
||||
|
||||
fs.lchmodSync(link, mode_sync);
|
||||
assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode_sync);
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
[false, 1, {}, [], null, undefined].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "path" argument must be of type string or an instance ' +
|
||||
'of Buffer or URL.' +
|
||||
common.invalidArgTypeHelper(input)
|
||||
};
|
||||
assert.throws(() => fs.chmod(input, 1, common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.chmodSync(input, 1), errObj);
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(openCount, 0);
|
||||
});
|
||||
35
test/js/node/test/parallel/test-fs-close-errors.js
Normal file
35
test/js/node/test/parallel/test-fs-close-errors.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
// This tests that the errors thrown from fs.close and fs.closeSync
|
||||
// include the desired properties
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
['', false, null, undefined, {}, []].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "fd" argument must be of type number.' +
|
||||
common.invalidArgTypeHelper(input)
|
||||
};
|
||||
assert.throws(() => fs.close(input), errObj);
|
||||
assert.throws(() => fs.closeSync(input), errObj);
|
||||
});
|
||||
|
||||
{
|
||||
// Test error when cb is not a function
|
||||
const fd = fs.openSync(__filename, 'r');
|
||||
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
};
|
||||
|
||||
['', false, null, {}, []].forEach((input) => {
|
||||
assert.throws(() => fs.close(fd, input), errObj);
|
||||
});
|
||||
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
12
test/js/node/test/parallel/test-fs-close.js
Normal file
12
test/js/node/test/parallel/test-fs-close.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const fd = fs.openSync(__filename, 'r');
|
||||
|
||||
fs.close(fd, common.mustCall(function(...args) {
|
||||
assert.deepStrictEqual(args, [null]);
|
||||
}));
|
||||
166
test/js/node/test/parallel/test-fs-copyfile.js
Normal file
166
test/js/node/test/parallel/test-fs-copyfile.js
Normal file
@@ -0,0 +1,166 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const {
|
||||
UV_ENOENT,
|
||||
UV_EEXIST
|
||||
} = internalBinding('uv');
|
||||
const src = fixtures.path('a.js');
|
||||
const dest = tmpdir.resolve('copyfile.out');
|
||||
const {
|
||||
COPYFILE_EXCL,
|
||||
COPYFILE_FICLONE,
|
||||
COPYFILE_FICLONE_FORCE,
|
||||
UV_FS_COPYFILE_EXCL,
|
||||
UV_FS_COPYFILE_FICLONE,
|
||||
UV_FS_COPYFILE_FICLONE_FORCE
|
||||
} = fs.constants;
|
||||
|
||||
function verify(src, dest) {
|
||||
const srcData = fs.readFileSync(src, 'utf8');
|
||||
const srcStat = fs.statSync(src);
|
||||
const destData = fs.readFileSync(dest, 'utf8');
|
||||
const destStat = fs.statSync(dest);
|
||||
|
||||
assert.strictEqual(srcData, destData);
|
||||
assert.strictEqual(srcStat.mode, destStat.mode);
|
||||
assert.strictEqual(srcStat.size, destStat.size);
|
||||
}
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
// Verify that flags are defined.
|
||||
assert.strictEqual(typeof COPYFILE_EXCL, 'number');
|
||||
assert.strictEqual(typeof COPYFILE_FICLONE, 'number');
|
||||
assert.strictEqual(typeof COPYFILE_FICLONE_FORCE, 'number');
|
||||
assert.strictEqual(typeof UV_FS_COPYFILE_EXCL, 'number');
|
||||
assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE, 'number');
|
||||
assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE_FORCE, 'number');
|
||||
assert.strictEqual(COPYFILE_EXCL, UV_FS_COPYFILE_EXCL);
|
||||
assert.strictEqual(COPYFILE_FICLONE, UV_FS_COPYFILE_FICLONE);
|
||||
assert.strictEqual(COPYFILE_FICLONE_FORCE, UV_FS_COPYFILE_FICLONE_FORCE);
|
||||
|
||||
// Verify that files are overwritten when no flags are provided.
|
||||
fs.writeFileSync(dest, '', 'utf8');
|
||||
const result = fs.copyFileSync(src, dest);
|
||||
assert.strictEqual(result, undefined);
|
||||
verify(src, dest);
|
||||
|
||||
// Verify that files are overwritten with default flags.
|
||||
fs.copyFileSync(src, dest, 0);
|
||||
verify(src, dest);
|
||||
|
||||
// Verify that UV_FS_COPYFILE_FICLONE can be used.
|
||||
fs.unlinkSync(dest);
|
||||
fs.copyFileSync(src, dest, UV_FS_COPYFILE_FICLONE);
|
||||
verify(src, dest);
|
||||
|
||||
// Verify that COPYFILE_FICLONE_FORCE can be used.
|
||||
try {
|
||||
fs.unlinkSync(dest);
|
||||
fs.copyFileSync(src, dest, COPYFILE_FICLONE_FORCE);
|
||||
verify(src, dest);
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.syscall, 'copyfile');
|
||||
assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' ||
|
||||
err.code === 'ENOSYS' || err.code === 'EXDEV');
|
||||
assert.strictEqual(err.path, src);
|
||||
assert.strictEqual(err.dest, dest);
|
||||
}
|
||||
|
||||
// Copies asynchronously.
|
||||
tmpdir.refresh(); // Don't use unlinkSync() since the last test may fail.
|
||||
fs.copyFile(src, dest, common.mustSucceed(() => {
|
||||
verify(src, dest);
|
||||
|
||||
// Copy asynchronously with flags.
|
||||
fs.copyFile(src, dest, COPYFILE_EXCL, common.mustCall((err) => {
|
||||
if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST
|
||||
assert.strictEqual(err.message,
|
||||
'ENOENT: no such file or directory, copyfile ' +
|
||||
`'${src}' -> '${dest}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'copyfile');
|
||||
} else {
|
||||
assert.strictEqual(err.message,
|
||||
'EEXIST: file already exists, copyfile ' +
|
||||
`'${src}' -> '${dest}'`);
|
||||
assert.strictEqual(err.errno, UV_EEXIST);
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.syscall, 'copyfile');
|
||||
}
|
||||
}));
|
||||
}));
|
||||
|
||||
// Throws if callback is not a function.
|
||||
assert.throws(() => {
|
||||
fs.copyFile(src, dest, 0, 0);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
|
||||
// Throws if the source path is not a string.
|
||||
[false, 1, {}, [], null, undefined].forEach((i) => {
|
||||
assert.throws(
|
||||
() => fs.copyFile(i, dest, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /src/
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.copyFile(src, i, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /dest/
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.copyFileSync(i, dest),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /src/
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.copyFileSync(src, i),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /dest/
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(src, dest, 'r');
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /mode/
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(src, dest, 8);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.copyFile(src, dest, 'r', common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /mode/
|
||||
});
|
||||
850
test/js/node/test/parallel/test-fs-error-messages.js
Normal file
850
test/js/node/test/parallel/test-fs-error-messages.js
Normal file
@@ -0,0 +1,850 @@
|
||||
// Flags: --expose-internals
|
||||
// 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 fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
|
||||
const nonexistentFile = tmpdir.resolve('non-existent');
|
||||
const nonexistentDir = tmpdir.resolve('non-existent', 'foo', 'bar');
|
||||
const existingFile = tmpdir.resolve('existingFile.js');
|
||||
const existingFile2 = tmpdir.resolve('existingFile2.js');
|
||||
const existingDir = tmpdir.resolve('dir');
|
||||
const existingDir2 = fixtures.path('keys');
|
||||
fs.mkdirSync(existingDir);
|
||||
fs.writeFileSync(existingFile, 'test', 'utf-8');
|
||||
fs.writeFileSync(existingFile2, 'test', 'utf-8');
|
||||
|
||||
|
||||
const { COPYFILE_EXCL } = fs.constants;
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const {
|
||||
UV_EBADF,
|
||||
UV_EEXIST,
|
||||
UV_EINVAL,
|
||||
UV_ENOENT,
|
||||
UV_ENOTDIR,
|
||||
UV_ENOTEMPTY,
|
||||
UV_EPERM
|
||||
} = internalBinding('uv');
|
||||
|
||||
// Template tag function for escaping special characters in strings so that:
|
||||
// new RegExp(re`${str}`).test(str) === true
|
||||
function re(literals, ...values) {
|
||||
const escapeRE = /[\\^$.*+?()[\]{}|=!<>:-]/g;
|
||||
let result = literals[0].replace(escapeRE, '\\$&');
|
||||
for (const [i, value] of values.entries()) {
|
||||
result += value.replace(escapeRE, '\\$&');
|
||||
result += literals[i + 1].replace(escapeRE, '\\$&');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// stat
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, stat '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'stat');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.stat(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.statSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// lstat
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, lstat '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'lstat');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.lstat(nonexistentFile, common.mustCall(validateError));
|
||||
assert.throws(
|
||||
() => fs.lstatSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// fstat
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, fstat');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'fstat');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.fstat(fd, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.fstatSync(fd),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// realpath
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, lstat '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'lstat');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.realpath(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.realpathSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// native realpath
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, realpath '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'realpath');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.realpath.native(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.realpathSync.native(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// readlink
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, readlink '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'readlink');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.readlink(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.readlinkSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// Link nonexistent file
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
// Could be resolved to an absolute path
|
||||
assert.ok(err.dest.endsWith('foo'),
|
||||
`expect ${err.dest} to end with 'foo'`);
|
||||
const regexp = new RegExp('^ENOENT: no such file or directory, link ' +
|
||||
re`'${nonexistentFile}' -> ` + '\'.*foo\'');
|
||||
assert.match(err.message, regexp);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'link');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.link(nonexistentFile, 'foo', common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.linkSync(nonexistentFile, 'foo'),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// link existing file
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(existingFile, err.path);
|
||||
assert.strictEqual(existingFile2, err.dest);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`EEXIST: file already exists, link '${existingFile}' -> ` +
|
||||
`'${existingFile2}'`);
|
||||
assert.strictEqual(err.errno, UV_EEXIST);
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.syscall, 'link');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.link(existingFile, existingFile2, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.linkSync(existingFile, existingFile2),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// symlink
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(existingFile, err.path);
|
||||
assert.strictEqual(existingFile2, err.dest);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`EEXIST: file already exists, symlink '${existingFile}' -> ` +
|
||||
`'${existingFile2}'`);
|
||||
assert.strictEqual(err.errno, UV_EEXIST);
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.syscall, 'symlink');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.symlink(existingFile, existingFile2, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.symlinkSync(existingFile, existingFile2),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// unlink
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, unlink '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'unlink');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.unlink(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.unlinkSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// rename
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
// Could be resolved to an absolute path
|
||||
assert.ok(err.dest.endsWith('foo'),
|
||||
`expect ${err.dest} to end with 'foo'`);
|
||||
const regexp = new RegExp('ENOENT: no such file or directory, rename ' +
|
||||
re`'${nonexistentFile}' -> ` + '\'.*foo\'');
|
||||
assert.match(err.message, regexp);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'rename');
|
||||
return true;
|
||||
};
|
||||
|
||||
const destFile = tmpdir.resolve('foo');
|
||||
fs.rename(nonexistentFile, destFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.renameSync(nonexistentFile, destFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// Rename non-empty directory
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(existingDir, err.path);
|
||||
assert.strictEqual(existingDir2, err.dest);
|
||||
assert.strictEqual(err.syscall, 'rename');
|
||||
// Could be ENOTEMPTY, EEXIST, or EPERM, depending on the platform
|
||||
if (err.code === 'ENOTEMPTY') {
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOTEMPTY: directory not empty, rename '${existingDir}' -> ` +
|
||||
`'${existingDir2}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOTEMPTY);
|
||||
} else if (err.code === 'EXDEV') { // Not on the same mounted filesystem
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`EXDEV: cross-device link not permitted, rename '${existingDir}' -> ` +
|
||||
`'${existingDir2}'`);
|
||||
} else if (err.code === 'EEXIST') { // smartos and aix
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`EEXIST: file already exists, rename '${existingDir}' -> ` +
|
||||
`'${existingDir2}'`);
|
||||
assert.strictEqual(err.errno, UV_EEXIST);
|
||||
} else { // windows
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`EPERM: operation not permitted, rename '${existingDir}' -> ` +
|
||||
`'${existingDir2}'`);
|
||||
assert.strictEqual(err.errno, UV_EPERM);
|
||||
assert.strictEqual(err.code, 'EPERM');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.rename(existingDir, existingDir2, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.renameSync(existingDir, existingDir2),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// rmdir
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, rmdir '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'rmdir');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.rmdir(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.rmdirSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// rmdir a file
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(existingFile, err.path);
|
||||
assert.strictEqual(err.syscall, 'rmdir');
|
||||
if (err.code === 'ENOTDIR') {
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOTDIR: not a directory, rmdir '${existingFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOTDIR);
|
||||
} else { // windows
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, rmdir '${existingFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.rmdir(existingFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.rmdirSync(existingFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// mkdir
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(existingFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`EEXIST: file already exists, mkdir '${existingFile}'`);
|
||||
assert.strictEqual(err.errno, UV_EEXIST);
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.syscall, 'mkdir');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.mkdir(existingFile, 0o666, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.mkdirSync(existingFile, 0o666),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// chmod
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, chmod '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'chmod');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.chmod(nonexistentFile, 0o666, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.chmodSync(nonexistentFile, 0o666),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// open
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, open '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'open');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.open(nonexistentFile, 'r', 0o666, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.openSync(nonexistentFile, 'r', 0o666),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// close
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, close');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'close');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.close(fd, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.closeSync(fd),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// readFile
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, open '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'open');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.readFile(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.readFileSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// readdir
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, scandir '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'scandir');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.readdir(nonexistentFile, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.readdirSync(nonexistentFile),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// ftruncate
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.syscall, 'ftruncate');
|
||||
// Could be EBADF or EINVAL, depending on the platform
|
||||
if (err.code === 'EBADF') {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, ftruncate');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
} else {
|
||||
assert.strictEqual(err.message, 'EINVAL: invalid argument, ftruncate');
|
||||
assert.strictEqual(err.errno, UV_EINVAL);
|
||||
assert.strictEqual(err.code, 'EINVAL');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.ftruncate(fd, 4, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.ftruncateSync(fd, 4),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// fdatasync
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, fdatasync');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'fdatasync');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.fdatasync(fd, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.fdatasyncSync(fd),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// fsync
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, fsync');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'fsync');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.fsync(fd, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.fsyncSync(fd),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// chown
|
||||
if (!common.isWindows) {
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, chown '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'chown');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.chown(nonexistentFile, process.getuid(), process.getgid(),
|
||||
common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.chownSync(nonexistentFile,
|
||||
process.getuid(), process.getgid()),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// utimes
|
||||
if (!common.isAIX) {
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(nonexistentFile, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, utime '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'utime');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.utimes(nonexistentFile, new Date(), new Date(),
|
||||
common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.utimesSync(nonexistentFile, new Date(), new Date()),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// mkdtemp
|
||||
{
|
||||
const validateError = (err) => {
|
||||
const pathPrefix = new RegExp('^' + re`${nonexistentDir}`);
|
||||
assert.match(err.path, pathPrefix);
|
||||
|
||||
const prefix = new RegExp('^ENOENT: no such file or directory, mkdtemp ' +
|
||||
re`'${nonexistentDir}`);
|
||||
assert.match(err.message, prefix);
|
||||
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'mkdtemp');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.mkdtemp(nonexistentDir, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.mkdtempSync(nonexistentDir),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// Check copyFile with invalid modes.
|
||||
{
|
||||
const validateError = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
};
|
||||
|
||||
assert.throws(
|
||||
() => fs.copyFile(existingFile, nonexistentFile, -1, () => {}),
|
||||
validateError
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.copyFileSync(existingFile, nonexistentFile, -1),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// copyFile: destination exists but the COPYFILE_EXCL flag is provided.
|
||||
{
|
||||
const validateError = (err) => {
|
||||
if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST
|
||||
assert.strictEqual(err.message,
|
||||
'ENOENT: no such file or directory, copyfile ' +
|
||||
`'${existingFile}' -> '${existingFile2}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'copyfile');
|
||||
} else {
|
||||
assert.strictEqual(err.message,
|
||||
'EEXIST: file already exists, copyfile ' +
|
||||
`'${existingFile}' -> '${existingFile2}'`);
|
||||
assert.strictEqual(err.errno, UV_EEXIST);
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.syscall, 'copyfile');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.copyFile(existingFile, existingFile2, COPYFILE_EXCL,
|
||||
common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.copyFileSync(existingFile, existingFile2, COPYFILE_EXCL),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// copyFile: the source does not exist.
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message,
|
||||
'ENOENT: no such file or directory, copyfile ' +
|
||||
`'${nonexistentFile}' -> '${existingFile2}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'copyfile');
|
||||
return true;
|
||||
};
|
||||
|
||||
fs.copyFile(nonexistentFile, existingFile2, COPYFILE_EXCL,
|
||||
common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.copyFileSync(nonexistentFile, existingFile2, COPYFILE_EXCL),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
// read
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, read');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'read');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
const buf = Buffer.alloc(5);
|
||||
fs.read(fd, buf, 0, 1, 1, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.readSync(fd, buf, 0, 1, 1),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// fchmod
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, fchmod');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'fchmod');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.fchmod(fd, 0o666, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.fchmodSync(fd, 0o666),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// fchown
|
||||
if (!common.isWindows) {
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, fchown');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'fchown');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.fchown(fd, process.getuid(), process.getgid(),
|
||||
common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.fchownSync(fd, process.getuid(), process.getgid()),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// write buffer
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, write');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'write');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
const buf = Buffer.alloc(5);
|
||||
fs.write(fd, buf, 0, 1, 1, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.writeSync(fd, buf, 0, 1, 1),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// write string
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, write');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'write');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.write(fd, 'test', 1, common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.writeSync(fd, 'test', 1),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// futimes
|
||||
if (!common.isAIX) {
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.message, 'EBADF: bad file descriptor, futime');
|
||||
assert.strictEqual(err.errno, UV_EBADF);
|
||||
assert.strictEqual(err.code, 'EBADF');
|
||||
assert.strictEqual(err.syscall, 'futime');
|
||||
return true;
|
||||
};
|
||||
|
||||
common.runWithInvalidFD((fd) => {
|
||||
fs.futimes(fd, new Date(), new Date(), common.mustCall(validateError));
|
||||
|
||||
assert.throws(
|
||||
() => fs.futimesSync(fd, new Date(), new Date()),
|
||||
validateError
|
||||
);
|
||||
});
|
||||
}
|
||||
83
test/js/node/test/parallel/test-fs-fchmod.js
Normal file
83
test/js/node/test/parallel/test-fs-fchmod.js
Normal file
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
// This test ensures that input for fchmod is valid, testing for valid
|
||||
// inputs for fd and mode
|
||||
|
||||
// Check input type
|
||||
[false, null, undefined, {}, [], ''].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "fd" argument must be of type number.' +
|
||||
common.invalidArgTypeHelper(input)
|
||||
};
|
||||
assert.throws(() => fs.fchmod(input, 0o666, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(input, 0o666), errObj);
|
||||
});
|
||||
|
||||
|
||||
[false, null, {}, []].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
};
|
||||
assert.throws(() => fs.fchmod(1, input), errObj);
|
||||
assert.throws(() => fs.fchmodSync(1, input), errObj);
|
||||
});
|
||||
|
||||
assert.throws(() => fs.fchmod(1, '123x'), {
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
|
||||
[-1, 2 ** 32].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "fd" is out of range. It must be >= 0 && <= ' +
|
||||
`2147483647. Received ${input}`
|
||||
};
|
||||
assert.throws(() => fs.fchmod(input, 0o666, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(input, 0o666), errObj);
|
||||
});
|
||||
|
||||
[-1, 2 ** 32].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "mode" is out of range. It must be >= 0 && <= ' +
|
||||
`4294967295. Received ${input}`
|
||||
};
|
||||
|
||||
assert.throws(() => fs.fchmod(1, input, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(1, input), errObj);
|
||||
});
|
||||
|
||||
[NaN, Infinity].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "fd" is out of range. It must be an integer. ' +
|
||||
`Received ${input}`
|
||||
};
|
||||
assert.throws(() => fs.fchmod(input, 0o666, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(input, 0o666), errObj);
|
||||
errObj.message = errObj.message.replace('fd', 'mode');
|
||||
assert.throws(() => fs.fchmod(1, input, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(1, input), errObj);
|
||||
});
|
||||
|
||||
[1.5].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "fd" is out of range. It must be an integer. ' +
|
||||
`Received ${input}`
|
||||
};
|
||||
assert.throws(() => fs.fchmod(input, 0o666, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(input, 0o666), errObj);
|
||||
errObj.message = errObj.message.replace('fd', 'mode');
|
||||
assert.throws(() => fs.fchmod(1, input, () => {}), errObj);
|
||||
assert.throws(() => fs.fchmodSync(1, input), errObj);
|
||||
});
|
||||
60
test/js/node/test/parallel/test-fs-fchown.js
Normal file
60
test/js/node/test/parallel/test-fs-fchown.js
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
function testFd(input, errObj) {
|
||||
assert.throws(() => fs.fchown(input, 0, 0, () => {}), errObj);
|
||||
assert.throws(() => fs.fchownSync(input, 0, 0), errObj);
|
||||
}
|
||||
|
||||
function testUid(input, errObj) {
|
||||
assert.throws(() => fs.fchown(1, input), errObj);
|
||||
assert.throws(() => fs.fchownSync(1, input), errObj);
|
||||
}
|
||||
|
||||
function testGid(input, errObj) {
|
||||
assert.throws(() => fs.fchown(1, 1, input), errObj);
|
||||
assert.throws(() => fs.fchownSync(1, 1, input), errObj);
|
||||
}
|
||||
|
||||
['', false, null, undefined, {}, []].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /fd|uid|gid/
|
||||
};
|
||||
testFd(input, errObj);
|
||||
testUid(input, errObj);
|
||||
testGid(input, errObj);
|
||||
});
|
||||
|
||||
[Infinity, NaN].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "fd" is out of range. It must be an integer. ' +
|
||||
`Received ${input}`
|
||||
};
|
||||
testFd(input, errObj);
|
||||
errObj.message = errObj.message.replace('fd', 'uid');
|
||||
testUid(input, errObj);
|
||||
errObj.message = errObj.message.replace('uid', 'gid');
|
||||
testGid(input, errObj);
|
||||
});
|
||||
|
||||
[-2, 2 ** 32].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "fd" is out of range. It must be ' +
|
||||
`>= 0 && <= 2147483647. Received ${input}`
|
||||
};
|
||||
testFd(input, errObj);
|
||||
errObj.message = 'The value of "uid" is out of range. It must be >= -1 && ' +
|
||||
`<= 4294967295. Received ${input}`;
|
||||
testUid(input, errObj);
|
||||
errObj.message = errObj.message.replace('uid', 'gid');
|
||||
testGid(input, errObj);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
(async () => {
|
||||
const filehandle = await fs.open(__filename);
|
||||
|
||||
assert.notStrictEqual(filehandle.fd, -1);
|
||||
await filehandle.close();
|
||||
assert.strictEqual(filehandle.fd, -1);
|
||||
|
||||
// Open another file handle first. This would typically receive the fd
|
||||
// that `filehandle` previously used. In earlier versions of Node.js, the
|
||||
// .stat() call would then succeed because it still used the original fd;
|
||||
// See https://github.com/nodejs/node/issues/31361 for more details.
|
||||
const otherFilehandle = await fs.open(process.execPath);
|
||||
|
||||
await assert.rejects(() => filehandle.stat(), {
|
||||
code: 'EBADF',
|
||||
syscall: 'fstat'
|
||||
});
|
||||
|
||||
await otherFilehandle.close();
|
||||
})().then(common.mustCall());
|
||||
40
test/js/node/test/parallel/test-fs-filehandle.js
Normal file
40
test/js/node/test/parallel/test-fs-filehandle.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// Flags: --expose-gc --no-warnings --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const fs = internalBinding('fs');
|
||||
const { stringToFlags } = require('internal/fs/utils');
|
||||
|
||||
// Verifies that the FileHandle object is garbage collected and that a
|
||||
// warning is emitted if it is not closed.
|
||||
|
||||
let fdnum;
|
||||
{
|
||||
const ctx = {};
|
||||
fdnum = fs.openFileHandle(path.toNamespacedPath(__filename),
|
||||
stringToFlags('r'), 0o666, undefined, ctx).fd;
|
||||
assert.strictEqual(ctx.errno, undefined);
|
||||
}
|
||||
|
||||
const deprecationWarning =
|
||||
'Closing a FileHandle object on garbage collection is deprecated. ' +
|
||||
'Please close FileHandle objects explicitly using ' +
|
||||
'FileHandle.prototype.close(). In the future, an error will be ' +
|
||||
'thrown if a file descriptor is closed during garbage collection.';
|
||||
|
||||
common.expectWarning({
|
||||
'internal/test/binding': [
|
||||
'These APIs are for internal testing only. Do not use them.',
|
||||
],
|
||||
'Warning': [
|
||||
`Closing file descriptor ${fdnum} on garbage collection`,
|
||||
],
|
||||
'DeprecationWarning': [[deprecationWarning, 'DEP0137']]
|
||||
});
|
||||
|
||||
global.gc();
|
||||
|
||||
setTimeout(() => {}, 10);
|
||||
66
test/js/node/test/parallel/test-fs-lchmod.js
Normal file
66
test/js/node/test/parallel/test-fs-lchmod.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const { promises } = fs;
|
||||
const f = __filename;
|
||||
|
||||
// This test ensures that input for lchmod is valid, testing for valid
|
||||
// inputs for path, mode and callback
|
||||
|
||||
if (!common.isMacOS) {
|
||||
common.skip('lchmod is only available on macOS');
|
||||
}
|
||||
|
||||
// Check callback
|
||||
assert.throws(() => fs.lchmod(f), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => fs.lchmod(), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => fs.lchmod(f, {}), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
|
||||
// Check path
|
||||
[false, 1, {}, [], null, undefined].forEach((i) => {
|
||||
assert.throws(
|
||||
() => fs.lchmod(i, 0o777, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.lchmodSync(i),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Check mode
|
||||
[false, null, {}, []].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
};
|
||||
|
||||
assert.rejects(promises.lchmod(f, input, () => {}), errObj).then(common.mustCall());
|
||||
assert.throws(() => fs.lchmodSync(f, input), errObj);
|
||||
});
|
||||
|
||||
assert.throws(() => fs.lchmod(f, '123x', common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
assert.throws(() => fs.lchmodSync(f, '123x'), {
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
});
|
||||
|
||||
[-1, 2 ** 32].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "mode" is out of range. It must be >= 0 && <= ' +
|
||||
`4294967295. Received ${input}`
|
||||
};
|
||||
|
||||
assert.rejects(promises.lchmod(f, input, () => {}), errObj).then(common.mustCall());
|
||||
assert.throws(() => fs.lchmodSync(f, input), errObj);
|
||||
});
|
||||
64
test/js/node/test/parallel/test-fs-lchown.js
Normal file
64
test/js/node/test/parallel/test-fs-lchown.js
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { promises } = fs;
|
||||
|
||||
// Validate the path argument.
|
||||
[false, 1, {}, [], null, undefined].forEach((i) => {
|
||||
const err = { name: 'TypeError', code: 'ERR_INVALID_ARG_TYPE' };
|
||||
|
||||
assert.throws(() => fs.lchown(i, 1, 1, common.mustNotCall()), err);
|
||||
assert.throws(() => fs.lchownSync(i, 1, 1), err);
|
||||
promises.lchown(false, 1, 1)
|
||||
.then(common.mustNotCall())
|
||||
.catch(common.expectsError(err));
|
||||
});
|
||||
|
||||
// Validate the uid and gid arguments.
|
||||
[false, 'test', {}, [], null, undefined].forEach((i) => {
|
||||
const err = { name: 'TypeError', code: 'ERR_INVALID_ARG_TYPE' };
|
||||
|
||||
assert.throws(
|
||||
() => fs.lchown('not_a_file_that_exists', i, 1, common.mustNotCall()),
|
||||
err
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.lchown('not_a_file_that_exists', 1, i, common.mustNotCall()),
|
||||
err
|
||||
);
|
||||
assert.throws(() => fs.lchownSync('not_a_file_that_exists', i, 1), err);
|
||||
assert.throws(() => fs.lchownSync('not_a_file_that_exists', 1, i), err);
|
||||
|
||||
promises.lchown('not_a_file_that_exists', i, 1)
|
||||
.then(common.mustNotCall())
|
||||
.catch(common.expectsError(err));
|
||||
|
||||
promises.lchown('not_a_file_that_exists', 1, i)
|
||||
.then(common.mustNotCall())
|
||||
.catch(common.expectsError(err));
|
||||
});
|
||||
|
||||
// Validate the callback argument.
|
||||
[false, 1, 'test', {}, [], null, undefined].forEach((i) => {
|
||||
assert.throws(() => fs.lchown('not_a_file_that_exists', 1, 1, i), {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
if (!common.isWindows) {
|
||||
const testFile = tmpdir.resolve(path.basename(__filename));
|
||||
const uid = process.geteuid();
|
||||
const gid = process.getegid();
|
||||
|
||||
tmpdir.refresh();
|
||||
fs.copyFileSync(__filename, testFile);
|
||||
fs.lchownSync(testFile, uid, gid);
|
||||
fs.lchown(testFile, uid, gid, common.mustSucceed(async (err) => {
|
||||
await promises.lchown(testFile, uid, gid);
|
||||
}));
|
||||
}
|
||||
40
test/js/node/test/parallel/test-fs-mkdir-mode-mask.js
Normal file
40
test/js/node/test/parallel/test-fs-mkdir-mode-mask.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
// This tests that the lower bits of mode > 0o777 still works in fs.mkdir().
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
if (common.isWindows) {
|
||||
common.skip('mode is not supported in mkdir on Windows');
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = 0o644;
|
||||
const maskToIgnore = 0o10000;
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
function test(mode, asString) {
|
||||
const suffix = asString ? 'str' : 'num';
|
||||
const input = asString ?
|
||||
(mode | maskToIgnore).toString(8) : (mode | maskToIgnore);
|
||||
|
||||
{
|
||||
const dir = tmpdir.resolve(`mkdirSync-${suffix}`);
|
||||
fs.mkdirSync(dir, input);
|
||||
assert.strictEqual(fs.statSync(dir).mode & 0o777, mode);
|
||||
}
|
||||
|
||||
{
|
||||
const dir = tmpdir.resolve(`mkdir-${suffix}`);
|
||||
fs.mkdir(dir, input, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.statSync(dir).mode & 0o777, mode);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
test(mode, true);
|
||||
test(mode, false);
|
||||
37
test/js/node/test/parallel/test-fs-mkdir-rmdir.js
Normal file
37
test/js/node/test/parallel/test-fs-mkdir-rmdir.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const d = tmpdir.resolve('dir');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
// Make sure the directory does not exist
|
||||
assert(!fs.existsSync(d));
|
||||
// Create the directory now
|
||||
fs.mkdirSync(d);
|
||||
// Make sure the directory exists
|
||||
assert(fs.existsSync(d));
|
||||
// Try creating again, it should fail with EEXIST
|
||||
assert.throws(function() {
|
||||
fs.mkdirSync(d);
|
||||
}, /EEXIST: file already exists, mkdir/);
|
||||
// Remove the directory now
|
||||
fs.rmdirSync(d);
|
||||
// Make sure the directory does not exist
|
||||
assert(!fs.existsSync(d));
|
||||
|
||||
// Similarly test the Async version
|
||||
fs.mkdir(d, 0o666, common.mustSucceed(() => {
|
||||
fs.mkdir(d, 0o666, common.mustCall(function(err) {
|
||||
assert.strictEqual(this, undefined);
|
||||
assert.ok(err, 'got no error');
|
||||
assert.match(err.message, /^EEXIST/);
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.path, d);
|
||||
|
||||
fs.rmdir(d, assert.ifError);
|
||||
}));
|
||||
}));
|
||||
363
test/js/node/test/parallel/test-fs-mkdir.js
Normal file
363
test/js/node/test/parallel/test-fs-mkdir.js
Normal file
@@ -0,0 +1,363 @@
|
||||
// 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 fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
let dirc = 0;
|
||||
function nextdir() {
|
||||
return `test${++dirc}`;
|
||||
}
|
||||
|
||||
// fs.mkdir creates directory using assigned path
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
|
||||
fs.mkdir(pathname, common.mustCall(function(err) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
}));
|
||||
}
|
||||
|
||||
// fs.mkdir creates directory with assigned mode value
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
|
||||
fs.mkdir(pathname, 0o777, common.mustCall(function(err) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
}));
|
||||
}
|
||||
|
||||
// fs.mkdir creates directory with mode passed as an options object
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ mode: 0o777 }), common.mustCall(function(err) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
}));
|
||||
}
|
||||
|
||||
// fs.mkdirSync creates directory with mode passed as an options object
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
|
||||
fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ mode: 0o777 }));
|
||||
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
}
|
||||
|
||||
// mkdirSync successfully creates directory from given path
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
|
||||
fs.mkdirSync(pathname);
|
||||
|
||||
const exists = fs.existsSync(pathname);
|
||||
assert.strictEqual(exists, true);
|
||||
}
|
||||
|
||||
// mkdirSync and mkdir require path to be a string, buffer or url.
|
||||
// Anything else generates an error.
|
||||
[false, 1, {}, [], null, undefined].forEach((i) => {
|
||||
assert.throws(
|
||||
() => fs.mkdir(i, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.mkdirSync(i),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// mkdirpSync when both top-level, and sub-folders do not exist.
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir(), nextdir());
|
||||
|
||||
fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
|
||||
const exists = fs.existsSync(pathname);
|
||||
assert.strictEqual(exists, true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
}
|
||||
|
||||
// mkdirpSync when folder already exists.
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir(), nextdir());
|
||||
|
||||
fs.mkdirSync(pathname, { recursive: true });
|
||||
// Should not cause an error.
|
||||
fs.mkdirSync(pathname, { recursive: true });
|
||||
|
||||
const exists = fs.existsSync(pathname);
|
||||
assert.strictEqual(exists, true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
}
|
||||
|
||||
// mkdirpSync ../
|
||||
{
|
||||
const pathname = `${tmpdir.path}/${nextdir()}/../${nextdir()}/${nextdir()}`;
|
||||
fs.mkdirSync(pathname, { recursive: true });
|
||||
const exists = fs.existsSync(pathname);
|
||||
assert.strictEqual(exists, true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
}
|
||||
|
||||
// mkdirpSync when path is a file.
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir(), nextdir());
|
||||
|
||||
fs.mkdirSync(path.dirname(pathname));
|
||||
fs.writeFileSync(pathname, '', 'utf8');
|
||||
|
||||
assert.throws(
|
||||
() => { fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); },
|
||||
{
|
||||
code: 'EEXIST',
|
||||
message: /EEXIST: .*mkdir/,
|
||||
name: 'Error',
|
||||
syscall: 'mkdir',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// mkdirpSync when part of the path is a file.
|
||||
{
|
||||
const filename = tmpdir.resolve(nextdir(), nextdir());
|
||||
const pathname = path.join(filename, nextdir(), nextdir());
|
||||
|
||||
fs.mkdirSync(path.dirname(filename));
|
||||
fs.writeFileSync(filename, '', 'utf8');
|
||||
|
||||
assert.throws(
|
||||
() => { fs.mkdirSync(pathname, { recursive: true }); },
|
||||
{
|
||||
code: 'ENOTDIR',
|
||||
message: /ENOTDIR: .*mkdir/,
|
||||
name: 'Error',
|
||||
syscall: 'mkdir',
|
||||
path: pathname // See: https://github.com/nodejs/node/issues/28015
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// `mkdirp` when folder does not yet exist.
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir(), nextdir());
|
||||
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
}));
|
||||
}
|
||||
|
||||
// `mkdirp` when path is a file.
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir(), nextdir());
|
||||
|
||||
fs.mkdirSync(path.dirname(pathname));
|
||||
fs.writeFileSync(pathname, '', 'utf8');
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
assert.strictEqual(err.syscall, 'mkdir');
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), false);
|
||||
}));
|
||||
}
|
||||
|
||||
// `mkdirp` when part of the path is a file.
|
||||
{
|
||||
const filename = tmpdir.resolve(nextdir(), nextdir());
|
||||
const pathname = path.join(filename, nextdir(), nextdir());
|
||||
|
||||
fs.mkdirSync(path.dirname(filename));
|
||||
fs.writeFileSync(filename, '', 'utf8');
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ENOTDIR');
|
||||
assert.strictEqual(err.syscall, 'mkdir');
|
||||
assert.strictEqual(fs.existsSync(pathname), false);
|
||||
// See: https://github.com/nodejs/node/issues/28015
|
||||
// The path field varies slightly in Windows errors, vs., other platforms
|
||||
// see: https://github.com/libuv/libuv/issues/2661, for this reason we
|
||||
// use startsWith() rather than comparing to the full "pathname".
|
||||
assert(err.path.startsWith(filename));
|
||||
}));
|
||||
}
|
||||
|
||||
// mkdirpSync dirname loop
|
||||
// XXX: windows and smartos have issues removing a directory that you're in.
|
||||
if (common.isMainThread && (common.isLinux || common.isMacOS)) {
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
fs.mkdirSync(pathname);
|
||||
process.chdir(pathname);
|
||||
fs.rmdirSync(pathname);
|
||||
assert.throws(
|
||||
() => { fs.mkdirSync('X', common.mustNotMutateObjectDeep({ recursive: true })); },
|
||||
{
|
||||
code: 'ENOENT',
|
||||
message: /ENOENT: .*mkdir/,
|
||||
name: 'Error',
|
||||
syscall: 'mkdir',
|
||||
}
|
||||
);
|
||||
fs.mkdir('X', common.mustNotMutateObjectDeep({ recursive: true }), (err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'mkdir');
|
||||
});
|
||||
}
|
||||
|
||||
// mkdirSync and mkdir require options.recursive to be a boolean.
|
||||
// Anything else generates an error.
|
||||
{
|
||||
const pathname = tmpdir.resolve(nextdir());
|
||||
['', 1, {}, [], null, Symbol('test'), () => {}].forEach((recursive) => {
|
||||
const received = common.invalidArgTypeHelper(recursive);
|
||||
assert.throws(
|
||||
() => fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive }), common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options.recursive" property must be of type boolean.' +
|
||||
received
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive })),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options.recursive" property must be of type boolean.' +
|
||||
received
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// `mkdirp` returns first folder created, when all folders are new.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const firstPathCreated = tmpdir.resolve(dir1);
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err, result) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(result, path.toNamespacedPath(firstPathCreated));
|
||||
}));
|
||||
}
|
||||
|
||||
// `mkdirp` returns first folder created, when last folder is new.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
fs.mkdirSync(tmpdir.resolve(dir1));
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err, result) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(result, path.toNamespacedPath(pathname));
|
||||
}));
|
||||
}
|
||||
|
||||
// `mkdirp` returns undefined, when no new folders are created.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
fs.mkdirSync(tmpdir.resolve(dir1, dir2), common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
fs.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall(function(err, path) {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(path, undefined);
|
||||
}));
|
||||
}
|
||||
|
||||
// `mkdirp.sync` returns first folder created, when all folders are new.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const firstPathCreated = tmpdir.resolve(dir1);
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
const p = fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(p, path.toNamespacedPath(firstPathCreated));
|
||||
}
|
||||
|
||||
// `mkdirp.sync` returns first folder created, when last folder is new.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
fs.mkdirSync(tmpdir.resolve(dir1), common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
const p = fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(p, path.toNamespacedPath(pathname));
|
||||
}
|
||||
|
||||
// `mkdirp.sync` returns undefined, when no new folders are created.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
fs.mkdirSync(tmpdir.resolve(dir1, dir2), common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
const p = fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(p, undefined);
|
||||
}
|
||||
|
||||
// `mkdirp.promises` returns first folder created, when all folders are new.
|
||||
{
|
||||
const dir1 = nextdir();
|
||||
const dir2 = nextdir();
|
||||
const firstPathCreated = tmpdir.resolve(dir1);
|
||||
const pathname = tmpdir.resolve(dir1, dir2);
|
||||
async function testCase() {
|
||||
const p = await fs.promises.mkdir(pathname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(pathname), true);
|
||||
assert.strictEqual(fs.statSync(pathname).isDirectory(), true);
|
||||
assert.strictEqual(p, path.toNamespacedPath(firstPathCreated));
|
||||
}
|
||||
testCase();
|
||||
}
|
||||
|
||||
// Keep the event loop alive so the async mkdir() requests
|
||||
// have a chance to run (since they don't ref the event loop).
|
||||
process.nextTick(() => {});
|
||||
107
test/js/node/test/parallel/test-fs-mkdtemp.js
Normal file
107
test/js/node/test/parallel/test-fs-mkdtemp.js
Normal file
@@ -0,0 +1,107 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
function handler(err, folder) {
|
||||
assert.ifError(err);
|
||||
assert(fs.existsSync(folder));
|
||||
assert.strictEqual(this, undefined);
|
||||
}
|
||||
|
||||
// Test with plain string
|
||||
{
|
||||
const tmpFolder = fs.mkdtempSync(tmpdir.resolve('foo.'));
|
||||
|
||||
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
|
||||
assert(fs.existsSync(tmpFolder));
|
||||
|
||||
const utf8 = fs.mkdtempSync(tmpdir.resolve('\u0222abc.'));
|
||||
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
|
||||
Buffer.byteLength('\u0222abc.XXXXXX'));
|
||||
assert(fs.existsSync(utf8));
|
||||
|
||||
fs.mkdtemp(tmpdir.resolve('bar.'), common.mustCall(handler));
|
||||
|
||||
// Same test as above, but making sure that passing an options object doesn't
|
||||
// affect the way the callback function is handled.
|
||||
fs.mkdtemp(tmpdir.resolve('bar.'), {}, common.mustCall(handler));
|
||||
|
||||
const warningMsg = 'mkdtemp() templates ending with X are not portable. ' +
|
||||
'For details see: https://nodejs.org/api/fs.html';
|
||||
common.expectWarning('Warning', warningMsg);
|
||||
fs.mkdtemp(tmpdir.resolve('bar.X'), common.mustCall(handler));
|
||||
}
|
||||
|
||||
// Test with URL object
|
||||
{
|
||||
const tmpFolder = fs.mkdtempSync(tmpdir.fileURL('foo.'));
|
||||
|
||||
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
|
||||
assert(fs.existsSync(tmpFolder));
|
||||
|
||||
const utf8 = fs.mkdtempSync(tmpdir.fileURL('\u0222abc.'));
|
||||
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
|
||||
Buffer.byteLength('\u0222abc.XXXXXX'));
|
||||
assert(fs.existsSync(utf8));
|
||||
|
||||
fs.mkdtemp(tmpdir.fileURL('bar.'), common.mustCall(handler));
|
||||
|
||||
// Same test as above, but making sure that passing an options object doesn't
|
||||
// affect the way the callback function is handled.
|
||||
fs.mkdtemp(tmpdir.fileURL('bar.'), {}, common.mustCall(handler));
|
||||
|
||||
// Warning fires only once
|
||||
fs.mkdtemp(tmpdir.fileURL('bar.X'), common.mustCall(handler));
|
||||
}
|
||||
|
||||
// Test with Buffer
|
||||
{
|
||||
const tmpFolder = fs.mkdtempSync(Buffer.from(tmpdir.resolve('foo.')));
|
||||
|
||||
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
|
||||
assert(fs.existsSync(tmpFolder));
|
||||
|
||||
const utf8 = fs.mkdtempSync(Buffer.from(tmpdir.resolve('\u0222abc.')));
|
||||
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
|
||||
Buffer.byteLength('\u0222abc.XXXXXX'));
|
||||
assert(fs.existsSync(utf8));
|
||||
|
||||
fs.mkdtemp(Buffer.from(tmpdir.resolve('bar.')), common.mustCall(handler));
|
||||
|
||||
// Same test as above, but making sure that passing an options object doesn't
|
||||
// affect the way the callback function is handled.
|
||||
fs.mkdtemp(Buffer.from(tmpdir.resolve('bar.')), {}, common.mustCall(handler));
|
||||
|
||||
// Warning fires only once
|
||||
fs.mkdtemp(Buffer.from(tmpdir.resolve('bar.X')), common.mustCall(handler));
|
||||
}
|
||||
|
||||
// Test with Uint8Array
|
||||
{
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const tmpFolder = fs.mkdtempSync(encoder.encode(tmpdir.resolve('foo.')));
|
||||
|
||||
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
|
||||
assert(fs.existsSync(tmpFolder));
|
||||
|
||||
const utf8 = fs.mkdtempSync(encoder.encode(tmpdir.resolve('\u0222abc.')));
|
||||
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
|
||||
Buffer.byteLength('\u0222abc.XXXXXX'));
|
||||
assert(fs.existsSync(utf8));
|
||||
|
||||
fs.mkdtemp(encoder.encode(tmpdir.resolve('bar.')), common.mustCall(handler));
|
||||
|
||||
// Same test as above, but making sure that passing an options object doesn't
|
||||
// affect the way the callback function is handled.
|
||||
fs.mkdtemp(encoder.encode(tmpdir.resolve('bar.')), {}, common.mustCall(handler));
|
||||
|
||||
// Warning fires only once
|
||||
fs.mkdtemp(encoder.encode(tmpdir.resolve('bar.X')), common.mustCall(handler));
|
||||
}
|
||||
158
test/js/node/test/parallel/test-fs-null-bytes.js
Normal file
158
test/js/node/test/parallel/test-fs-null-bytes.js
Normal file
@@ -0,0 +1,158 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
function check(async, sync) {
|
||||
const argsSync = Array.prototype.slice.call(arguments, 2);
|
||||
const argsAsync = argsSync.concat(common.mustNotCall());
|
||||
|
||||
if (sync) {
|
||||
assert.throws(
|
||||
() => {
|
||||
sync.apply(null, argsSync);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
}
|
||||
|
||||
if (async) {
|
||||
assert.throws(
|
||||
() => {
|
||||
async.apply(null, argsAsync);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
check(fs.access, fs.accessSync, 'foo\u0000bar');
|
||||
check(fs.access, fs.accessSync, 'foo\u0000bar', fs.constants.F_OK);
|
||||
check(fs.appendFile, fs.appendFileSync, 'foo\u0000bar', 'abc');
|
||||
check(fs.chmod, fs.chmodSync, 'foo\u0000bar', '0644');
|
||||
check(fs.chown, fs.chownSync, 'foo\u0000bar', 12, 34);
|
||||
check(fs.copyFile, fs.copyFileSync, 'foo\u0000bar', 'abc');
|
||||
check(fs.copyFile, fs.copyFileSync, 'abc', 'foo\u0000bar');
|
||||
check(fs.lchown, fs.lchownSync, 'foo\u0000bar', 12, 34);
|
||||
check(fs.link, fs.linkSync, 'foo\u0000bar', 'foobar');
|
||||
check(fs.link, fs.linkSync, 'foobar', 'foo\u0000bar');
|
||||
check(fs.lstat, fs.lstatSync, 'foo\u0000bar');
|
||||
check(fs.mkdir, fs.mkdirSync, 'foo\u0000bar', '0755');
|
||||
check(fs.open, fs.openSync, 'foo\u0000bar', 'r');
|
||||
check(fs.readFile, fs.readFileSync, 'foo\u0000bar');
|
||||
check(fs.readdir, fs.readdirSync, 'foo\u0000bar');
|
||||
check(fs.readdir, fs.readdirSync, 'foo\u0000bar', { recursive: true });
|
||||
check(fs.readlink, fs.readlinkSync, 'foo\u0000bar');
|
||||
check(fs.realpath, fs.realpathSync, 'foo\u0000bar');
|
||||
check(fs.rename, fs.renameSync, 'foo\u0000bar', 'foobar');
|
||||
check(fs.rename, fs.renameSync, 'foobar', 'foo\u0000bar');
|
||||
check(fs.rmdir, fs.rmdirSync, 'foo\u0000bar');
|
||||
check(fs.stat, fs.statSync, 'foo\u0000bar');
|
||||
check(fs.symlink, fs.symlinkSync, 'foo\u0000bar', 'foobar');
|
||||
check(fs.symlink, fs.symlinkSync, 'foobar', 'foo\u0000bar');
|
||||
check(fs.truncate, fs.truncateSync, 'foo\u0000bar');
|
||||
check(fs.unlink, fs.unlinkSync, 'foo\u0000bar');
|
||||
check(null, fs.unwatchFile, 'foo\u0000bar', common.mustNotCall());
|
||||
check(fs.utimes, fs.utimesSync, 'foo\u0000bar', 0, 0);
|
||||
check(null, fs.watch, 'foo\u0000bar', common.mustNotCall());
|
||||
check(null, fs.watchFile, 'foo\u0000bar', common.mustNotCall());
|
||||
check(fs.writeFile, fs.writeFileSync, 'foo\u0000bar', 'abc');
|
||||
|
||||
const fileUrl = new URL('file:///C:/foo\u0000bar');
|
||||
const fileUrl2 = new URL('file:///C:/foo%00bar');
|
||||
|
||||
check(fs.access, fs.accessSync, fileUrl);
|
||||
check(fs.access, fs.accessSync, fileUrl, fs.constants.F_OK);
|
||||
check(fs.appendFile, fs.appendFileSync, fileUrl, 'abc');
|
||||
check(fs.chmod, fs.chmodSync, fileUrl, '0644');
|
||||
check(fs.chown, fs.chownSync, fileUrl, 12, 34);
|
||||
check(fs.copyFile, fs.copyFileSync, fileUrl, 'abc');
|
||||
check(fs.copyFile, fs.copyFileSync, 'abc', fileUrl);
|
||||
check(fs.lchown, fs.lchownSync, fileUrl, 12, 34);
|
||||
check(fs.link, fs.linkSync, fileUrl, 'foobar');
|
||||
check(fs.link, fs.linkSync, 'foobar', fileUrl);
|
||||
check(fs.lstat, fs.lstatSync, fileUrl);
|
||||
check(fs.mkdir, fs.mkdirSync, fileUrl, '0755');
|
||||
check(fs.open, fs.openSync, fileUrl, 'r');
|
||||
check(fs.readFile, fs.readFileSync, fileUrl);
|
||||
check(fs.readdir, fs.readdirSync, fileUrl);
|
||||
check(fs.readdir, fs.readdirSync, fileUrl, { recursive: true });
|
||||
check(fs.readlink, fs.readlinkSync, fileUrl);
|
||||
check(fs.realpath, fs.realpathSync, fileUrl);
|
||||
check(fs.rename, fs.renameSync, fileUrl, 'foobar');
|
||||
check(fs.rename, fs.renameSync, 'foobar', fileUrl);
|
||||
check(fs.rmdir, fs.rmdirSync, fileUrl);
|
||||
check(fs.stat, fs.statSync, fileUrl);
|
||||
check(fs.symlink, fs.symlinkSync, fileUrl, 'foobar');
|
||||
check(fs.symlink, fs.symlinkSync, 'foobar', fileUrl);
|
||||
check(fs.truncate, fs.truncateSync, fileUrl);
|
||||
check(fs.unlink, fs.unlinkSync, fileUrl);
|
||||
check(null, fs.unwatchFile, fileUrl, assert.fail);
|
||||
check(fs.utimes, fs.utimesSync, fileUrl, 0, 0);
|
||||
check(null, fs.watch, fileUrl, assert.fail);
|
||||
check(null, fs.watchFile, fileUrl, assert.fail);
|
||||
check(fs.writeFile, fs.writeFileSync, fileUrl, 'abc');
|
||||
|
||||
check(fs.access, fs.accessSync, fileUrl2);
|
||||
check(fs.access, fs.accessSync, fileUrl2, fs.constants.F_OK);
|
||||
check(fs.appendFile, fs.appendFileSync, fileUrl2, 'abc');
|
||||
check(fs.chmod, fs.chmodSync, fileUrl2, '0644');
|
||||
check(fs.chown, fs.chownSync, fileUrl2, 12, 34);
|
||||
check(fs.copyFile, fs.copyFileSync, fileUrl2, 'abc');
|
||||
check(fs.copyFile, fs.copyFileSync, 'abc', fileUrl2);
|
||||
check(fs.lchown, fs.lchownSync, fileUrl2, 12, 34);
|
||||
check(fs.link, fs.linkSync, fileUrl2, 'foobar');
|
||||
check(fs.link, fs.linkSync, 'foobar', fileUrl2);
|
||||
check(fs.lstat, fs.lstatSync, fileUrl2);
|
||||
check(fs.mkdir, fs.mkdirSync, fileUrl2, '0755');
|
||||
check(fs.open, fs.openSync, fileUrl2, 'r');
|
||||
check(fs.readFile, fs.readFileSync, fileUrl2);
|
||||
check(fs.readdir, fs.readdirSync, fileUrl2);
|
||||
check(fs.readdir, fs.readdirSync, fileUrl2, { recursive: true });
|
||||
check(fs.readlink, fs.readlinkSync, fileUrl2);
|
||||
check(fs.realpath, fs.realpathSync, fileUrl2);
|
||||
check(fs.rename, fs.renameSync, fileUrl2, 'foobar');
|
||||
check(fs.rename, fs.renameSync, 'foobar', fileUrl2);
|
||||
check(fs.rmdir, fs.rmdirSync, fileUrl2);
|
||||
check(fs.stat, fs.statSync, fileUrl2);
|
||||
check(fs.symlink, fs.symlinkSync, fileUrl2, 'foobar');
|
||||
check(fs.symlink, fs.symlinkSync, 'foobar', fileUrl2);
|
||||
check(fs.truncate, fs.truncateSync, fileUrl2);
|
||||
check(fs.unlink, fs.unlinkSync, fileUrl2);
|
||||
check(null, fs.unwatchFile, fileUrl2, assert.fail);
|
||||
check(fs.utimes, fs.utimesSync, fileUrl2, 0, 0);
|
||||
check(null, fs.watch, fileUrl2, assert.fail);
|
||||
check(null, fs.watchFile, fileUrl2, assert.fail);
|
||||
check(fs.writeFile, fs.writeFileSync, fileUrl2, 'abc');
|
||||
|
||||
// An 'error' for exists means that it doesn't exist.
|
||||
// One of many reasons why this file is the absolute worst.
|
||||
fs.exists('foo\u0000bar', common.mustCall((exists) => {
|
||||
assert(!exists);
|
||||
}));
|
||||
assert(!fs.existsSync('foo\u0000bar'));
|
||||
93
test/js/node/test/parallel/test-fs-open-flags.js
Normal file
93
test/js/node/test/parallel/test-fs-open-flags.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
// 0 if not found in fs.constants
|
||||
const { O_APPEND = 0,
|
||||
O_CREAT = 0,
|
||||
O_EXCL = 0,
|
||||
O_RDONLY = 0,
|
||||
O_RDWR = 0,
|
||||
O_SYNC = 0,
|
||||
O_DSYNC = 0,
|
||||
O_TRUNC = 0,
|
||||
O_WRONLY = 0 } = fs.constants;
|
||||
|
||||
const { stringToFlags } = require('internal/fs/utils');
|
||||
|
||||
assert.strictEqual(stringToFlags('r'), O_RDONLY);
|
||||
assert.strictEqual(stringToFlags('r+'), O_RDWR);
|
||||
assert.strictEqual(stringToFlags('rs+'), O_RDWR | O_SYNC);
|
||||
assert.strictEqual(stringToFlags('sr+'), O_RDWR | O_SYNC);
|
||||
assert.strictEqual(stringToFlags('w'), O_TRUNC | O_CREAT | O_WRONLY);
|
||||
assert.strictEqual(stringToFlags('w+'), O_TRUNC | O_CREAT | O_RDWR);
|
||||
assert.strictEqual(stringToFlags('a'), O_APPEND | O_CREAT | O_WRONLY);
|
||||
assert.strictEqual(stringToFlags('a+'), O_APPEND | O_CREAT | O_RDWR);
|
||||
|
||||
assert.strictEqual(stringToFlags('wx'), O_TRUNC | O_CREAT | O_WRONLY | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('xw'), O_TRUNC | O_CREAT | O_WRONLY | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('wx+'), O_TRUNC | O_CREAT | O_RDWR | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('xw+'), O_TRUNC | O_CREAT | O_RDWR | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('ax'), O_APPEND | O_CREAT | O_WRONLY | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('xa'), O_APPEND | O_CREAT | O_WRONLY | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('as'), O_APPEND | O_CREAT | O_WRONLY | O_SYNC);
|
||||
assert.strictEqual(stringToFlags('sa'), O_APPEND | O_CREAT | O_WRONLY | O_SYNC);
|
||||
assert.strictEqual(stringToFlags('ax+'), O_APPEND | O_CREAT | O_RDWR | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('xa+'), O_APPEND | O_CREAT | O_RDWR | O_EXCL);
|
||||
assert.strictEqual(stringToFlags('as+'), O_APPEND | O_CREAT | O_RDWR | O_SYNC);
|
||||
assert.strictEqual(stringToFlags('sa+'), O_APPEND | O_CREAT | O_RDWR | O_SYNC);
|
||||
|
||||
('+ +a +r +w rw wa war raw r++ a++ w++ x +x x+ rx rx+ wxx wax xwx xxx')
|
||||
.split(' ')
|
||||
.forEach(function(flags) {
|
||||
assert.throws(
|
||||
() => stringToFlags(flags),
|
||||
{ code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' }
|
||||
);
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => stringToFlags({}),
|
||||
{ code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' }
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => stringToFlags(true),
|
||||
{ code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' }
|
||||
);
|
||||
|
||||
if (common.isLinux || common.isMacOS) {
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
const file = tmpdir.resolve('a.js');
|
||||
fs.copyFileSync(fixtures.path('a.js'), file);
|
||||
fs.open(file, O_DSYNC, common.mustSucceed((fd) => {
|
||||
fs.closeSync(fd);
|
||||
}));
|
||||
}
|
||||
289
test/js/node/test/parallel/test-fs-opendir.js
Normal file
289
test/js/node/test/parallel/test-fs-opendir.js
Normal file
@@ -0,0 +1,289 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const testDir = tmpdir.path;
|
||||
const files = ['empty', 'files', 'for', 'just', 'testing'];
|
||||
|
||||
// Make sure tmp directory is clean
|
||||
tmpdir.refresh();
|
||||
|
||||
// Create the necessary files
|
||||
files.forEach(function(filename) {
|
||||
fs.closeSync(fs.openSync(path.join(testDir, filename), 'w'));
|
||||
});
|
||||
|
||||
function assertDirent(dirent) {
|
||||
assert(dirent instanceof fs.Dirent);
|
||||
assert.strictEqual(dirent.isFile(), true);
|
||||
assert.strictEqual(dirent.isDirectory(), false);
|
||||
assert.strictEqual(dirent.isSocket(), false);
|
||||
assert.strictEqual(dirent.isBlockDevice(), false);
|
||||
assert.strictEqual(dirent.isCharacterDevice(), false);
|
||||
assert.strictEqual(dirent.isFIFO(), false);
|
||||
assert.strictEqual(dirent.isSymbolicLink(), false);
|
||||
}
|
||||
|
||||
const dirclosedError = {
|
||||
code: 'ERR_DIR_CLOSED'
|
||||
};
|
||||
|
||||
const dirconcurrentError = {
|
||||
code: 'ERR_DIR_CONCURRENT_OPERATION'
|
||||
};
|
||||
|
||||
const invalidCallbackObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
};
|
||||
|
||||
// Check the opendir Sync version
|
||||
{
|
||||
const dir = fs.opendirSync(testDir);
|
||||
const entries = files.map(() => {
|
||||
const dirent = dir.readSync();
|
||||
assertDirent(dirent);
|
||||
return { name: dirent.name, parentPath: dirent.parentPath, toString() { return dirent.name; } };
|
||||
}).sort();
|
||||
assert.deepStrictEqual(entries.map((d) => d.name), files);
|
||||
assert.deepStrictEqual(entries.map((d) => d.parentPath), Array(entries.length).fill(testDir));
|
||||
|
||||
// dir.read should return null when no more entries exist
|
||||
assert.strictEqual(dir.readSync(), null);
|
||||
|
||||
// check .path
|
||||
assert.strictEqual(dir.path, testDir);
|
||||
|
||||
dir.closeSync();
|
||||
|
||||
assert.throws(() => dir.readSync(), dirclosedError);
|
||||
assert.throws(() => dir.closeSync(), dirclosedError);
|
||||
}
|
||||
|
||||
// Check the opendir async version
|
||||
fs.opendir(testDir, common.mustSucceed((dir) => {
|
||||
let sync = true;
|
||||
dir.read(common.mustSucceed((dirent) => {
|
||||
assert(!sync);
|
||||
|
||||
// Order is operating / file system dependent
|
||||
assert(files.includes(dirent.name), `'files' should include ${dirent}`);
|
||||
assertDirent(dirent);
|
||||
|
||||
let syncInner = true;
|
||||
dir.read(common.mustSucceed((dirent) => {
|
||||
assert(!syncInner);
|
||||
|
||||
dir.close(common.mustSucceed());
|
||||
}));
|
||||
syncInner = false;
|
||||
}));
|
||||
sync = false;
|
||||
}));
|
||||
|
||||
// opendir() on file should throw ENOTDIR
|
||||
assert.throws(function() {
|
||||
fs.opendirSync(__filename);
|
||||
}, /Error: ENOTDIR: not a directory/);
|
||||
|
||||
assert.throws(function() {
|
||||
fs.opendir(__filename);
|
||||
}, /TypeError \[ERR_INVALID_ARG_TYPE\]: The "callback" argument must be of type function/);
|
||||
|
||||
fs.opendir(__filename, common.mustCall(function(e) {
|
||||
assert.strictEqual(e.code, 'ENOTDIR');
|
||||
}));
|
||||
|
||||
[false, 1, [], {}, null, undefined].forEach((i) => {
|
||||
assert.throws(
|
||||
() => fs.opendir(i, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.opendirSync(i),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Promise-based tests
|
||||
async function doPromiseTest() {
|
||||
// Check the opendir Promise version
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
const entries = [];
|
||||
|
||||
let i = files.length;
|
||||
while (i--) {
|
||||
const dirent = await dir.read();
|
||||
entries.push(dirent.name);
|
||||
assertDirent(dirent);
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(files, entries.sort());
|
||||
|
||||
// dir.read should return null when no more entries exist
|
||||
assert.strictEqual(await dir.read(), null);
|
||||
|
||||
await dir.close();
|
||||
}
|
||||
doPromiseTest().then(common.mustCall());
|
||||
|
||||
// Async iterator
|
||||
async function doAsyncIterTest() {
|
||||
const entries = [];
|
||||
for await (const dirent of await fs.promises.opendir(testDir)) {
|
||||
entries.push(dirent.name);
|
||||
assertDirent(dirent);
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(files, entries.sort());
|
||||
|
||||
// Automatically closed during iterator
|
||||
}
|
||||
doAsyncIterTest().then(common.mustCall());
|
||||
|
||||
// Async iterators should do automatic cleanup
|
||||
|
||||
async function doAsyncIterBreakTest() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
for await (const dirent of dir) { // eslint-disable-line no-unused-vars
|
||||
break;
|
||||
}
|
||||
|
||||
await assert.rejects(async () => dir.read(), dirclosedError);
|
||||
}
|
||||
doAsyncIterBreakTest().then(common.mustCall());
|
||||
|
||||
async function doAsyncIterReturnTest() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
await (async function() {
|
||||
for await (const dirent of dir) {
|
||||
return;
|
||||
}
|
||||
})();
|
||||
|
||||
await assert.rejects(async () => dir.read(), dirclosedError);
|
||||
}
|
||||
doAsyncIterReturnTest().then(common.mustCall());
|
||||
|
||||
async function doAsyncIterThrowTest() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
try {
|
||||
for await (const dirent of dir) { // eslint-disable-line no-unused-vars
|
||||
throw new Error('oh no');
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.message !== 'oh no') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await assert.rejects(async () => dir.read(), dirclosedError);
|
||||
}
|
||||
doAsyncIterThrowTest().then(common.mustCall());
|
||||
|
||||
// Check error thrown on invalid values of bufferSize
|
||||
for (const bufferSize of [-1, 0, 0.5, 1.5, Infinity, NaN]) {
|
||||
assert.throws(
|
||||
() => fs.opendirSync(testDir, common.mustNotMutateObjectDeep({ bufferSize })),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE'
|
||||
});
|
||||
}
|
||||
for (const bufferSize of ['', '1', null]) {
|
||||
assert.throws(
|
||||
() => fs.opendirSync(testDir, common.mustNotMutateObjectDeep({ bufferSize })),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
}
|
||||
|
||||
// Check that passing a positive integer as bufferSize works
|
||||
{
|
||||
const dir = fs.opendirSync(testDir, common.mustNotMutateObjectDeep({ bufferSize: 1024 }));
|
||||
assertDirent(dir.readSync());
|
||||
dir.close();
|
||||
}
|
||||
|
||||
// Check that when passing a string instead of function - throw an exception
|
||||
async function doAsyncIterInvalidCallbackTest() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
assert.throws(() => dir.close('not function'), invalidCallbackObj);
|
||||
}
|
||||
doAsyncIterInvalidCallbackTest().then(common.mustCall());
|
||||
|
||||
// Check first call to close() - should not report an error.
|
||||
async function doAsyncIterDirClosedTest() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
await dir.close();
|
||||
await assert.rejects(() => dir.close(), dirclosedError);
|
||||
}
|
||||
doAsyncIterDirClosedTest().then(common.mustCall());
|
||||
|
||||
// Check that readSync() and closeSync() during read() throw exceptions
|
||||
async function doConcurrentAsyncAndSyncOps() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
const promise = dir.read();
|
||||
|
||||
assert.throws(() => dir.closeSync(), dirconcurrentError);
|
||||
assert.throws(() => dir.readSync(), dirconcurrentError);
|
||||
|
||||
await promise;
|
||||
dir.closeSync();
|
||||
}
|
||||
doConcurrentAsyncAndSyncOps().then(common.mustCall());
|
||||
|
||||
// Check read throw exceptions on invalid callback
|
||||
{
|
||||
const dir = fs.opendirSync(testDir);
|
||||
assert.throws(() => dir.read('INVALID_CALLBACK'), /ERR_INVALID_ARG_TYPE/);
|
||||
}
|
||||
|
||||
// Check that concurrent read() operations don't do weird things.
|
||||
async function doConcurrentAsyncOps() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
const promise1 = dir.read();
|
||||
const promise2 = dir.read();
|
||||
|
||||
assertDirent(await promise1);
|
||||
assertDirent(await promise2);
|
||||
dir.closeSync();
|
||||
}
|
||||
doConcurrentAsyncOps().then(common.mustCall());
|
||||
|
||||
// Check that concurrent read() + close() operations don't do weird things.
|
||||
async function doConcurrentAsyncMixedOps() {
|
||||
const dir = await fs.promises.opendir(testDir);
|
||||
const promise1 = dir.read();
|
||||
const promise2 = dir.close();
|
||||
|
||||
assertDirent(await promise1);
|
||||
await promise2;
|
||||
}
|
||||
doConcurrentAsyncMixedOps().then(common.mustCall());
|
||||
|
||||
// Check if directory already closed - the callback should pass an error.
|
||||
{
|
||||
const dir = fs.opendirSync(testDir);
|
||||
dir.closeSync();
|
||||
dir.close(common.mustCall((error) => {
|
||||
assert.strictEqual(error.code, dirclosedError.code);
|
||||
}));
|
||||
}
|
||||
|
||||
// Check if directory already closed - throw an promise exception.
|
||||
{
|
||||
const dir = fs.opendirSync(testDir);
|
||||
dir.closeSync();
|
||||
assert.rejects(dir.close(), dirclosedError).then(common.mustCall());
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const assert = require('node:assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
describe('File operations with filenames containing surrogate pairs', () => {
|
||||
it('should write, read, and delete a file with surrogate pairs in the filename', () => {
|
||||
// Create a temporary directory
|
||||
const tempdir = fs.mkdtempSync(tmpdir.resolve('emoji-fruit-🍇 🍈 🍉 🍊 🍋'));
|
||||
assert.strictEqual(fs.existsSync(tempdir), true);
|
||||
|
||||
const filename = '🚀🔥🛸.txt';
|
||||
const content = 'Test content';
|
||||
|
||||
// Write content to a file
|
||||
fs.writeFileSync(path.join(tempdir, filename), content);
|
||||
|
||||
// Read content from the file
|
||||
const readContent = fs.readFileSync(path.join(tempdir, filename), 'utf8');
|
||||
|
||||
// Check if the content matches
|
||||
assert.strictEqual(readContent, content);
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
// Flags: --expose-internals
|
||||
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
// The following tests validate aggregate errors are thrown correctly
|
||||
// when both an operation and close throw.
|
||||
|
||||
const {
|
||||
readFile,
|
||||
writeFile,
|
||||
truncate,
|
||||
lchmod,
|
||||
} = require('fs/promises');
|
||||
const {
|
||||
FileHandle,
|
||||
} = require('internal/fs/promises');
|
||||
|
||||
const assert = require('assert');
|
||||
const originalFd = Object.getOwnPropertyDescriptor(FileHandle.prototype, 'fd');
|
||||
|
||||
let count = 0;
|
||||
async function createFile() {
|
||||
const filePath = tmpdir.resolve(`aggregate_errors_${++count}.txt`);
|
||||
await writeFile(filePath, 'content');
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async function checkAggregateError(op) {
|
||||
try {
|
||||
const filePath = await createFile();
|
||||
Object.defineProperty(FileHandle.prototype, 'fd', {
|
||||
get: function() {
|
||||
// Close is set by using a setter,
|
||||
// so it needs to be set on the instance.
|
||||
const originalClose = this.close;
|
||||
this.close = async () => {
|
||||
// close the file
|
||||
await originalClose.call(this);
|
||||
const closeError = new Error('CLOSE_ERROR');
|
||||
closeError.code = 456;
|
||||
throw closeError;
|
||||
};
|
||||
const opError = new Error('INTERNAL_ERROR');
|
||||
opError.code = 123;
|
||||
throw opError;
|
||||
}
|
||||
});
|
||||
|
||||
await assert.rejects(op(filePath), common.mustCall((err) => {
|
||||
assert.strictEqual(err.name, 'AggregateError');
|
||||
assert.strictEqual(err.code, 123);
|
||||
assert.strictEqual(err.errors.length, 2);
|
||||
assert.strictEqual(err.errors[0].message, 'INTERNAL_ERROR');
|
||||
assert.strictEqual(err.errors[1].message, 'CLOSE_ERROR');
|
||||
return true;
|
||||
}));
|
||||
} finally {
|
||||
Object.defineProperty(FileHandle.prototype, 'fd', originalFd);
|
||||
}
|
||||
}
|
||||
(async function() {
|
||||
tmpdir.refresh();
|
||||
await checkAggregateError((filePath) => truncate(filePath));
|
||||
await checkAggregateError((filePath) => readFile(filePath));
|
||||
await checkAggregateError((filePath) => writeFile(filePath, '123'));
|
||||
if (common.isMacOS) {
|
||||
await checkAggregateError((filePath) => lchmod(filePath, 0o777));
|
||||
}
|
||||
})().then(common.mustCall());
|
||||
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
// Flags: --expose-internals
|
||||
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
// The following tests validate aggregate errors are thrown correctly
|
||||
// when both an operation and close throw.
|
||||
|
||||
const {
|
||||
readFile,
|
||||
writeFile,
|
||||
truncate,
|
||||
lchmod,
|
||||
} = require('fs/promises');
|
||||
const {
|
||||
FileHandle,
|
||||
} = require('internal/fs/promises');
|
||||
|
||||
const assert = require('assert');
|
||||
const originalFd = Object.getOwnPropertyDescriptor(FileHandle.prototype, 'fd');
|
||||
|
||||
let count = 0;
|
||||
async function createFile() {
|
||||
const filePath = tmpdir.resolve(`close_errors_${++count}.txt`);
|
||||
await writeFile(filePath, 'content');
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async function checkCloseError(op) {
|
||||
try {
|
||||
const filePath = await createFile();
|
||||
Object.defineProperty(FileHandle.prototype, 'fd', {
|
||||
get: function() {
|
||||
// Close is set by using a setter,
|
||||
// so it needs to be set on the instance.
|
||||
const originalClose = this.close;
|
||||
this.close = async () => {
|
||||
// close the file
|
||||
await originalClose.call(this);
|
||||
const closeError = new Error('CLOSE_ERROR');
|
||||
closeError.code = 456;
|
||||
throw closeError;
|
||||
};
|
||||
return originalFd.get.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
await assert.rejects(op(filePath), {
|
||||
name: 'Error',
|
||||
message: 'CLOSE_ERROR',
|
||||
code: 456,
|
||||
});
|
||||
} finally {
|
||||
Object.defineProperty(FileHandle.prototype, 'fd', originalFd);
|
||||
}
|
||||
}
|
||||
(async function() {
|
||||
tmpdir.refresh();
|
||||
await checkCloseError((filePath) => truncate(filePath));
|
||||
await checkCloseError((filePath) => readFile(filePath));
|
||||
await checkCloseError((filePath) => writeFile(filePath, '123'));
|
||||
if (common.isMacOS) {
|
||||
await checkCloseError((filePath) => lchmod(filePath, 0o777));
|
||||
}
|
||||
})().then(common.mustCall());
|
||||
@@ -0,0 +1,41 @@
|
||||
// Flags: --expose-gc --no-warnings
|
||||
'use strict';
|
||||
|
||||
// Test that a runtime warning is emitted when a FileHandle object
|
||||
// is allowed to close on garbage collection. In the future, this
|
||||
// test should verify that closing on garbage collection throws a
|
||||
// process fatal exception.
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const { promises: fs } = require('fs');
|
||||
|
||||
const warning =
|
||||
'Closing a FileHandle object on garbage collection is deprecated. ' +
|
||||
'Please close FileHandle objects explicitly using ' +
|
||||
'FileHandle.prototype.close(). In the future, an error will be ' +
|
||||
'thrown if a file descriptor is closed during garbage collection.';
|
||||
|
||||
async function doOpen() {
|
||||
const fh = await fs.open(__filename);
|
||||
|
||||
common.expectWarning({
|
||||
Warning: [[`Closing file descriptor ${fh.fd} on garbage collection`]],
|
||||
DeprecationWarning: [[warning, 'DEP0137']]
|
||||
});
|
||||
|
||||
return fh;
|
||||
}
|
||||
|
||||
doOpen().then(common.mustCall((fd) => {
|
||||
assert.strictEqual(typeof fd, 'object');
|
||||
})).then(common.mustCall(() => {
|
||||
setImmediate(() => {
|
||||
// The FileHandle should be out-of-scope and no longer accessed now.
|
||||
global.gc();
|
||||
|
||||
// Wait an extra event loop turn, as the warning is emitted from the
|
||||
// native layer in an unref()'ed setImmediate() callback.
|
||||
setImmediate(common.mustCall());
|
||||
});
|
||||
}));
|
||||
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
// Flags: --expose-internals
|
||||
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
// The following tests validate aggregate errors are thrown correctly
|
||||
// when both an operation and close throw.
|
||||
|
||||
const {
|
||||
readFile,
|
||||
writeFile,
|
||||
truncate,
|
||||
lchmod,
|
||||
} = require('fs/promises');
|
||||
const {
|
||||
FileHandle,
|
||||
} = require('internal/fs/promises');
|
||||
|
||||
const assert = require('assert');
|
||||
const originalFd = Object.getOwnPropertyDescriptor(FileHandle.prototype, 'fd');
|
||||
|
||||
let count = 0;
|
||||
async function createFile() {
|
||||
const filePath = tmpdir.resolve(`op_errors_${++count}.txt`);
|
||||
await writeFile(filePath, 'content');
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async function checkOperationError(op) {
|
||||
try {
|
||||
const filePath = await createFile();
|
||||
Object.defineProperty(FileHandle.prototype, 'fd', {
|
||||
get: function() {
|
||||
// Verify that close is called when an error is thrown
|
||||
this.close = common.mustCall(this.close);
|
||||
const opError = new Error('INTERNAL_ERROR');
|
||||
opError.code = 123;
|
||||
throw opError;
|
||||
}
|
||||
});
|
||||
|
||||
await assert.rejects(op(filePath), {
|
||||
name: 'Error',
|
||||
message: 'INTERNAL_ERROR',
|
||||
code: 123,
|
||||
});
|
||||
} finally {
|
||||
Object.defineProperty(FileHandle.prototype, 'fd', originalFd);
|
||||
}
|
||||
}
|
||||
(async function() {
|
||||
tmpdir.refresh();
|
||||
await checkOperationError((filePath) => truncate(filePath));
|
||||
await checkOperationError((filePath) => readFile(filePath));
|
||||
await checkOperationError((filePath) => writeFile(filePath, '123'));
|
||||
if (common.isMacOS) {
|
||||
await checkOperationError((filePath) => lchmod(filePath, 0o777));
|
||||
}
|
||||
})().then(common.mustCall());
|
||||
129
test/js/node/test/parallel/test-fs-promises-file-handle-read.js
Normal file
129
test/js/node/test/parallel/test-fs-promises-file-handle-read.js
Normal file
@@ -0,0 +1,129 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
// The following tests validate base functionality for the fs.promises
|
||||
// FileHandle.read method.
|
||||
|
||||
const fs = require('fs');
|
||||
const { open } = fs.promises;
|
||||
const path = require('path');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const tmpDir = tmpdir.path;
|
||||
|
||||
async function read(fileHandle, buffer, offset, length, position, options) {
|
||||
return options?.useConf ?
|
||||
fileHandle.read({ buffer, offset, length, position }) :
|
||||
fileHandle.read(buffer, offset, length, position);
|
||||
}
|
||||
|
||||
async function validateRead(data, file, options) {
|
||||
const filePath = path.resolve(tmpDir, file);
|
||||
const buffer = Buffer.from(data, 'utf8');
|
||||
|
||||
const fd = fs.openSync(filePath, 'w+');
|
||||
const fileHandle = await open(filePath, 'w+');
|
||||
const streamFileHandle = await open(filePath, 'w+');
|
||||
|
||||
fs.writeSync(fd, buffer, 0, buffer.length);
|
||||
fs.closeSync(fd);
|
||||
|
||||
fileHandle.on('close', common.mustCall());
|
||||
const readAsyncHandle =
|
||||
await read(fileHandle, Buffer.alloc(11), 0, 11, 0, options);
|
||||
assert.deepStrictEqual(data.length, readAsyncHandle.bytesRead);
|
||||
if (data.length)
|
||||
assert.deepStrictEqual(buffer, readAsyncHandle.buffer);
|
||||
await fileHandle.close();
|
||||
|
||||
const stream = fs.createReadStream(null, { fd: streamFileHandle });
|
||||
let streamData = Buffer.alloc(0);
|
||||
for await (const chunk of stream)
|
||||
streamData = Buffer.from(chunk);
|
||||
assert.deepStrictEqual(buffer, streamData);
|
||||
if (data.length)
|
||||
assert.deepStrictEqual(streamData, readAsyncHandle.buffer);
|
||||
await streamFileHandle.close();
|
||||
}
|
||||
|
||||
async function validateLargeRead(options) {
|
||||
// Reading beyond file length (3 in this case) should return no data.
|
||||
// This is a test for a bug where reads > uint32 would return data
|
||||
// from the current position in the file.
|
||||
const filePath = fixtures.path('x.txt');
|
||||
const fileHandle = await open(filePath, 'r');
|
||||
const pos = 0xffffffff + 1; // max-uint32 + 1
|
||||
const readHandle =
|
||||
await read(fileHandle, Buffer.alloc(1), 0, 1, pos, options);
|
||||
|
||||
assert.strictEqual(readHandle.bytesRead, 0);
|
||||
}
|
||||
|
||||
async function validateReadNoParams() {
|
||||
const filePath = fixtures.path('x.txt');
|
||||
const fileHandle = await open(filePath, 'r');
|
||||
// Should not throw
|
||||
await fileHandle.read();
|
||||
}
|
||||
|
||||
// Validates that the zero position is respected after the position has been
|
||||
// moved. The test iterates over the xyz chars twice making sure that the values
|
||||
// are read from the correct position.
|
||||
async function validateReadWithPositionZero() {
|
||||
const opts = { useConf: true };
|
||||
const filePath = fixtures.path('x.txt');
|
||||
const fileHandle = await open(filePath, 'r');
|
||||
const expectedSequence = ['x', 'y', 'z'];
|
||||
|
||||
for (let i = 0; i < expectedSequence.length * 2; i++) {
|
||||
const len = 1;
|
||||
const pos = i % 3;
|
||||
const buf = Buffer.alloc(len);
|
||||
const { bytesRead } = await read(fileHandle, buf, 0, len, pos, opts);
|
||||
assert.strictEqual(bytesRead, len);
|
||||
assert.strictEqual(buf.toString(), expectedSequence[pos]);
|
||||
}
|
||||
}
|
||||
|
||||
async function validateReadLength(len) {
|
||||
const buf = Buffer.alloc(4);
|
||||
const opts = { useConf: true };
|
||||
const filePath = fixtures.path('x.txt');
|
||||
const fileHandle = await open(filePath, 'r');
|
||||
const { bytesRead } = await read(fileHandle, buf, 0, len, 0, opts);
|
||||
assert.strictEqual(bytesRead, len);
|
||||
}
|
||||
|
||||
async function validateReadWithNoOptions(byte) {
|
||||
const buf = Buffer.alloc(byte);
|
||||
const filePath = fixtures.path('x.txt');
|
||||
const fileHandle = await open(filePath, 'r');
|
||||
let response = await fileHandle.read(buf);
|
||||
assert.strictEqual(response.bytesRead, byte);
|
||||
response = await read(fileHandle, buf, 0, undefined, 0);
|
||||
assert.strictEqual(response.bytesRead, byte);
|
||||
response = await read(fileHandle, buf, 0, null, 0);
|
||||
assert.strictEqual(response.bytesRead, byte);
|
||||
response = await read(fileHandle, buf, 0, undefined, 0, { useConf: true });
|
||||
assert.strictEqual(response.bytesRead, byte);
|
||||
response = await read(fileHandle, buf, 0, null, 0, { useConf: true });
|
||||
assert.strictEqual(response.bytesRead, byte);
|
||||
}
|
||||
|
||||
(async function() {
|
||||
tmpdir.refresh();
|
||||
await validateRead('Hello world', 'read-file', { useConf: false });
|
||||
await validateRead('', 'read-empty-file', { useConf: false });
|
||||
await validateRead('Hello world', 'read-file-conf', { useConf: true });
|
||||
await validateRead('', 'read-empty-file-conf', { useConf: true });
|
||||
await validateLargeRead({ useConf: false });
|
||||
await validateLargeRead({ useConf: true });
|
||||
await validateReadNoParams();
|
||||
await validateReadWithPositionZero();
|
||||
await validateReadLength(0);
|
||||
await validateReadLength(1);
|
||||
await validateReadWithNoOptions(0);
|
||||
await validateReadWithNoOptions(1);
|
||||
})().then(common.mustCall());
|
||||
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
// The following tests validate base functionality for the fs.promises
|
||||
// FileHandle.readFile method.
|
||||
|
||||
const fs = require('fs');
|
||||
const {
|
||||
open,
|
||||
readFile,
|
||||
writeFile,
|
||||
truncate,
|
||||
} = fs.promises;
|
||||
const path = require('path');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const tick = require('../common/tick');
|
||||
const assert = require('assert');
|
||||
const tmpDir = tmpdir.path;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
async function validateReadFile() {
|
||||
const filePath = path.resolve(tmpDir, 'tmp-read-file.txt');
|
||||
const fileHandle = await open(filePath, 'w+');
|
||||
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
|
||||
|
||||
const fd = fs.openSync(filePath, 'w+');
|
||||
fs.writeSync(fd, buffer, 0, buffer.length);
|
||||
fs.closeSync(fd);
|
||||
|
||||
const readFileData = await fileHandle.readFile();
|
||||
assert.deepStrictEqual(buffer, readFileData);
|
||||
|
||||
await fileHandle.close();
|
||||
}
|
||||
|
||||
async function validateReadFileProc() {
|
||||
// Test to make sure reading a file under the /proc directory works. Adapted
|
||||
// from test-fs-read-file-sync-hostname.js.
|
||||
// Refs:
|
||||
// - https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0
|
||||
// - https://github.com/nodejs/node/issues/21331
|
||||
|
||||
// Test is Linux-specific.
|
||||
if (!common.isLinux)
|
||||
return;
|
||||
|
||||
const fileHandle = await open('/proc/sys/kernel/hostname', 'r');
|
||||
const hostname = await fileHandle.readFile();
|
||||
assert.ok(hostname.length > 0);
|
||||
}
|
||||
|
||||
async function doReadAndCancel() {
|
||||
// Signal aborted from the start
|
||||
{
|
||||
const filePathForHandle = path.resolve(tmpDir, 'dogs-running.txt');
|
||||
const fileHandle = await open(filePathForHandle, 'w+');
|
||||
try {
|
||||
const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8');
|
||||
fs.writeFileSync(filePathForHandle, buffer);
|
||||
const signal = AbortSignal.abort();
|
||||
await assert.rejects(readFile(fileHandle, common.mustNotMutateObjectDeep({ signal })), {
|
||||
name: 'AbortError'
|
||||
});
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Signal aborted on first tick
|
||||
{
|
||||
const filePathForHandle = path.resolve(tmpDir, 'dogs-running1.txt');
|
||||
const fileHandle = await open(filePathForHandle, 'w+');
|
||||
const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8');
|
||||
fs.writeFileSync(filePathForHandle, buffer);
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
process.nextTick(() => controller.abort());
|
||||
await assert.rejects(readFile(fileHandle, common.mustNotMutateObjectDeep({ signal })), {
|
||||
name: 'AbortError'
|
||||
}, 'tick-0');
|
||||
await fileHandle.close();
|
||||
}
|
||||
|
||||
// Signal aborted right before buffer read
|
||||
{
|
||||
const newFile = path.resolve(tmpDir, 'dogs-running2.txt');
|
||||
const buffer = Buffer.from('Dogs running'.repeat(1000), 'utf8');
|
||||
fs.writeFileSync(newFile, buffer);
|
||||
|
||||
const fileHandle = await open(newFile, 'r');
|
||||
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
tick(1, () => controller.abort());
|
||||
await assert.rejects(fileHandle.readFile(common.mustNotMutateObjectDeep({ signal, encoding: 'utf8' })), {
|
||||
name: 'AbortError'
|
||||
}, 'tick-1');
|
||||
|
||||
await fileHandle.close();
|
||||
}
|
||||
|
||||
// Validate file size is within range for reading
|
||||
{
|
||||
// Variable taken from https://github.com/nodejs/node/blob/1377163f3351/lib/internal/fs/promises.js#L5
|
||||
const kIoMaxLength = 2 ** 31 - 1;
|
||||
|
||||
if (!tmpdir.hasEnoughSpace(kIoMaxLength)) {
|
||||
// truncate() will fail with ENOSPC if there is not enough space.
|
||||
common.printSkipMessage(`Not enough space in ${tmpDir}`);
|
||||
} else {
|
||||
const newFile = path.resolve(tmpDir, 'dogs-running3.txt');
|
||||
await writeFile(newFile, Buffer.from('0'));
|
||||
await truncate(newFile, kIoMaxLength + 1);
|
||||
|
||||
const fileHandle = await open(newFile, 'r');
|
||||
|
||||
await assert.rejects(fileHandle.readFile(), {
|
||||
name: 'RangeError',
|
||||
code: 'ERR_FS_FILE_TOO_LARGE'
|
||||
});
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateReadFile()
|
||||
.then(validateReadFileProc)
|
||||
.then(doReadAndCancel)
|
||||
.then(common.mustCall());
|
||||
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
// The following tests validate base functionality for the fs.promises
|
||||
// FileHandle.write method.
|
||||
|
||||
const fs = require('fs');
|
||||
const { open } = fs.promises;
|
||||
const path = require('path');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const { finished } = require('stream/promises');
|
||||
const { buffer } = require('stream/consumers');
|
||||
const tmpDir = tmpdir.path;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
async function validateWrite() {
|
||||
const filePathForHandle = path.resolve(tmpDir, 'tmp-write.txt');
|
||||
const fileHandle = await open(filePathForHandle, 'w');
|
||||
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
|
||||
|
||||
const stream = fileHandle.createWriteStream();
|
||||
stream.end(buffer);
|
||||
await finished(stream);
|
||||
|
||||
const readFileData = fs.readFileSync(filePathForHandle);
|
||||
assert.deepStrictEqual(buffer, readFileData);
|
||||
}
|
||||
|
||||
async function validateRead() {
|
||||
const filePathForHandle = path.resolve(tmpDir, 'tmp-read.txt');
|
||||
const buf = Buffer.from('Hello world'.repeat(100), 'utf8');
|
||||
|
||||
fs.writeFileSync(filePathForHandle, buf);
|
||||
|
||||
const fileHandle = await open(filePathForHandle);
|
||||
assert.deepStrictEqual(
|
||||
await buffer(fileHandle.createReadStream()),
|
||||
buf
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
validateWrite(),
|
||||
validateRead(),
|
||||
]).then(common.mustCall());
|
||||
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const { access, copyFile, open } = require('fs').promises;
|
||||
|
||||
async function validate() {
|
||||
tmpdir.refresh();
|
||||
const dest = tmpdir.resolve('baz.js');
|
||||
await assert.rejects(
|
||||
copyFile(fixtures.path('baz.js'), dest, 'r'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
);
|
||||
await copyFile(fixtures.path('baz.js'), dest);
|
||||
await assert.rejects(
|
||||
access(dest, 'r'),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE', message: /mode/ }
|
||||
);
|
||||
await access(dest);
|
||||
const handle = await open(dest, 'r+');
|
||||
await handle.datasync();
|
||||
await handle.sync();
|
||||
const buf = Buffer.from('hello world');
|
||||
await handle.write(buf);
|
||||
const ret = await handle.read(Buffer.alloc(11), 0, 11, 0);
|
||||
assert.strictEqual(ret.bytesRead, 11);
|
||||
assert.deepStrictEqual(ret.buffer, buf);
|
||||
await handle.close();
|
||||
}
|
||||
|
||||
validate();
|
||||
@@ -0,0 +1,200 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
// The following tests validate base functionality for the fs.promises
|
||||
// FileHandle.writeFile method.
|
||||
|
||||
const fs = require('fs');
|
||||
const { open, writeFile } = fs.promises;
|
||||
const path = require('path');
|
||||
const { Readable } = require('stream');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const tmpDir = tmpdir.path;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
async function validateWriteFile() {
|
||||
const filePathForHandle = path.resolve(tmpDir, 'tmp-write-file2.txt');
|
||||
const fileHandle = await open(filePathForHandle, 'w+');
|
||||
try {
|
||||
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
|
||||
|
||||
await fileHandle.writeFile(buffer);
|
||||
const readFileData = fs.readFileSync(filePathForHandle);
|
||||
assert.deepStrictEqual(buffer, readFileData);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Signal aborted while writing file
|
||||
async function doWriteAndCancel() {
|
||||
const filePathForHandle = path.resolve(tmpDir, 'dogs-running.txt');
|
||||
const fileHandle = await open(filePathForHandle, 'w+');
|
||||
try {
|
||||
const buffer = Buffer.from('dogs running'.repeat(512 * 1024), 'utf8');
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
process.nextTick(() => controller.abort());
|
||||
await assert.rejects(writeFile(fileHandle, buffer, { signal }), {
|
||||
name: 'AbortError'
|
||||
});
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
const dest = path.resolve(tmpDir, 'tmp.txt');
|
||||
const otherDest = path.resolve(tmpDir, 'tmp-2.txt');
|
||||
const stream = Readable.from(['a', 'b', 'c']);
|
||||
const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']);
|
||||
const iterable = {
|
||||
expected: 'abc',
|
||||
*[Symbol.iterator]() {
|
||||
yield 'a';
|
||||
yield 'b';
|
||||
yield 'c';
|
||||
}
|
||||
};
|
||||
function iterableWith(value) {
|
||||
return {
|
||||
*[Symbol.iterator]() {
|
||||
yield value;
|
||||
}
|
||||
};
|
||||
}
|
||||
const bufferIterable = {
|
||||
expected: 'abc',
|
||||
*[Symbol.iterator]() {
|
||||
yield Buffer.from('a');
|
||||
yield Buffer.from('b');
|
||||
yield Buffer.from('c');
|
||||
}
|
||||
};
|
||||
const asyncIterable = {
|
||||
expected: 'abc',
|
||||
async* [Symbol.asyncIterator]() {
|
||||
yield 'a';
|
||||
yield 'b';
|
||||
yield 'c';
|
||||
}
|
||||
};
|
||||
|
||||
async function doWriteStream() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await fileHandle.writeFile(stream);
|
||||
const expected = 'abc';
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, expected);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteStreamWithCancel() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
process.nextTick(() => controller.abort());
|
||||
const fileHandle = await open(otherDest, 'w+');
|
||||
try {
|
||||
await assert.rejects(
|
||||
fileHandle.writeFile(stream, { signal }),
|
||||
{ name: 'AbortError' }
|
||||
);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteIterable() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await fileHandle.writeFile(iterable);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, iterable.expected);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteInvalidIterable() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await Promise.all(
|
||||
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
|
||||
assert.rejects(
|
||||
fileHandle.writeFile(iterableWith(value)),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }
|
||||
)
|
||||
)
|
||||
);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteIterableWithEncoding() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await fileHandle.writeFile(stream2, 'latin1');
|
||||
const expected = 'ümlaut sechzig';
|
||||
const data = fs.readFileSync(dest, 'latin1');
|
||||
assert.deepStrictEqual(data, expected);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteBufferIterable() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await fileHandle.writeFile(bufferIterable);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, bufferIterable.expected);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteAsyncIterable() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await fileHandle.writeFile(asyncIterable);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, asyncIterable.expected);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function doWriteInvalidValues() {
|
||||
const fileHandle = await open(dest, 'w+');
|
||||
try {
|
||||
await Promise.all(
|
||||
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
|
||||
assert.rejects(
|
||||
fileHandle.writeFile(value),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }
|
||||
)
|
||||
)
|
||||
);
|
||||
} finally {
|
||||
await fileHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await validateWriteFile();
|
||||
await doWriteAndCancel();
|
||||
await doWriteStream();
|
||||
await doWriteStreamWithCancel();
|
||||
await doWriteIterable();
|
||||
await doWriteInvalidIterable();
|
||||
await doWriteIterableWithEncoding();
|
||||
await doWriteBufferIterable();
|
||||
await doWriteAsyncIterable();
|
||||
await doWriteInvalidValues();
|
||||
})().then(common.mustCall());
|
||||
90
test/js/node/test/parallel/test-fs-promises-readfile.js
Normal file
90
test/js/node/test/parallel/test-fs-promises-readfile.js
Normal file
@@ -0,0 +1,90 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const { writeFile, readFile } = require('fs').promises;
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const fsBinding = internalBinding('fs');
|
||||
tmpdir.refresh();
|
||||
|
||||
const fn = tmpdir.resolve('large-file');
|
||||
|
||||
// Creating large buffer with random content
|
||||
const largeBuffer = Buffer.from(
|
||||
Array.from({ length: 1024 ** 2 + 19 }, (_, index) => index)
|
||||
);
|
||||
|
||||
async function createLargeFile() {
|
||||
// Writing buffer to a file then try to read it
|
||||
await writeFile(fn, largeBuffer);
|
||||
}
|
||||
|
||||
async function validateReadFile() {
|
||||
const readBuffer = await readFile(fn);
|
||||
assert.strictEqual(readBuffer.equals(largeBuffer), true);
|
||||
}
|
||||
|
||||
async function validateReadFileProc() {
|
||||
// Test to make sure reading a file under the /proc directory works. Adapted
|
||||
// from test-fs-read-file-sync-hostname.js.
|
||||
// Refs:
|
||||
// - https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0
|
||||
// - https://github.com/nodejs/node/issues/21331
|
||||
|
||||
// Test is Linux-specific.
|
||||
if (!common.isLinux)
|
||||
return;
|
||||
|
||||
const hostname = await readFile('/proc/sys/kernel/hostname');
|
||||
assert.ok(hostname.length > 0);
|
||||
}
|
||||
|
||||
function validateReadFileAbortLogicBefore() {
|
||||
const signal = AbortSignal.abort();
|
||||
assert.rejects(readFile(fn, { signal }), {
|
||||
name: 'AbortError'
|
||||
}).then(common.mustCall());
|
||||
}
|
||||
|
||||
function validateReadFileAbortLogicDuring() {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
process.nextTick(() => controller.abort());
|
||||
assert.rejects(readFile(fn, { signal }), {
|
||||
name: 'AbortError'
|
||||
}).then(common.mustCall());
|
||||
}
|
||||
|
||||
async function validateWrongSignalParam() {
|
||||
// Verify that if something different than Abortcontroller.signal
|
||||
// is passed, ERR_INVALID_ARG_TYPE is thrown
|
||||
|
||||
await assert.rejects(async () => {
|
||||
const callback = common.mustNotCall();
|
||||
await readFile(fn, { signal: 'hello' }, callback);
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' });
|
||||
|
||||
}
|
||||
|
||||
async function validateZeroByteLiar() {
|
||||
const originalFStat = fsBinding.fstat;
|
||||
fsBinding.fstat = common.mustCall(
|
||||
async () => (/* stat fields */ [0, 1, 2, 3, 4, 5, 6, 7, 0 /* size */])
|
||||
);
|
||||
const readBuffer = await readFile(fn);
|
||||
assert.strictEqual(readBuffer.toString(), largeBuffer.toString());
|
||||
fsBinding.fstat = originalFStat;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await createLargeFile();
|
||||
await validateReadFile();
|
||||
await validateReadFileProc();
|
||||
await validateReadFileAbortLogicBefore();
|
||||
await validateReadFileAbortLogicDuring();
|
||||
await validateWrongSignalParam();
|
||||
await validateZeroByteLiar();
|
||||
})().then(common.mustCall());
|
||||
136
test/js/node/test/parallel/test-fs-promises-watch.js
Normal file
136
test/js/node/test/parallel/test-fs-promises-watch.js
Normal file
@@ -0,0 +1,136 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
if (common.isIBMi)
|
||||
common.skip('IBMi does not support `fs.watch()`');
|
||||
|
||||
const { watch } = require('fs/promises');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const { join } = require('path');
|
||||
const { setTimeout } = require('timers/promises');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
class WatchTestCase {
|
||||
constructor(shouldInclude, dirName, fileName, field) {
|
||||
this.dirName = dirName;
|
||||
this.fileName = fileName;
|
||||
this.field = field;
|
||||
this.shouldSkip = !shouldInclude;
|
||||
}
|
||||
get dirPath() { return tmpdir.resolve(this.dirName); }
|
||||
get filePath() { return join(this.dirPath, this.fileName); }
|
||||
}
|
||||
|
||||
const kCases = [
|
||||
// Watch on a directory should callback with a filename on supported systems
|
||||
new WatchTestCase(
|
||||
common.isLinux || common.isMacOS || common.isWindows || common.isAIX,
|
||||
'watch1',
|
||||
'foo',
|
||||
'filePath'
|
||||
),
|
||||
// Watch on a file should callback with a filename on supported systems
|
||||
new WatchTestCase(
|
||||
common.isLinux || common.isMacOS || common.isWindows,
|
||||
'watch2',
|
||||
'bar',
|
||||
'dirPath'
|
||||
),
|
||||
];
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
for (const testCase of kCases) {
|
||||
if (testCase.shouldSkip) continue;
|
||||
fs.mkdirSync(testCase.dirPath);
|
||||
// Long content so it's actually flushed.
|
||||
const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4);
|
||||
fs.writeFileSync(testCase.filePath, content1);
|
||||
|
||||
let interval;
|
||||
async function test() {
|
||||
if (common.isMacOS) {
|
||||
// On macOS delay watcher start to avoid leaking previous events.
|
||||
// Refs: https://github.com/libuv/libuv/pull/4503
|
||||
await setTimeout(common.platformTimeout(100));
|
||||
}
|
||||
|
||||
const watcher = watch(testCase[testCase.field]);
|
||||
for await (const { eventType, filename } of watcher) {
|
||||
clearInterval(interval);
|
||||
assert.strictEqual(['rename', 'change'].includes(eventType), true);
|
||||
assert.strictEqual(filename, testCase.fileName);
|
||||
break;
|
||||
}
|
||||
|
||||
// Waiting on it again is a non-op
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for await (const p of watcher) {
|
||||
assert.fail('should not run');
|
||||
}
|
||||
}
|
||||
|
||||
// Long content so it's actually flushed. toUpperCase so there's real change.
|
||||
const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4);
|
||||
interval = setInterval(() => {
|
||||
fs.writeFileSync(testCase.filePath, '');
|
||||
fs.writeFileSync(testCase.filePath, content2);
|
||||
}, 100);
|
||||
|
||||
test().then(common.mustCall());
|
||||
}
|
||||
|
||||
assert.rejects(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch(1)) { }
|
||||
},
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch(__filename, 1)) { }
|
||||
},
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch('', { persistent: 1 })) { }
|
||||
},
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch('', { recursive: 1 })) { }
|
||||
},
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch('', { encoding: 1 })) { }
|
||||
},
|
||||
{ code: 'ERR_INVALID_ARG_VALUE' }).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
async () => {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch('', { signal: 1 })) { }
|
||||
},
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }).then(common.mustCall());
|
||||
|
||||
(async () => {
|
||||
const ac = new AbortController();
|
||||
const { signal } = ac;
|
||||
setImmediate(() => ac.abort());
|
||||
try {
|
||||
// eslint-disable-next-line no-unused-vars, no-empty
|
||||
for await (const _ of watch(__filename, { signal })) { }
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
}
|
||||
})().then(common.mustCall());
|
||||
@@ -0,0 +1,110 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
// This test ensures that filehandle.write accepts "named parameters" object
|
||||
// and doesn't interpret objects as strings
|
||||
|
||||
const assert = require('assert');
|
||||
const fsPromises = require('fs').promises;
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
const dest = tmpdir.resolve('tmp.txt');
|
||||
const buffer = Buffer.from('zyx');
|
||||
|
||||
async function testInvalid(dest, expectedCode, ...params) {
|
||||
if (params.length >= 2) {
|
||||
params[1] = common.mustNotMutateObjectDeep(params[1]);
|
||||
}
|
||||
let fh;
|
||||
try {
|
||||
fh = await fsPromises.open(dest, 'w+');
|
||||
await assert.rejects(
|
||||
fh.write(...params),
|
||||
{ code: expectedCode });
|
||||
} finally {
|
||||
await fh?.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function testValid(dest, buffer, options) {
|
||||
const length = options?.length;
|
||||
const offset = options?.offset;
|
||||
let fh, writeResult, writeBufCopy, readResult, readBufCopy;
|
||||
|
||||
try {
|
||||
fh = await fsPromises.open(dest, 'w');
|
||||
writeResult = await fh.write(buffer, options);
|
||||
writeBufCopy = Uint8Array.prototype.slice.call(writeResult.buffer);
|
||||
} finally {
|
||||
await fh?.close();
|
||||
}
|
||||
|
||||
try {
|
||||
fh = await fsPromises.open(dest, 'r');
|
||||
readResult = await fh.read(buffer, options);
|
||||
readBufCopy = Uint8Array.prototype.slice.call(readResult.buffer);
|
||||
} finally {
|
||||
await fh?.close();
|
||||
}
|
||||
|
||||
assert.ok(writeResult.bytesWritten >= readResult.bytesRead);
|
||||
if (length !== undefined && length !== null) {
|
||||
assert.strictEqual(writeResult.bytesWritten, length);
|
||||
assert.strictEqual(readResult.bytesRead, length);
|
||||
}
|
||||
if (offset === undefined || offset === 0) {
|
||||
assert.deepStrictEqual(writeBufCopy, readBufCopy);
|
||||
}
|
||||
assert.deepStrictEqual(writeResult.buffer, readResult.buffer);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// Test if first argument is not wrongly interpreted as ArrayBufferView|string
|
||||
for (const badBuffer of [
|
||||
undefined, null, true, 42, 42n, Symbol('42'), NaN, [], () => {},
|
||||
common.mustNotCall(),
|
||||
common.mustNotMutateObjectDeep({}),
|
||||
Promise.resolve(new Uint8Array(1)),
|
||||
{},
|
||||
{ buffer: 'amNotParam' },
|
||||
{ string: 'amNotParam' },
|
||||
{ buffer: new Uint8Array(1).buffer },
|
||||
new Date(),
|
||||
new String('notPrimitive'),
|
||||
{ toString() { return 'amObject'; } },
|
||||
{ [Symbol.toPrimitive]: (hint) => 'amObject' },
|
||||
]) {
|
||||
await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', common.mustNotMutateObjectDeep(badBuffer), {});
|
||||
}
|
||||
|
||||
// First argument (buffer or string) is mandatory
|
||||
await testInvalid(dest, 'ERR_INVALID_ARG_TYPE');
|
||||
|
||||
// Various invalid options
|
||||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: 5 });
|
||||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { offset: 5 });
|
||||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: 1, offset: 3 });
|
||||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: -1 });
|
||||
await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { offset: -1 });
|
||||
await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', buffer, { offset: false });
|
||||
await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', buffer, { offset: true });
|
||||
|
||||
// Test compatibility with filehandle.read counterpart
|
||||
for (const options of [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
{ length: 1 },
|
||||
{ position: 5 },
|
||||
{ length: 1, position: 5 },
|
||||
{ length: 1, position: -1, offset: 2 },
|
||||
{ length: null },
|
||||
{ position: null },
|
||||
{ offset: 1 },
|
||||
]) {
|
||||
await testValid(dest, buffer, common.mustNotMutateObjectDeep(options));
|
||||
}
|
||||
})().then(common.mustCall());
|
||||
179
test/js/node/test/parallel/test-fs-promises-writefile.js
Normal file
179
test/js/node/test/parallel/test-fs-promises-writefile.js
Normal file
@@ -0,0 +1,179 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const fsPromises = fs.promises;
|
||||
const path = require('path');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const tmpDir = tmpdir.path;
|
||||
const { Readable } = require('stream');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
const dest = path.resolve(tmpDir, 'tmp.txt');
|
||||
const otherDest = path.resolve(tmpDir, 'tmp-2.txt');
|
||||
const buffer = Buffer.from('abc'.repeat(1000));
|
||||
const buffer2 = Buffer.from('xyz'.repeat(1000));
|
||||
const stream = Readable.from(['a', 'b', 'c']);
|
||||
const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']);
|
||||
const iterable = {
|
||||
expected: 'abc',
|
||||
*[Symbol.iterator]() {
|
||||
yield 'a';
|
||||
yield 'b';
|
||||
yield 'c';
|
||||
}
|
||||
};
|
||||
|
||||
const veryLargeBuffer = {
|
||||
expected: 'dogs running'.repeat(512 * 1024),
|
||||
*[Symbol.iterator]() {
|
||||
yield Buffer.from('dogs running'.repeat(512 * 1024), 'utf8');
|
||||
}
|
||||
};
|
||||
|
||||
function iterableWith(value) {
|
||||
return {
|
||||
*[Symbol.iterator]() {
|
||||
yield value;
|
||||
}
|
||||
};
|
||||
}
|
||||
const bufferIterable = {
|
||||
expected: 'abc',
|
||||
*[Symbol.iterator]() {
|
||||
yield Buffer.from('a');
|
||||
yield Buffer.from('b');
|
||||
yield Buffer.from('c');
|
||||
}
|
||||
};
|
||||
const asyncIterable = {
|
||||
expected: 'abc',
|
||||
async* [Symbol.asyncIterator]() {
|
||||
yield 'a';
|
||||
yield 'b';
|
||||
yield 'c';
|
||||
}
|
||||
};
|
||||
|
||||
async function doWrite() {
|
||||
await fsPromises.writeFile(dest, buffer);
|
||||
const data = fs.readFileSync(dest);
|
||||
assert.deepStrictEqual(data, buffer);
|
||||
}
|
||||
|
||||
async function doWriteStream() {
|
||||
await fsPromises.writeFile(dest, stream);
|
||||
const expected = 'abc';
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, expected);
|
||||
}
|
||||
|
||||
async function doWriteStreamWithCancel() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
process.nextTick(() => controller.abort());
|
||||
await assert.rejects(
|
||||
fsPromises.writeFile(otherDest, stream, { signal }),
|
||||
{ name: 'AbortError' }
|
||||
);
|
||||
}
|
||||
|
||||
async function doWriteIterable() {
|
||||
await fsPromises.writeFile(dest, iterable);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, iterable.expected);
|
||||
}
|
||||
|
||||
async function doWriteInvalidIterable() {
|
||||
await Promise.all(
|
||||
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
|
||||
assert.rejects(fsPromises.writeFile(dest, iterableWith(value)), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function doWriteIterableWithEncoding() {
|
||||
await fsPromises.writeFile(dest, stream2, 'latin1');
|
||||
const expected = 'ümlaut sechzig';
|
||||
const data = fs.readFileSync(dest, 'latin1');
|
||||
assert.deepStrictEqual(data, expected);
|
||||
}
|
||||
|
||||
async function doWriteBufferIterable() {
|
||||
await fsPromises.writeFile(dest, bufferIterable);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, bufferIterable.expected);
|
||||
}
|
||||
|
||||
async function doWriteAsyncIterable() {
|
||||
await fsPromises.writeFile(dest, asyncIterable);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, asyncIterable.expected);
|
||||
}
|
||||
|
||||
async function doWriteAsyncLargeIterable() {
|
||||
await fsPromises.writeFile(dest, veryLargeBuffer);
|
||||
const data = fs.readFileSync(dest, 'utf-8');
|
||||
assert.deepStrictEqual(data, veryLargeBuffer.expected);
|
||||
}
|
||||
|
||||
async function doWriteInvalidValues() {
|
||||
await Promise.all(
|
||||
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
|
||||
assert.rejects(fsPromises.writeFile(dest, value), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function doWriteWithCancel() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
process.nextTick(() => controller.abort());
|
||||
await assert.rejects(
|
||||
fsPromises.writeFile(otherDest, buffer, { signal }),
|
||||
{ name: 'AbortError' }
|
||||
);
|
||||
}
|
||||
|
||||
async function doAppend() {
|
||||
await fsPromises.appendFile(dest, buffer2, { flag: null });
|
||||
const data = fs.readFileSync(dest);
|
||||
const buf = Buffer.concat([buffer, buffer2]);
|
||||
assert.deepStrictEqual(buf, data);
|
||||
}
|
||||
|
||||
async function doRead() {
|
||||
const data = await fsPromises.readFile(dest);
|
||||
const buf = fs.readFileSync(dest);
|
||||
assert.deepStrictEqual(buf, data);
|
||||
}
|
||||
|
||||
async function doReadWithEncoding() {
|
||||
const data = await fsPromises.readFile(dest, 'utf-8');
|
||||
const syncData = fs.readFileSync(dest, 'utf-8');
|
||||
assert.strictEqual(typeof data, 'string');
|
||||
assert.deepStrictEqual(data, syncData);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await doWrite();
|
||||
await doWriteWithCancel();
|
||||
await doAppend();
|
||||
await doRead();
|
||||
await doReadWithEncoding();
|
||||
await doWriteStream();
|
||||
await doWriteStreamWithCancel();
|
||||
await doWriteIterable();
|
||||
await doWriteInvalidIterable();
|
||||
await doWriteIterableWithEncoding();
|
||||
await doWriteBufferIterable();
|
||||
await doWriteAsyncIterable();
|
||||
await doWriteAsyncLargeIterable();
|
||||
await doWriteInvalidValues();
|
||||
})().then(common.mustCall());
|
||||
512
test/js/node/test/parallel/test-fs-promises.js
Normal file
512
test/js/node/test/parallel/test-fs-promises.js
Normal file
@@ -0,0 +1,512 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const fsPromises = fs.promises;
|
||||
const {
|
||||
access,
|
||||
chmod,
|
||||
chown,
|
||||
copyFile,
|
||||
lchown,
|
||||
link,
|
||||
lchmod,
|
||||
lstat,
|
||||
lutimes,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
open,
|
||||
readFile,
|
||||
readdir,
|
||||
readlink,
|
||||
realpath,
|
||||
rename,
|
||||
rmdir,
|
||||
stat,
|
||||
statfs,
|
||||
symlink,
|
||||
truncate,
|
||||
unlink,
|
||||
utimes,
|
||||
writeFile
|
||||
} = fsPromises;
|
||||
|
||||
const tmpDir = tmpdir.path;
|
||||
|
||||
let dirc = 0;
|
||||
function nextdir() {
|
||||
return `test${++dirc}`;
|
||||
}
|
||||
|
||||
// fs.promises should be enumerable.
|
||||
assert.strictEqual(
|
||||
Object.prototype.propertyIsEnumerable.call(fs, 'promises'),
|
||||
true
|
||||
);
|
||||
|
||||
{
|
||||
access(__filename, 0)
|
||||
.then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
access('this file does not exist', 0),
|
||||
{
|
||||
code: 'ENOENT',
|
||||
name: 'Error',
|
||||
message: /^ENOENT: no such file or directory, access/,
|
||||
stack: /at async Function\.rejects/
|
||||
}
|
||||
).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
access(__filename, 8),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
}
|
||||
).then(common.mustCall());
|
||||
|
||||
assert.rejects(
|
||||
access(__filename, { [Symbol.toPrimitive]() { return 5; } }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
).then(common.mustCall());
|
||||
}
|
||||
|
||||
function verifyStatObject(stat) {
|
||||
assert.strictEqual(typeof stat, 'object');
|
||||
assert.strictEqual(typeof stat.dev, 'number');
|
||||
assert.strictEqual(typeof stat.mode, 'number');
|
||||
}
|
||||
|
||||
function verifyStatFsObject(stat, isBigint = false) {
|
||||
const valueType = isBigint ? 'bigint' : 'number';
|
||||
|
||||
assert.strictEqual(typeof stat, 'object');
|
||||
assert.strictEqual(typeof stat.type, valueType);
|
||||
assert.strictEqual(typeof stat.bsize, valueType);
|
||||
assert.strictEqual(typeof stat.blocks, valueType);
|
||||
assert.strictEqual(typeof stat.bfree, valueType);
|
||||
assert.strictEqual(typeof stat.bavail, valueType);
|
||||
assert.strictEqual(typeof stat.files, valueType);
|
||||
assert.strictEqual(typeof stat.ffree, valueType);
|
||||
}
|
||||
|
||||
async function getHandle(dest) {
|
||||
await copyFile(fixtures.path('baz.js'), dest);
|
||||
await access(dest);
|
||||
|
||||
return open(dest, 'r+');
|
||||
}
|
||||
|
||||
async function executeOnHandle(dest, func) {
|
||||
let handle;
|
||||
try {
|
||||
handle = await getHandle(dest);
|
||||
await func(handle);
|
||||
} finally {
|
||||
if (handle) {
|
||||
await handle.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
async function doTest() {
|
||||
tmpdir.refresh();
|
||||
|
||||
const dest = path.resolve(tmpDir, 'baz.js');
|
||||
|
||||
// handle is object
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
assert.strictEqual(typeof handle, 'object');
|
||||
});
|
||||
}
|
||||
|
||||
// file stats
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
let stats = await handle.stat();
|
||||
verifyStatObject(stats);
|
||||
assert.strictEqual(stats.size, 35);
|
||||
|
||||
await handle.truncate(1);
|
||||
|
||||
stats = await handle.stat();
|
||||
verifyStatObject(stats);
|
||||
assert.strictEqual(stats.size, 1);
|
||||
|
||||
stats = await stat(dest);
|
||||
verifyStatObject(stats);
|
||||
|
||||
stats = await handle.stat();
|
||||
verifyStatObject(stats);
|
||||
|
||||
await handle.datasync();
|
||||
await handle.sync();
|
||||
});
|
||||
}
|
||||
|
||||
// File system stats
|
||||
{
|
||||
const statFs = await statfs(dest);
|
||||
verifyStatFsObject(statFs);
|
||||
}
|
||||
|
||||
// File system stats bigint
|
||||
{
|
||||
const statFs = await statfs(dest, { bigint: true });
|
||||
verifyStatFsObject(statFs, true);
|
||||
}
|
||||
|
||||
// Test fs.read promises when length to read is zero bytes
|
||||
{
|
||||
const dest = path.resolve(tmpDir, 'test1.js');
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
const buf = Buffer.from('DAWGS WIN');
|
||||
const bufLen = buf.length;
|
||||
await handle.write(buf);
|
||||
const ret = await handle.read(Buffer.alloc(bufLen), 0, 0, 0);
|
||||
assert.strictEqual(ret.bytesRead, 0);
|
||||
|
||||
await unlink(dest);
|
||||
});
|
||||
}
|
||||
|
||||
// Use fallback buffer allocation when first argument is null
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
const ret = await handle.read(null, 0, 0, 0);
|
||||
assert.strictEqual(ret.buffer.length, 16384);
|
||||
});
|
||||
}
|
||||
|
||||
// TypeError if buffer is not ArrayBufferView or nullable object
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
await assert.rejects(
|
||||
async () => handle.read(0, 0, 0, 0),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Bytes written to file match buffer
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
const buf = Buffer.from('hello fsPromises');
|
||||
const bufLen = buf.length;
|
||||
await handle.write(buf);
|
||||
const ret = await handle.read(Buffer.alloc(bufLen), 0, bufLen, 0);
|
||||
assert.strictEqual(ret.bytesRead, bufLen);
|
||||
assert.deepStrictEqual(ret.buffer, buf);
|
||||
});
|
||||
}
|
||||
|
||||
// Truncate file to specified length
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
const buf = Buffer.from('hello FileHandle');
|
||||
const bufLen = buf.length;
|
||||
await handle.write(buf, 0, bufLen, 0);
|
||||
const ret = await handle.read(Buffer.alloc(bufLen), 0, bufLen, 0);
|
||||
assert.strictEqual(ret.bytesRead, bufLen);
|
||||
assert.deepStrictEqual(ret.buffer, buf);
|
||||
await truncate(dest, 5);
|
||||
assert.strictEqual((await readFile(dest)).toString(), 'hello');
|
||||
});
|
||||
}
|
||||
|
||||
// Invalid change of ownership
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
await chmod(dest, 0o666);
|
||||
await handle.chmod(0o666);
|
||||
|
||||
await chmod(dest, (0o10777));
|
||||
await handle.chmod(0o10777);
|
||||
|
||||
if (!common.isWindows) {
|
||||
await chown(dest, process.getuid(), process.getgid());
|
||||
await handle.chown(process.getuid(), process.getgid());
|
||||
}
|
||||
|
||||
await assert.rejects(
|
||||
async () => {
|
||||
await chown(dest, 1, -2);
|
||||
},
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "gid" is out of range. ' +
|
||||
'It must be >= -1 && <= 4294967295. Received -2'
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
async () => {
|
||||
await handle.chown(1, -2);
|
||||
},
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "gid" is out of range. ' +
|
||||
'It must be >= -1 && <= 4294967295. Received -2'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Set modification times
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
|
||||
await utimes(dest, new Date(), new Date());
|
||||
|
||||
try {
|
||||
await handle.utimes(new Date(), new Date());
|
||||
} catch (err) {
|
||||
// Some systems do not have futimes. If there is an error,
|
||||
// expect it to be ENOSYS
|
||||
common.expectsError({
|
||||
code: 'ENOSYS',
|
||||
name: 'Error'
|
||||
})(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set modification times with lutimes
|
||||
{
|
||||
const a_time = new Date();
|
||||
a_time.setMinutes(a_time.getMinutes() - 1);
|
||||
const m_time = new Date();
|
||||
m_time.setHours(m_time.getHours() - 1);
|
||||
await lutimes(dest, a_time, m_time);
|
||||
const stats = await stat(dest);
|
||||
|
||||
assert.strictEqual(a_time.toString(), stats.atime.toString());
|
||||
assert.strictEqual(m_time.toString(), stats.mtime.toString());
|
||||
}
|
||||
|
||||
// create symlink
|
||||
{
|
||||
const newPath = path.resolve(tmpDir, 'baz2.js');
|
||||
await rename(dest, newPath);
|
||||
let stats = await stat(newPath);
|
||||
verifyStatObject(stats);
|
||||
|
||||
if (common.canCreateSymLink()) {
|
||||
const newLink = path.resolve(tmpDir, 'baz3.js');
|
||||
await symlink(newPath, newLink);
|
||||
if (!common.isWindows) {
|
||||
await lchown(newLink, process.getuid(), process.getgid());
|
||||
}
|
||||
stats = await lstat(newLink);
|
||||
verifyStatObject(stats);
|
||||
|
||||
assert.strictEqual(newPath.toLowerCase(),
|
||||
(await realpath(newLink)).toLowerCase());
|
||||
assert.strictEqual(newPath.toLowerCase(),
|
||||
(await readlink(newLink)).toLowerCase());
|
||||
|
||||
const newMode = 0o666;
|
||||
if (common.isMacOS) {
|
||||
// `lchmod` is only available on macOS.
|
||||
await lchmod(newLink, newMode);
|
||||
stats = await lstat(newLink);
|
||||
assert.strictEqual(stats.mode & 0o777, newMode);
|
||||
} else {
|
||||
await Promise.all([
|
||||
assert.rejects(
|
||||
lchmod(newLink, newMode),
|
||||
common.expectsError({
|
||||
code: 'ERR_METHOD_NOT_IMPLEMENTED',
|
||||
name: 'Error',
|
||||
message: 'The lchmod() method is not implemented'
|
||||
})
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
await unlink(newLink);
|
||||
}
|
||||
}
|
||||
|
||||
// specify symlink type
|
||||
{
|
||||
const dir = path.join(tmpDir, nextdir());
|
||||
await symlink(tmpDir, dir, 'dir');
|
||||
const stats = await lstat(dir);
|
||||
assert.strictEqual(stats.isSymbolicLink(), true);
|
||||
await unlink(dir);
|
||||
}
|
||||
|
||||
// create hard link
|
||||
{
|
||||
const newPath = path.resolve(tmpDir, 'baz2.js');
|
||||
const newLink = path.resolve(tmpDir, 'baz4.js');
|
||||
await link(newPath, newLink);
|
||||
|
||||
await unlink(newLink);
|
||||
}
|
||||
|
||||
// Testing readdir lists both files and directories
|
||||
{
|
||||
const newDir = path.resolve(tmpDir, 'dir');
|
||||
const newFile = path.resolve(tmpDir, 'foo.js');
|
||||
|
||||
await mkdir(newDir);
|
||||
await writeFile(newFile, 'DAWGS WIN!', 'utf8');
|
||||
|
||||
const stats = await stat(newDir);
|
||||
assert(stats.isDirectory());
|
||||
const list = await readdir(tmpDir);
|
||||
assert.notStrictEqual(list.indexOf('dir'), -1);
|
||||
assert.notStrictEqual(list.indexOf('foo.js'), -1);
|
||||
await rmdir(newDir);
|
||||
await unlink(newFile);
|
||||
}
|
||||
|
||||
// Use fallback encoding when input is null
|
||||
{
|
||||
const newFile = path.resolve(tmpDir, 'dogs_running.js');
|
||||
await writeFile(newFile, 'dogs running', { encoding: null });
|
||||
const fileExists = fs.existsSync(newFile);
|
||||
assert.strictEqual(fileExists, true);
|
||||
}
|
||||
|
||||
// `mkdir` when options is number.
|
||||
{
|
||||
const dir = path.join(tmpDir, nextdir());
|
||||
await mkdir(dir, 777);
|
||||
const stats = await stat(dir);
|
||||
assert(stats.isDirectory());
|
||||
}
|
||||
|
||||
// `mkdir` when options is string.
|
||||
{
|
||||
const dir = path.join(tmpDir, nextdir());
|
||||
await mkdir(dir, '777');
|
||||
const stats = await stat(dir);
|
||||
assert(stats.isDirectory());
|
||||
}
|
||||
|
||||
// `mkdirp` when folder does not yet exist.
|
||||
{
|
||||
const dir = path.join(tmpDir, nextdir(), nextdir());
|
||||
await mkdir(dir, { recursive: true });
|
||||
const stats = await stat(dir);
|
||||
assert(stats.isDirectory());
|
||||
}
|
||||
|
||||
// `mkdirp` when path is a file.
|
||||
{
|
||||
const dir = path.join(tmpDir, nextdir(), nextdir());
|
||||
await mkdir(path.dirname(dir));
|
||||
await writeFile(dir, '');
|
||||
await assert.rejects(
|
||||
mkdir(dir, { recursive: true }),
|
||||
{
|
||||
code: 'EEXIST',
|
||||
message: /EEXIST: .*mkdir/,
|
||||
name: 'Error',
|
||||
syscall: 'mkdir',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// `mkdirp` when part of the path is a file.
|
||||
{
|
||||
const file = path.join(tmpDir, nextdir(), nextdir());
|
||||
const dir = path.join(file, nextdir(), nextdir());
|
||||
await mkdir(path.dirname(file));
|
||||
await writeFile(file, '');
|
||||
await assert.rejects(
|
||||
mkdir(dir, { recursive: true }),
|
||||
{
|
||||
code: 'ENOTDIR',
|
||||
message: /ENOTDIR: .*mkdir/,
|
||||
name: 'Error',
|
||||
syscall: 'mkdir',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// mkdirp ./
|
||||
{
|
||||
const dir = path.resolve(tmpDir, `${nextdir()}/./${nextdir()}`);
|
||||
await mkdir(dir, { recursive: true });
|
||||
const stats = await stat(dir);
|
||||
assert(stats.isDirectory());
|
||||
}
|
||||
|
||||
// mkdirp ../
|
||||
{
|
||||
const dir = path.resolve(tmpDir, `${nextdir()}/../${nextdir()}`);
|
||||
await mkdir(dir, { recursive: true });
|
||||
const stats = await stat(dir);
|
||||
assert(stats.isDirectory());
|
||||
}
|
||||
|
||||
// fs.mkdirp requires the recursive option to be of type boolean.
|
||||
// Everything else generates an error.
|
||||
{
|
||||
const dir = path.join(tmpDir, nextdir(), nextdir());
|
||||
['', 1, {}, [], null, Symbol('test'), () => {}].forEach((recursive) => {
|
||||
assert.rejects(
|
||||
// mkdir() expects to get a boolean value for options.recursive.
|
||||
async () => mkdir(dir, { recursive }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
).then(common.mustCall());
|
||||
});
|
||||
}
|
||||
|
||||
// `mkdtemp` with invalid numeric prefix
|
||||
{
|
||||
await mkdtemp(path.resolve(tmpDir, 'FOO'));
|
||||
await assert.rejects(
|
||||
// mkdtemp() expects to get a string prefix.
|
||||
async () => mkdtemp(1),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/nodejs/node/issues/38168
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
await assert.rejects(
|
||||
async () => handle.write('abc', 0, 'hex'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: /'encoding' is invalid for data of length 3/
|
||||
}
|
||||
);
|
||||
|
||||
const ret = await handle.write('abcd', 0, 'hex');
|
||||
assert.strictEqual(ret.bytesWritten, 2);
|
||||
});
|
||||
}
|
||||
|
||||
// Test prototype methods calling with contexts other than FileHandle
|
||||
{
|
||||
await executeOnHandle(dest, async (handle) => {
|
||||
await assert.rejects(() => handle.stat.call({}), {
|
||||
code: 'ERR_INTERNAL_ASSERTION',
|
||||
message: /handle must be an instance of FileHandle/
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
doTest().then(common.mustCall());
|
||||
}
|
||||
41
test/js/node/test/parallel/test-fs-read-empty-buffer.js
Normal file
41
test/js/node/test/parallel/test-fs-read-empty-buffer.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const filepath = fixtures.path('x.txt');
|
||||
const fd = fs.openSync(filepath, 'r');
|
||||
const fsPromises = fs.promises;
|
||||
|
||||
const buffer = new Uint8Array();
|
||||
|
||||
assert.throws(
|
||||
() => fs.readSync(fd, buffer, 0, 10, 0),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: 'The argument \'buffer\' is empty and cannot be written. ' +
|
||||
'Received Uint8Array(0) []'
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.read(fd, buffer, 0, 1, 0, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: 'The argument \'buffer\' is empty and cannot be written. ' +
|
||||
'Received Uint8Array(0) []'
|
||||
}
|
||||
);
|
||||
|
||||
(async () => {
|
||||
const filehandle = await fsPromises.open(filepath, 'r');
|
||||
assert.rejects(
|
||||
() => filehandle.read(buffer, 0, 1, 0),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: 'The argument \'buffer\' is empty and cannot be written. ' +
|
||||
'Received Uint8Array(0) []'
|
||||
}
|
||||
).then(common.mustCall());
|
||||
})().then(common.mustCall());
|
||||
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
// Test that concurrent file read streams don’t interfere with each other’s
|
||||
// contents, and that the chunks generated by the reads only retain a
|
||||
// 'reasonable' amount of memory.
|
||||
|
||||
// Refs: https://github.com/nodejs/node/issues/21967
|
||||
|
||||
const filename = fixtures.path('loop.js'); // Some small non-homogeneous file.
|
||||
const content = fs.readFileSync(filename);
|
||||
|
||||
const N = 2000;
|
||||
let started = 0;
|
||||
let done = 0;
|
||||
|
||||
const arrayBuffers = new Set();
|
||||
|
||||
function startRead() {
|
||||
++started;
|
||||
const chunks = [];
|
||||
fs.createReadStream(filename)
|
||||
.on('data', (chunk) => {
|
||||
chunks.push(chunk);
|
||||
arrayBuffers.add(chunk.buffer);
|
||||
})
|
||||
.on('end', common.mustCall(() => {
|
||||
if (started < N)
|
||||
startRead();
|
||||
assert.deepStrictEqual(Buffer.concat(chunks), content);
|
||||
if (++done === N) {
|
||||
const retainedMemory =
|
||||
[...arrayBuffers].map((ab) => ab.byteLength).reduce((a, b) => a + b);
|
||||
assert(retainedMemory / (N * content.length) <= 3,
|
||||
`Retaining ${retainedMemory} bytes in ABs for ${N} ` +
|
||||
`chunks of size ${content.length}`);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Don’t start the reads all at once – that way we would have to allocate
|
||||
// a large amount of memory upfront.
|
||||
for (let i = 0; i < 6; ++i)
|
||||
startRead();
|
||||
63
test/js/node/test/parallel/test-fs-read-stream-err.js
Normal file
63
test/js/node/test/parallel/test-fs-read-stream-err.js
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
const stream = fs.createReadStream(__filename, {
|
||||
bufferSize: 64
|
||||
});
|
||||
const err = new Error('BAM');
|
||||
|
||||
stream.on('error', common.mustCall((err_) => {
|
||||
process.nextTick(common.mustCall(() => {
|
||||
assert.strictEqual(stream.fd, null);
|
||||
assert.strictEqual(err_, err);
|
||||
}));
|
||||
}));
|
||||
|
||||
fs.close = common.mustCall((fd_, cb) => {
|
||||
assert.strictEqual(fd_, stream.fd);
|
||||
process.nextTick(cb);
|
||||
});
|
||||
|
||||
const read = fs.read;
|
||||
fs.read = function() {
|
||||
// First time is ok.
|
||||
read.apply(fs, arguments);
|
||||
// Then it breaks.
|
||||
fs.read = common.mustCall(function() {
|
||||
const cb = arguments[arguments.length - 1];
|
||||
process.nextTick(() => {
|
||||
cb(err);
|
||||
});
|
||||
// It should not be called again!
|
||||
fs.read = () => {
|
||||
throw new Error('BOOM!');
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
stream.on('data', (buf) => {
|
||||
stream.on('data', common.mustNotCall("no more 'data' events should follow"));
|
||||
});
|
||||
154
test/js/node/test/parallel/test-fs-read-stream-file-handle.js
Normal file
154
test/js/node/test/parallel/test-fs-read-stream-file-handle.js
Normal file
@@ -0,0 +1,154 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const file = tmpdir.resolve('read_stream_filehandle_test.txt');
|
||||
const input = 'hello world';
|
||||
|
||||
tmpdir.refresh();
|
||||
fs.writeFileSync(file, input);
|
||||
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
handle.on('close', common.mustCall());
|
||||
const stream = fs.createReadStream(null, { fd: handle });
|
||||
|
||||
let output = '';
|
||||
stream.on('data', common.mustCallAtLeast((data) => {
|
||||
output += data;
|
||||
}));
|
||||
|
||||
stream.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(output, input);
|
||||
}));
|
||||
|
||||
stream.on('close', common.mustCall());
|
||||
}).then(common.mustCall());
|
||||
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
handle.on('close', common.mustCall());
|
||||
const stream = fs.createReadStream(null, { fd: handle });
|
||||
stream.on('data', common.mustNotCall());
|
||||
stream.on('close', common.mustCall());
|
||||
|
||||
return handle.close();
|
||||
}).then(common.mustCall());
|
||||
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
handle.on('close', common.mustCall());
|
||||
const stream = fs.createReadStream(null, { fd: handle });
|
||||
stream.on('close', common.mustCall());
|
||||
|
||||
stream.on('data', common.mustCall(() => {
|
||||
handle.close();
|
||||
}));
|
||||
}).then(common.mustCall());
|
||||
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
handle.on('close', common.mustCall());
|
||||
const stream = fs.createReadStream(null, { fd: handle });
|
||||
stream.on('close', common.mustCall());
|
||||
|
||||
stream.close();
|
||||
}).then(common.mustCall());
|
||||
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
assert.throws(() => {
|
||||
fs.createReadStream(null, { fd: handle, fs });
|
||||
}, {
|
||||
code: 'ERR_METHOD_NOT_IMPLEMENTED',
|
||||
name: 'Error',
|
||||
message: 'The FileHandle with fs method is not implemented'
|
||||
});
|
||||
return handle.close();
|
||||
}).then(common.mustCall());
|
||||
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
const { read: originalReadFunction } = handle;
|
||||
handle.read = common.mustCallAtLeast(function read() {
|
||||
return Reflect.apply(originalReadFunction, this, arguments);
|
||||
});
|
||||
|
||||
const stream = fs.createReadStream(null, { fd: handle });
|
||||
|
||||
let output = '';
|
||||
stream.on('data', common.mustCallAtLeast((data) => {
|
||||
output += data;
|
||||
}));
|
||||
|
||||
stream.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(output, input);
|
||||
}));
|
||||
}).then(common.mustCall());
|
||||
|
||||
// AbortSignal option test
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
const stream = handle.createReadStream({ signal });
|
||||
|
||||
stream.on('data', common.mustNotCall());
|
||||
stream.on('end', common.mustNotCall());
|
||||
|
||||
stream.on('error', common.mustCall((err) => {
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
}));
|
||||
|
||||
stream.on('close', common.mustCall(() => {
|
||||
handle.close();
|
||||
}));
|
||||
|
||||
controller.abort();
|
||||
}).then(common.mustCall());
|
||||
|
||||
// Already-aborted signal test
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
const signal = AbortSignal.abort();
|
||||
const stream = handle.createReadStream({ signal });
|
||||
|
||||
stream.on('data', common.mustNotCall());
|
||||
stream.on('end', common.mustNotCall());
|
||||
|
||||
stream.on('error', common.mustCall((err) => {
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
}));
|
||||
|
||||
stream.on('close', common.mustCall(() => {
|
||||
handle.close();
|
||||
}));
|
||||
}).then(common.mustCall());
|
||||
|
||||
// Invalid signal type test
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
for (const signal of [1, {}, [], '', null, NaN, 1n, () => {}, Symbol(), false, true]) {
|
||||
assert.throws(() => {
|
||||
handle.createReadStream({ signal });
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
}
|
||||
return handle.close();
|
||||
}).then(common.mustCall());
|
||||
|
||||
// Custom abort reason test
|
||||
fs.promises.open(file, 'r').then((handle) => {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
const reason = new Error('some silly abort reason');
|
||||
const stream = handle.createReadStream({ signal });
|
||||
|
||||
stream.on('data', common.mustNotCall());
|
||||
stream.on('end', common.mustNotCall());
|
||||
|
||||
stream.on('error', common.mustCall((err) => {
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
assert.strictEqual(err.cause, reason);
|
||||
}));
|
||||
|
||||
stream.on('close', common.mustCall(() => {
|
||||
handle.close();
|
||||
}));
|
||||
|
||||
controller.abort(reason);
|
||||
}).then(common.mustCall());
|
||||
205
test/js/node/test/parallel/test-fs-read-stream-inherit.js
Normal file
205
test/js/node/test/parallel/test-fs-read-stream-inherit.js
Normal file
@@ -0,0 +1,205 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const fn = fixtures.path('elipses.txt');
|
||||
const rangeFile = fixtures.path('x.txt');
|
||||
|
||||
{
|
||||
let paused = false;
|
||||
|
||||
const file = fs.ReadStream(fn);
|
||||
|
||||
file.on('open', common.mustCall(function(fd) {
|
||||
file.length = 0;
|
||||
assert.strictEqual(typeof fd, 'number');
|
||||
assert.ok(file.readable);
|
||||
|
||||
// GH-535
|
||||
file.pause();
|
||||
file.resume();
|
||||
file.pause();
|
||||
file.resume();
|
||||
}));
|
||||
|
||||
file.on('data', common.mustCallAtLeast(function(data) {
|
||||
assert.ok(data instanceof Buffer);
|
||||
assert.ok(!paused);
|
||||
file.length += data.length;
|
||||
|
||||
paused = true;
|
||||
file.pause();
|
||||
|
||||
setTimeout(function() {
|
||||
paused = false;
|
||||
file.resume();
|
||||
}, 10);
|
||||
}));
|
||||
|
||||
|
||||
file.on('end', common.mustCall());
|
||||
|
||||
|
||||
file.on('close', common.mustCall(function() {
|
||||
assert.strictEqual(file.length, 30000);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file = fs.createReadStream(fn, { __proto__: { encoding: 'utf8' } });
|
||||
file.length = 0;
|
||||
file.on('data', function(data) {
|
||||
assert.strictEqual(typeof data, 'string');
|
||||
file.length += data.length;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
// http://www.fileformat.info/info/unicode/char/2026/index.htm
|
||||
assert.strictEqual(data[i], '\u2026');
|
||||
}
|
||||
});
|
||||
|
||||
file.on('close', common.mustCall(function() {
|
||||
assert.strictEqual(file.length, 10000);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const options = { __proto__: { bufferSize: 1, start: 1, end: 2 } };
|
||||
const file = fs.createReadStream(rangeFile, options);
|
||||
assert.strictEqual(file.start, 1);
|
||||
assert.strictEqual(file.end, 2);
|
||||
let contentRead = '';
|
||||
file.on('data', function(data) {
|
||||
contentRead += data.toString('utf-8');
|
||||
});
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(contentRead, 'yz');
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const options = { __proto__: { bufferSize: 1, start: 1 } };
|
||||
const file = fs.createReadStream(rangeFile, options);
|
||||
assert.strictEqual(file.start, 1);
|
||||
file.data = '';
|
||||
file.on('data', function(data) {
|
||||
file.data += data.toString('utf-8');
|
||||
});
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(file.data, 'yz\n');
|
||||
}));
|
||||
}
|
||||
|
||||
// https://github.com/joyent/node/issues/2320
|
||||
{
|
||||
const options = { __proto__: { bufferSize: 1.23, start: 1 } };
|
||||
const file = fs.createReadStream(rangeFile, options);
|
||||
assert.strictEqual(file.start, 1);
|
||||
file.data = '';
|
||||
file.on('data', function(data) {
|
||||
file.data += data.toString('utf-8');
|
||||
});
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(file.data, 'yz\n');
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const message =
|
||||
'The value of "start" is out of range. It must be <= "end" (here: 2).' +
|
||||
' Received 10';
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.createReadStream(rangeFile, { __proto__: { start: 10, end: 2 } });
|
||||
},
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message,
|
||||
name: 'RangeError'
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const options = { __proto__: { start: 0, end: 0 } };
|
||||
const stream = fs.createReadStream(rangeFile, options);
|
||||
assert.strictEqual(stream.start, 0);
|
||||
assert.strictEqual(stream.end, 0);
|
||||
stream.data = '';
|
||||
|
||||
stream.on('data', function(chunk) {
|
||||
stream.data += chunk;
|
||||
});
|
||||
|
||||
stream.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(stream.data, 'x');
|
||||
}));
|
||||
}
|
||||
|
||||
// Pause and then resume immediately.
|
||||
{
|
||||
const pauseRes = fs.createReadStream(rangeFile);
|
||||
pauseRes.pause();
|
||||
pauseRes.resume();
|
||||
}
|
||||
|
||||
{
|
||||
let data = '';
|
||||
let file =
|
||||
fs.createReadStream(rangeFile, { __proto__: { autoClose: false } });
|
||||
assert.strictEqual(file.autoClose, false);
|
||||
file.on('data', (chunk) => { data += chunk; });
|
||||
file.on('end', common.mustCall(function() {
|
||||
process.nextTick(common.mustCall(function() {
|
||||
assert(!file.closed);
|
||||
assert(!file.destroyed);
|
||||
assert.strictEqual(data, 'xyz\n');
|
||||
fileNext();
|
||||
}));
|
||||
}));
|
||||
|
||||
function fileNext() {
|
||||
// This will tell us if the fd is usable again or not.
|
||||
file = fs.createReadStream(null, { __proto__: { fd: file.fd, start: 0 } });
|
||||
file.data = '';
|
||||
file.on('data', function(data) {
|
||||
file.data += data;
|
||||
});
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(file.data, 'xyz\n');
|
||||
}));
|
||||
}
|
||||
process.on('exit', function() {
|
||||
assert(file.closed);
|
||||
assert(file.destroyed);
|
||||
});
|
||||
}
|
||||
|
||||
// Just to make sure autoClose won't close the stream because of error.
|
||||
{
|
||||
const options = { __proto__: { fd: 13337, autoClose: false } };
|
||||
const file = fs.createReadStream(null, options);
|
||||
file.on('data', common.mustNotCall());
|
||||
file.on('error', common.mustCall());
|
||||
process.on('exit', function() {
|
||||
assert(!file.closed);
|
||||
assert(!file.destroyed);
|
||||
assert(file.fd);
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure stream is destroyed when file does not exist.
|
||||
{
|
||||
const file = fs.createReadStream('/path/to/file/that/does/not/exist');
|
||||
file.on('data', common.mustNotCall());
|
||||
file.on('error', common.mustCall());
|
||||
|
||||
process.on('exit', function() {
|
||||
assert(file.closed);
|
||||
assert(file.destroyed);
|
||||
});
|
||||
}
|
||||
17
test/js/node/test/parallel/test-fs-read-stream-patch-open.js
Normal file
17
test/js/node/test/parallel/test-fs-read-stream-patch-open.js
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
|
||||
common.expectWarning(
|
||||
'DeprecationWarning',
|
||||
'ReadStream.prototype.open() is deprecated', 'DEP0135');
|
||||
const s = fs.createReadStream('asd')
|
||||
// We don't care about errors in this test.
|
||||
.on('error', () => {});
|
||||
s.open();
|
||||
|
||||
process.nextTick(() => {
|
||||
// Allow overriding open().
|
||||
fs.ReadStream.prototype.open = common.mustCall();
|
||||
fs.createReadStream('asd');
|
||||
});
|
||||
82
test/js/node/test/parallel/test-fs-read-stream-pos.js
Normal file
82
test/js/node/test/parallel/test-fs-read-stream-pos.js
Normal file
@@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
|
||||
// Refs: https://github.com/nodejs/node/issues/33940
|
||||
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
const file = tmpdir.resolve('read_stream_pos_test.txt');
|
||||
|
||||
fs.writeFileSync(file, '');
|
||||
|
||||
let counter = 0;
|
||||
|
||||
const writeInterval = setInterval(() => {
|
||||
counter = counter + 1;
|
||||
const line = `hello at ${counter}\n`;
|
||||
fs.writeFileSync(file, line, { flag: 'a' });
|
||||
}, 1);
|
||||
|
||||
const hwm = 10;
|
||||
let bufs = [];
|
||||
let isLow = false;
|
||||
let cur = 0;
|
||||
let stream;
|
||||
|
||||
const readInterval = setInterval(() => {
|
||||
if (stream) return;
|
||||
|
||||
stream = fs.createReadStream(file, {
|
||||
highWaterMark: hwm,
|
||||
start: cur
|
||||
});
|
||||
stream.on('data', common.mustCallAtLeast((chunk) => {
|
||||
cur += chunk.length;
|
||||
bufs.push(chunk);
|
||||
if (isLow) {
|
||||
const brokenLines = Buffer.concat(bufs).toString()
|
||||
.split('\n')
|
||||
.filter((line) => {
|
||||
const s = 'hello at'.slice(0, line.length);
|
||||
if (line && !line.startsWith(s)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
assert.strictEqual(brokenLines.length, 0);
|
||||
exitTest();
|
||||
return;
|
||||
}
|
||||
if (chunk.length !== hwm) {
|
||||
isLow = true;
|
||||
}
|
||||
}));
|
||||
stream.on('end', () => {
|
||||
stream = null;
|
||||
isLow = false;
|
||||
bufs = [];
|
||||
});
|
||||
}, 10);
|
||||
|
||||
// Time longer than 90 seconds to exit safely
|
||||
const endTimer = setTimeout(() => {
|
||||
exitTest();
|
||||
}, 90000);
|
||||
|
||||
const exitTest = () => {
|
||||
clearInterval(readInterval);
|
||||
clearInterval(writeInterval);
|
||||
clearTimeout(endTimer);
|
||||
if (stream && !stream.destroyed) {
|
||||
stream.on('close', () => {
|
||||
process.exit();
|
||||
});
|
||||
stream.destroy();
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
// This test ensures that appropriate TypeError is thrown by createReadStream
|
||||
// when an argument with invalid type is passed
|
||||
|
||||
const example = fixtures.path('x.txt');
|
||||
// Should not throw.
|
||||
fs.createReadStream(example, undefined);
|
||||
fs.createReadStream(example, null);
|
||||
fs.createReadStream(example, 'utf8');
|
||||
fs.createReadStream(example, { encoding: 'utf8' });
|
||||
|
||||
const createReadStreamErr = (path, opt, error) => {
|
||||
assert.throws(() => {
|
||||
fs.createReadStream(path, opt);
|
||||
}, error);
|
||||
};
|
||||
|
||||
const typeError = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
};
|
||||
|
||||
const rangeError = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError'
|
||||
};
|
||||
|
||||
[123, 0, true, false].forEach((opts) =>
|
||||
createReadStreamErr(example, opts, typeError)
|
||||
);
|
||||
|
||||
// Case 0: Should not throw if either start or end is undefined
|
||||
[{}, { start: 0 }, { end: Infinity }].forEach((opts) =>
|
||||
fs.createReadStream(example, opts)
|
||||
);
|
||||
|
||||
// Case 1: Should throw TypeError if either start or end is not of type 'number'
|
||||
[
|
||||
{ start: 'invalid' },
|
||||
{ end: 'invalid' },
|
||||
{ start: 'invalid', end: 'invalid' },
|
||||
].forEach((opts) => createReadStreamErr(example, opts, typeError));
|
||||
|
||||
// Case 2: Should throw RangeError if either start or end is NaN
|
||||
[{ start: NaN }, { end: NaN }, { start: NaN, end: NaN }].forEach((opts) =>
|
||||
createReadStreamErr(example, opts, rangeError)
|
||||
);
|
||||
|
||||
// Case 3: Should throw RangeError if either start or end is negative
|
||||
[{ start: -1 }, { end: -1 }, { start: -1, end: -1 }].forEach((opts) =>
|
||||
createReadStreamErr(example, opts, rangeError)
|
||||
);
|
||||
|
||||
// Case 4: Should throw RangeError if either start or end is fractional
|
||||
[{ start: 0.1 }, { end: 0.1 }, { start: 0.1, end: 0.1 }].forEach((opts) =>
|
||||
createReadStreamErr(example, opts, rangeError)
|
||||
);
|
||||
|
||||
// Case 5: Should not throw if both start and end are whole numbers
|
||||
fs.createReadStream(example, { start: 1, end: 5 });
|
||||
|
||||
// Case 6: Should throw RangeError if start is greater than end
|
||||
createReadStreamErr(example, { start: 5, end: 1 }, rangeError);
|
||||
|
||||
// Case 7: Should throw RangeError if start or end is not safe integer
|
||||
const NOT_SAFE_INTEGER = 2 ** 53;
|
||||
[
|
||||
{ start: NOT_SAFE_INTEGER, end: Infinity },
|
||||
{ start: 0, end: NOT_SAFE_INTEGER },
|
||||
].forEach((opts) =>
|
||||
createReadStreamErr(example, opts, rangeError)
|
||||
);
|
||||
277
test/js/node/test/parallel/test-fs-read-stream.js
Normal file
277
test/js/node/test/parallel/test-fs-read-stream.js
Normal file
@@ -0,0 +1,277 @@
|
||||
// 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 tmpdir = require('../common/tmpdir');
|
||||
|
||||
const child_process = require('child_process');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const fn = fixtures.path('elipses.txt');
|
||||
const rangeFile = fixtures.path('x.txt');
|
||||
|
||||
function test1(options) {
|
||||
let paused = false;
|
||||
let bytesRead = 0;
|
||||
|
||||
const file = fs.createReadStream(fn, options);
|
||||
const fileSize = fs.statSync(fn).size;
|
||||
|
||||
assert.strictEqual(file.bytesRead, 0);
|
||||
|
||||
file.on('open', common.mustCall(function(fd) {
|
||||
file.length = 0;
|
||||
assert.strictEqual(typeof fd, 'number');
|
||||
assert.strictEqual(file.bytesRead, 0);
|
||||
assert.ok(file.readable);
|
||||
|
||||
// GH-535
|
||||
file.pause();
|
||||
file.resume();
|
||||
file.pause();
|
||||
file.resume();
|
||||
}));
|
||||
|
||||
file.on('data', function(data) {
|
||||
assert.ok(data instanceof Buffer);
|
||||
assert.ok(data.byteOffset % 8 === 0);
|
||||
assert.ok(!paused);
|
||||
file.length += data.length;
|
||||
|
||||
bytesRead += data.length;
|
||||
assert.strictEqual(file.bytesRead, bytesRead);
|
||||
|
||||
paused = true;
|
||||
file.pause();
|
||||
|
||||
setTimeout(function() {
|
||||
paused = false;
|
||||
file.resume();
|
||||
}, 10);
|
||||
});
|
||||
|
||||
|
||||
file.on('end', common.mustCall(function(chunk) {
|
||||
assert.strictEqual(bytesRead, fileSize);
|
||||
assert.strictEqual(file.bytesRead, fileSize);
|
||||
}));
|
||||
|
||||
|
||||
file.on('close', common.mustCall(function() {
|
||||
assert.strictEqual(bytesRead, fileSize);
|
||||
assert.strictEqual(file.bytesRead, fileSize);
|
||||
}));
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(file.length, 30000);
|
||||
});
|
||||
}
|
||||
|
||||
test1({});
|
||||
test1({
|
||||
fs: {
|
||||
open: common.mustCall(fs.open),
|
||||
read: common.mustCallAtLeast(fs.read, 1),
|
||||
close: common.mustCall(fs.close),
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
const file = fs.createReadStream(fn, common.mustNotMutateObjectDeep({ encoding: 'utf8' }));
|
||||
file.length = 0;
|
||||
file.on('data', function(data) {
|
||||
assert.strictEqual(typeof data, 'string');
|
||||
file.length += data.length;
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
// http://www.fileformat.info/info/unicode/char/2026/index.htm
|
||||
assert.strictEqual(data[i], '\u2026');
|
||||
}
|
||||
});
|
||||
|
||||
file.on('close', common.mustCall());
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(file.length, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const file =
|
||||
fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ bufferSize: 1, start: 1, end: 2 }));
|
||||
let contentRead = '';
|
||||
file.on('data', function(data) {
|
||||
contentRead += data.toString('utf-8');
|
||||
});
|
||||
file.on('end', common.mustCall(function(data) {
|
||||
assert.strictEqual(contentRead, 'yz');
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ bufferSize: 1, start: 1 }));
|
||||
file.data = '';
|
||||
file.on('data', function(data) {
|
||||
file.data += data.toString('utf-8');
|
||||
});
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(file.data, 'yz\n');
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Ref: https://github.com/nodejs/node-v0.x-archive/issues/2320
|
||||
const file = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ bufferSize: 1.23, start: 1 }));
|
||||
file.data = '';
|
||||
file.on('data', function(data) {
|
||||
file.data += data.toString('utf-8');
|
||||
});
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(file.data, 'yz\n');
|
||||
}));
|
||||
}
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ start: 10, end: 2 }));
|
||||
},
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: 'The value of "start" is out of range. It must be <= "end"' +
|
||||
' (here: 2). Received 10',
|
||||
name: 'RangeError'
|
||||
});
|
||||
|
||||
{
|
||||
const stream = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ start: 0, end: 0 }));
|
||||
stream.data = '';
|
||||
|
||||
stream.on('data', function(chunk) {
|
||||
stream.data += chunk;
|
||||
});
|
||||
|
||||
stream.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(stream.data, 'x');
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Verify that end works when start is not specified.
|
||||
const stream = new fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ end: 1 }));
|
||||
stream.data = '';
|
||||
|
||||
stream.on('data', function(chunk) {
|
||||
stream.data += chunk;
|
||||
});
|
||||
|
||||
stream.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(stream.data, 'xy');
|
||||
}));
|
||||
}
|
||||
|
||||
if (!common.isWindows) {
|
||||
// Verify that end works when start is not specified, and we do not try to
|
||||
// use positioned reads. This makes sure that this keeps working for
|
||||
// non-seekable file descriptors.
|
||||
tmpdir.refresh();
|
||||
const filename = `${tmpdir.path}/foo.pipe`;
|
||||
const mkfifoResult = child_process.spawnSync('mkfifo', [filename]);
|
||||
if (!mkfifoResult.error) {
|
||||
child_process.exec(...common.escapePOSIXShell`echo "xyz foobar" > "${filename}"`);
|
||||
const stream = new fs.createReadStream(filename, common.mustNotMutateObjectDeep({ end: 1 }));
|
||||
stream.data = '';
|
||||
|
||||
stream.on('data', function(chunk) {
|
||||
stream.data += chunk;
|
||||
});
|
||||
|
||||
stream.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(stream.data, 'xy');
|
||||
fs.unlinkSync(filename);
|
||||
}));
|
||||
} else {
|
||||
common.printSkipMessage('mkfifo not available');
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Pause and then resume immediately.
|
||||
const pauseRes = fs.createReadStream(rangeFile);
|
||||
pauseRes.pause();
|
||||
pauseRes.resume();
|
||||
}
|
||||
|
||||
{
|
||||
let file = fs.createReadStream(rangeFile, common.mustNotMutateObjectDeep({ autoClose: false }));
|
||||
let data = '';
|
||||
file.on('data', function(chunk) { data += chunk; });
|
||||
file.on('end', common.mustCall(function() {
|
||||
assert.strictEqual(data, 'xyz\n');
|
||||
process.nextTick(function() {
|
||||
assert(!file.closed);
|
||||
assert(!file.destroyed);
|
||||
fileNext();
|
||||
});
|
||||
}));
|
||||
|
||||
function fileNext() {
|
||||
// This will tell us if the fd is usable again or not.
|
||||
file = fs.createReadStream(null, common.mustNotMutateObjectDeep({ fd: file.fd, start: 0 }));
|
||||
file.data = '';
|
||||
file.on('data', function(data) {
|
||||
file.data += data;
|
||||
});
|
||||
file.on('end', common.mustCall(function(err) {
|
||||
assert.strictEqual(file.data, 'xyz\n');
|
||||
}));
|
||||
process.on('exit', function() {
|
||||
assert(file.closed);
|
||||
assert(file.destroyed);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Just to make sure autoClose won't close the stream because of error.
|
||||
const file = fs.createReadStream(null, common.mustNotMutateObjectDeep({ fd: 13337, autoClose: false }));
|
||||
file.on('data', common.mustNotCall());
|
||||
file.on('error', common.mustCall());
|
||||
process.on('exit', function() {
|
||||
assert(!file.closed);
|
||||
assert(!file.destroyed);
|
||||
assert(file.fd);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure stream is destroyed when file does not exist.
|
||||
const file = fs.createReadStream('/path/to/file/that/does/not/exist');
|
||||
file.on('data', common.mustNotCall());
|
||||
file.on('error', common.mustCall());
|
||||
|
||||
process.on('exit', function() {
|
||||
assert(file.closed);
|
||||
assert(file.destroyed);
|
||||
});
|
||||
}
|
||||
243
test/js/node/test/parallel/test-fs-read-type.js
Normal file
243
test/js/node/test/parallel/test-fs-read-type.js
Normal file
@@ -0,0 +1,243 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const filepath = fixtures.path('x.txt');
|
||||
const fd = fs.openSync(filepath, 'r');
|
||||
const expected = 'xyz\n';
|
||||
|
||||
|
||||
// Error must be thrown with string
|
||||
assert.throws(
|
||||
() => fs.read(fd, expected.length, 0, 'utf-8', common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "buffer" argument must be an instance of Buffer, ' +
|
||||
'TypedArray, or DataView. Received type number (4)'
|
||||
}
|
||||
);
|
||||
|
||||
[true, null, undefined, () => {}, {}].forEach((value) => {
|
||||
assert.throws(() => {
|
||||
fs.read(value,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
0,
|
||||
common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
-1,
|
||||
expected.length,
|
||||
0,
|
||||
common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
NaN,
|
||||
expected.length,
|
||||
0,
|
||||
common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "offset" is out of range. It must be an integer. ' +
|
||||
'Received NaN'
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "length" is out of range. ' +
|
||||
'It must be >= 0. Received -1'
|
||||
});
|
||||
|
||||
[true, () => {}, {}, ''].forEach((value) => {
|
||||
assert.throws(() => {
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
value,
|
||||
common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
});
|
||||
|
||||
[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
|
||||
assert.throws(() => {
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
value,
|
||||
common.mustNotCall());
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError'
|
||||
});
|
||||
});
|
||||
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
0n,
|
||||
common.mustSucceed());
|
||||
|
||||
fs.read(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
2n ** 53n - 1n,
|
||||
common.mustCall((err) => {
|
||||
if (err) {
|
||||
if (common.isIBMi)
|
||||
assert.strictEqual(err.code, 'EOVERFLOW');
|
||||
else
|
||||
assert.strictEqual(err.code, 'EFBIG');
|
||||
}
|
||||
}));
|
||||
|
||||
assert.throws(
|
||||
() => fs.readSync(fd, expected.length, 0, 'utf-8'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "buffer" argument must be an instance of Buffer, ' +
|
||||
'TypedArray, or DataView. Received type number (4)'
|
||||
}
|
||||
);
|
||||
|
||||
[true, null, undefined, () => {}, {}].forEach((value) => {
|
||||
assert.throws(() => {
|
||||
fs.readSync(value,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
0);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
-1,
|
||||
expected.length,
|
||||
0);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
NaN,
|
||||
expected.length,
|
||||
0);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "offset" is out of range. It must be an integer. ' +
|
||||
'Received NaN'
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
-1,
|
||||
0);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "length" is out of range. ' +
|
||||
'It must be >= 0. Received -1'
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length + 1,
|
||||
0);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "length" is out of range. ' +
|
||||
'It must be <= 4. Received 5'
|
||||
});
|
||||
|
||||
[true, () => {}, {}, ''].forEach((value) => {
|
||||
assert.throws(() => {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
value);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
});
|
||||
|
||||
[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
|
||||
assert.throws(() => {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
value);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError'
|
||||
});
|
||||
});
|
||||
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
0n);
|
||||
|
||||
try {
|
||||
fs.readSync(fd,
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
0,
|
||||
expected.length,
|
||||
2n ** 53n - 1n);
|
||||
} catch (err) {
|
||||
// On systems where max file size is below 2^53-1, we'd expect a EFBIG error.
|
||||
// This is not using `assert.throws` because the above call should not raise
|
||||
// any error on systems that allows file of that size.
|
||||
if (err.code !== 'EFBIG' && !(common.isIBMi && err.code === 'EOVERFLOW'))
|
||||
throw err;
|
||||
}
|
||||
102
test/js/node/test/parallel/test-fs-read.js
Normal file
102
test/js/node/test/parallel/test-fs-read.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 fixtures = require('../common/fixtures');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const filepath = fixtures.path('x.txt');
|
||||
const fd = fs.openSync(filepath, 'r');
|
||||
|
||||
const expected = Buffer.from('xyz\n');
|
||||
|
||||
function test(bufferAsync, bufferSync, expected) {
|
||||
fs.read(fd,
|
||||
bufferAsync,
|
||||
0,
|
||||
expected.length,
|
||||
0,
|
||||
common.mustSucceed((bytesRead) => {
|
||||
assert.strictEqual(bytesRead, expected.length);
|
||||
assert.deepStrictEqual(bufferAsync, expected);
|
||||
}));
|
||||
|
||||
const r = fs.readSync(fd, bufferSync, 0, expected.length, 0);
|
||||
assert.deepStrictEqual(bufferSync, expected);
|
||||
assert.strictEqual(r, expected.length);
|
||||
}
|
||||
|
||||
test(Buffer.allocUnsafe(expected.length),
|
||||
Buffer.allocUnsafe(expected.length),
|
||||
expected);
|
||||
|
||||
test(new Uint8Array(expected.length),
|
||||
new Uint8Array(expected.length),
|
||||
Uint8Array.from(expected));
|
||||
|
||||
{
|
||||
// Reading beyond file length (3 in this case) should return no data.
|
||||
// This is a test for a bug where reads > uint32 would return data
|
||||
// from the current position in the file.
|
||||
const pos = 0xffffffff + 1; // max-uint32 + 1
|
||||
const nRead = fs.readSync(fd, Buffer.alloc(1), 0, 1, pos);
|
||||
assert.strictEqual(nRead, 0);
|
||||
|
||||
fs.read(fd, Buffer.alloc(1), 0, 1, pos, common.mustSucceed((nRead) => {
|
||||
assert.strictEqual(nRead, 0);
|
||||
}));
|
||||
}
|
||||
|
||||
assert.throws(() => new fs.Dir(), {
|
||||
code: 'ERR_MISSING_ARGS',
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => fs.read(fd, Buffer.alloc(1), 0, 1, 0),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.read(fd, { buffer: null }, common.mustNotCall()),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' },
|
||||
'throws when options.buffer is null'
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.readSync(fd, { buffer: null }),
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: 'The "buffer" argument must be an instance of Buffer, ' +
|
||||
'TypedArray, or DataView. Received an instance of Object',
|
||||
},
|
||||
'throws when options.buffer is null'
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.read(null, Buffer.alloc(1), 0, 1, 0),
|
||||
{
|
||||
message: 'The "fd" argument must be of type number. Received null',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
const { mustNotMutateObjectDeep } = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const filepath = fixtures.path('x.txt');
|
||||
|
||||
const expected = Buffer.from('xyz\n');
|
||||
|
||||
function runTest(defaultBuffer, options, errorCode = false) {
|
||||
let fd;
|
||||
try {
|
||||
fd = fs.openSync(filepath, 'r');
|
||||
if (errorCode) {
|
||||
assert.throws(
|
||||
() => fs.readSync(fd, defaultBuffer, options),
|
||||
{ code: errorCode }
|
||||
);
|
||||
} else {
|
||||
const result = fs.readSync(fd, defaultBuffer, options);
|
||||
assert.strictEqual(result, expected.length);
|
||||
assert.deepStrictEqual(defaultBuffer, expected);
|
||||
}
|
||||
} finally {
|
||||
if (fd != null) fs.closeSync(fd);
|
||||
}
|
||||
}
|
||||
|
||||
for (const options of [
|
||||
|
||||
// Test options object
|
||||
{ offset: 0 },
|
||||
{ length: expected.length },
|
||||
{ position: 0 },
|
||||
{ offset: 0, length: expected.length },
|
||||
{ offset: 0, position: 0 },
|
||||
{ length: expected.length, position: 0 },
|
||||
{ offset: 0, length: expected.length, position: 0 },
|
||||
|
||||
{ position: null },
|
||||
{ position: -1 },
|
||||
{ position: 0n },
|
||||
|
||||
// Test default params
|
||||
{},
|
||||
null,
|
||||
undefined,
|
||||
|
||||
// Test malicious corner case: it works as {length: 4} but not intentionally
|
||||
new String('4444'),
|
||||
]) {
|
||||
runTest(Buffer.allocUnsafe(expected.length), options);
|
||||
}
|
||||
|
||||
for (const options of [
|
||||
|
||||
// Test various invalid options
|
||||
false,
|
||||
true,
|
||||
Infinity,
|
||||
42n,
|
||||
Symbol(),
|
||||
'amString',
|
||||
[],
|
||||
() => {},
|
||||
|
||||
// Test if arbitrary entity with expected .length is not mistaken for options
|
||||
'4'.repeat(expected.length),
|
||||
[4, 4, 4, 4],
|
||||
]) {
|
||||
runTest(Buffer.allocUnsafe(expected.length), mustNotMutateObjectDeep(options), 'ERR_INVALID_ARG_TYPE');
|
||||
}
|
||||
21
test/js/node/test/parallel/test-fs-readdir-pipe.js
Normal file
21
test/js/node/test/parallel/test-fs-readdir-pipe.js
Normal file
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const { readdir, readdirSync } = require('fs');
|
||||
|
||||
if (!common.isWindows) {
|
||||
common.skip('This test is specific to Windows to test enumerate pipes');
|
||||
}
|
||||
|
||||
// Ref: https://github.com/nodejs/node/issues/56002
|
||||
// This test is specific to Windows.
|
||||
|
||||
const pipe = '\\\\.\\pipe\\';
|
||||
|
||||
const { length } = readdirSync(pipe);
|
||||
assert.ok(length >= 0, `${length} is not greater or equal to 0`);
|
||||
|
||||
readdir(pipe, common.mustSucceed((files) => {
|
||||
assert.ok(files.length >= 0, `${files.length} is not greater or equal to 0`);
|
||||
}));
|
||||
19
test/js/node/test/parallel/test-fs-readdir-stack-overflow.js
Normal file
19
test/js/node/test/parallel/test-fs-readdir-stack-overflow.js
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
function recurse() {
|
||||
fs.readdirSync('.');
|
||||
recurse();
|
||||
}
|
||||
|
||||
assert.throws(
|
||||
() => recurse(),
|
||||
{
|
||||
name: 'RangeError',
|
||||
message: 'Maximum call stack size exceeded'
|
||||
}
|
||||
);
|
||||
132
test/js/node/test/parallel/test-fs-readdir-types.js
Normal file
132
test/js/node/test/parallel/test-fs-readdir-types.js
Normal file
@@ -0,0 +1,132 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const binding = internalBinding('fs');
|
||||
|
||||
const readdirDir = tmpdir.path;
|
||||
const files = ['empty', 'files', 'for', 'just', 'testing'];
|
||||
const constants = require('fs').constants;
|
||||
const types = {
|
||||
isDirectory: constants.UV_DIRENT_DIR,
|
||||
isFile: constants.UV_DIRENT_FILE,
|
||||
isBlockDevice: constants.UV_DIRENT_BLOCK,
|
||||
isCharacterDevice: constants.UV_DIRENT_CHAR,
|
||||
isSymbolicLink: constants.UV_DIRENT_LINK,
|
||||
isFIFO: constants.UV_DIRENT_FIFO,
|
||||
isSocket: constants.UV_DIRENT_SOCKET
|
||||
};
|
||||
const typeMethods = Object.keys(types);
|
||||
|
||||
// Make sure tmp directory is clean
|
||||
tmpdir.refresh();
|
||||
|
||||
// Create the necessary files
|
||||
files.forEach(function(currentFile) {
|
||||
fs.writeFileSync(`${readdirDir}/${currentFile}`, '', 'utf8');
|
||||
});
|
||||
|
||||
|
||||
function assertDirents(dirents) {
|
||||
assert.strictEqual(files.length, dirents.length);
|
||||
for (const [i, dirent] of dirents.entries()) {
|
||||
assert(dirent instanceof fs.Dirent);
|
||||
assert.strictEqual(dirent.name, files[i]);
|
||||
assert.strictEqual(dirent.isFile(), true);
|
||||
assert.strictEqual(dirent.isDirectory(), false);
|
||||
assert.strictEqual(dirent.isSocket(), false);
|
||||
assert.strictEqual(dirent.isBlockDevice(), false);
|
||||
assert.strictEqual(dirent.isCharacterDevice(), false);
|
||||
assert.strictEqual(dirent.isFIFO(), false);
|
||||
assert.strictEqual(dirent.isSymbolicLink(), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the readdir Sync version
|
||||
assertDirents(fs.readdirSync(readdirDir, { withFileTypes: true }));
|
||||
|
||||
fs.readdir(__filename, {
|
||||
withFileTypes: true
|
||||
}, common.mustCall((err) => {
|
||||
assert.throws(
|
||||
() => { throw err; },
|
||||
{
|
||||
code: 'ENOTDIR',
|
||||
name: 'Error',
|
||||
message: `ENOTDIR: not a directory, scandir '${__filename}'`
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
// Check the readdir async version
|
||||
fs.readdir(readdirDir, {
|
||||
withFileTypes: true
|
||||
}, common.mustSucceed((dirents) => {
|
||||
assertDirents(dirents);
|
||||
}));
|
||||
|
||||
(async () => {
|
||||
const dirents = await fs.promises.readdir(readdirDir, {
|
||||
withFileTypes: true
|
||||
});
|
||||
assertDirents(dirents);
|
||||
})().then(common.mustCall());
|
||||
|
||||
// Check that mutating options doesn't affect results
|
||||
(async () => {
|
||||
const options = { withFileTypes: true };
|
||||
const direntsPromise = fs.promises.readdir(readdirDir, options);
|
||||
options.withFileTypes = false;
|
||||
assertDirents(await direntsPromise);
|
||||
})().then(common.mustCall());
|
||||
|
||||
{
|
||||
const options = { recursive: true, withFileTypes: true };
|
||||
fs.readdir(readdirDir, options, common.mustSucceed((dirents) => {
|
||||
assertDirents(dirents);
|
||||
}));
|
||||
options.withFileTypes = false;
|
||||
}
|
||||
|
||||
// Check for correct types when the binding returns unknowns
|
||||
const UNKNOWN = constants.UV_DIRENT_UNKNOWN;
|
||||
const oldReaddir = binding.readdir;
|
||||
process.on('beforeExit', () => { binding.readdir = oldReaddir; });
|
||||
binding.readdir = common.mustCall((path, encoding, types, req, ctx) => {
|
||||
if (req) {
|
||||
const oldCb = req.oncomplete;
|
||||
req.oncomplete = (err, results) => {
|
||||
if (err) {
|
||||
oldCb(err);
|
||||
return;
|
||||
}
|
||||
results[1] = results[1].map(() => UNKNOWN);
|
||||
oldCb(null, results);
|
||||
};
|
||||
oldReaddir(path, encoding, types, req);
|
||||
} else {
|
||||
const results = oldReaddir(path, encoding, types);
|
||||
results[1] = results[1].map(() => UNKNOWN);
|
||||
return results;
|
||||
}
|
||||
}, 2);
|
||||
assertDirents(fs.readdirSync(readdirDir, { withFileTypes: true }));
|
||||
fs.readdir(readdirDir, {
|
||||
withFileTypes: true
|
||||
}, common.mustSucceed((dirents) => {
|
||||
assertDirents(dirents);
|
||||
}));
|
||||
|
||||
// Dirent types
|
||||
for (const method of typeMethods) {
|
||||
const dirent = new fs.Dirent('foo', types[method]);
|
||||
for (const testMethod of typeMethods) {
|
||||
assert.strictEqual(dirent[testMethod](), testMethod === method);
|
||||
}
|
||||
}
|
||||
31
test/js/node/test/parallel/test-fs-readdir-ucs2.js
Normal file
31
test/js/node/test/parallel/test-fs-readdir-ucs2.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.isLinux)
|
||||
common.skip('Test is linux specific.');
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
const filename = '\uD83D\uDC04';
|
||||
const root = Buffer.from(`${tmpdir.path}${path.sep}`);
|
||||
const filebuff = Buffer.from(filename, 'ucs2');
|
||||
const fullpath = Buffer.concat([root, filebuff]);
|
||||
|
||||
try {
|
||||
fs.closeSync(fs.openSync(fullpath, 'w+'));
|
||||
} catch (e) {
|
||||
if (e.code === 'EINVAL')
|
||||
common.skip('test requires filesystem that supports UCS2');
|
||||
throw e;
|
||||
}
|
||||
|
||||
fs.readdir(tmpdir.path, 'ucs2', common.mustSucceed((list) => {
|
||||
assert.strictEqual(list.length, 1);
|
||||
const fn = list[0];
|
||||
assert.deepStrictEqual(Buffer.from(fn, 'ucs2'), filebuff);
|
||||
assert.strictEqual(fn, filename);
|
||||
}));
|
||||
53
test/js/node/test/parallel/test-fs-readdir.js
Normal file
53
test/js/node/test/parallel/test-fs-readdir.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const readdirDir = tmpdir.path;
|
||||
const files = ['empty', 'files', 'for', 'just', 'testing'];
|
||||
|
||||
// Make sure tmp directory is clean
|
||||
tmpdir.refresh();
|
||||
|
||||
// Create the necessary files
|
||||
files.forEach(function(currentFile) {
|
||||
fs.closeSync(fs.openSync(`${readdirDir}/${currentFile}`, 'w'));
|
||||
});
|
||||
|
||||
// Check the readdir Sync version
|
||||
assert.deepStrictEqual(files, fs.readdirSync(readdirDir).sort());
|
||||
|
||||
// Check the readdir async version
|
||||
fs.readdir(readdirDir, common.mustSucceed((f) => {
|
||||
assert.deepStrictEqual(files, f.sort());
|
||||
}));
|
||||
|
||||
// readdir() on file should throw ENOTDIR
|
||||
// https://github.com/joyent/node/issues/1869
|
||||
assert.throws(function() {
|
||||
fs.readdirSync(__filename);
|
||||
}, /Error: ENOTDIR: not a directory/);
|
||||
|
||||
fs.readdir(__filename, common.mustCall(function(e) {
|
||||
assert.strictEqual(e.code, 'ENOTDIR');
|
||||
}));
|
||||
|
||||
[false, 1, [], {}, null, undefined].forEach((i) => {
|
||||
assert.throws(
|
||||
() => fs.readdir(i, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.readdirSync(i),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
65
test/js/node/test/parallel/test-fs-readfile-error.js
Normal file
65
test/js/node/test/parallel/test-fs-readfile-error.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
// Test that fs.readFile fails correctly on a non-existent file.
|
||||
|
||||
// `fs.readFile('/')` does not fail on AIX and FreeBSD because you can open
|
||||
// and read the directory there.
|
||||
if (common.isAIX || common.isFreeBSD)
|
||||
common.skip('platform not supported.');
|
||||
|
||||
const assert = require('assert');
|
||||
const exec = require('child_process').exec;
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
function test(env, cb) {
|
||||
const filename = fixtures.path('test-fs-readfile-error.js');
|
||||
exec(...common.escapePOSIXShell`"${process.execPath}" "${filename}"`, (err, stdout, stderr) => {
|
||||
assert(err);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.notStrictEqual(stderr, '');
|
||||
cb(String(stderr));
|
||||
});
|
||||
}
|
||||
|
||||
test({ NODE_DEBUG: '' }, common.mustCall((data) => {
|
||||
assert.match(data, /EISDIR/);
|
||||
assert.match(data, /test-fs-readfile-error/);
|
||||
}));
|
||||
|
||||
test({ NODE_DEBUG: 'fs' }, common.mustCall((data) => {
|
||||
assert.match(data, /EISDIR/);
|
||||
assert.match(data, /test-fs-readfile-error/);
|
||||
}));
|
||||
|
||||
assert.throws(
|
||||
() => { fs.readFile(() => {}, common.mustNotCall()); },
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: 'The "path" argument must be of type string or an instance of ' +
|
||||
'Buffer or URL. Received function ',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
50
test/js/node/test/parallel/test-fs-readfile-flags.js
Normal file
50
test/js/node/test/parallel/test-fs-readfile-flags.js
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
// Test of fs.readFile with different flags.
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
const emptyFile = tmpdir.resolve('empty.txt');
|
||||
fs.closeSync(fs.openSync(emptyFile, 'w'));
|
||||
|
||||
fs.readFile(
|
||||
emptyFile,
|
||||
// With `a+` the file is created if it does not exist
|
||||
common.mustNotMutateObjectDeep({ encoding: 'utf8', flag: 'a+' }),
|
||||
common.mustCall((err, data) => { assert.strictEqual(data, ''); })
|
||||
);
|
||||
|
||||
fs.readFile(
|
||||
emptyFile,
|
||||
// Like `a+` but fails if the path exists.
|
||||
common.mustNotMutateObjectDeep({ encoding: 'utf8', flag: 'ax+' }),
|
||||
common.mustCall((err, data) => { assert.strictEqual(err.code, 'EEXIST'); })
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const willBeCreated = tmpdir.resolve('will-be-created');
|
||||
|
||||
fs.readFile(
|
||||
willBeCreated,
|
||||
// With `a+` the file is created if it does not exist
|
||||
common.mustNotMutateObjectDeep({ encoding: 'utf8', flag: 'a+' }),
|
||||
common.mustCall((err, data) => { assert.strictEqual(data, ''); })
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const willNotBeCreated = tmpdir.resolve('will-not-be-created');
|
||||
|
||||
fs.readFile(
|
||||
willNotBeCreated,
|
||||
// Default flag is `r`. An exception occurs if the file does not exist.
|
||||
common.mustNotMutateObjectDeep({ encoding: 'utf8' }),
|
||||
common.mustCall((err, data) => { assert.strictEqual(err.code, 'ENOENT'); })
|
||||
);
|
||||
}
|
||||
100
test/js/node/test/parallel/test-fs-readfile.js
Normal file
100
test/js/node/test/parallel/test-fs-readfile.js
Normal file
@@ -0,0 +1,100 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
// This test ensures that fs.readFile correctly returns the
|
||||
// contents of varying-sized files.
|
||||
|
||||
const tmpdir = require('../../test/common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const prefix = `.removeme-fs-readfile-${process.pid}`;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
const fileInfo = [
|
||||
{ name: tmpdir.resolve(`${prefix}-1K.txt`),
|
||||
len: 1024 },
|
||||
{ name: tmpdir.resolve(`${prefix}-64K.txt`),
|
||||
len: 64 * 1024 },
|
||||
{ name: tmpdir.resolve(`${prefix}-64KLessOne.txt`),
|
||||
len: (64 * 1024) - 1 },
|
||||
{ name: tmpdir.resolve(`${prefix}-1M.txt`),
|
||||
len: 1 * 1024 * 1024 },
|
||||
{ name: tmpdir.resolve(`${prefix}-1MPlusOne.txt`),
|
||||
len: (1 * 1024 * 1024) + 1 },
|
||||
];
|
||||
|
||||
// Populate each fileInfo (and file) with unique fill.
|
||||
const sectorSize = 512;
|
||||
for (const e of fileInfo) {
|
||||
e.contents = Buffer.allocUnsafe(e.len);
|
||||
|
||||
// This accounts for anything unusual in Node's implementation of readFile.
|
||||
// Using e.g. 'aa...aa' would miss bugs like Node re-reading
|
||||
// the same section twice instead of two separate sections.
|
||||
for (let offset = 0; offset < e.len; offset += sectorSize) {
|
||||
const fillByte = 256 * Math.random();
|
||||
const nBytesToFill = Math.min(sectorSize, e.len - offset);
|
||||
e.contents.fill(fillByte, offset, offset + nBytesToFill);
|
||||
}
|
||||
|
||||
fs.writeFileSync(e.name, e.contents);
|
||||
}
|
||||
// All files are now populated.
|
||||
|
||||
// Test readFile on each size.
|
||||
for (const e of fileInfo) {
|
||||
fs.readFile(e.name, common.mustCall((err, buf) => {
|
||||
console.log(`Validating readFile on file ${e.name} of length ${e.len}`);
|
||||
assert.ifError(err);
|
||||
assert.deepStrictEqual(buf, e.contents);
|
||||
}));
|
||||
}
|
||||
|
||||
// readFile() and readFileSync() should fail if the file is too big.
|
||||
{
|
||||
const kIoMaxLength = 2 ** 31 - 1;
|
||||
|
||||
if (!tmpdir.hasEnoughSpace(kIoMaxLength)) {
|
||||
// truncateSync() will fail with ENOSPC if there is not enough space.
|
||||
common.printSkipMessage(`Not enough space in ${tmpdir.path}`);
|
||||
} else {
|
||||
const file = tmpdir.resolve(`${prefix}-too-large.txt`);
|
||||
fs.writeFileSync(file, Buffer.from('0'));
|
||||
fs.truncateSync(file, kIoMaxLength + 1);
|
||||
|
||||
fs.readFile(file, common.expectsError({
|
||||
code: 'ERR_FS_FILE_TOO_LARGE',
|
||||
name: 'RangeError',
|
||||
}));
|
||||
assert.throws(() => {
|
||||
fs.readFileSync(file);
|
||||
}, { code: 'ERR_FS_FILE_TOO_LARGE', name: 'RangeError' });
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test cancellation, before
|
||||
const signal = AbortSignal.abort();
|
||||
fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => {
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
}));
|
||||
}
|
||||
{
|
||||
// Test cancellation, during read
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => {
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
}));
|
||||
process.nextTick(() => controller.abort());
|
||||
}
|
||||
{
|
||||
// Verify that if something different than Abortcontroller.signal
|
||||
// is passed, ERR_INVALID_ARG_TYPE is thrown
|
||||
assert.throws(() => {
|
||||
const callback = common.mustNotCall();
|
||||
fs.readFile(fileInfo[0].name, { signal: 'hello' }, callback);
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' });
|
||||
}
|
||||
18
test/js/node/test/parallel/test-fs-readv-promisify.js
Normal file
18
test/js/node/test/parallel/test-fs-readv-promisify.js
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const fs = require('fs');
|
||||
const readv = require('util').promisify(fs.readv);
|
||||
const assert = require('assert');
|
||||
const filepath = fixtures.path('x.txt');
|
||||
const fd = fs.openSync(filepath, 'r');
|
||||
|
||||
const expected = [Buffer.from('xyz\n')];
|
||||
|
||||
readv(fd, expected)
|
||||
.then(function({ bytesRead, buffers }) {
|
||||
assert.deepStrictEqual(bytesRead, expected[0].length);
|
||||
assert.deepStrictEqual(buffers, expected);
|
||||
})
|
||||
.then(common.mustCall());
|
||||
20
test/js/node/test/parallel/test-fs-ready-event-stream.js
Normal file
20
test/js/node/test/parallel/test-fs-ready-event-stream.js
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const readStream = fs.createReadStream(__filename);
|
||||
assert.strictEqual(readStream.pending, true);
|
||||
readStream.on('ready', common.mustCall(() => {
|
||||
assert.strictEqual(readStream.pending, false);
|
||||
}));
|
||||
|
||||
const writeFile = tmpdir.resolve('write-fsreadyevent.txt');
|
||||
tmpdir.refresh();
|
||||
const writeStream = fs.createWriteStream(writeFile, { autoClose: true });
|
||||
assert.strictEqual(writeStream.pending, true);
|
||||
writeStream.on('ready', common.mustCall(() => {
|
||||
assert.strictEqual(writeStream.pending, false);
|
||||
writeStream.end();
|
||||
}));
|
||||
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const string_dir = fs.realpathSync(fixtures.fixturesDir);
|
||||
const buffer_dir = Buffer.from(string_dir);
|
||||
|
||||
const encodings = ['ascii', 'utf8', 'utf16le', 'ucs2',
|
||||
'base64', 'binary', 'hex'];
|
||||
const expected = {};
|
||||
for (const encoding of encodings) {
|
||||
expected[encoding] = buffer_dir.toString(encoding);
|
||||
}
|
||||
|
||||
|
||||
// test sync version
|
||||
let encoding;
|
||||
for (encoding in expected) {
|
||||
const expected_value = expected[encoding];
|
||||
let result;
|
||||
|
||||
result = fs.realpathSync(string_dir, { encoding });
|
||||
assert.strictEqual(result, expected_value);
|
||||
|
||||
result = fs.realpathSync(string_dir, encoding);
|
||||
assert.strictEqual(result, expected_value);
|
||||
|
||||
result = fs.realpathSync(buffer_dir, { encoding });
|
||||
assert.strictEqual(result, expected_value);
|
||||
|
||||
result = fs.realpathSync(buffer_dir, encoding);
|
||||
assert.strictEqual(result, expected_value);
|
||||
}
|
||||
|
||||
let buffer_result;
|
||||
buffer_result = fs.realpathSync(string_dir, { encoding: 'buffer' });
|
||||
assert.deepStrictEqual(buffer_result, buffer_dir);
|
||||
|
||||
buffer_result = fs.realpathSync(string_dir, 'buffer');
|
||||
assert.deepStrictEqual(buffer_result, buffer_dir);
|
||||
|
||||
buffer_result = fs.realpathSync(buffer_dir, { encoding: 'buffer' });
|
||||
assert.deepStrictEqual(buffer_result, buffer_dir);
|
||||
|
||||
buffer_result = fs.realpathSync(buffer_dir, 'buffer');
|
||||
assert.deepStrictEqual(buffer_result, buffer_dir);
|
||||
|
||||
// test async version
|
||||
for (encoding in expected) {
|
||||
const expected_value = expected[encoding];
|
||||
|
||||
fs.realpath(
|
||||
string_dir,
|
||||
{ encoding },
|
||||
common.mustSucceed((res) => {
|
||||
assert.strictEqual(res, expected_value);
|
||||
})
|
||||
);
|
||||
fs.realpath(string_dir, encoding, common.mustSucceed((res) => {
|
||||
assert.strictEqual(res, expected_value);
|
||||
}));
|
||||
fs.realpath(
|
||||
buffer_dir,
|
||||
{ encoding },
|
||||
common.mustSucceed((res) => {
|
||||
assert.strictEqual(res, expected_value);
|
||||
})
|
||||
);
|
||||
fs.realpath(buffer_dir, encoding, common.mustSucceed((res) => {
|
||||
assert.strictEqual(res, expected_value);
|
||||
}));
|
||||
}
|
||||
|
||||
fs.realpath(string_dir, { encoding: 'buffer' }, common.mustSucceed((res) => {
|
||||
assert.deepStrictEqual(res, buffer_dir);
|
||||
}));
|
||||
|
||||
fs.realpath(string_dir, 'buffer', common.mustSucceed((res) => {
|
||||
assert.deepStrictEqual(res, buffer_dir);
|
||||
}));
|
||||
|
||||
fs.realpath(buffer_dir, { encoding: 'buffer' }, common.mustSucceed((res) => {
|
||||
assert.deepStrictEqual(res, buffer_dir);
|
||||
}));
|
||||
|
||||
fs.realpath(buffer_dir, 'buffer', common.mustSucceed((res) => {
|
||||
assert.deepStrictEqual(res, buffer_dir);
|
||||
}));
|
||||
18
test/js/node/test/parallel/test-fs-realpath-native.js
Normal file
18
test/js/node/test/parallel/test-fs-realpath-native.js
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const filename = __filename.toLowerCase();
|
||||
|
||||
assert.strictEqual(
|
||||
fs.realpathSync.native('./test/parallel/test-fs-realpath-native.js')
|
||||
.toLowerCase(),
|
||||
filename);
|
||||
|
||||
fs.realpath.native(
|
||||
'./test/parallel/test-fs-realpath-native.js',
|
||||
common.mustSucceed(function(res) {
|
||||
assert.strictEqual(res.toLowerCase(), filename);
|
||||
assert.strictEqual(this, undefined);
|
||||
}));
|
||||
618
test/js/node/test/parallel/test-fs-realpath.js
Normal file
618
test/js/node/test/parallel/test-fs-realpath.js
Normal file
@@ -0,0 +1,618 @@
|
||||
// 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 fixtures = require('../common/fixtures');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
if (!common.isMainThread)
|
||||
common.skip('process.chdir is not available in Workers');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
let async_completed = 0;
|
||||
let async_expected = 0;
|
||||
const unlink = [];
|
||||
const skipSymlinks = !common.canCreateSymLink();
|
||||
const tmpDir = tmpdir.path;
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
let root = '/';
|
||||
let assertEqualPath = assert.strictEqual;
|
||||
if (common.isWindows) {
|
||||
// Something like "C:\\"
|
||||
root = process.cwd().slice(0, 3);
|
||||
assertEqualPath = function(path_left, path_right, message) {
|
||||
assert
|
||||
.strictEqual(path_left.toLowerCase(), path_right.toLowerCase(), message);
|
||||
};
|
||||
}
|
||||
|
||||
process.nextTick(runTest);
|
||||
|
||||
function tmp(p) {
|
||||
return path.join(tmpDir, p);
|
||||
}
|
||||
|
||||
const targetsAbsDir = path.join(tmpDir, 'targets');
|
||||
const tmpAbsDir = tmpDir;
|
||||
|
||||
// Set up targetsAbsDir and expected subdirectories
|
||||
fs.mkdirSync(targetsAbsDir);
|
||||
fs.mkdirSync(path.join(targetsAbsDir, 'nested-index'));
|
||||
fs.mkdirSync(path.join(targetsAbsDir, 'nested-index', 'one'));
|
||||
fs.mkdirSync(path.join(targetsAbsDir, 'nested-index', 'two'));
|
||||
|
||||
function asynctest(testBlock, args, callback, assertBlock) {
|
||||
async_expected++;
|
||||
testBlock.apply(testBlock, args.concat(function(err) {
|
||||
let ignoreError = false;
|
||||
if (assertBlock) {
|
||||
try {
|
||||
ignoreError = assertBlock.apply(assertBlock, arguments);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
}
|
||||
async_completed++;
|
||||
callback(ignoreError ? null : err);
|
||||
}));
|
||||
}
|
||||
|
||||
// sub-tests:
|
||||
function test_simple_error_callback(realpath, realpathSync, cb) {
|
||||
realpath('/this/path/does/not/exist', common.mustCall(function(err, s) {
|
||||
assert(err);
|
||||
assert(!s);
|
||||
cb();
|
||||
}));
|
||||
}
|
||||
|
||||
function test_simple_error_cb_with_null_options(realpath, realpathSync, cb) {
|
||||
realpath('/this/path/does/not/exist', null, common.mustCall(function(err, s) {
|
||||
assert(err);
|
||||
assert(!s);
|
||||
cb();
|
||||
}));
|
||||
}
|
||||
|
||||
function test_simple_relative_symlink(realpath, realpathSync, callback) {
|
||||
console.log('test_simple_relative_symlink');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
const entry = `${tmpDir}/symlink`;
|
||||
const expected = `${tmpDir}/cycles/root.js`;
|
||||
[
|
||||
[entry, `../${path.basename(tmpDir)}/cycles/root.js`],
|
||||
].forEach(function(t) {
|
||||
try { fs.unlinkSync(t[0]); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
console.log('fs.symlinkSync(%j, %j, %j)', t[1], t[0], 'file');
|
||||
fs.symlinkSync(t[1], t[0], 'file');
|
||||
unlink.push(t[0]);
|
||||
});
|
||||
const result = realpathSync(entry);
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
});
|
||||
}
|
||||
|
||||
function test_simple_absolute_symlink(realpath, realpathSync, callback) {
|
||||
console.log('test_simple_absolute_symlink');
|
||||
|
||||
// This one should still run, even if skipSymlinks is set,
|
||||
// because it uses a junction.
|
||||
const type = skipSymlinks ? 'junction' : 'dir';
|
||||
|
||||
console.log('using type=%s', type);
|
||||
|
||||
const entry = `${tmpAbsDir}/symlink`;
|
||||
const expected = fixtures.path('nested-index', 'one');
|
||||
[
|
||||
[entry, expected],
|
||||
].forEach(function(t) {
|
||||
try { fs.unlinkSync(t[0]); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
console.error('fs.symlinkSync(%j, %j, %j)', t[1], t[0], type);
|
||||
fs.symlinkSync(t[1], t[0], type);
|
||||
unlink.push(t[0]);
|
||||
});
|
||||
const result = realpathSync(entry);
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
});
|
||||
}
|
||||
|
||||
function test_deep_relative_file_symlink(realpath, realpathSync, callback) {
|
||||
console.log('test_deep_relative_file_symlink');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
|
||||
const expected = fixtures.path('cycles', 'root.js');
|
||||
const linkData1 = path
|
||||
.relative(path.join(targetsAbsDir, 'nested-index', 'one'),
|
||||
expected);
|
||||
const linkPath1 = path.join(targetsAbsDir,
|
||||
'nested-index', 'one', 'symlink1.js');
|
||||
try { fs.unlinkSync(linkPath1); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(linkData1, linkPath1, 'file');
|
||||
|
||||
const linkData2 = '../one/symlink1.js';
|
||||
const entry = path.join(targetsAbsDir,
|
||||
'nested-index', 'two', 'symlink1-b.js');
|
||||
try { fs.unlinkSync(entry); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(linkData2, entry, 'file');
|
||||
unlink.push(linkPath1);
|
||||
unlink.push(entry);
|
||||
|
||||
assertEqualPath(realpathSync(entry), path.resolve(expected));
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
});
|
||||
}
|
||||
|
||||
function test_deep_relative_dir_symlink(realpath, realpathSync, callback) {
|
||||
console.log('test_deep_relative_dir_symlink');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
const expected = fixtures.path('cycles', 'folder');
|
||||
const path1b = path.join(targetsAbsDir, 'nested-index', 'one');
|
||||
const linkPath1b = path.join(path1b, 'symlink1-dir');
|
||||
const linkData1b = path.relative(path1b, expected);
|
||||
try { fs.unlinkSync(linkPath1b); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(linkData1b, linkPath1b, 'dir');
|
||||
|
||||
const linkData2b = '../one/symlink1-dir';
|
||||
const entry = path.join(targetsAbsDir,
|
||||
'nested-index', 'two', 'symlink12-dir');
|
||||
try { fs.unlinkSync(entry); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(linkData2b, entry, 'dir');
|
||||
unlink.push(linkPath1b);
|
||||
unlink.push(entry);
|
||||
|
||||
assertEqualPath(realpathSync(entry), path.resolve(expected));
|
||||
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
});
|
||||
}
|
||||
|
||||
function test_cyclic_link_protection(realpath, realpathSync, callback) {
|
||||
console.log('test_cyclic_link_protection');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
const entry = path.join(tmpDir, '/cycles/realpath-3a');
|
||||
[
|
||||
[entry, '../cycles/realpath-3b'],
|
||||
[path.join(tmpDir, '/cycles/realpath-3b'), '../cycles/realpath-3c'],
|
||||
[path.join(tmpDir, '/cycles/realpath-3c'), '../cycles/realpath-3a'],
|
||||
].forEach(function(t) {
|
||||
try { fs.unlinkSync(t[0]); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(t[1], t[0], 'dir');
|
||||
unlink.push(t[0]);
|
||||
});
|
||||
assert.throws(() => {
|
||||
realpathSync(entry);
|
||||
}, { code: 'ELOOP', name: 'Error' });
|
||||
asynctest(
|
||||
realpath, [entry], callback, common.mustCall(function(err, result) {
|
||||
assert.strictEqual(err.path, entry);
|
||||
assert.strictEqual(result, undefined);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
function test_cyclic_link_overprotection(realpath, realpathSync, callback) {
|
||||
console.log('test_cyclic_link_overprotection');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
const cycles = `${tmpDir}/cycles`;
|
||||
const expected = realpathSync(cycles);
|
||||
const folder = `${cycles}/folder`;
|
||||
const link = `${folder}/cycles`;
|
||||
let testPath = cycles;
|
||||
testPath += '/folder/cycles'.repeat(10);
|
||||
try { fs.unlinkSync(link); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(cycles, link, 'dir');
|
||||
unlink.push(link);
|
||||
assertEqualPath(realpathSync(testPath), path.resolve(expected));
|
||||
asynctest(realpath, [testPath], callback, function(er, res) {
|
||||
assertEqualPath(res, path.resolve(expected));
|
||||
});
|
||||
}
|
||||
|
||||
function test_relative_input_cwd(realpath, realpathSync, callback) {
|
||||
console.log('test_relative_input_cwd');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
|
||||
// We need to calculate the relative path to the tmp dir from cwd
|
||||
const entrydir = process.cwd();
|
||||
const entry = path.relative(entrydir,
|
||||
path.join(`${tmpDir}/cycles/realpath-3a`));
|
||||
const expected = `${tmpDir}/cycles/root.js`;
|
||||
[
|
||||
[entry, '../cycles/realpath-3b'],
|
||||
[`${tmpDir}/cycles/realpath-3b`, '../cycles/realpath-3c'],
|
||||
[`${tmpDir}/cycles/realpath-3c`, 'root.js'],
|
||||
].forEach(function(t) {
|
||||
const fn = t[0];
|
||||
console.error('fn=%j', fn);
|
||||
try { fs.unlinkSync(fn); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
const b = path.basename(t[1]);
|
||||
const type = (b === 'root.js' ? 'file' : 'dir');
|
||||
console.log('fs.symlinkSync(%j, %j, %j)', t[1], fn, type);
|
||||
fs.symlinkSync(t[1], fn, 'file');
|
||||
unlink.push(fn);
|
||||
});
|
||||
|
||||
const origcwd = process.cwd();
|
||||
process.chdir(entrydir);
|
||||
assertEqualPath(realpathSync(entry), path.resolve(expected));
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
process.chdir(origcwd);
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function test_deep_symlink_mix(realpath, realpathSync, callback) {
|
||||
console.log('test_deep_symlink_mix');
|
||||
if (common.isWindows) {
|
||||
// This one is a mix of files and directories, and it's quite tricky
|
||||
// to get the file/dir links sorted out correctly.
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return callback();
|
||||
}
|
||||
|
||||
// /tmp/node-test-realpath-f1 -> $tmpDir/node-test-realpath-d1/foo
|
||||
// /tmp/node-test-realpath-d1 -> $tmpDir/node-test-realpath-d2
|
||||
// /tmp/node-test-realpath-d2/foo -> $tmpDir/node-test-realpath-f2
|
||||
// /tmp/node-test-realpath-f2
|
||||
// -> $tmpDir/targets/nested-index/one/realpath-c
|
||||
// $tmpDir/targets/nested-index/one/realpath-c
|
||||
// -> $tmpDir/targets/nested-index/two/realpath-c
|
||||
// $tmpDir/targets/nested-index/two/realpath-c -> $tmpDir/cycles/root.js
|
||||
// $tmpDir/targets/cycles/root.js (hard)
|
||||
|
||||
const entry = tmp('node-test-realpath-f1');
|
||||
try { fs.unlinkSync(tmp('node-test-realpath-d2/foo')); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
try { fs.rmdirSync(tmp('node-test-realpath-d2')); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.mkdirSync(tmp('node-test-realpath-d2'), 0o700);
|
||||
try {
|
||||
[
|
||||
[entry, `${tmpDir}/node-test-realpath-d1/foo`],
|
||||
[tmp('node-test-realpath-d1'),
|
||||
`${tmpDir}/node-test-realpath-d2`],
|
||||
[tmp('node-test-realpath-d2/foo'), '../node-test-realpath-f2'],
|
||||
[tmp('node-test-realpath-f2'),
|
||||
`${targetsAbsDir}/nested-index/one/realpath-c`],
|
||||
[`${targetsAbsDir}/nested-index/one/realpath-c`,
|
||||
`${targetsAbsDir}/nested-index/two/realpath-c`],
|
||||
[`${targetsAbsDir}/nested-index/two/realpath-c`,
|
||||
`${tmpDir}/cycles/root.js`],
|
||||
].forEach(function(t) {
|
||||
try { fs.unlinkSync(t[0]); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
fs.symlinkSync(t[1], t[0]);
|
||||
unlink.push(t[0]);
|
||||
});
|
||||
} finally {
|
||||
unlink.push(tmp('node-test-realpath-d2'));
|
||||
}
|
||||
const expected = `${tmpAbsDir}/cycles/root.js`;
|
||||
assertEqualPath(realpathSync(entry), path.resolve(expected));
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function test_non_symlinks(realpath, realpathSync, callback) {
|
||||
console.log('test_non_symlinks');
|
||||
const entrydir = path.dirname(tmpAbsDir);
|
||||
const entry = `${tmpAbsDir.slice(entrydir.length + 1)}/cycles/root.js`;
|
||||
const expected = `${tmpAbsDir}/cycles/root.js`;
|
||||
const origcwd = process.cwd();
|
||||
process.chdir(entrydir);
|
||||
assertEqualPath(realpathSync(entry), path.resolve(expected));
|
||||
asynctest(realpath, [entry], callback, function(err, result) {
|
||||
process.chdir(origcwd);
|
||||
assertEqualPath(result, path.resolve(expected));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
const upone = path.join(process.cwd(), '..');
|
||||
function test_escape_cwd(realpath, realpathSync, cb) {
|
||||
console.log('test_escape_cwd');
|
||||
asynctest(realpath, ['..'], cb, function(er, uponeActual) {
|
||||
assertEqualPath(
|
||||
upone, uponeActual,
|
||||
`realpath("..") expected: ${path.resolve(upone)} actual:${uponeActual}`);
|
||||
});
|
||||
}
|
||||
|
||||
function test_upone_actual(realpath, realpathSync, cb) {
|
||||
console.log('test_upone_actual');
|
||||
const uponeActual = realpathSync('..');
|
||||
assertEqualPath(upone, uponeActual);
|
||||
cb();
|
||||
}
|
||||
|
||||
// Going up with .. multiple times
|
||||
// .
|
||||
// `-- a/
|
||||
// |-- b/
|
||||
// | `-- e -> ..
|
||||
// `-- d -> ..
|
||||
// realpath(a/b/e/d/a/b/e/d/a) ==> a
|
||||
function test_up_multiple(realpath, realpathSync, cb) {
|
||||
console.error('test_up_multiple');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return cb();
|
||||
}
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
fs.mkdirSync(tmp('a'), 0o755);
|
||||
fs.mkdirSync(tmp('a/b'), 0o755);
|
||||
fs.symlinkSync('..', tmp('a/d'), 'dir');
|
||||
unlink.push(tmp('a/d'));
|
||||
fs.symlinkSync('..', tmp('a/b/e'), 'dir');
|
||||
unlink.push(tmp('a/b/e'));
|
||||
|
||||
const abedabed = tmp('abedabed'.split('').join('/'));
|
||||
const abedabed_real = tmp('');
|
||||
|
||||
const abedabeda = tmp('abedabeda'.split('').join('/'));
|
||||
const abedabeda_real = tmp('a');
|
||||
|
||||
assertEqualPath(realpathSync(abedabeda), abedabeda_real);
|
||||
assertEqualPath(realpathSync(abedabed), abedabed_real);
|
||||
|
||||
realpath(abedabeda, function(er, real) {
|
||||
assert.ifError(er);
|
||||
assertEqualPath(abedabeda_real, real);
|
||||
realpath(abedabed, function(er, real) {
|
||||
assert.ifError(er);
|
||||
assertEqualPath(abedabed_real, real);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Going up with .. multiple times with options = null
|
||||
// .
|
||||
// `-- a/
|
||||
// |-- b/
|
||||
// | `-- e -> ..
|
||||
// `-- d -> ..
|
||||
// realpath(a/b/e/d/a/b/e/d/a) ==> a
|
||||
function test_up_multiple_with_null_options(realpath, realpathSync, cb) {
|
||||
console.error('test_up_multiple');
|
||||
if (skipSymlinks) {
|
||||
common.printSkipMessage('symlink test (no privs)');
|
||||
return cb();
|
||||
}
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
fs.mkdirSync(tmp('a'), 0o755);
|
||||
fs.mkdirSync(tmp('a/b'), 0o755);
|
||||
fs.symlinkSync('..', tmp('a/d'), 'dir');
|
||||
unlink.push(tmp('a/d'));
|
||||
fs.symlinkSync('..', tmp('a/b/e'), 'dir');
|
||||
unlink.push(tmp('a/b/e'));
|
||||
|
||||
const abedabed = tmp('abedabed'.split('').join('/'));
|
||||
const abedabed_real = tmp('');
|
||||
|
||||
const abedabeda = tmp('abedabeda'.split('').join('/'));
|
||||
const abedabeda_real = tmp('a');
|
||||
|
||||
assertEqualPath(realpathSync(abedabeda), abedabeda_real);
|
||||
assertEqualPath(realpathSync(abedabed), abedabed_real);
|
||||
|
||||
realpath(abedabeda, null, function(er, real) {
|
||||
assert.ifError(er);
|
||||
assertEqualPath(abedabeda_real, real);
|
||||
realpath(abedabed, null, function(er, real) {
|
||||
assert.ifError(er);
|
||||
assertEqualPath(abedabed_real, real);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Absolute symlinks with children.
|
||||
// .
|
||||
// `-- a/
|
||||
// |-- b/
|
||||
// | `-- c/
|
||||
// | `-- x.txt
|
||||
// `-- link -> /tmp/node-test-realpath-abs-kids/a/b/
|
||||
// realpath(root+'/a/link/c/x.txt') ==> root+'/a/b/c/x.txt'
|
||||
function test_abs_with_kids(realpath, realpathSync, cb) {
|
||||
console.log('test_abs_with_kids');
|
||||
|
||||
// This one should still run, even if skipSymlinks is set,
|
||||
// because it uses a junction.
|
||||
const type = skipSymlinks ? 'junction' : 'dir';
|
||||
|
||||
console.log('using type=%s', type);
|
||||
|
||||
const root = `${tmpAbsDir}/node-test-realpath-abs-kids`;
|
||||
function cleanup() {
|
||||
['/a/b/c/x.txt',
|
||||
'/a/link',
|
||||
].forEach(function(file) {
|
||||
try { fs.unlinkSync(root + file); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
});
|
||||
['/a/b/c',
|
||||
'/a/b',
|
||||
'/a',
|
||||
'',
|
||||
].forEach(function(folder) {
|
||||
try { fs.rmdirSync(root + folder); } catch {
|
||||
// Continue regardless of error.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setup() {
|
||||
cleanup();
|
||||
['',
|
||||
'/a',
|
||||
'/a/b',
|
||||
'/a/b/c',
|
||||
].forEach(function(folder) {
|
||||
console.log(`mkdir ${root}${folder}`);
|
||||
fs.mkdirSync(root + folder, 0o700);
|
||||
});
|
||||
fs.writeFileSync(`${root}/a/b/c/x.txt`, 'foo');
|
||||
fs.symlinkSync(`${root}/a/b`, `${root}/a/link`, type);
|
||||
}
|
||||
setup();
|
||||
const linkPath = `${root}/a/link/c/x.txt`;
|
||||
const expectPath = `${root}/a/b/c/x.txt`;
|
||||
const actual = realpathSync(linkPath);
|
||||
// console.log({link:linkPath,expect:expectPath,actual:actual},'sync');
|
||||
assertEqualPath(actual, path.resolve(expectPath));
|
||||
asynctest(realpath, [linkPath], cb, function(er, actual) {
|
||||
// console.log({link:linkPath,expect:expectPath,actual:actual},'async');
|
||||
assertEqualPath(actual, path.resolve(expectPath));
|
||||
cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
function test_root(realpath, realpathSync, cb) {
|
||||
assertEqualPath(root, realpathSync('/'));
|
||||
realpath('/', function(err, result) {
|
||||
assert.ifError(err);
|
||||
assertEqualPath(root, result);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
function test_root_with_null_options(realpath, realpathSync, cb) {
|
||||
realpath('/', null, function(err, result) {
|
||||
assert.ifError(err);
|
||||
assertEqualPath(root, result);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const tests = [
|
||||
test_simple_error_callback,
|
||||
test_simple_error_cb_with_null_options,
|
||||
test_simple_relative_symlink,
|
||||
test_simple_absolute_symlink,
|
||||
test_deep_relative_file_symlink,
|
||||
test_deep_relative_dir_symlink,
|
||||
test_cyclic_link_protection,
|
||||
test_cyclic_link_overprotection,
|
||||
test_relative_input_cwd,
|
||||
test_deep_symlink_mix,
|
||||
test_non_symlinks,
|
||||
test_escape_cwd,
|
||||
test_upone_actual,
|
||||
test_abs_with_kids,
|
||||
test_up_multiple,
|
||||
test_up_multiple_with_null_options,
|
||||
test_root,
|
||||
test_root_with_null_options,
|
||||
];
|
||||
const numtests = tests.length;
|
||||
let testsRun = 0;
|
||||
function runNextTest(err) {
|
||||
assert.ifError(err);
|
||||
const test = tests.shift();
|
||||
if (!test) {
|
||||
return console.log(`${numtests} subtests completed OK for fs.realpath`);
|
||||
}
|
||||
testsRun++;
|
||||
test(fs.realpath, fs.realpathSync, common.mustSucceed(() => {
|
||||
testsRun++;
|
||||
test(fs.realpath.native,
|
||||
fs.realpathSync.native,
|
||||
common.mustCall(runNextTest));
|
||||
}));
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
const tmpDirs = ['cycles', 'cycles/folder'];
|
||||
tmpDirs.forEach(function(t) {
|
||||
t = tmp(t);
|
||||
fs.mkdirSync(t, 0o700);
|
||||
});
|
||||
fs.writeFileSync(tmp('cycles/root.js'), "console.error('roooot!');");
|
||||
console.error('start tests');
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(2 * numtests, testsRun);
|
||||
assert.strictEqual(async_completed, async_expected);
|
||||
});
|
||||
42
test/js/node/test/parallel/test-fs-rename-type-check.js
Normal file
42
test/js/node/test/parallel/test-fs-rename-type-check.js
Normal file
@@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
[false, 1, [], {}, null, undefined].forEach((input) => {
|
||||
const type = 'of type string or an instance of Buffer or URL.' +
|
||||
common.invalidArgTypeHelper(input);
|
||||
assert.throws(
|
||||
() => fs.rename(input, 'does-not-exist', common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "oldPath" argument must be ${type}`
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.rename('does-not-exist', input, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "newPath" argument must be ${type}`
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.renameSync(input, 'does-not-exist'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "oldPath" argument must be ${type}`
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.renameSync('does-not-exist', input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "newPath" argument must be ${type}`
|
||||
}
|
||||
);
|
||||
});
|
||||
555
test/js/node/test/parallel/test-fs-rm.js
Normal file
555
test/js/node/test/parallel/test-fs-rm.js
Normal file
@@ -0,0 +1,555 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { pathToFileURL } = require('url');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const { validateRmOptionsSync } = require('internal/fs/utils');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
let count = 0;
|
||||
const nextDirPath = (name = 'rm') =>
|
||||
tmpdir.resolve(`${name}-${count++}`);
|
||||
|
||||
const isGitPresent = (() => {
|
||||
try { execSync('git --version'); return true; } catch { return false; }
|
||||
})();
|
||||
|
||||
function gitInit(gitDirectory) {
|
||||
fs.mkdirSync(gitDirectory);
|
||||
execSync('git init', common.mustNotMutateObjectDeep({ cwd: gitDirectory }));
|
||||
}
|
||||
|
||||
function makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) {
|
||||
fs.mkdirSync(dirname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8');
|
||||
|
||||
const options = common.mustNotMutateObjectDeep({ flag: 'wx' });
|
||||
|
||||
for (let f = files; f > 0; f--) {
|
||||
fs.writeFileSync(path.join(dirname, `f-${depth}-${f}`), '', options);
|
||||
}
|
||||
|
||||
if (createSymLinks) {
|
||||
// Valid symlink
|
||||
fs.symlinkSync(
|
||||
`f-${depth}-1`,
|
||||
path.join(dirname, `link-${depth}-good`),
|
||||
'file'
|
||||
);
|
||||
|
||||
// Invalid symlink
|
||||
fs.symlinkSync(
|
||||
'does-not-exist',
|
||||
path.join(dirname, `link-${depth}-bad`),
|
||||
'file'
|
||||
);
|
||||
|
||||
// Symlinks that form a loop
|
||||
[['a', 'b'], ['b', 'a']].forEach(([x, y]) => {
|
||||
fs.symlinkSync(
|
||||
`link-${depth}-loop-${x}`,
|
||||
path.join(dirname, `link-${depth}-loop-${y}`),
|
||||
'file'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// File with a name that looks like a glob
|
||||
fs.writeFileSync(path.join(dirname, '[a-z0-9].txt'), '', options);
|
||||
|
||||
depth--;
|
||||
if (depth <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let f = folders; f > 0; f--) {
|
||||
fs.mkdirSync(
|
||||
path.join(dirname, `folder-${depth}-${f}`),
|
||||
{ recursive: true }
|
||||
);
|
||||
makeNonEmptyDirectory(
|
||||
depth,
|
||||
files,
|
||||
folders,
|
||||
path.join(dirname, `d-${depth}-${f}`),
|
||||
createSymLinks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function removeAsync(dir) {
|
||||
// Removal should fail without the recursive option.
|
||||
fs.rm(dir, common.mustCall((err) => {
|
||||
assert.strictEqual(err.syscall, 'rm');
|
||||
|
||||
// Removal should fail without the recursive option set to true.
|
||||
fs.rm(dir, common.mustNotMutateObjectDeep({ recursive: false }), common.mustCall((err) => {
|
||||
assert.strictEqual(err.syscall, 'rm');
|
||||
|
||||
// Recursive removal should succeed.
|
||||
fs.rm(dir, common.mustNotMutateObjectDeep({ recursive: true }), common.mustSucceed(() => {
|
||||
|
||||
// Attempted removal should fail now because the directory is gone.
|
||||
fs.rm(dir, common.mustCall((err) => {
|
||||
assert.strictEqual(err.syscall, 'lstat');
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
// Test the asynchronous version
|
||||
{
|
||||
// Create a 4-level folder hierarchy including symlinks
|
||||
let dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
removeAsync(dir);
|
||||
|
||||
// Create a 2-level folder hierarchy without symlinks
|
||||
dir = nextDirPath();
|
||||
makeNonEmptyDirectory(2, 10, 2, dir, false);
|
||||
removeAsync(dir);
|
||||
|
||||
// Same test using URL instead of a path
|
||||
dir = nextDirPath();
|
||||
makeNonEmptyDirectory(2, 10, 2, dir, false);
|
||||
removeAsync(pathToFileURL(dir));
|
||||
|
||||
// Create a flat folder including symlinks
|
||||
dir = nextDirPath();
|
||||
makeNonEmptyDirectory(1, 10, 2, dir, true);
|
||||
removeAsync(dir);
|
||||
|
||||
// Should fail if target does not exist
|
||||
fs.rm(
|
||||
tmpdir.resolve('noexist.txt'),
|
||||
common.mustNotMutateObjectDeep({ recursive: true }),
|
||||
common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
})
|
||||
);
|
||||
|
||||
// Should delete a file
|
||||
const filePath = tmpdir.resolve('rm-async-file.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
fs.rm(filePath, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => {
|
||||
try {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(filePath), false);
|
||||
} finally {
|
||||
fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
}));
|
||||
|
||||
// Should delete a valid symlink
|
||||
const linkTarget = tmpdir.resolve('link-target-async.txt');
|
||||
fs.writeFileSync(linkTarget, '');
|
||||
const validLink = tmpdir.resolve('valid-link-async');
|
||||
fs.symlinkSync(linkTarget, validLink);
|
||||
fs.rm(validLink, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => {
|
||||
try {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(validLink), false);
|
||||
} finally {
|
||||
fs.rmSync(linkTarget, common.mustNotMutateObjectDeep({ force: true }));
|
||||
fs.rmSync(validLink, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
}));
|
||||
|
||||
// Should delete an invalid symlink
|
||||
const invalidLink = tmpdir.resolve('invalid-link-async');
|
||||
fs.symlinkSync('definitely-does-not-exist-async', invalidLink);
|
||||
fs.rm(invalidLink, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => {
|
||||
try {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(invalidLink), false);
|
||||
} finally {
|
||||
fs.rmSync(invalidLink, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
}));
|
||||
|
||||
// Should delete a symlink that is part of a loop
|
||||
const loopLinkA = tmpdir.resolve('loop-link-async-a');
|
||||
const loopLinkB = tmpdir.resolve('loop-link-async-b');
|
||||
fs.symlinkSync(loopLinkA, loopLinkB);
|
||||
fs.symlinkSync(loopLinkB, loopLinkA);
|
||||
fs.rm(loopLinkA, common.mustNotMutateObjectDeep({ recursive: true }), common.mustCall((err) => {
|
||||
try {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(fs.existsSync(loopLinkA), false);
|
||||
} finally {
|
||||
fs.rmSync(loopLinkA, common.mustNotMutateObjectDeep({ force: true }));
|
||||
fs.rmSync(loopLinkB, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Removing a .git directory should not throw an EPERM.
|
||||
// Refs: https://github.com/isaacs/rimraf/issues/21.
|
||||
if (isGitPresent) {
|
||||
const gitDirectory = nextDirPath();
|
||||
gitInit(gitDirectory);
|
||||
fs.rm(gitDirectory, common.mustNotMutateObjectDeep({ recursive: true }), common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.existsSync(gitDirectory), false);
|
||||
}));
|
||||
}
|
||||
|
||||
// Test the synchronous version.
|
||||
{
|
||||
const dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
|
||||
// Removal should fail without the recursive option set to true.
|
||||
assert.throws(() => {
|
||||
fs.rmSync(dir);
|
||||
}, { syscall: 'rm' });
|
||||
assert.throws(() => {
|
||||
fs.rmSync(dir, common.mustNotMutateObjectDeep({ recursive: false }));
|
||||
}, { syscall: 'rm' });
|
||||
|
||||
// Should fail if target does not exist
|
||||
assert.throws(() => {
|
||||
fs.rmSync(tmpdir.resolve('noexist.txt'), common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
}, {
|
||||
code: 'ENOENT',
|
||||
name: 'Error',
|
||||
message: /^ENOENT: no such file or directory, lstat/
|
||||
});
|
||||
|
||||
// Should delete a file
|
||||
const filePath = tmpdir.resolve('rm-file.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
|
||||
try {
|
||||
fs.rmSync(filePath, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(filePath), false);
|
||||
} finally {
|
||||
fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should delete a valid symlink
|
||||
const linkTarget = tmpdir.resolve('link-target.txt');
|
||||
fs.writeFileSync(linkTarget, '');
|
||||
const validLink = tmpdir.resolve('valid-link');
|
||||
fs.symlinkSync(linkTarget, validLink);
|
||||
try {
|
||||
fs.rmSync(validLink);
|
||||
assert.strictEqual(fs.existsSync(validLink), false);
|
||||
} finally {
|
||||
fs.rmSync(linkTarget, common.mustNotMutateObjectDeep({ force: true }));
|
||||
fs.rmSync(validLink, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should delete an invalid symlink
|
||||
const invalidLink = tmpdir.resolve('invalid-link');
|
||||
fs.symlinkSync('definitely-does-not-exist', invalidLink);
|
||||
try {
|
||||
fs.rmSync(invalidLink);
|
||||
assert.strictEqual(fs.existsSync(invalidLink), false);
|
||||
} finally {
|
||||
fs.rmSync(invalidLink, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should delete a symlink that is part of a loop
|
||||
const loopLinkA = tmpdir.resolve('loop-link-a');
|
||||
const loopLinkB = tmpdir.resolve('loop-link-b');
|
||||
fs.symlinkSync(loopLinkA, loopLinkB);
|
||||
fs.symlinkSync(loopLinkB, loopLinkA);
|
||||
try {
|
||||
fs.rmSync(loopLinkA);
|
||||
assert.strictEqual(fs.existsSync(loopLinkA), false);
|
||||
} finally {
|
||||
fs.rmSync(loopLinkA, common.mustNotMutateObjectDeep({ force: true }));
|
||||
fs.rmSync(loopLinkB, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should accept URL
|
||||
const fileURL = tmpdir.fileURL('rm-file.txt');
|
||||
fs.writeFileSync(fileURL, '');
|
||||
|
||||
try {
|
||||
fs.rmSync(fileURL, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(fileURL), false);
|
||||
} finally {
|
||||
fs.rmSync(fileURL, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Recursive removal should succeed.
|
||||
fs.rmSync(dir, { recursive: true });
|
||||
assert.strictEqual(fs.existsSync(dir), false);
|
||||
|
||||
// Attempted removal should fail now because the directory is gone.
|
||||
assert.throws(() => fs.rmSync(dir), { syscall: 'lstat' });
|
||||
}
|
||||
|
||||
// Removing a .git directory should not throw an EPERM.
|
||||
// Refs: https://github.com/isaacs/rimraf/issues/21.
|
||||
if (isGitPresent) {
|
||||
const gitDirectory = nextDirPath();
|
||||
gitInit(gitDirectory);
|
||||
fs.rmSync(gitDirectory, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(gitDirectory), false);
|
||||
}
|
||||
|
||||
// Test the Promises based version.
|
||||
(async () => {
|
||||
const dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
|
||||
// Removal should fail without the recursive option set to true.
|
||||
await assert.rejects(fs.promises.rm(dir), { syscall: 'rm' });
|
||||
await assert.rejects(fs.promises.rm(dir, common.mustNotMutateObjectDeep({ recursive: false })), {
|
||||
syscall: 'rm'
|
||||
});
|
||||
|
||||
// Recursive removal should succeed.
|
||||
await fs.promises.rm(dir, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(dir), false);
|
||||
|
||||
// Attempted removal should fail now because the directory is gone.
|
||||
await assert.rejects(fs.promises.rm(dir), { syscall: 'lstat' });
|
||||
|
||||
// Should fail if target does not exist
|
||||
await assert.rejects(fs.promises.rm(
|
||||
tmpdir.resolve('noexist.txt'),
|
||||
{ recursive: true }
|
||||
), {
|
||||
code: 'ENOENT',
|
||||
name: 'Error',
|
||||
message: /^ENOENT: no such file or directory, lstat/
|
||||
});
|
||||
|
||||
// Should not fail if target does not exist and force option is true
|
||||
await fs.promises.rm(tmpdir.resolve('noexist.txt'), common.mustNotMutateObjectDeep({ force: true }));
|
||||
|
||||
// Should delete file
|
||||
const filePath = tmpdir.resolve('rm-promises-file.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
|
||||
try {
|
||||
await fs.promises.rm(filePath, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(filePath), false);
|
||||
} finally {
|
||||
fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should delete a valid symlink
|
||||
const linkTarget = tmpdir.resolve('link-target-prom.txt');
|
||||
fs.writeFileSync(linkTarget, '');
|
||||
const validLink = tmpdir.resolve('valid-link-prom');
|
||||
fs.symlinkSync(linkTarget, validLink);
|
||||
try {
|
||||
await fs.promises.rm(validLink);
|
||||
assert.strictEqual(fs.existsSync(validLink), false);
|
||||
} finally {
|
||||
fs.rmSync(linkTarget, common.mustNotMutateObjectDeep({ force: true }));
|
||||
fs.rmSync(validLink, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should delete an invalid symlink
|
||||
const invalidLink = tmpdir.resolve('invalid-link-prom');
|
||||
fs.symlinkSync('definitely-does-not-exist-prom', invalidLink);
|
||||
try {
|
||||
await fs.promises.rm(invalidLink);
|
||||
assert.strictEqual(fs.existsSync(invalidLink), false);
|
||||
} finally {
|
||||
fs.rmSync(invalidLink, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should delete a symlink that is part of a loop
|
||||
const loopLinkA = tmpdir.resolve('loop-link-prom-a');
|
||||
const loopLinkB = tmpdir.resolve('loop-link-prom-b');
|
||||
fs.symlinkSync(loopLinkA, loopLinkB);
|
||||
fs.symlinkSync(loopLinkB, loopLinkA);
|
||||
try {
|
||||
await fs.promises.rm(loopLinkA);
|
||||
assert.strictEqual(fs.existsSync(loopLinkA), false);
|
||||
} finally {
|
||||
fs.rmSync(loopLinkA, common.mustNotMutateObjectDeep({ force: true }));
|
||||
fs.rmSync(loopLinkB, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
|
||||
// Should accept URL
|
||||
const fileURL = tmpdir.fileURL('rm-promises-file.txt');
|
||||
fs.writeFileSync(fileURL, '');
|
||||
|
||||
try {
|
||||
await fs.promises.rm(fileURL, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(fileURL), false);
|
||||
} finally {
|
||||
fs.rmSync(fileURL, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}
|
||||
})().then(common.mustCall());
|
||||
|
||||
// Removing a .git directory should not throw an EPERM.
|
||||
// Refs: https://github.com/isaacs/rimraf/issues/21.
|
||||
if (isGitPresent) {
|
||||
(async () => {
|
||||
const gitDirectory = nextDirPath();
|
||||
gitInit(gitDirectory);
|
||||
await fs.promises.rm(gitDirectory, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
assert.strictEqual(fs.existsSync(gitDirectory), false);
|
||||
})().then(common.mustCall());
|
||||
}
|
||||
|
||||
// Test input validation.
|
||||
{
|
||||
const dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
const filePath = (tmpdir.resolve('rm-args-file.txt'));
|
||||
fs.writeFileSync(filePath, '');
|
||||
|
||||
const defaults = {
|
||||
retryDelay: 100,
|
||||
maxRetries: 0,
|
||||
recursive: false,
|
||||
force: false
|
||||
};
|
||||
const modified = {
|
||||
retryDelay: 953,
|
||||
maxRetries: 5,
|
||||
recursive: true,
|
||||
force: false
|
||||
};
|
||||
|
||||
assert.deepStrictEqual(validateRmOptionsSync(filePath), defaults);
|
||||
assert.deepStrictEqual(validateRmOptionsSync(filePath, {}), defaults);
|
||||
assert.deepStrictEqual(validateRmOptionsSync(filePath, modified), modified);
|
||||
assert.deepStrictEqual(validateRmOptionsSync(filePath, {
|
||||
maxRetries: 99
|
||||
}), {
|
||||
retryDelay: 100,
|
||||
maxRetries: 99,
|
||||
recursive: false,
|
||||
force: false
|
||||
});
|
||||
|
||||
[null, 'foo', 5, NaN].forEach((bad) => {
|
||||
assert.throws(() => {
|
||||
validateRmOptionsSync(filePath, bad);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /^The "options" argument must be of type object\./
|
||||
});
|
||||
});
|
||||
|
||||
[undefined, null, 'foo', Infinity, function() {}].forEach((bad) => {
|
||||
assert.throws(() => {
|
||||
validateRmOptionsSync(filePath, { recursive: bad });
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /^The "options\.recursive" property must be of type boolean\./
|
||||
});
|
||||
});
|
||||
|
||||
[undefined, null, 'foo', Infinity, function() {}].forEach((bad) => {
|
||||
assert.throws(() => {
|
||||
validateRmOptionsSync(filePath, { force: bad });
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /^The "options\.force" property must be of type boolean\./
|
||||
});
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
validateRmOptionsSync(filePath, { retryDelay: -1 });
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: /^The value of "options\.retryDelay" is out of range\./
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
validateRmOptionsSync(filePath, { maxRetries: -1 });
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: /^The value of "options\.maxRetries" is out of range\./
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// IBMi has a different access permission mechanism
|
||||
// This test should not be run as `root`
|
||||
if (!common.isIBMi && (common.isWindows || process.getuid() !== 0)) {
|
||||
function makeDirectoryReadOnly(dir, mode) {
|
||||
let accessErrorCode = 'EACCES';
|
||||
if (common.isWindows) {
|
||||
accessErrorCode = 'EPERM';
|
||||
execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC)"`);
|
||||
} else {
|
||||
fs.chmodSync(dir, mode);
|
||||
}
|
||||
return accessErrorCode;
|
||||
}
|
||||
|
||||
function makeDirectoryWritable(dir) {
|
||||
if (fs.existsSync(dir)) {
|
||||
if (common.isWindows) {
|
||||
execSync(`icacls ${dir} /remove:d "everyone"`);
|
||||
} else {
|
||||
fs.chmodSync(dir, 0o777);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Check that deleting a file that cannot be accessed using rmsync throws
|
||||
// https://github.com/nodejs/node/issues/38683
|
||||
const dirname = nextDirPath();
|
||||
const filePath = path.join(dirname, 'text.txt');
|
||||
try {
|
||||
fs.mkdirSync(dirname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
fs.writeFileSync(filePath, 'hello');
|
||||
const code = makeDirectoryReadOnly(dirname, 0o444);
|
||||
assert.throws(() => {
|
||||
fs.rmSync(filePath, common.mustNotMutateObjectDeep({ force: true }));
|
||||
}, {
|
||||
code,
|
||||
name: 'Error',
|
||||
});
|
||||
} finally {
|
||||
makeDirectoryWritable(dirname);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Check endless recursion.
|
||||
// https://github.com/nodejs/node/issues/34580
|
||||
const dirname = nextDirPath();
|
||||
fs.mkdirSync(dirname, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
const root = fs.mkdtempSync(path.join(dirname, 'fs-'));
|
||||
const middle = path.join(root, 'middle');
|
||||
fs.mkdirSync(middle);
|
||||
fs.mkdirSync(path.join(middle, 'leaf')); // Make `middle` non-empty
|
||||
try {
|
||||
const code = makeDirectoryReadOnly(middle, 0o555);
|
||||
try {
|
||||
assert.throws(() => {
|
||||
fs.rmSync(root, common.mustNotMutateObjectDeep({ recursive: true }));
|
||||
}, {
|
||||
code,
|
||||
name: 'Error',
|
||||
});
|
||||
} catch (err) {
|
||||
// Only fail the test if the folder was not deleted.
|
||||
// as in some cases rmSync successfully deletes read-only folders.
|
||||
if (fs.existsSync(root)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
makeDirectoryWritable(middle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
// Should warn when trying to delete a nonexistent path
|
||||
common.expectWarning(
|
||||
'DeprecationWarning',
|
||||
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
|
||||
'will be removed. Use fs.rm(path, { recursive: true }) instead',
|
||||
'DEP0147'
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.rmdirSync(tmpdir.resolve('noexist.txt'),
|
||||
{ recursive: true }),
|
||||
{ code: 'ENOENT' }
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
common.expectWarning(
|
||||
'DeprecationWarning',
|
||||
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
|
||||
'will be removed. Use fs.rm(path, { recursive: true }) instead',
|
||||
'DEP0147'
|
||||
);
|
||||
const filePath = tmpdir.resolve('rmdir-recursive.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
assert.throws(
|
||||
() => fs.rmdirSync(filePath, { recursive: true }),
|
||||
{ code: common.isWindows ? 'ENOENT' : 'ENOTDIR' }
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
assert.throws(
|
||||
() =>
|
||||
fs.rmdirSync(tmpdir.resolve('noexist.txt'), { recursive: true }),
|
||||
{
|
||||
code: 'ENOENT',
|
||||
}
|
||||
);
|
||||
}
|
||||
{
|
||||
fs.rmdir(
|
||||
tmpdir.resolve('noexist.txt'),
|
||||
{ recursive: true },
|
||||
common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
})
|
||||
);
|
||||
}
|
||||
{
|
||||
assert.rejects(
|
||||
() => fs.promises.rmdir(tmpdir.resolve('noexist.txt'),
|
||||
{ recursive: true }),
|
||||
{
|
||||
code: 'ENOENT',
|
||||
}
|
||||
).then(common.mustCall());
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
const code = common.isWindows ? 'ENOENT' : 'ENOTDIR';
|
||||
|
||||
{
|
||||
const filePath = tmpdir.resolve('rmdir-recursive.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
assert.throws(() => fs.rmdirSync(filePath, { recursive: true }), { code });
|
||||
}
|
||||
{
|
||||
const filePath = tmpdir.resolve('rmdir-recursive.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
fs.rmdir(filePath, { recursive: true }, common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, code);
|
||||
}));
|
||||
}
|
||||
{
|
||||
const filePath = tmpdir.resolve('rmdir-recursive.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
assert.rejects(() => fs.promises.rmdir(filePath, { recursive: true }),
|
||||
{ code }).then(common.mustCall());
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
// Should warn when trying to delete a nonexistent path
|
||||
common.expectWarning(
|
||||
'DeprecationWarning',
|
||||
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
|
||||
'will be removed. Use fs.rm(path, { recursive: true }) instead',
|
||||
'DEP0147'
|
||||
);
|
||||
fs.rmdir(
|
||||
tmpdir.resolve('noexist.txt'),
|
||||
{ recursive: true },
|
||||
common.mustCall()
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
common.expectWarning(
|
||||
'DeprecationWarning',
|
||||
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
|
||||
'will be removed. Use fs.rm(path, { recursive: true }) instead',
|
||||
'DEP0147'
|
||||
);
|
||||
const filePath = tmpdir.resolve('rmdir-recursive.txt');
|
||||
fs.writeFileSync(filePath, '');
|
||||
fs.rmdir(filePath, { recursive: true }, common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, common.isWindows ? 'ENOENT' : 'ENOTDIR');
|
||||
}));
|
||||
}
|
||||
219
test/js/node/test/parallel/test-fs-rmdir-recursive.js
Normal file
219
test/js/node/test/parallel/test-fs-rmdir-recursive.js
Normal file
@@ -0,0 +1,219 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { validateRmdirOptions } = require('internal/fs/utils');
|
||||
|
||||
common.expectWarning(
|
||||
'DeprecationWarning',
|
||||
'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' +
|
||||
'will be removed. Use fs.rm(path, { recursive: true }) instead',
|
||||
'DEP0147'
|
||||
);
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
let count = 0;
|
||||
const nextDirPath = (name = 'rmdir-recursive') =>
|
||||
tmpdir.resolve(`${name}-${count++}`);
|
||||
|
||||
function makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) {
|
||||
fs.mkdirSync(dirname, { recursive: true });
|
||||
fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8');
|
||||
|
||||
const options = { flag: 'wx' };
|
||||
|
||||
for (let f = files; f > 0; f--) {
|
||||
fs.writeFileSync(path.join(dirname, `f-${depth}-${f}`), '', options);
|
||||
}
|
||||
|
||||
if (createSymLinks) {
|
||||
// Valid symlink
|
||||
fs.symlinkSync(
|
||||
`f-${depth}-1`,
|
||||
path.join(dirname, `link-${depth}-good`),
|
||||
'file'
|
||||
);
|
||||
|
||||
// Invalid symlink
|
||||
fs.symlinkSync(
|
||||
'does-not-exist',
|
||||
path.join(dirname, `link-${depth}-bad`),
|
||||
'file'
|
||||
);
|
||||
}
|
||||
|
||||
// File with a name that looks like a glob
|
||||
fs.writeFileSync(path.join(dirname, '[a-z0-9].txt'), '', options);
|
||||
|
||||
depth--;
|
||||
if (depth <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let f = folders; f > 0; f--) {
|
||||
fs.mkdirSync(
|
||||
path.join(dirname, `folder-${depth}-${f}`),
|
||||
{ recursive: true }
|
||||
);
|
||||
makeNonEmptyDirectory(
|
||||
depth,
|
||||
files,
|
||||
folders,
|
||||
path.join(dirname, `d-${depth}-${f}`),
|
||||
createSymLinks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function removeAsync(dir) {
|
||||
// Removal should fail without the recursive option.
|
||||
fs.rmdir(dir, common.mustCall((err) => {
|
||||
assert.strictEqual(err.syscall, 'rmdir');
|
||||
|
||||
// Removal should fail without the recursive option set to true.
|
||||
fs.rmdir(dir, { recursive: false }, common.mustCall((err) => {
|
||||
assert.strictEqual(err.syscall, 'rmdir');
|
||||
|
||||
// Recursive removal should succeed.
|
||||
fs.rmdir(dir, { recursive: true }, common.mustSucceed(() => {
|
||||
// An error should occur if recursive and the directory does not exist.
|
||||
fs.rmdir(dir, { recursive: true }, common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
// Attempted removal should fail now because the directory is gone.
|
||||
fs.rmdir(dir, common.mustCall((err) => {
|
||||
assert.strictEqual(err.syscall, 'rmdir');
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
// Test the asynchronous version
|
||||
{
|
||||
// Create a 4-level folder hierarchy including symlinks
|
||||
let dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
removeAsync(dir);
|
||||
|
||||
// Create a 2-level folder hierarchy without symlinks
|
||||
dir = nextDirPath();
|
||||
makeNonEmptyDirectory(2, 10, 2, dir, false);
|
||||
removeAsync(dir);
|
||||
|
||||
// Create a flat folder including symlinks
|
||||
dir = nextDirPath();
|
||||
makeNonEmptyDirectory(1, 10, 2, dir, true);
|
||||
removeAsync(dir);
|
||||
}
|
||||
|
||||
// Test the synchronous version.
|
||||
{
|
||||
const dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
|
||||
// Removal should fail without the recursive option set to true.
|
||||
assert.throws(() => {
|
||||
fs.rmdirSync(dir);
|
||||
}, { syscall: 'rmdir' });
|
||||
assert.throws(() => {
|
||||
fs.rmdirSync(dir, { recursive: false });
|
||||
}, { syscall: 'rmdir' });
|
||||
|
||||
// Recursive removal should succeed.
|
||||
fs.rmdirSync(dir, { recursive: true });
|
||||
|
||||
// An error should occur if recursive and the directory does not exist.
|
||||
assert.throws(() => fs.rmdirSync(dir, { recursive: true }),
|
||||
{ code: 'ENOENT' });
|
||||
|
||||
// Attempted removal should fail now because the directory is gone.
|
||||
assert.throws(() => fs.rmdirSync(dir), { syscall: 'rmdir' });
|
||||
}
|
||||
|
||||
// Test the Promises based version.
|
||||
(async () => {
|
||||
const dir = nextDirPath();
|
||||
makeNonEmptyDirectory(4, 10, 2, dir, true);
|
||||
|
||||
// Removal should fail without the recursive option set to true.
|
||||
await assert.rejects(fs.promises.rmdir(dir), { syscall: 'rmdir' });
|
||||
await assert.rejects(fs.promises.rmdir(dir, { recursive: false }), {
|
||||
syscall: 'rmdir'
|
||||
});
|
||||
|
||||
// Recursive removal should succeed.
|
||||
await fs.promises.rmdir(dir, { recursive: true });
|
||||
|
||||
// An error should occur if recursive and the directory does not exist.
|
||||
await assert.rejects(fs.promises.rmdir(dir, { recursive: true }),
|
||||
{ code: 'ENOENT' });
|
||||
|
||||
// Attempted removal should fail now because the directory is gone.
|
||||
await assert.rejects(fs.promises.rmdir(dir), { syscall: 'rmdir' });
|
||||
})().then(common.mustCall());
|
||||
|
||||
// Test input validation.
|
||||
{
|
||||
const defaults = {
|
||||
retryDelay: 100,
|
||||
maxRetries: 0,
|
||||
recursive: false
|
||||
};
|
||||
const modified = {
|
||||
retryDelay: 953,
|
||||
maxRetries: 5,
|
||||
recursive: true
|
||||
};
|
||||
|
||||
assert.deepStrictEqual(validateRmdirOptions(), defaults);
|
||||
assert.deepStrictEqual(validateRmdirOptions({}), defaults);
|
||||
assert.deepStrictEqual(validateRmdirOptions(modified), modified);
|
||||
assert.deepStrictEqual(validateRmdirOptions({
|
||||
maxRetries: 99
|
||||
}), {
|
||||
retryDelay: 100,
|
||||
maxRetries: 99,
|
||||
recursive: false
|
||||
});
|
||||
|
||||
[null, 'foo', 5, NaN].forEach((bad) => {
|
||||
assert.throws(() => {
|
||||
validateRmdirOptions(bad);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /^The "options" argument must be of type object\./
|
||||
});
|
||||
});
|
||||
|
||||
[undefined, null, 'foo', Infinity, function() {}].forEach((bad) => {
|
||||
assert.throws(() => {
|
||||
validateRmdirOptions({ recursive: bad });
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /^The "options\.recursive" property must be of type boolean\./
|
||||
});
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
validateRmdirOptions({ retryDelay: -1 });
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: /^The value of "options\.retryDelay" is out of range\./
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
validateRmdirOptions({ maxRetries: -1 });
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: /^The value of "options\.maxRetries" is out of range\./
|
||||
});
|
||||
}
|
||||
223
test/js/node/test/parallel/test-fs-stat.js
Normal file
223
test/js/node/test/parallel/test-fs-stat.js
Normal file
@@ -0,0 +1,223 @@
|
||||
// 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 fs = require('fs');
|
||||
|
||||
fs.stat('.', common.mustSucceed(function(stats) {
|
||||
assert.ok(stats.mtime instanceof Date);
|
||||
assert.ok(Object.hasOwn(stats, 'blksize'));
|
||||
assert.ok(Object.hasOwn(stats, 'blocks'));
|
||||
// Confirm that we are not running in the context of the internal binding
|
||||
// layer.
|
||||
// Ref: https://github.com/nodejs/node/commit/463d6bac8b349acc462d345a6e298a76f7d06fb1
|
||||
assert.strictEqual(this, undefined);
|
||||
}));
|
||||
|
||||
fs.lstat('.', common.mustSucceed(function(stats) {
|
||||
assert.ok(stats.mtime instanceof Date);
|
||||
// Confirm that we are not running in the context of the internal binding
|
||||
// layer.
|
||||
// Ref: https://github.com/nodejs/node/commit/463d6bac8b349acc462d345a6e298a76f7d06fb1
|
||||
assert.strictEqual(this, undefined);
|
||||
}));
|
||||
|
||||
// fstat
|
||||
fs.open('.', 'r', undefined, common.mustSucceed(function(fd) {
|
||||
assert.ok(fd);
|
||||
|
||||
fs.fstat(-0, common.mustSucceed());
|
||||
|
||||
fs.fstat(fd, common.mustSucceed(function(stats) {
|
||||
assert.ok(stats.mtime instanceof Date);
|
||||
fs.close(fd, assert.ifError);
|
||||
// Confirm that we are not running in the context of the internal binding
|
||||
// layer.
|
||||
// Ref: https://github.com/nodejs/node/commit/463d6bac8b349acc462d345a6e298a76f7d06fb1
|
||||
assert.strictEqual(this, undefined);
|
||||
}));
|
||||
|
||||
// Confirm that we are not running in the context of the internal binding
|
||||
// layer.
|
||||
// Ref: https://github.com/nodejs/node/commit/463d6bac8b349acc462d345a6e298a76f7d06fb1
|
||||
assert.strictEqual(this, undefined);
|
||||
}));
|
||||
|
||||
// fstatSync
|
||||
fs.open('.', 'r', undefined, common.mustCall(function(err, fd) {
|
||||
const stats = fs.fstatSync(fd);
|
||||
assert.ok(stats.mtime instanceof Date);
|
||||
fs.close(fd, common.mustSucceed());
|
||||
}));
|
||||
|
||||
fs.stat(__filename, common.mustSucceed((s) => {
|
||||
assert.strictEqual(s.isDirectory(), false);
|
||||
assert.strictEqual(s.isFile(), true);
|
||||
assert.strictEqual(s.isSocket(), false);
|
||||
assert.strictEqual(s.isBlockDevice(), false);
|
||||
assert.strictEqual(s.isCharacterDevice(), false);
|
||||
assert.strictEqual(s.isFIFO(), false);
|
||||
assert.strictEqual(s.isSymbolicLink(), false);
|
||||
|
||||
[
|
||||
'dev', 'mode', 'nlink', 'uid',
|
||||
'gid', 'rdev', 'blksize', 'ino', 'size', 'blocks',
|
||||
'atime', 'mtime', 'ctime', 'birthtime',
|
||||
'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs',
|
||||
].forEach(function(k) {
|
||||
assert.ok(k in s, `${k} should be in Stats`);
|
||||
assert.notStrictEqual(s[k], undefined, `${k} should not be undefined`);
|
||||
assert.notStrictEqual(s[k], null, `${k} should not be null`);
|
||||
});
|
||||
[
|
||||
'dev', 'mode', 'nlink', 'uid', 'gid', 'rdev', 'blksize', 'ino', 'size',
|
||||
'blocks', 'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs',
|
||||
].forEach((k) => {
|
||||
assert.strictEqual(typeof s[k], 'number', `${k} should be a number`);
|
||||
});
|
||||
['atime', 'mtime', 'ctime', 'birthtime'].forEach((k) => {
|
||||
assert.ok(s[k] instanceof Date, `${k} should be a Date`);
|
||||
});
|
||||
}));
|
||||
|
||||
['', false, null, undefined, {}, []].forEach((input) => {
|
||||
['fstat', 'fstatSync'].forEach((fnName) => {
|
||||
assert.throws(
|
||||
() => fs[fnName](input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
[false, 1, {}, [], null, undefined].forEach((input) => {
|
||||
assert.throws(
|
||||
() => fs.lstat(input, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.lstatSync(input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.stat(input, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.statSync(input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Should not throw an error
|
||||
fs.stat(__filename, undefined, common.mustCall());
|
||||
|
||||
fs.open(__filename, 'r', undefined, common.mustCall((err, fd) => {
|
||||
// Should not throw an error
|
||||
fs.fstat(fd, undefined, common.mustCall());
|
||||
}));
|
||||
|
||||
// Should not throw an error
|
||||
fs.lstat(__filename, undefined, common.mustCall());
|
||||
|
||||
{
|
||||
fs.Stats(
|
||||
0, // dev
|
||||
0, // mode
|
||||
0, // nlink
|
||||
0, // uid
|
||||
0, // gid
|
||||
0, // rdev
|
||||
0, // blksize
|
||||
0, // ino
|
||||
0, // size
|
||||
0, // blocks
|
||||
Date.UTC(1970, 0, 1, 0, 0, 0), // atime
|
||||
Date.UTC(1970, 0, 1, 0, 0, 0), // mtime
|
||||
Date.UTC(1970, 0, 1, 0, 0, 0), // ctime
|
||||
Date.UTC(1970, 0, 1, 0, 0, 0) // birthtime
|
||||
);
|
||||
common.expectWarning({
|
||||
DeprecationWarning: [
|
||||
['fs.Stats constructor is deprecated.',
|
||||
'DEP0180'],
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// These two tests have an equivalent in ./test-fs-stat-bigint.js
|
||||
|
||||
// Stats Date properties can be set before reading them
|
||||
fs.stat(__filename, 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);
|
||||
}));
|
||||
|
||||
// Stats Date properties can be set after reading them
|
||||
fs.stat(__filename, 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);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
assert.throws(
|
||||
() => fs.fstat(Symbol('test'), () => {}),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
},
|
||||
);
|
||||
}
|
||||
59
test/js/node/test/parallel/test-fs-statfs.js
Normal file
59
test/js/node/test/parallel/test-fs-statfs.js
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function verifyStatFsObject(statfs, isBigint = false) {
|
||||
const valueType = isBigint ? 'bigint' : 'number';
|
||||
|
||||
[
|
||||
'type', 'bsize', 'blocks', 'bfree', 'bavail', 'files', 'ffree',
|
||||
].forEach((k) => {
|
||||
assert.ok(Object.hasOwn(statfs, k));
|
||||
assert.strictEqual(typeof statfs[k], valueType,
|
||||
`${k} should be a ${valueType}`);
|
||||
});
|
||||
}
|
||||
|
||||
fs.statfs(__filename, common.mustSucceed(function(stats) {
|
||||
verifyStatFsObject(stats);
|
||||
assert.strictEqual(this, undefined);
|
||||
}));
|
||||
|
||||
fs.statfs(__filename, { bigint: true }, function(err, stats) {
|
||||
assert.ifError(err);
|
||||
verifyStatFsObject(stats, true);
|
||||
assert.strictEqual(this, undefined);
|
||||
});
|
||||
|
||||
// Synchronous
|
||||
{
|
||||
const statFsObj = fs.statfsSync(__filename);
|
||||
verifyStatFsObject(statFsObj);
|
||||
}
|
||||
|
||||
// Synchronous Bigint
|
||||
{
|
||||
const statFsBigIntObj = fs.statfsSync(__filename, { bigint: true });
|
||||
verifyStatFsObject(statFsBigIntObj, true);
|
||||
}
|
||||
|
||||
[false, 1, {}, [], null, undefined].forEach((input) => {
|
||||
assert.throws(
|
||||
() => fs.statfs(input, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => fs.statfsSync(input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Should not throw an error
|
||||
fs.statfs(__filename, undefined, common.mustCall());
|
||||
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
// Compat error.
|
||||
|
||||
function ReadStream(...args) {
|
||||
fs.ReadStream.call(this, ...args);
|
||||
}
|
||||
Object.setPrototypeOf(ReadStream.prototype, fs.ReadStream.prototype);
|
||||
Object.setPrototypeOf(ReadStream, fs.ReadStream);
|
||||
|
||||
ReadStream.prototype.open = common.mustCall(function ReadStream$open() {
|
||||
const that = this;
|
||||
fs.open(that.path, that.flags, that.mode, (err, fd) => {
|
||||
that.emit('error', err);
|
||||
});
|
||||
});
|
||||
|
||||
const r = new ReadStream('/doesnotexist', { emitClose: true })
|
||||
.on('error', common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(r.destroyed, true);
|
||||
r.on('close', common.mustCall());
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
|
||||
const debuglog = (arg) => {
|
||||
console.log(new Date().toLocaleString(), arg);
|
||||
};
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
// Compat error.
|
||||
debuglog('start test');
|
||||
|
||||
function WriteStream(...args) {
|
||||
debuglog('WriteStream constructor');
|
||||
fs.WriteStream.call(this, ...args);
|
||||
}
|
||||
Object.setPrototypeOf(WriteStream.prototype, fs.WriteStream.prototype);
|
||||
Object.setPrototypeOf(WriteStream, fs.WriteStream);
|
||||
|
||||
WriteStream.prototype.open = common.mustCall(function WriteStream$open() {
|
||||
debuglog('WriteStream open() callback');
|
||||
const that = this;
|
||||
fs.open(that.path, that.flags, that.mode, (err, fd) => {
|
||||
debuglog('inner fs open() callback');
|
||||
that.emit('error', err);
|
||||
});
|
||||
});
|
||||
|
||||
fs.open(`${tmpdir.path}/dummy`, 'wx+', common.mustCall((err, fd) => {
|
||||
debuglog('fs open() callback');
|
||||
assert.ifError(err);
|
||||
fs.close(fd, () => { debuglog(`closed ${fd}`); });
|
||||
const w = new WriteStream(`${tmpdir.path}/dummy`,
|
||||
{ flags: 'wx+', emitClose: true })
|
||||
.on('error', common.mustCall((err) => {
|
||||
debuglog('error event callback');
|
||||
assert.strictEqual(err.code, 'EEXIST');
|
||||
w.destroy();
|
||||
w.on('close', common.mustCall(() => {
|
||||
debuglog('close event callback');
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
debuglog('waiting for callbacks');
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
// Compat with graceful-fs.
|
||||
|
||||
function ReadStream(...args) {
|
||||
fs.ReadStream.call(this, ...args);
|
||||
}
|
||||
Object.setPrototypeOf(ReadStream.prototype, fs.ReadStream.prototype);
|
||||
Object.setPrototypeOf(ReadStream, fs.ReadStream);
|
||||
|
||||
ReadStream.prototype.open = common.mustCall(function ReadStream$open() {
|
||||
const that = this;
|
||||
fs.open(that.path, that.flags, that.mode, (err, fd) => {
|
||||
if (err) {
|
||||
if (that.autoClose)
|
||||
that.destroy();
|
||||
|
||||
that.emit('error', err);
|
||||
} else {
|
||||
that.fd = fd;
|
||||
that.emit('open', fd);
|
||||
that.read();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const r = new ReadStream(fixtures.path('x.txt'))
|
||||
.on('open', common.mustCall((fd) => {
|
||||
assert.strictEqual(fd, r.fd);
|
||||
r.destroy();
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Compat with graceful-fs.
|
||||
|
||||
function WriteStream(...args) {
|
||||
fs.WriteStream.call(this, ...args);
|
||||
}
|
||||
Object.setPrototypeOf(WriteStream.prototype, fs.WriteStream.prototype);
|
||||
Object.setPrototypeOf(WriteStream, fs.WriteStream);
|
||||
|
||||
WriteStream.prototype.open = common.mustCall(function WriteStream$open() {
|
||||
const that = this;
|
||||
fs.open(that.path, that.flags, that.mode, function(err, fd) {
|
||||
if (err) {
|
||||
that.destroy();
|
||||
that.emit('error', err);
|
||||
} else {
|
||||
that.fd = fd;
|
||||
that.emit('open', fd);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const w = new WriteStream(`${tmpdir.path}/dummy`)
|
||||
.on('open', common.mustCall((fd) => {
|
||||
assert.strictEqual(fd, w.fd);
|
||||
w.destroy();
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
// Compat with old node.
|
||||
|
||||
function ReadStream(...args) {
|
||||
fs.ReadStream.call(this, ...args);
|
||||
}
|
||||
Object.setPrototypeOf(ReadStream.prototype, fs.ReadStream.prototype);
|
||||
Object.setPrototypeOf(ReadStream, fs.ReadStream);
|
||||
|
||||
ReadStream.prototype.open = common.mustCall(function() {
|
||||
fs.open(this.path, this.flags, this.mode, (er, fd) => {
|
||||
if (er) {
|
||||
if (this.autoClose) {
|
||||
this.destroy();
|
||||
}
|
||||
this.emit('error', er);
|
||||
return;
|
||||
}
|
||||
|
||||
this.fd = fd;
|
||||
this.emit('open', fd);
|
||||
this.emit('ready');
|
||||
});
|
||||
});
|
||||
|
||||
let readyCalled = false;
|
||||
let ticked = false;
|
||||
const r = new ReadStream(fixtures.path('x.txt'))
|
||||
.on('ready', common.mustCall(() => {
|
||||
readyCalled = true;
|
||||
// Make sure 'ready' is emitted in same tick as 'open'.
|
||||
assert.strictEqual(ticked, false);
|
||||
}))
|
||||
.on('error', common.mustNotCall())
|
||||
.on('open', common.mustCall((fd) => {
|
||||
process.nextTick(() => {
|
||||
ticked = true;
|
||||
r.destroy();
|
||||
});
|
||||
assert.strictEqual(readyCalled, false);
|
||||
assert.strictEqual(fd, r.fd);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Compat with old node.
|
||||
|
||||
function WriteStream(...args) {
|
||||
fs.WriteStream.call(this, ...args);
|
||||
}
|
||||
Object.setPrototypeOf(WriteStream.prototype, fs.WriteStream.prototype);
|
||||
Object.setPrototypeOf(WriteStream, fs.WriteStream);
|
||||
|
||||
WriteStream.prototype.open = common.mustCall(function() {
|
||||
fs.open(this.path, this.flags, this.mode, (er, fd) => {
|
||||
if (er) {
|
||||
if (this.autoClose) {
|
||||
this.destroy();
|
||||
}
|
||||
this.emit('error', er);
|
||||
return;
|
||||
}
|
||||
|
||||
this.fd = fd;
|
||||
this.emit('open', fd);
|
||||
this.emit('ready');
|
||||
});
|
||||
});
|
||||
|
||||
let readyCalled = false;
|
||||
let ticked = false;
|
||||
const w = new WriteStream(`${tmpdir.path}/dummy`)
|
||||
.on('ready', common.mustCall(() => {
|
||||
readyCalled = true;
|
||||
// Make sure 'ready' is emitted in same tick as 'open'.
|
||||
assert.strictEqual(ticked, false);
|
||||
}))
|
||||
.on('error', common.mustNotCall())
|
||||
.on('open', common.mustCall((fd) => {
|
||||
process.nextTick(() => {
|
||||
ticked = true;
|
||||
w.destroy();
|
||||
});
|
||||
assert.strictEqual(readyCalled, false);
|
||||
assert.strictEqual(fd, w.fd);
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
const stream = fs.createReadStream(__filename);
|
||||
stream.on('close', common.mustCall());
|
||||
test(stream);
|
||||
}
|
||||
|
||||
{
|
||||
const stream = fs.createWriteStream(`${tmpdir.path}/dummy`);
|
||||
stream.on('close', common.mustCall());
|
||||
test(stream);
|
||||
}
|
||||
|
||||
{
|
||||
const stream = fs.createReadStream(__filename, { emitClose: true });
|
||||
stream.on('close', common.mustCall());
|
||||
test(stream);
|
||||
}
|
||||
|
||||
{
|
||||
const stream = fs.createWriteStream(`${tmpdir.path}/dummy2`,
|
||||
{ emitClose: true });
|
||||
stream.on('close', common.mustCall());
|
||||
test(stream);
|
||||
}
|
||||
|
||||
|
||||
function test(stream) {
|
||||
const err = new Error('DESTROYED');
|
||||
stream.on('open', function() {
|
||||
stream.destroy(err);
|
||||
});
|
||||
stream.on('error', common.mustCall(function(err_) {
|
||||
assert.strictEqual(err_, err);
|
||||
}));
|
||||
}
|
||||
72
test/js/node/test/parallel/test-fs-stream-fs-options.js
Normal file
72
test/js/node/test/parallel/test-fs-stream-fs-options.js
Normal file
@@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
const streamOpts = ['open', 'close'];
|
||||
const writeStreamOptions = [...streamOpts, 'write'];
|
||||
const readStreamOptions = [...streamOpts, 'read'];
|
||||
const originalFs = { fs };
|
||||
|
||||
{
|
||||
const file = tmpdir.resolve('write-end-test0.txt');
|
||||
|
||||
writeStreamOptions.forEach((fn) => {
|
||||
const overrideFs = Object.assign({}, originalFs.fs, { [fn]: null });
|
||||
if (fn === 'write') overrideFs.writev = null;
|
||||
|
||||
const opts = {
|
||||
fs: overrideFs
|
||||
};
|
||||
assert.throws(
|
||||
() => fs.createWriteStream(file, opts), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "options.fs.${fn}" property must be of type function. ` +
|
||||
'Received null'
|
||||
},
|
||||
`createWriteStream options.fs.${fn} should throw if isn't a function`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const file = tmpdir.resolve('write-end-test0.txt');
|
||||
const overrideFs = Object.assign({}, originalFs.fs, { writev: 'not a fn' });
|
||||
const opts = {
|
||||
fs: overrideFs
|
||||
};
|
||||
assert.throws(
|
||||
() => fs.createWriteStream(file, opts), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options.fs.writev" property must be of type function. ' +
|
||||
'Received type string (\'not a fn\')'
|
||||
},
|
||||
'createWriteStream options.fs.writev should throw if isn\'t a function'
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const file = fixtures.path('x.txt');
|
||||
readStreamOptions.forEach((fn) => {
|
||||
const overrideFs = Object.assign({}, originalFs.fs, { [fn]: null });
|
||||
const opts = {
|
||||
fs: overrideFs
|
||||
};
|
||||
assert.throws(
|
||||
() => fs.createReadStream(file, opts), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "options.fs.${fn}" property must be of type function. ` +
|
||||
'Received null'
|
||||
},
|
||||
`createReadStream options.fs.${fn} should throw if isn't a function`
|
||||
);
|
||||
});
|
||||
}
|
||||
49
test/js/node/test/parallel/test-fs-stream-options.js
Normal file
49
test/js/node/test/parallel/test-fs-stream-options.js
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
const { mustNotMutateObjectDeep } = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
{
|
||||
const fd = 'k';
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.createReadStream(null, mustNotMutateObjectDeep({ fd }));
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.createWriteStream(null, mustNotMutateObjectDeep({ fd }));
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const path = 46;
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.createReadStream(path);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs.createWriteStream(path);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
}
|
||||
102
test/js/node/test/parallel/test-fs-symlink.js
Normal file
102
test/js/node/test/parallel/test-fs-symlink.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 fixtures = require('../common/fixtures');
|
||||
if (!common.canCreateSymLink())
|
||||
common.skip('insufficient privileges');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
let linkTime;
|
||||
let fileTime;
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
// Test creating and reading symbolic link
|
||||
const linkData = fixtures.path('/cycles/root.js');
|
||||
const linkPath = tmpdir.resolve('symlink1.js');
|
||||
|
||||
fs.symlink(linkData, linkPath, common.mustSucceed(() => {
|
||||
fs.lstat(linkPath, common.mustSucceed((stats) => {
|
||||
linkTime = stats.mtime.getTime();
|
||||
}));
|
||||
|
||||
fs.stat(linkPath, common.mustSucceed((stats) => {
|
||||
fileTime = stats.mtime.getTime();
|
||||
}));
|
||||
|
||||
fs.readlink(linkPath, common.mustSucceed((destination) => {
|
||||
assert.strictEqual(destination, linkData);
|
||||
}));
|
||||
}));
|
||||
|
||||
// Test invalid symlink
|
||||
{
|
||||
const linkData = fixtures.path('/not/exists/file');
|
||||
const linkPath = tmpdir.resolve('symlink2.js');
|
||||
|
||||
fs.symlink(linkData, linkPath, common.mustSucceed(() => {
|
||||
assert(!fs.existsSync(linkPath));
|
||||
}));
|
||||
}
|
||||
|
||||
[false, 1, {}, [], null, undefined].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /target|path/
|
||||
};
|
||||
assert.throws(() => fs.symlink(input, '', common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.symlinkSync(input, ''), errObj);
|
||||
|
||||
assert.throws(() => fs.symlink('', input, common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.symlinkSync('', input), errObj);
|
||||
});
|
||||
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
name: 'TypeError',
|
||||
};
|
||||
assert.throws(() => fs.symlink('', '', '🍏', common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.symlinkSync('', '', '🍏'), errObj);
|
||||
|
||||
assert.throws(() => fs.symlink('', '', 'nonExistentType', common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.symlinkSync('', '', 'nonExistentType'), errObj);
|
||||
assert.rejects(() => fs.promises.symlink('', '', 'nonExistentType'), errObj)
|
||||
.then(common.mustCall());
|
||||
|
||||
assert.throws(() => fs.symlink('', '', false, common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.symlinkSync('', '', false), errObj);
|
||||
assert.rejects(() => fs.promises.symlink('', '', false), errObj)
|
||||
.then(common.mustCall());
|
||||
|
||||
assert.throws(() => fs.symlink('', '', {}, common.mustNotCall()), errObj);
|
||||
assert.throws(() => fs.symlinkSync('', '', {}), errObj);
|
||||
assert.rejects(() => fs.promises.symlink('', '', {}), errObj)
|
||||
.then(common.mustCall());
|
||||
|
||||
process.on('exit', () => {
|
||||
assert.notStrictEqual(linkTime, fileTime);
|
||||
});
|
||||
86
test/js/node/test/parallel/test-fs-sync-fd-leak.js
Normal file
86
test/js/node/test/parallel/test-fs-sync-fd-leak.js
Normal file
@@ -0,0 +1,86 @@
|
||||
// Flags: --expose-internals
|
||||
// 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';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
|
||||
// Ensure that (read|write|append)FileSync() closes the file descriptor
|
||||
fs.openSync = function() {
|
||||
return 42;
|
||||
};
|
||||
fs.closeSync = function(fd) {
|
||||
assert.strictEqual(fd, 42);
|
||||
close_called++;
|
||||
};
|
||||
fs.readSync = function() {
|
||||
throw new Error('BAM');
|
||||
};
|
||||
fs.writeSync = function() {
|
||||
throw new Error('BAM');
|
||||
};
|
||||
|
||||
// Internal fast paths are pure C++, can't error inside write
|
||||
internalBinding('fs').writeFileUtf8 = function() {
|
||||
// Fake close
|
||||
close_called++;
|
||||
throw new Error('BAM');
|
||||
};
|
||||
|
||||
internalBinding('fs').fstat = function() {
|
||||
throw new Error('EBADF: bad file descriptor, fstat');
|
||||
};
|
||||
|
||||
let close_called = 0;
|
||||
ensureThrows(function() {
|
||||
// Fast path: writeFileSync utf8
|
||||
fs.writeFileSync('dummy', 'xxx');
|
||||
}, 'BAM');
|
||||
ensureThrows(function() {
|
||||
// Non-fast path
|
||||
fs.writeFileSync('dummy', 'xxx', { encoding: 'base64' });
|
||||
}, 'BAM');
|
||||
ensureThrows(function() {
|
||||
// Fast path: writeFileSync utf8
|
||||
fs.appendFileSync('dummy', 'xxx');
|
||||
}, 'BAM');
|
||||
ensureThrows(function() {
|
||||
// Non-fast path
|
||||
fs.appendFileSync('dummy', 'xxx', { encoding: 'base64' });
|
||||
}, 'BAM');
|
||||
|
||||
function ensureThrows(cb, message) {
|
||||
let got_exception = false;
|
||||
|
||||
close_called = 0;
|
||||
try {
|
||||
cb();
|
||||
} catch (e) {
|
||||
assert.strictEqual(e.message, message);
|
||||
got_exception = true;
|
||||
}
|
||||
|
||||
assert.strictEqual(close_called, 1);
|
||||
assert.strictEqual(got_exception, true);
|
||||
}
|
||||
40
test/js/node/test/parallel/test-fs-syncwritestream.js
Normal file
40
test/js/node/test/parallel/test-fs-syncwritestream.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const spawn = require('child_process').spawn;
|
||||
const stream = require('stream');
|
||||
const fs = require('fs');
|
||||
|
||||
// require('internal/fs/utils').SyncWriteStream is used as a stdio
|
||||
// implementation when stdout/stderr point to files.
|
||||
|
||||
if (process.argv[2] === 'child') {
|
||||
// Note: Calling console.log() is part of this test as it exercises the
|
||||
// SyncWriteStream#_write() code path.
|
||||
console.log(JSON.stringify([process.stdout, process.stderr].map((stdio) => ({
|
||||
instance: stdio instanceof stream.Writable,
|
||||
readable: stdio.readable,
|
||||
writable: stdio.writable,
|
||||
}))));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
const filename = tmpdir.resolve('stdout');
|
||||
const stdoutFd = fs.openSync(filename, 'w');
|
||||
|
||||
const proc = spawn(process.execPath, [__filename, 'child'], {
|
||||
stdio: ['inherit', stdoutFd, stdoutFd ]
|
||||
});
|
||||
|
||||
proc.on('close', common.mustCall(() => {
|
||||
fs.closeSync(stdoutFd);
|
||||
|
||||
assert.deepStrictEqual(JSON.parse(fs.readFileSync(filename, 'utf8')), [
|
||||
{ instance: true, readable: false, writable: true },
|
||||
{ instance: true, readable: false, writable: true },
|
||||
]);
|
||||
}));
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
for (const input of [Infinity, -Infinity, NaN]) {
|
||||
assert.throws(
|
||||
() => {
|
||||
fs._toUnixTimestamp(input);
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
}
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
fs._toUnixTimestamp({});
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError'
|
||||
});
|
||||
|
||||
const okInputs = [1, -1, '1', '-1', Date.now()];
|
||||
for (const input of okInputs) {
|
||||
fs._toUnixTimestamp(input);
|
||||
}
|
||||
27
test/js/node/test/parallel/test-fs-truncate-fd.js
Normal file
27
test/js/node/test/parallel/test-fs-truncate-fd.js
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const tmp = tmpdir.path;
|
||||
tmpdir.refresh();
|
||||
const filename = path.resolve(tmp, 'truncate-file.txt');
|
||||
|
||||
fs.writeFileSync(filename, 'hello world', 'utf8');
|
||||
const fd = fs.openSync(filename, 'r+');
|
||||
|
||||
const msg = 'Using fs.truncate with a file descriptor is deprecated.' +
|
||||
' Please use fs.ftruncate with a file descriptor instead.';
|
||||
|
||||
|
||||
common.expectWarning('DeprecationWarning', msg, 'DEP0081');
|
||||
fs.truncate(fd, 5, common.mustSucceed(() => {
|
||||
assert.strictEqual(fs.readFileSync(filename, 'utf8'), 'hello');
|
||||
}));
|
||||
|
||||
process.once('beforeExit', () => {
|
||||
fs.closeSync(fd);
|
||||
fs.unlinkSync(filename);
|
||||
console.log('ok');
|
||||
});
|
||||
298
test/js/node/test/parallel/test-fs-truncate.js
Normal file
298
test/js/node/test/parallel/test-fs-truncate.js
Normal file
@@ -0,0 +1,298 @@
|
||||
// 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 path = require('path');
|
||||
const fs = require('fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const tmp = tmpdir.path;
|
||||
const filename = path.resolve(tmp, 'truncate-file.txt');
|
||||
const data = Buffer.alloc(1024 * 16, 'x');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
let stat;
|
||||
|
||||
const msg = 'Using fs.truncate with a file descriptor is deprecated.' +
|
||||
' Please use fs.ftruncate with a file descriptor instead.';
|
||||
|
||||
// Check truncateSync
|
||||
fs.writeFileSync(filename, data);
|
||||
stat = fs.statSync(filename);
|
||||
assert.strictEqual(stat.size, 1024 * 16);
|
||||
|
||||
fs.truncateSync(filename, 1024);
|
||||
stat = fs.statSync(filename);
|
||||
assert.strictEqual(stat.size, 1024);
|
||||
|
||||
fs.truncateSync(filename);
|
||||
stat = fs.statSync(filename);
|
||||
assert.strictEqual(stat.size, 0);
|
||||
|
||||
// Check ftruncateSync
|
||||
fs.writeFileSync(filename, data);
|
||||
const fd = fs.openSync(filename, 'r+');
|
||||
|
||||
stat = fs.statSync(filename);
|
||||
assert.strictEqual(stat.size, 1024 * 16);
|
||||
|
||||
fs.ftruncateSync(fd, 1024);
|
||||
stat = fs.statSync(filename);
|
||||
assert.strictEqual(stat.size, 1024);
|
||||
|
||||
fs.ftruncateSync(fd);
|
||||
stat = fs.statSync(filename);
|
||||
assert.strictEqual(stat.size, 0);
|
||||
|
||||
// truncateSync
|
||||
common.expectWarning('DeprecationWarning', msg, 'DEP0081');
|
||||
fs.truncateSync(fd);
|
||||
|
||||
fs.closeSync(fd);
|
||||
|
||||
// Async tests
|
||||
testTruncate(common.mustSucceed(() => {
|
||||
testFtruncate(common.mustSucceed());
|
||||
}));
|
||||
|
||||
function testTruncate(cb) {
|
||||
fs.writeFile(filename, data, function(er) {
|
||||
if (er) return cb(er);
|
||||
fs.stat(filename, function(er, stat) {
|
||||
if (er) return cb(er);
|
||||
assert.strictEqual(stat.size, 1024 * 16);
|
||||
|
||||
fs.truncate(filename, 1024, function(er) {
|
||||
if (er) return cb(er);
|
||||
fs.stat(filename, function(er, stat) {
|
||||
if (er) return cb(er);
|
||||
assert.strictEqual(stat.size, 1024);
|
||||
|
||||
fs.truncate(filename, function(er) {
|
||||
if (er) return cb(er);
|
||||
fs.stat(filename, function(er, stat) {
|
||||
if (er) return cb(er);
|
||||
assert.strictEqual(stat.size, 0);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testFtruncate(cb) {
|
||||
fs.writeFile(filename, data, function(er) {
|
||||
if (er) return cb(er);
|
||||
fs.stat(filename, function(er, stat) {
|
||||
if (er) return cb(er);
|
||||
assert.strictEqual(stat.size, 1024 * 16);
|
||||
|
||||
fs.open(filename, 'w', function(er, fd) {
|
||||
if (er) return cb(er);
|
||||
fs.ftruncate(fd, 1024, function(er) {
|
||||
if (er) return cb(er);
|
||||
fs.stat(filename, function(er, stat) {
|
||||
if (er) return cb(er);
|
||||
assert.strictEqual(stat.size, 1024);
|
||||
|
||||
fs.ftruncate(fd, function(er) {
|
||||
if (er) return cb(er);
|
||||
fs.stat(filename, function(er, stat) {
|
||||
if (er) return cb(er);
|
||||
assert.strictEqual(stat.size, 0);
|
||||
fs.close(fd, cb);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure if the size of the file is smaller than the length then it is
|
||||
// filled with zeroes.
|
||||
|
||||
{
|
||||
const file1 = path.resolve(tmp, 'truncate-file-1.txt');
|
||||
fs.writeFileSync(file1, 'Hi');
|
||||
fs.truncateSync(file1, 4);
|
||||
assert(fs.readFileSync(file1).equals(Buffer.from('Hi\u0000\u0000')));
|
||||
}
|
||||
|
||||
{
|
||||
const file2 = path.resolve(tmp, 'truncate-file-2.txt');
|
||||
fs.writeFileSync(file2, 'Hi');
|
||||
const fd = fs.openSync(file2, 'r+');
|
||||
process.on('beforeExit', () => fs.closeSync(fd));
|
||||
fs.ftruncateSync(fd, 4);
|
||||
assert(fs.readFileSync(file2).equals(Buffer.from('Hi\u0000\u0000')));
|
||||
}
|
||||
|
||||
{
|
||||
const file3 = path.resolve(tmp, 'truncate-file-3.txt');
|
||||
fs.writeFileSync(file3, 'Hi');
|
||||
fs.truncate(file3, 4, common.mustSucceed(() => {
|
||||
assert(fs.readFileSync(file3).equals(Buffer.from('Hi\u0000\u0000')));
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file4 = path.resolve(tmp, 'truncate-file-4.txt');
|
||||
fs.writeFileSync(file4, 'Hi');
|
||||
const fd = fs.openSync(file4, 'r+');
|
||||
process.on('beforeExit', () => fs.closeSync(fd));
|
||||
fs.ftruncate(fd, 4, common.mustSucceed(() => {
|
||||
assert(fs.readFileSync(file4).equals(Buffer.from('Hi\u0000\u0000')));
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file5 = path.resolve(tmp, 'truncate-file-5.txt');
|
||||
fs.writeFileSync(file5, 'Hi');
|
||||
const fd = fs.openSync(file5, 'r+');
|
||||
process.on('beforeExit', () => fs.closeSync(fd));
|
||||
|
||||
['', false, null, {}, []].forEach((input) => {
|
||||
const received = common.invalidArgTypeHelper(input);
|
||||
assert.throws(
|
||||
() => fs.truncate(file5, input, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "len" argument must be of type number.${received}`
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.ftruncate(fd, input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: `The "len" argument must be of type number.${received}`
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
[-1.5, 1.5].forEach((input) => {
|
||||
assert.throws(
|
||||
() => fs.truncate(file5, input),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "len" is out of range. It must be ' +
|
||||
`an integer. Received ${input}`
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => fs.ftruncate(fd, input),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "len" is out of range. It must be ' +
|
||||
`an integer. Received ${input}`
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
fs.ftruncate(fd, undefined, common.mustSucceed(() => {
|
||||
assert(fs.readFileSync(file5).equals(Buffer.from('')));
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file6 = path.resolve(tmp, 'truncate-file-6.txt');
|
||||
fs.writeFileSync(file6, 'Hi');
|
||||
const fd = fs.openSync(file6, 'r+');
|
||||
process.on('beforeExit', () => fs.closeSync(fd));
|
||||
fs.ftruncate(fd, -1, common.mustSucceed(() => {
|
||||
assert(fs.readFileSync(file6).equals(Buffer.from('')));
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file7 = path.resolve(tmp, 'truncate-file-7.txt');
|
||||
fs.writeFileSync(file7, 'Hi');
|
||||
fs.truncate(file7, undefined, common.mustSucceed(() => {
|
||||
assert(fs.readFileSync(file7).equals(Buffer.from('')));
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
const file8 = path.resolve(tmp, 'non-existent-truncate-file.txt');
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(file8, err.path);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, open '${file8}'`);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'open');
|
||||
return true;
|
||||
};
|
||||
fs.truncate(file8, 0, common.mustCall(validateError));
|
||||
}
|
||||
|
||||
['', false, null, {}, []].forEach((input) => {
|
||||
assert.throws(
|
||||
() => fs.truncate('/foo/bar', input),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "len" argument must be of type number.' +
|
||||
common.invalidArgTypeHelper(input)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
['', false, null, undefined, {}, []].forEach((input) => {
|
||||
['ftruncate', 'ftruncateSync'].forEach((fnName) => {
|
||||
assert.throws(
|
||||
() => fs[fnName](input, 1, () => {}),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "fd" argument must be of type number.' +
|
||||
common.invalidArgTypeHelper(input)
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
{
|
||||
const file1 = path.resolve(tmp, 'truncate-file-1.txt');
|
||||
fs.writeFileSync(file1, 'Hi');
|
||||
fs.truncateSync(file1, -1); // Negative coerced to 0, No error.
|
||||
assert(fs.readFileSync(file1).equals(Buffer.alloc(0)));
|
||||
}
|
||||
|
||||
{
|
||||
const file1 = path.resolve(tmp, 'truncate-file-2.txt');
|
||||
fs.writeFileSync(file1, 'Hi');
|
||||
// Negative coerced to 0, No error.
|
||||
fs.truncate(file1, -1, common.mustSucceed(() => {
|
||||
assert(fs.readFileSync(file1).equals(Buffer.alloc(0)));
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const {
|
||||
validateOffsetLengthRead,
|
||||
validateOffsetLengthWrite,
|
||||
} = require('internal/fs/utils');
|
||||
|
||||
{
|
||||
const offset = -1;
|
||||
assert.throws(
|
||||
() => validateOffsetLengthRead(offset, 0, 0),
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "offset" is out of range. ' +
|
||||
`It must be >= 0. Received ${offset}`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const length = -1;
|
||||
assert.throws(
|
||||
() => validateOffsetLengthRead(0, length, 0),
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "length" is out of range. ' +
|
||||
`It must be >= 0. Received ${length}`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const offset = 1;
|
||||
const length = 1;
|
||||
const byteLength = offset + length - 1;
|
||||
assert.throws(
|
||||
() => validateOffsetLengthRead(offset, length, byteLength),
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "length" is out of range. ' +
|
||||
`It must be <= ${byteLength - offset}. Received ${length}`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Most platforms don't allow reads or writes >= 2 GiB.
|
||||
// See https://github.com/libuv/libuv/pull/1501.
|
||||
const kIoMaxLength = 2 ** 31 - 1;
|
||||
|
||||
// RangeError when offset > byteLength
|
||||
{
|
||||
const offset = 100;
|
||||
const length = 100;
|
||||
const byteLength = 50;
|
||||
assert.throws(
|
||||
() => validateOffsetLengthWrite(offset, length, byteLength),
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "offset" is out of range. ' +
|
||||
`It must be <= ${byteLength}. Received ${offset}`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// RangeError when byteLength < kIoMaxLength, and length > byteLength - offset.
|
||||
{
|
||||
const offset = kIoMaxLength - 150;
|
||||
const length = 200;
|
||||
const byteLength = kIoMaxLength - 100;
|
||||
assert.throws(
|
||||
() => validateOffsetLengthWrite(offset, length, byteLength),
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError',
|
||||
message: 'The value of "length" is out of range. ' +
|
||||
`It must be <= ${byteLength - offset}. Received ${length}`
|
||||
})
|
||||
);
|
||||
}
|
||||
138
test/js/node/test/parallel/test-fs-utils-get-dirents.js
Normal file
138
test/js/node/test/parallel/test-fs-utils-get-dirents.js
Normal file
@@ -0,0 +1,138 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const { getDirents, getDirent } = require('internal/fs/utils');
|
||||
const assert = require('assert');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const { UV_DIRENT_UNKNOWN } = internalBinding('constants').fs;
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const filename = 'foo';
|
||||
|
||||
{
|
||||
// setup
|
||||
tmpdir.refresh();
|
||||
fs.writeFileSync(tmpdir.resolve(filename), '');
|
||||
}
|
||||
// getDirents
|
||||
{
|
||||
// string + string
|
||||
getDirents(
|
||||
tmpdir.path,
|
||||
[[filename], [UV_DIRENT_UNKNOWN]],
|
||||
common.mustCall((err, names) => {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(names.length, 1);
|
||||
},
|
||||
));
|
||||
}
|
||||
{
|
||||
// string + Buffer
|
||||
getDirents(
|
||||
tmpdir.path,
|
||||
[[Buffer.from(filename)], [UV_DIRENT_UNKNOWN]],
|
||||
common.mustCall((err, names) => {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(names.length, 1);
|
||||
},
|
||||
));
|
||||
}
|
||||
{
|
||||
// Buffer + Buffer
|
||||
getDirents(
|
||||
Buffer.from(tmpdir.path),
|
||||
[[Buffer.from(filename)], [UV_DIRENT_UNKNOWN]],
|
||||
common.mustCall((err, names) => {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(names.length, 1);
|
||||
},
|
||||
));
|
||||
}
|
||||
{
|
||||
// wrong combination
|
||||
getDirents(
|
||||
42,
|
||||
[[Buffer.from(filename)], [UV_DIRENT_UNKNOWN]],
|
||||
common.mustCall((err) => {
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
[
|
||||
'The "path" argument must be of type string or an ' +
|
||||
'instance of Buffer. Received type number (42)',
|
||||
].join(''));
|
||||
},
|
||||
));
|
||||
}
|
||||
// getDirent
|
||||
{
|
||||
// string + string
|
||||
getDirent(
|
||||
tmpdir.path,
|
||||
filename,
|
||||
UV_DIRENT_UNKNOWN,
|
||||
common.mustCall((err, dirent) => {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(dirent.name, filename);
|
||||
assert.strictEqual(dirent.parentPath, tmpdir.path);
|
||||
},
|
||||
));
|
||||
}
|
||||
{
|
||||
// Reassigning `.path` property should not trigger a warning
|
||||
const dirent = getDirent(
|
||||
tmpdir.path,
|
||||
filename,
|
||||
UV_DIRENT_UNKNOWN,
|
||||
);
|
||||
assert.strictEqual(dirent.name, filename);
|
||||
dirent.path = 'some other value';
|
||||
assert.strictEqual(dirent.parentPath, tmpdir.path);
|
||||
assert.strictEqual(dirent.path, 'some other value');
|
||||
}
|
||||
{
|
||||
// string + Buffer
|
||||
const filenameBuffer = Buffer.from(filename);
|
||||
getDirent(
|
||||
tmpdir.path,
|
||||
filenameBuffer,
|
||||
UV_DIRENT_UNKNOWN,
|
||||
common.mustCall((err, dirent) => {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(dirent.name, filenameBuffer);
|
||||
assert.strictEqual(dirent.parentPath, tmpdir.path);
|
||||
},
|
||||
));
|
||||
}
|
||||
{
|
||||
// Buffer + Buffer
|
||||
const filenameBuffer = Buffer.from(filename);
|
||||
const dirnameBuffer = Buffer.from(tmpdir.path);
|
||||
getDirent(
|
||||
dirnameBuffer,
|
||||
filenameBuffer,
|
||||
UV_DIRENT_UNKNOWN,
|
||||
common.mustCall((err, dirent) => {
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(dirent.name, filenameBuffer);
|
||||
assert.deepStrictEqual(dirent.parentPath, dirnameBuffer);
|
||||
},
|
||||
));
|
||||
}
|
||||
{
|
||||
// wrong combination
|
||||
getDirent(
|
||||
42,
|
||||
Buffer.from(filename),
|
||||
UV_DIRENT_UNKNOWN,
|
||||
common.mustCall((err) => {
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
[
|
||||
'The "path" argument must be of type string or an ' +
|
||||
'instance of Buffer. Received type number (42)',
|
||||
].join(''));
|
||||
},
|
||||
));
|
||||
}
|
||||
211
test/js/node/test/parallel/test-fs-utimes.js
Normal file
211
test/js/node/test/parallel/test-fs-utimes.js
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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
|
||||
);
|
||||
}
|
||||
72
test/js/node/test/parallel/test-fs-watch-enoent.js
Normal file
72
test/js/node/test/parallel/test-fs-watch-enoent.js
Normal file
@@ -0,0 +1,72 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
// This verifies the error thrown by fs.watch.
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
if (common.isIBMi)
|
||||
common.skip('IBMi does not support `fs.watch()`');
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const nonexistentFile = tmpdir.resolve('non-existent');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const {
|
||||
UV_ENODEV,
|
||||
UV_ENOENT
|
||||
} = internalBinding('uv');
|
||||
|
||||
tmpdir.refresh();
|
||||
|
||||
{
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.path, nonexistentFile);
|
||||
assert.strictEqual(err.filename, nonexistentFile);
|
||||
assert.ok(err.syscall === 'watch' || err.syscall === 'stat');
|
||||
if (err.code === 'ENOENT') {
|
||||
assert.ok(err.message.startsWith('ENOENT: no such file or directory'));
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
} else { // AIX
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENODEV: no such device, watch '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENODEV);
|
||||
assert.strictEqual(err.code, 'ENODEV');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
assert.throws(
|
||||
() => fs.watch(nonexistentFile, common.mustNotCall()),
|
||||
validateError
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
if (common.isMacOS || common.isWindows) {
|
||||
const file = tmpdir.resolve('file-to-watch');
|
||||
fs.writeFileSync(file, 'test');
|
||||
const watcher = fs.watch(file, common.mustNotCall());
|
||||
|
||||
const validateError = (err) => {
|
||||
assert.strictEqual(err.path, nonexistentFile);
|
||||
assert.strictEqual(err.filename, nonexistentFile);
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
`ENOENT: no such file or directory, watch '${nonexistentFile}'`);
|
||||
assert.strictEqual(err.errno, UV_ENOENT);
|
||||
assert.strictEqual(err.code, 'ENOENT');
|
||||
assert.strictEqual(err.syscall, 'watch');
|
||||
fs.unlinkSync(file);
|
||||
return true;
|
||||
};
|
||||
|
||||
watcher.on('error', common.mustCall(validateError));
|
||||
|
||||
// Simulate the invocation from the binding
|
||||
watcher._handle.onchange(UV_ENOENT, 'ENOENT', nonexistentFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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');
|
||||
|
||||
// Make sure the deletion event gets reported in the following scenario:
|
||||
// 1. Watch a file.
|
||||
// 2. The initial stat() goes okay.
|
||||
// 3. Something deletes the watched file.
|
||||
// 4. The second stat() fails with ENOENT.
|
||||
|
||||
// The second stat() translates into the first 'change' event but a logic error
|
||||
// stopped it from getting emitted.
|
||||
// https://github.com/nodejs/node-v0.x-archive/issues/4027
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
const filename = tmpdir.resolve('watched');
|
||||
fs.writeFileSync(filename, 'quis custodiet ipsos custodes');
|
||||
|
||||
fs.watchFile(filename, { interval: 50 }, common.mustCall(function(curr, prev) {
|
||||
fs.unwatchFile(filename);
|
||||
}));
|
||||
|
||||
fs.unlinkSync(filename);
|
||||
@@ -40,9 +40,8 @@ const relativePath = path.join(file, path.basename(subfolderPath), childrenFile)
|
||||
const watcher = fs.watch(testDirectory, { recursive: true });
|
||||
let watcherClosed = false;
|
||||
watcher.on('change', function(event, filename) {
|
||||
assert.strictEqual(event, 'rename');
|
||||
|
||||
if (filename === relativePath) {
|
||||
assert.strictEqual(event, 'rename');
|
||||
watcher.close();
|
||||
watcherClosed = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
if (common.isIBMi)
|
||||
common.skip('IBMi does not support `fs.watch()`');
|
||||
|
||||
// fs-watch on folders have limited capability in AIX.
|
||||
// The testcase makes use of folder watching, and causes
|
||||
// hang. This behavior is documented. Skip this for AIX.
|
||||
|
||||
if (common.isAIX)
|
||||
common.skip('folder watch capability is limited in AIX.');
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const testDir = tmpdir.path;
|
||||
tmpdir.refresh();
|
||||
|
||||
// Add a file to newly created folder to already watching folder
|
||||
|
||||
const rootDirectory = fs.mkdtempSync(testDir + path.sep);
|
||||
const testDirectory = path.join(rootDirectory, 'test-3');
|
||||
fs.mkdirSync(testDirectory);
|
||||
|
||||
const filePath = path.join(testDirectory, 'folder-3');
|
||||
|
||||
const childrenFile = 'file-4.txt';
|
||||
const childrenAbsolutePath = path.join(filePath, childrenFile);
|
||||
const childrenRelativePath = path.join(path.basename(filePath), childrenFile);
|
||||
let watcherClosed = false;
|
||||
|
||||
const watcher = fs.watch(testDirectory, { recursive: true });
|
||||
watcher.on('change', function(event, filename) {
|
||||
if (filename === childrenRelativePath) {
|
||||
assert.strictEqual(event, 'rename');
|
||||
watcher.close();
|
||||
watcherClosed = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Do the write with a delay to ensure that the OS is ready to notify us.
|
||||
setTimeout(() => {
|
||||
fs.mkdirSync(filePath);
|
||||
fs.writeFileSync(childrenAbsolutePath, 'world');
|
||||
}, common.platformTimeout(200));
|
||||
|
||||
process.once('exit', function() {
|
||||
assert(watcherClosed, 'watcher Object was not closed');
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
if (common.isIBMi)
|
||||
common.skip('IBMi does not support `fs.watch()`');
|
||||
|
||||
// fs-watch on folders have limited capability in AIX.
|
||||
// The testcase makes use of folder watching, and causes
|
||||
// hang. This behavior is documented. Skip this for AIX.
|
||||
|
||||
if (common.isAIX)
|
||||
common.skip('folder watch capability is limited in AIX.');
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const fs = require('fs/promises');
|
||||
const fsSync = require('fs');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const testDir = tmpdir.path;
|
||||
tmpdir.refresh();
|
||||
|
||||
(async function run() {
|
||||
// Add a file to already watching folder
|
||||
|
||||
const testsubdir = await fs.mkdtemp(testDir + path.sep);
|
||||
const file = '1.txt';
|
||||
const filePath = path.join(testsubdir, file);
|
||||
const watcher = fs.watch(testsubdir, { recursive: true });
|
||||
|
||||
let interval;
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.ok(interval === null, 'watcher Object was not closed');
|
||||
});
|
||||
|
||||
process.nextTick(common.mustCall(() => {
|
||||
interval = setInterval(() => {
|
||||
fsSync.writeFileSync(filePath, 'world');
|
||||
}, 500);
|
||||
}));
|
||||
|
||||
for await (const payload of watcher) {
|
||||
const { eventType, filename } = payload;
|
||||
|
||||
assert.ok(eventType === 'change' || eventType === 'rename');
|
||||
|
||||
if (filename === file) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
})().then(common.mustCall());
|
||||
|
||||
(async function() {
|
||||
// Test that aborted AbortSignal are reported.
|
||||
const testsubdir = await fs.mkdtemp(testDir + path.sep);
|
||||
const error = new Error();
|
||||
const watcher = fs.watch(testsubdir, { recursive: true, signal: AbortSignal.abort(error) });
|
||||
await assert.rejects(async () => {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for await (const _ of watcher);
|
||||
}, { code: 'ABORT_ERR', cause: error });
|
||||
})().then(common.mustCall());
|
||||
|
||||
(async function() {
|
||||
// Test that with AbortController.
|
||||
const testsubdir = await fs.mkdtemp(testDir + path.sep);
|
||||
const file = '2.txt';
|
||||
const filePath = path.join(testsubdir, file);
|
||||
const error = new Error();
|
||||
const ac = new AbortController();
|
||||
const watcher = fs.watch(testsubdir, { recursive: true, signal: ac.signal });
|
||||
let interval;
|
||||
process.on('exit', function() {
|
||||
assert.ok(interval === null, 'watcher Object was not closed');
|
||||
});
|
||||
process.nextTick(common.mustCall(() => {
|
||||
interval = setInterval(() => {
|
||||
fsSync.writeFileSync(filePath, 'world');
|
||||
}, 50);
|
||||
ac.abort(error);
|
||||
}));
|
||||
await assert.rejects(async () => {
|
||||
for await (const { eventType } of watcher) {
|
||||
assert.ok(eventType === 'change' || eventType === 'rename');
|
||||
}
|
||||
}, { code: 'ABORT_ERR', cause: error });
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
})().then(common.mustCall());
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user