| '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 dirent.name; |
| }); |
| assert.deepStrictEqual(files, entries.sort()); |
| |
| // 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()); |
| } |