1'use strict'; 2 3const common = require('../common'); 4const assert = require('assert'); 5const fs = require('fs'); 6const path = require('path'); 7 8const tmpdir = require('../common/tmpdir'); 9 10const testDir = tmpdir.path; 11const files = ['empty', 'files', 'for', 'just', 'testing']; 12 13// Make sure tmp directory is clean 14tmpdir.refresh(); 15 16// Create the necessary files 17files.forEach(function(filename) { 18 fs.closeSync(fs.openSync(path.join(testDir, filename), 'w')); 19}); 20 21function assertDirent(dirent) { 22 assert(dirent instanceof fs.Dirent); 23 assert.strictEqual(dirent.isFile(), true); 24 assert.strictEqual(dirent.isDirectory(), false); 25 assert.strictEqual(dirent.isSocket(), false); 26 assert.strictEqual(dirent.isBlockDevice(), false); 27 assert.strictEqual(dirent.isCharacterDevice(), false); 28 assert.strictEqual(dirent.isFIFO(), false); 29 assert.strictEqual(dirent.isSymbolicLink(), false); 30} 31 32const dirclosedError = { 33 code: 'ERR_DIR_CLOSED' 34}; 35 36const dirconcurrentError = { 37 code: 'ERR_DIR_CONCURRENT_OPERATION' 38}; 39 40const invalidCallbackObj = { 41 code: 'ERR_INVALID_CALLBACK', 42 name: 'TypeError' 43}; 44 45// Check the opendir Sync version 46{ 47 const dir = fs.opendirSync(testDir); 48 const entries = files.map(() => { 49 const dirent = dir.readSync(); 50 assertDirent(dirent); 51 return dirent.name; 52 }); 53 assert.deepStrictEqual(files, entries.sort()); 54 55 // dir.read should return null when no more entries exist 56 assert.strictEqual(dir.readSync(), null); 57 58 // check .path 59 assert.strictEqual(dir.path, testDir); 60 61 dir.closeSync(); 62 63 assert.throws(() => dir.readSync(), dirclosedError); 64 assert.throws(() => dir.closeSync(), dirclosedError); 65} 66 67// Check the opendir async version 68fs.opendir(testDir, common.mustCall(function(err, dir) { 69 assert.ifError(err); 70 let sync = true; 71 dir.read(common.mustCall((err, dirent) => { 72 assert(!sync); 73 assert.ifError(err); 74 75 // Order is operating / file system dependent 76 assert(files.includes(dirent.name), `'files' should include ${dirent}`); 77 assertDirent(dirent); 78 79 let syncInner = true; 80 dir.read(common.mustCall((err, dirent) => { 81 assert(!syncInner); 82 assert.ifError(err); 83 84 dir.close(common.mustCall(function(err) { 85 assert.ifError(err); 86 })); 87 })); 88 syncInner = false; 89 })); 90 sync = false; 91})); 92 93// opendir() on file should throw ENOTDIR 94assert.throws(function() { 95 fs.opendirSync(__filename); 96}, /Error: ENOTDIR: not a directory/); 97 98assert.throws(function() { 99 fs.opendir(__filename); 100}, /TypeError \[ERR_INVALID_CALLBACK\]: Callback must be a function/); 101 102fs.opendir(__filename, common.mustCall(function(e) { 103 assert.strictEqual(e.code, 'ENOTDIR'); 104})); 105 106[false, 1, [], {}, null, undefined].forEach((i) => { 107 assert.throws( 108 () => fs.opendir(i, common.mustNotCall()), 109 { 110 code: 'ERR_INVALID_ARG_TYPE', 111 name: 'TypeError' 112 } 113 ); 114 assert.throws( 115 () => fs.opendirSync(i), 116 { 117 code: 'ERR_INVALID_ARG_TYPE', 118 name: 'TypeError' 119 } 120 ); 121}); 122 123// Promise-based tests 124async function doPromiseTest() { 125 // Check the opendir Promise version 126 const dir = await fs.promises.opendir(testDir); 127 const entries = []; 128 129 let i = files.length; 130 while (i--) { 131 const dirent = await dir.read(); 132 entries.push(dirent.name); 133 assertDirent(dirent); 134 } 135 136 assert.deepStrictEqual(files, entries.sort()); 137 138 // dir.read should return null when no more entries exist 139 assert.strictEqual(await dir.read(), null); 140 141 await dir.close(); 142} 143doPromiseTest().then(common.mustCall()); 144 145// Async iterator 146async function doAsyncIterTest() { 147 const entries = []; 148 for await (const dirent of await fs.promises.opendir(testDir)) { 149 entries.push(dirent.name); 150 assertDirent(dirent); 151 } 152 153 assert.deepStrictEqual(files, entries.sort()); 154 155 // Automatically closed during iterator 156} 157doAsyncIterTest().then(common.mustCall()); 158 159// Async iterators should do automatic cleanup 160 161async function doAsyncIterBreakTest() { 162 const dir = await fs.promises.opendir(testDir); 163 for await (const dirent of dir) { // eslint-disable-line no-unused-vars 164 break; 165 } 166 167 await assert.rejects(async () => dir.read(), dirclosedError); 168} 169doAsyncIterBreakTest().then(common.mustCall()); 170 171async function doAsyncIterReturnTest() { 172 const dir = await fs.promises.opendir(testDir); 173 await (async function() { 174 for await (const dirent of dir) { // eslint-disable-line no-unused-vars 175 return; 176 } 177 })(); 178 179 await assert.rejects(async () => dir.read(), dirclosedError); 180} 181doAsyncIterReturnTest().then(common.mustCall()); 182 183async function doAsyncIterThrowTest() { 184 const dir = await fs.promises.opendir(testDir); 185 try { 186 for await (const dirent of dir) { // eslint-disable-line no-unused-vars 187 throw new Error('oh no'); 188 } 189 } catch (err) { 190 if (err.message !== 'oh no') { 191 throw err; 192 } 193 } 194 195 await assert.rejects(async () => dir.read(), dirclosedError); 196} 197doAsyncIterThrowTest().then(common.mustCall()); 198 199// Check error thrown on invalid values of bufferSize 200for (const bufferSize of [-1, 0, 0.5, 1.5, Infinity, NaN]) { 201 assert.throws( 202 () => fs.opendirSync(testDir, { bufferSize }), 203 { 204 code: 'ERR_OUT_OF_RANGE' 205 }); 206} 207for (const bufferSize of ['', '1', null]) { 208 assert.throws( 209 () => fs.opendirSync(testDir, { bufferSize }), 210 { 211 code: 'ERR_INVALID_ARG_TYPE' 212 }); 213} 214 215// Check that passing a positive integer as bufferSize works 216{ 217 const dir = fs.opendirSync(testDir, { bufferSize: 1024 }); 218 assertDirent(dir.readSync()); 219 dir.close(); 220} 221 222// Check that when passing a string instead of function - throw an exception 223async function doAsyncIterInvalidCallbackTest() { 224 const dir = await fs.promises.opendir(testDir); 225 assert.throws(() => dir.close('not function'), invalidCallbackObj); 226} 227doAsyncIterInvalidCallbackTest().then(common.mustCall()); 228 229// Check if directory already closed - throw an exception 230async function doAsyncIterDirClosedTest() { 231 const dir = await fs.promises.opendir(testDir); 232 await dir.close(); 233 234 assert.throws(() => dir.close(), dirclosedError); 235} 236doAsyncIterDirClosedTest().then(common.mustCall()); 237 238// Check that readSync() and closeSync() during read() throw exceptions 239async function doConcurrentAsyncAndSyncOps() { 240 const dir = await fs.promises.opendir(testDir); 241 const promise = dir.read(); 242 243 assert.throws(() => dir.closeSync(), dirconcurrentError); 244 assert.throws(() => dir.readSync(), dirconcurrentError); 245 246 await promise; 247 dir.closeSync(); 248} 249doConcurrentAsyncAndSyncOps().then(common.mustCall()); 250 251// Check that concurrent read() operations don't do weird things. 252async function doConcurrentAsyncOps() { 253 const dir = await fs.promises.opendir(testDir); 254 const promise1 = dir.read(); 255 const promise2 = dir.read(); 256 257 assertDirent(await promise1); 258 assertDirent(await promise2); 259 dir.closeSync(); 260} 261doConcurrentAsyncOps().then(common.mustCall()); 262 263// Check that concurrent read() + close() operations don't do weird things. 264async function doConcurrentAsyncMixedOps() { 265 const dir = await fs.promises.opendir(testDir); 266 const promise1 = dir.read(); 267 const promise2 = dir.close(); 268 269 assertDirent(await promise1); 270 await promise2; 271} 272doConcurrentAsyncMixedOps().then(common.mustCall()); 273