Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: use unique tmpdirs for each test #28858

Closed
wants to merge 11 commits into from
7 changes: 5 additions & 2 deletions benchmark/fs/read-stream-throughput.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

const path = require('path');
const common = require('../common.js');
const filename = path.resolve(process.env.NODE_TMPDIR || __dirname,
`.removeme-benchmark-garbage-${process.pid}`);
const fs = require('fs');
const assert = require('assert');

const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const filename = path.resolve(tmpdir.path,
`.removeme-benchmark-garbage-${process.pid}`);

let encodingType, encoding, size, filesize;

const bench = common.createBenchmark(main, {
Expand Down
7 changes: 5 additions & 2 deletions benchmark/fs/readfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@

const path = require('path');
const common = require('../common.js');
const filename = path.resolve(process.env.NODE_TMPDIR || __dirname,
`.removeme-benchmark-garbage-${process.pid}`);
const fs = require('fs');
const assert = require('assert');

const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const filename = path.resolve(tmpdir.path,
`.removeme-benchmark-garbage-${process.pid}`);

const bench = common.createBenchmark(main, {
dur: [5],
len: [1024, 16 * 1024 * 1024],
Expand Down
7 changes: 5 additions & 2 deletions benchmark/fs/write-stream-throughput.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

const path = require('path');
const common = require('../common.js');
const filename = path.resolve(process.env.NODE_TMPDIR || __dirname,
`.removeme-benchmark-garbage-${process.pid}`);
const fs = require('fs');

const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const filename = path.resolve(tmpdir.path,
`.removeme-benchmark-garbage-${process.pid}`);

const bench = common.createBenchmark(main, {
dur: [5],
encodingType: ['buf', 'asc', 'utf'],
Expand Down
3 changes: 2 additions & 1 deletion lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ async function rename(oldPath, newPath) {
}

async function truncate(path, len = 0) {
return ftruncate(await open(path, 'r+'), len);
const fd = await open(path, 'r+');
return ftruncate(fd, len).finally(fd.close.bind(fd));
}

async function ftruncate(handle, len = 0) {
Expand Down
10 changes: 10 additions & 0 deletions lib/internal/repl/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function setupHistory(repl, historyPath, ready) {
fs.ftruncate(hnd, 0, (err) => {
repl._historyHandle = hnd;
repl.on('line', online);
repl.once('exit', onexit);

// Reading the file data out erases it
repl.once('flushHistory', function() {
Expand Down Expand Up @@ -137,6 +138,15 @@ function setupHistory(repl, historyPath, ready) {
}
}
}

function onexit() {
if (repl._flushing) {
repl.once('flushHistory', onexit);
return;
}
repl.off('line', online);
fs.close(repl._historyHandle, () => {});
}
}

function _replHistoryMessage() {
Expand Down
29 changes: 23 additions & 6 deletions test/addons/load-long-path/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ if (common.isWindows && (process.env.PROCESSOR_ARCHITEW6432 !== undefined))
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const { fork } = require('child_process');

const tmpdir = require('../../common/tmpdir');
tmpdir.refresh();

// Make a path that is more than 260 chars long.
// Any given folder cannot have a name longer than 260 characters,
Expand All @@ -17,7 +17,6 @@ let addonDestinationDir = path.resolve(tmpdir.path);

for (let i = 0; i < 10; i++) {
addonDestinationDir = path.join(addonDestinationDir, 'x'.repeat(30));
fs.mkdirSync(addonDestinationDir);
}

const addonPath = path.join(__dirname,
Expand All @@ -26,11 +25,29 @@ const addonPath = path.join(__dirname,
'binding.node');
const addonDestinationPath = path.join(addonDestinationDir, 'binding.node');

// Loading an addon keeps the file open until the process terminates. Load
// the addon in a child process so that when the parent terminates the file
// is already closed and the tmpdir can be cleaned up.

// Child
if (process.argv[2] === 'child') {
// Attempt to load at long path destination
const addon = require(addonDestinationPath);
assert.notStrictEqual(addon, null);
assert.strictEqual(addon.hello(), 'world');
return;
}

// Parent
tmpdir.refresh();

// Copy binary to long path destination
fs.mkdirSync(addonDestinationDir, { recursive: true });
const contents = fs.readFileSync(addonPath);
fs.writeFileSync(addonDestinationPath, contents);

// Attempt to load at long path destination
const addon = require(addonDestinationPath);
assert.notStrictEqual(addon, null);
assert.strictEqual(addon.hello(), 'world');
// Run test
const child = fork(__filename, ['child'], { stdio: 'inherit' });
child.on('exit', common.mustCall(function(code) {
assert.strictEqual(code, 0);
}));
2 changes: 1 addition & 1 deletion test/benchmark/test-benchmark-fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ runBenchmark('fs', [
'filesize=1024',
'dir=.github',
'withFileTypes=false'
], { NODE_TMPDIR: tmpdir.path, NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });
], { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });
7 changes: 7 additions & 0 deletions test/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,13 @@ The realpath of the testing temporary directory.

Deletes and recreates the testing temporary directory.

The first time `refresh()` runs, it adds a listener to process `'exit'` that
cleans the temporary directory. Thus, every file under `tmpdir.path` needs to
be closed before the test completes. A good way to do this is to add a
listener to process `'beforeExit'`. If a file needs to be left open until
Node.js completes, use a child process and call `refresh()` only in the
parent.

## WPT Module

### harness
Expand Down
42 changes: 41 additions & 1 deletion test/common/tmpdir.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const { debuglog } = require('util');
const { isMainThread } = require('worker_threads');

const debug = debuglog('test/tmpdir');

Expand Down Expand Up @@ -61,6 +62,9 @@ function rimrafSync(pathname, { spawn = true } = {}) {
}
rmdirSync(pathname, e);
}

if (fs.existsSync(pathname))
throw new Error(`Unable to rimraf ${pathname}`);
}

function rmdirSync(p, originalEr) {
Expand All @@ -80,7 +84,9 @@ function rmdirSync(p, originalEr) {
}
});
fs.rmdirSync(p);
return;
}
throw e;
}
}

Expand All @@ -89,12 +95,46 @@ const testRoot = process.env.NODE_TEST_DIR ?

// Using a `.` prefixed name, which is the convention for "hidden" on POSIX,
// gets tools to ignore it by default or by simple rules, especially eslint.
const tmpdirName = '.tmp.' + (process.env.TEST_THREAD_ID || '0');
const tmpdirName = '.tmp.' +
(process.env.TEST_SERIAL_ID || process.env.TEST_THREAD_ID || '0');
const tmpPath = path.join(testRoot, tmpdirName);

let firstRefresh = true;
function refresh(opts = {}) {
rimrafSync(this.path, opts);
fs.mkdirSync(this.path);

if (firstRefresh) {
firstRefresh = false;
// Clean only when a test uses refresh. This allows for child processes to
// use the tmpdir and only the parent will clean on exit.
process.on('exit', onexit);
}
}

function onexit() {
// Change directory to avoid possible EBUSY
if (isMainThread)
process.chdir(testRoot);

try {
rimrafSync(tmpPath, { spawn: false });
} catch (e) {
console.error('Can\'t clean tmpdir:', tmpPath);

const files = fs.readdirSync(tmpPath);
console.error('Files blocking:', files);

if (files.some((f) => f.startsWith('.nfs'))) {
// Warn about NFS "silly rename"
console.error('Note: ".nfs*" might be files that were open and ' +
'unlinked but not closed.');
console.error('See http://nfs.sourceforge.net/#faq_d2 for details.');
}

console.error();
throw e;
}
}

module.exports = {
Expand Down
21 changes: 19 additions & 2 deletions test/parallel/test-child-process-server-close.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
'use strict';

const common = require('../common');
const { spawn } = require('child_process');
const assert = require('assert');
const { fork, spawn } = require('child_process');
const net = require('net');

const tmpdir = require('../common/tmpdir');
tmpdir.refresh();

// Run in a child process because the PIPE file descriptor stays open until
// Node.js completes, blocking the tmpdir and preventing cleanup.

if (process.argv[2] !== 'child') {
// Parent
tmpdir.refresh();

// Run test
const child = fork(__filename, ['child'], { stdio: 'inherit' });
child.on('exit', common.mustCall(function(code) {
assert.strictEqual(code, 0);
}));

return;
}

// Child
const server = net.createServer((conn) => {
spawn(process.execPath, ['-v'], {
stdio: ['ignore', conn, 'ignore']
Expand Down
10 changes: 0 additions & 10 deletions test/parallel/test-fs-append-file-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,3 @@ const fileData5 = fs.readFileSync(filename5);

assert.strictEqual(Buffer.byteLength(data) + currentFileData.length,
fileData5.length);

// Exit logic for cleanup.

process.on('exit', function() {
fs.unlinkSync(filename);
fs.unlinkSync(filename2);
fs.unlinkSync(filename3);
fs.unlinkSync(filename4);
fs.unlinkSync(filename5);
});
1 change: 1 addition & 0 deletions test/parallel/test-fs-buffertype-writesync.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ v.forEach((value) => {
const fd = fs.openSync(filePath, 'w');
fs.writeSync(fd, value);
assert.strictEqual(fs.readFileSync(filePath).toString(), String(value));
fs.closeSync(fd);
});
1 change: 1 addition & 0 deletions test/parallel/test-fs-fsync.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ fs.open(fileTemp, 'a', 0o777, common.mustCall(function(err, fd) {
assert.ifError(err);
fs.fsync(fd, common.mustCall(function(err) {
assert.ifError(err);
fs.closeSync(fd);
}));
}));
}));
Expand Down
4 changes: 0 additions & 4 deletions test/parallel/test-fs-long-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,3 @@ fs.writeFile(fullPath, 'ok', common.mustCall(function(err) {
assert.ifError(err);
}));
}));

process.on('exit', function() {
fs.unlinkSync(fullPath);
});
5 changes: 4 additions & 1 deletion test/parallel/test-fs-open-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,8 @@ if (common.isLinux || common.isOSX) {
tmpdir.refresh();
const file = path.join(tmpdir.path, 'a.js');
fs.copyFileSync(fixtures.path('a.js'), file);
fs.open(file, O_DSYNC, common.mustCall(assert.ifError));
fs.open(file, O_DSYNC, common.mustCall((err, fd) => {
assert.ifError(err);
fs.closeSync(fd);
}));
}
4 changes: 2 additions & 2 deletions test/parallel/test-fs-options-immutable.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ if (common.canCreateSymLink()) {
{
const fileName = path.resolve(tmpdir.path, 'streams');
fs.WriteStream(fileName, options).once('open', common.mustCall(() => {
fs.ReadStream(fileName, options);
}));
fs.ReadStream(fileName, options).destroy();
})).end();
}
4 changes: 4 additions & 0 deletions test/parallel/test-fs-promises-file-handle-append-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ async function validateAppendBuffer() {
await fileHandle.appendFile(buffer);
const appendedFileData = fs.readFileSync(filePath);
assert.deepStrictEqual(appendedFileData, buffer);

await fileHandle.close();
}

