1'use strict';
2const common = require('../common');
3
4// This test ensures that fs.readFile correctly returns the
5// contents of varying-sized files.
6
7const tmpdir = require('../../test/common/tmpdir');
8const assert = require('assert');
9const fs = require('fs');
10const path = require('path');
11
12const prefix = `.removeme-fs-readfile-${process.pid}`;
13
14tmpdir.refresh();
15
16const fileInfo = [
17  { name: path.join(tmpdir.path, `${prefix}-1K.txt`),
18    len: 1024 },
19  { name: path.join(tmpdir.path, `${prefix}-64K.txt`),
20    len: 64 * 1024 },
21  { name: path.join(tmpdir.path, `${prefix}-64KLessOne.txt`),
22    len: (64 * 1024) - 1 },
23  { name: path.join(tmpdir.path, `${prefix}-1M.txt`),
24    len: 1 * 1024 * 1024 },
25  { name: path.join(tmpdir.path, `${prefix}-1MPlusOne.txt`),
26    len: (1 * 1024 * 1024) + 1 },
27];
28
29// Populate each fileInfo (and file) with unique fill.
30const sectorSize = 512;
31for (const e of fileInfo) {
32  e.contents = Buffer.allocUnsafe(e.len);
33
34  // This accounts for anything unusual in Node's implementation of readFile.
35  // Using e.g. 'aa...aa' would miss bugs like Node re-reading
36  // the same section twice instead of two separate sections.
37  for (let offset = 0; offset < e.len; offset += sectorSize) {
38    const fillByte = 256 * Math.random();
39    const nBytesToFill = Math.min(sectorSize, e.len - offset);
40    e.contents.fill(fillByte, offset, offset + nBytesToFill);
41  }
42
43  fs.writeFileSync(e.name, e.contents);
44}
45// All files are now populated.
46
47// Test readFile on each size.
48for (const e of fileInfo) {
49  fs.readFile(e.name, common.mustCall((err, buf) => {
50    console.log(`Validating readFile on file ${e.name} of length ${e.len}`);
51    assert.ifError(err);
52    assert.deepStrictEqual(buf, e.contents);
53  }));
54}
55
56// readFile() and readFileSync() should fail if the file is too big.
57{
58  const kIoMaxLength = 2 ** 31 - 1;
59
60  if (!tmpdir.hasEnoughSpace(kIoMaxLength)) {
61    // truncateSync() will fail with ENOSPC if there is not enough space.
62    common.printSkipMessage(`Not enough space in ${tmpdir.path}`);
63  } else {
64    const file = path.join(tmpdir.path, `${prefix}-too-large.txt`);
65    fs.writeFileSync(file, Buffer.from('0'));
66    fs.truncateSync(file, kIoMaxLength + 1);
67
68    fs.readFile(file, common.expectsError({
69      code: 'ERR_FS_FILE_TOO_LARGE',
70      name: 'RangeError',
71    }));
72    assert.throws(() => {
73      fs.readFileSync(file);
74    }, { code: 'ERR_FS_FILE_TOO_LARGE', name: 'RangeError' });
75  }
76}
77
78{
79  // Test cancellation, before
80  const signal = AbortSignal.abort();
81  fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => {
82    assert.strictEqual(err.name, 'AbortError');
83  }));
84}
85{
86  // Test cancellation, during read
87  const controller = new AbortController();
88  const signal = controller.signal;
89  fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => {
90    assert.strictEqual(err.name, 'AbortError');
91  }));
92  process.nextTick(() => controller.abort());
93}
94{
95  // Verify that if something different than Abortcontroller.signal
96  // is passed, ERR_INVALID_ARG_TYPE is thrown
97  assert.throws(() => {
98    const callback = common.mustNotCall();
99    fs.readFile(fileInfo[0].name, { signal: 'hello' }, callback);
100  }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' });
101}
102