• 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.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