async function validateAppendString() {
Expand All @@ -33,6 +35,8 @@ async function validateAppendString() {
const stringAsBuffer = Buffer.from(string, 'utf8');
const appendedFileData = fs.readFileSync(filePath);
assert.deepStrictEqual(appendedFileData, stringAsBuffer);

await fileHandle.close();
}

validateAppendBuffer()
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-fs-promises-file-handle-chmod.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ async function validateFilePermission() {
await fileHandle.chmod(newPermissions);
const statsAfterMod = fs.statSync(filePath);
assert.deepStrictEqual(statsAfterMod.mode & expectedAccess, expectedAccess);

await fileHandle.close();
}

validateFilePermission().then(common.mustCall());
4 changes: 4 additions & 0 deletions test/parallel/test-fs-promises-file-handle-read.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ async function validateRead() {
const readAsyncHandle = await fileHandle.read(Buffer.alloc(11), 0, 11, 0);
assert.deepStrictEqual(buffer.length, readAsyncHandle.bytesRead);
assert.deepStrictEqual(buffer, readAsyncHandle.buffer);

await fileHandle.close();
}

async function validateEmptyRead() {
Expand All @@ -38,6 +40,8 @@ async function validateEmptyRead() {
fs.closeSync(fd);
const readAsyncHandle = await fileHandle.read(Buffer.alloc(11), 0, 11, 0);
assert.deepStrictEqual(buffer.length, readAsyncHandle.bytesRead);

await fileHandle.close();
}

async function validateLargeRead() {
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-fs-promises-file-handle-readFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ async function validateReadFile() {

const readFileData = await fileHandle.readFile();
assert.deepStrictEqual(buffer, readFileData);

await fileHandle.close();
}

async function validateReadFileProc() {
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-fs-promises-file-handle-stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ async function validateStat() {
const fileHandle = await open(filePath, 'w+');
const stats = await fileHandle.stat();
assert.ok(stats.mtime instanceof Date);
await fileHandle.close();
}

validateStat()
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-fs-promises-file-handle-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ async function validateSync() {
const ret = await handle.read(Buffer.alloc(11), 0, 11, 0);
assert.strictEqual(ret.bytesRead, 11);
assert.deepStrictEqual(ret.buffer, buf);
await handle.close();
}

validateSync();
2 changes: 2 additions & 0 deletions test/parallel/test-fs-promises-file-handle-truncate.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ async function validateTruncate() {

await fileHandle.truncate(5);
assert.deepStrictEqual((await readFile(filename)).toString(), 'Hello');

await fileHandle.close();
}

validateTruncate().then(common.mustCall());
Loading