node/test/parallel/test-fs-readfile.js
Tobias Nießen 97a3d39b8f
test: add and use tmpdir.hasEnoughSpace()
In general, we assume that the tmpdir will provide sufficient space for
most tests. Some tests, however, require hundreds of megabytes or even
gigabytes of space, which often causes them to fail, especially on our
macOS infrastructure. The most recent reliability report contains more
than 20 related CI failures.

This change adds a new function hasEnoughSpace() to the tmpdir module
that uses statfsSync() to guess whether allocating a certain amount of
space within the temporary directory will succeed.

This change also updates the most frequently failing tests to use the
new function such that the relevant parts of the tests are skipped if
tmpdir has insufficient space.

Refs: https://github.com/nodejs/reliability/issues/549
PR-URL: https://github.com/nodejs/node/pull/47767
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Richard Lau <rlau@redhat.com>
2023-05-03 08:34:26 +02:00

101 lines
3.2 KiB
JavaScript

'use strict';
const common = require('../common');
// This test ensures that fs.readFile correctly returns the
// contents of varying-sized files.
const tmpdir = require('../../test/common/tmpdir');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const prefix = `.removeme-fs-readfile-${process.pid}`;
tmpdir.refresh();
const fileInfo = [
{ name: path.join(tmpdir.path, `${prefix}-1K.txt`),
len: 1024 },
{ name: path.join(tmpdir.path, `${prefix}-64K.txt`),
len: 64 * 1024 },
{ name: path.join(tmpdir.path, `${prefix}-64KLessOne.txt`),
len: (64 * 1024) - 1 },
{ name: path.join(tmpdir.path, `${prefix}-1M.txt`),
len: 1 * 1024 * 1024 },
{ name: path.join(tmpdir.path, `${prefix}-1MPlusOne.txt`),
len: (1 * 1024 * 1024) + 1 },
];
// Populate each fileInfo (and file) with unique fill.
const sectorSize = 512;
for (const e of fileInfo) {
e.contents = Buffer.allocUnsafe(e.len);
// This accounts for anything unusual in Node's implementation of readFile.
// Using e.g. 'aa...aa' would miss bugs like Node re-reading
// the same section twice instead of two separate sections.
for (let offset = 0; offset < e.len; offset += sectorSize) {
const fillByte = 256 * Math.random();
const nBytesToFill = Math.min(sectorSize, e.len - offset);
e.contents.fill(fillByte, offset, offset + nBytesToFill);
}
fs.writeFileSync(e.name, e.contents);
}
// All files are now populated.
// Test readFile on each size.
for (const e of fileInfo) {
fs.readFile(e.name, common.mustCall((err, buf) => {
console.log(`Validating readFile on file ${e.name} of length ${e.len}`);
assert.ifError(err);
assert.deepStrictEqual(buf, e.contents);
}));
}
// readFile() and readFileSync() should fail if the file is too big.
{
const kIoMaxLength = 2 ** 31 - 1;
if (!tmpdir.hasEnoughSpace(kIoMaxLength)) {
// truncateSync() will fail with ENOSPC if there is not enough space.
common.printSkipMessage(`Not enough space in ${tmpdir.path}`);
} else {
const file = path.join(tmpdir.path, `${prefix}-too-large.txt`);
fs.writeFileSync(file, Buffer.from('0'));
fs.truncateSync(file, kIoMaxLength + 1);
fs.readFile(file, common.expectsError({
code: 'ERR_FS_FILE_TOO_LARGE',
name: 'RangeError',
}));
assert.throws(() => {
fs.readFileSync(file);
}, { code: 'ERR_FS_FILE_TOO_LARGE', name: 'RangeError' });
}
}
{
// Test cancellation, before
const signal = AbortSignal.abort();
fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => {
assert.strictEqual(err.name, 'AbortError');
}));
}
{
// Test cancellation, during read
const controller = new AbortController();
const signal = controller.signal;
fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => {
assert.strictEqual(err.name, 'AbortError');
}));
process.nextTick(() => controller.abort());
}
{
// Verify that if something different than Abortcontroller.signal
// is passed, ERR_INVALID_ARG_TYPE is thrown
assert.throws(() => {
const callback = common.mustNotCall(() => {});
fs.readFile(fileInfo[0].name, { signal: 'hello' }, callback);
}, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' });
}