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.mustSucceed((dir) => { 69 let sync = true; 70 dir.read(common.mustCall((err, dirent) => { 71 assert(!sync); 72 assert.ifError(err); 73 74 // Order is operating / file system dependent 75 assert(files.includes(dirent.name), `'files' should include ${dirent}`); 76 assertDirent(dirent); 77 78 let syncInner = true; 79 dir.read(common.mustCall((err, dirent) => { 80 assert(!syncInner); 81 assert.ifError(err); 82 83 dir.close(common.mustSucceed()); 84 })); 85 syncInner = false; 86 })); 87 sync = false; 88})); 89 90// opendir() on file should throw ENOTDIR 91assert.throws(function() { 92 fs.opendirSync(__filename); 93}, /Error: ENOTDIR: not a directory/); 94 95assert.throws(function() { 96 fs.opendir(__filename); 97}, /TypeError \[ERR_INVALID_CALLBACK\]: Callback must be a function/); 98 99fs.opendir(__filename, common.mustCall(function(e) { 100 assert.strictEqual(e.code, 'ENOTDIR'); 101})); 102 103[false, 1, [], {}, null, undefined].forEach((i) => { 104 assert.throws( 105 () => fs.opendir(i, common.mustNotCall()), 106 { 107 code: 'ERR_INVALID_ARG_TYPE', 108 name: 'TypeError' 109 } 110 ); 111 assert.throws( 112 () => fs.opendirSync(i), 113 { 114 code: 'ERR_INVALID_ARG_TYPE', 115 name: 'TypeError' 116 } 117 ); 118}); 119 120// Promise-based tests 121async function doPromiseTest() { 122 // Check the opendir Promise version 123 const dir = await fs.promises.opendir(testDir); 124 const entries = []; 125 126 let i = files.length; 127 while (i--) { 128 const dirent = await dir.read(); 129 entries.push(dirent.name); 130 assertDirent(dirent); 131 } 132 133 assert.deepStrictEqual(files, entries.sort()); 134 135 // dir.read should return null when no more entries exist 136 assert.strictEqual(await dir.read(), null); 137 138 await dir.close(); 139} 140doPromiseTest().then(common.mustCall()); 141 142// Async iterator 143async function doAsyncIterTest() { 144 const entries = []; 145 for await (const dirent of await fs.promises.opendir(testDir)) { 146 entries.push(dirent.name); 147 assertDirent(dirent); 148 } 149 150 assert.deepStrictEqual(files, entries.sort()); 151 152 // Automatically closed during iterator 153} 154doAsyncIterTest().then(common.mustCall()); 155 156// Async iterators should do automatic cleanup 157 158async function doAsyncIterBreakTest() { 159 const dir = await fs.promises.opendir(testDir); 160 for await (const dirent of dir) { // eslint-disable-line no-unused-vars 161 break; 162 } 163 164 await assert.rejects(async () => dir.read(), dirclosedError); 165} 166doAsyncIterBreakTest().then(common.mustCall()); 167 168async function doAsyncIterReturnTest() { 169 const dir = await fs.promises.opendir(testDir); 170 await (async function() { 171 for await (const dirent of dir) { // eslint-disable-line no-unused-vars 172 return; 173 } 174 })(); 175 176 await assert.rejects(async () => dir.read(), dirclosedError); 177} 178doAsyncIterReturnTest().then(common.mustCall()); 179 180async function doAsyncIterThrowTest() { 181 const dir = await fs.promises.opendir(testDir); 182 try { 183 for await (const dirent of dir) { // eslint-disable-line no-unused-vars 184 throw new Error('oh no'); 185 } 186 } catch (err) { 187 if (err.message !== 'oh no') { 188 throw err; 189 } 190 } 191 192 await assert.rejects(async () => dir.read(), dirclosedError); 193} 194doAsyncIterThrowTest().then(common.mustCall()); 195 196// Check error thrown on invalid values of bufferSize 197for (const bufferSize of [-1, 0, 0.5, 1.5, Infinity, NaN]) { 198 assert.throws( 199 () => fs.opendirSync(testDir, { bufferSize }), 200 { 201 code: 'ERR_OUT_OF_RANGE' 202 }); 203} 204for (const bufferSize of ['', '1', null]) { 205 assert.throws( 206 () => fs.opendirSync(testDir, { bufferSize }), 207 { 208 code: 'ERR_INVALID_ARG_TYPE' 209 }); 210} 211 212// Check that passing a positive integer as bufferSize works 213{ 214 const dir = fs.opendirSync(testDir, { bufferSize: 1024 }); 215 assertDirent(dir.readSync()); 216 dir.close(); 217} 218 219// Check that when passing a string instead of function - throw an exception 220async function doAsyncIterInvalidCallbackTest() { 221 const dir = await fs.promises.opendir(testDir); 222 assert.throws(() => dir.close('not function'), invalidCallbackObj); 223} 224doAsyncIterInvalidCallbackTest().then(common.mustCall()); 225 226// Check first call to close() - should not report an error. 227async function doAsyncIterDirClosedTest() { 228 const dir = await fs.promises.opendir(testDir); 229 await dir.close(); 230 await assert.rejects(() => dir.close(), dirclosedError); 231} 232doAsyncIterDirClosedTest().then(common.mustCall()); 233 234// Check that readSync() and closeSync() during read() throw exceptions 235async function doConcurrentAsyncAndSyncOps() { 236 const dir = await fs.promises.opendir(testDir); 237 const promise = dir.read(); 238 239 assert.throws(() => dir.closeSync(), dirconcurrentError); 240 assert.throws(() => dir.readSync(), dirconcurrentError); 241 242 await promise; 243 dir.closeSync(); 244} 245doConcurrentAsyncAndSyncOps().then(common.mustCall()); 246 247// Check read throw exceptions on invalid callback 248{ 249 const dir = fs.opendirSync(testDir); 250 assert.throws(() => dir.read('INVALID_CALLBACK'), /ERR_INVALID_CALLBACK/); 251} 252 253// Check that concurrent read() operations don't do weird things. 254async function doConcurrentAsyncOps() { 255 const dir = await fs.promises.opendir(testDir); 256 const promise1 = dir.read(); 257 const promise2 = dir.read(); 258 259 assertDirent(await promise1); 260 assertDirent(await promise2); 261 dir.closeSync(); 262} 263doConcurrentAsyncOps().then(common.mustCall()); 264 265// Check that concurrent read() + close() operations don't do weird things. 266async function doConcurrentAsyncMixedOps() { 267 const dir = await fs.promises.opendir(testDir); 268 const promise1 = dir.read(); 269 const promise2 = dir.close(); 270 271 assertDirent(await promise1); 272 await promise2; 273} 274doConcurrentAsyncMixedOps().then(common.mustCall()); 275 276// Check if directory already closed - the callback should pass an error. 277{ 278 const dir = fs.opendirSync(testDir); 279 dir.closeSync(); 280 dir.close(common.mustCall((error) => { 281 assert.strictEqual(error.code, dirclosedError.code); 282 })); 283} 284 285// Check if directory already closed - throw an promise exception. 286{ 287 const dir = fs.opendirSync(testDir); 288 dir.closeSync(); 289 assert.rejects(dir.close(), dirclosedError).then(common.mustCall()); 290} 291