• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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