From f5a4908696b9013c0b7cd51616170da9e3969ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Fri, 28 Apr 2023 13:25:30 +0000 Subject: [PATCH] test: add and use tmpdir.hasTooLittleSpace() 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 hasTooLittleSpace() to the tmpdir module that uses statfsSync() to guess whether allocating a certain amount of space within the temporary directory will fail. This change also updates the most frequently failing tests to use the new function, in which case the relevant parts of the tests are skipped. Refs: https://github.com/nodejs/reliability/issues/549 --- test/common/README.md | 9 ++++++ test/common/tmpdir.js | 6 ++++ .../test-fs-promises-file-handle-readFile.js | 27 ++++++++++-------- test/parallel/test-fs-readfile.js | 28 +++++++++++-------- test/pummel/test-fs-readfile-tostring-fail.js | 4 +++ 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index 3587dcaec86e42..e5e4d1a8ebf58c 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -1030,6 +1030,15 @@ Avoid calling it more than once in an asynchronous context as one call might refresh the temporary directory of a different context, causing the test to fail somewhat mysteriously. +### `hasTooLittleSpace(size)` + +* `x` [\][] Required size, in bytes. + +Returns `true` if the available blocks of the file system underlying `path` +might be insufficient to hold a single file of `size` bytes. This is useful for +skipping tests that require hundreds of megabytes or even gigabytes of temporary +files, but it is susceptible to race conditions. + ## UDP pair helper The `common/udppair` module exports a function `makeUDPPair` and a class diff --git a/test/common/tmpdir.js b/test/common/tmpdir.js index 3c4ca546d062d3..c26be8adcfbed6 100644 --- a/test/common/tmpdir.js +++ b/test/common/tmpdir.js @@ -69,7 +69,13 @@ function onexit(useSpawn) { } } +function hasTooLittleSpace(size) { + const { bavail, bsize } = fs.statfsSync(tmpPath); + return bavail < Math.ceil(size / bsize); +} + module.exports = { path: tmpPath, refresh, + hasTooLittleSpace, }; diff --git a/test/parallel/test-fs-promises-file-handle-readFile.js b/test/parallel/test-fs-promises-file-handle-readFile.js index 4cc2e59bb52780..a48ba7b7c4a3ee 100644 --- a/test/parallel/test-fs-promises-file-handle-readFile.js +++ b/test/parallel/test-fs-promises-file-handle-readFile.js @@ -106,17 +106,22 @@ async function doReadAndCancel() { // Variable taken from https://github.com/nodejs/node/blob/1377163f3351/lib/internal/fs/promises.js#L5 const kIoMaxLength = 2 ** 31 - 1; - const newFile = path.resolve(tmpDir, 'dogs-running3.txt'); - await writeFile(newFile, Buffer.from('0')); - await truncate(newFile, kIoMaxLength + 1); - - const fileHandle = await open(newFile, 'r'); - - await assert.rejects(fileHandle.readFile(), { - name: 'RangeError', - code: 'ERR_FS_FILE_TOO_LARGE' - }); - await fileHandle.close(); + if (tmpdir.hasTooLittleSpace(kIoMaxLength)) { + // truncate() will fail with ENOSPC if there is not enough space. + common.printSkipMessage(`Not enough space in ${tmpDir}`); + } else { + const newFile = path.resolve(tmpDir, 'dogs-running3.txt'); + await writeFile(newFile, Buffer.from('0')); + await truncate(newFile, kIoMaxLength + 1); + + const fileHandle = await open(newFile, 'r'); + + await assert.rejects(fileHandle.readFile(), { + name: 'RangeError', + code: 'ERR_FS_FILE_TOO_LARGE' + }); + await fileHandle.close(); + } } } diff --git a/test/parallel/test-fs-readfile.js b/test/parallel/test-fs-readfile.js index c0dd16255b44fd..cdf22974acb4f2 100644 --- a/test/parallel/test-fs-readfile.js +++ b/test/parallel/test-fs-readfile.js @@ -52,21 +52,27 @@ for (const e of fileInfo) { assert.deepStrictEqual(buf, e.contents); })); } -// Test readFile size too large + +// readFile() and readFileSync() should fail if the file is too big. { const kIoMaxLength = 2 ** 31 - 1; - const file = path.join(tmpdir.path, `${prefix}-too-large.txt`); - fs.writeFileSync(file, Buffer.from('0')); - fs.truncateSync(file, kIoMaxLength + 1); + if (tmpdir.hasTooLittleSpace(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' }); + 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' }); + } } { diff --git a/test/pummel/test-fs-readfile-tostring-fail.js b/test/pummel/test-fs-readfile-tostring-fail.js index 0b594520d21aea..89e4f274b740dd 100644 --- a/test/pummel/test-fs-readfile-tostring-fail.js +++ b/test/pummel/test-fs-readfile-tostring-fail.js @@ -16,6 +16,10 @@ if (common.isAIX && (Number(cp.execSync('ulimit -f')) * 512) < kStringMaxLength) const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); +if (tmpdir.hasTooLittleSpace(kStringMaxLength)) { + common.skip(`Not enough space in ${tmpdir.path}`); +} + const file = path.join(tmpdir.path, 'toobig.txt'); const stream = fs.createWriteStream(file, { flags: 'a',