Compare commits

...

1 Commits

Author SHA1 Message Date
Don Isaac
9e9d6769df fix(node/fs): several fs tests 2025-03-18 22:58:35 -07:00
6 changed files with 336 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
'use strict';
require('../common');
const assert = require('assert');
const fs = require('fs');
const {
O_CREAT = 0,
O_RDONLY = 0,
O_TRUNC = 0,
O_WRONLY = 0,
UV_FS_O_FILEMAP = 0
} = fs.constants;
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
// Run this test on all platforms. While UV_FS_O_FILEMAP is only available on
// Windows, it should be silently ignored on other platforms.
const filename = tmpdir.resolve('fmap.txt');
const text = 'Memory File Mapping Test';
const mw = UV_FS_O_FILEMAP | O_TRUNC | O_CREAT | O_WRONLY;
const mr = UV_FS_O_FILEMAP | O_RDONLY;
fs.writeFileSync(filename, text, { flag: mw });
const r1 = fs.readFileSync(filename, { encoding: 'utf8', flag: mr });
assert.strictEqual(r1, text);

View File

@@ -0,0 +1,82 @@
'use strict';
// Refs: https://github.com/nodejs/node/issues/33940
const common = require('../common');
const tmpdir = require('../common/tmpdir');
const fs = require('fs');
const assert = require('assert');
tmpdir.refresh();
const file = tmpdir.resolve('read_stream_pos_test.txt');
fs.writeFileSync(file, '');
let counter = 0;
const writeInterval = setInterval(() => {
counter = counter + 1;
const line = `hello at ${counter}\n`;
fs.writeFileSync(file, line, { flag: 'a' });
}, 1);
const hwm = 10;
let bufs = [];
let isLow = false;
let cur = 0;
let stream;
const readInterval = setInterval(() => {
if (stream) return;
stream = fs.createReadStream(file, {
highWaterMark: hwm,
start: cur
});
stream.on('data', common.mustCallAtLeast((chunk) => {
cur += chunk.length;
bufs.push(chunk);
if (isLow) {
const brokenLines = Buffer.concat(bufs).toString()
.split('\n')
.filter((line) => {
const s = 'hello at'.slice(0, line.length);
if (line && !line.startsWith(s)) {
return true;
}
return false;
});
assert.strictEqual(brokenLines.length, 0);
exitTest();
return;
}
if (chunk.length !== hwm) {
isLow = true;
}
}));
stream.on('end', () => {
stream = null;
isLow = false;
bufs = [];
});
}, 10);
// Time longer than 90 seconds to exit safely
const endTimer = setTimeout(() => {
exitTest();
}, 90000);
const exitTest = () => {
clearInterval(readInterval);
clearInterval(writeInterval);
clearTimeout(endTimer);
if (stream && !stream.destroyed) {
stream.on('close', () => {
process.exit();
});
stream.destroy();
} else {
process.exit();
}
};

View File

@@ -0,0 +1,27 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const tmpDir = tmpdir.path;
const longPath = path.join(...[tmpDir].concat(Array(30).fill('1234567890')));
fs.mkdirSync(longPath, { recursive: true });
// Test if we can have symlinks to files and folders with long filenames
const targetDirectory = path.join(longPath, 'target-directory');
fs.mkdirSync(targetDirectory);
const pathDirectory = path.join(tmpDir, 'new-directory');
fs.symlink(targetDirectory, pathDirectory, 'dir', common.mustSucceed(() => {
assert(fs.existsSync(pathDirectory));
}));
const targetFile = path.join(longPath, 'target-file');
fs.writeFileSync(targetFile, 'data');
const pathFile = path.join(tmpDir, 'new-file');
fs.symlink(targetFile, pathFile, common.mustSucceed(() => {
assert(fs.existsSync(pathFile));
}));

View File

@@ -0,0 +1,53 @@
'use strict';
const common = require('../common');
if (common.isIBMi)
common.skip('IBMi does not support `fs.watch()`');
// fs-watch on folders have limited capability in AIX.
// The testcase makes use of folder watching, and causes
// hang. This behavior is documented. Skip this for AIX.
if (common.isAIX)
common.skip('folder watch capability is limited in AIX.');
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
const testDir = tmpdir.path;
tmpdir.refresh();
// Add a file to newly created folder to already watching folder
const rootDirectory = fs.mkdtempSync(testDir + path.sep);
const testDirectory = path.join(rootDirectory, 'test-3');
fs.mkdirSync(testDirectory);
const filePath = path.join(testDirectory, 'folder-3');
const childrenFile = 'file-4.txt';
const childrenAbsolutePath = path.join(filePath, childrenFile);
const childrenRelativePath = path.join(path.basename(filePath), childrenFile);
let watcherClosed = false;
const watcher = fs.watch(testDirectory, { recursive: true });
watcher.on('change', function(event, filename) {
if (filename === childrenRelativePath) {
assert.strictEqual(event, 'rename');
watcher.close();
watcherClosed = true;
}
});
// Do the write with a delay to ensure that the OS is ready to notify us.
setTimeout(() => {
fs.mkdirSync(filePath);
fs.writeFileSync(childrenAbsolutePath, 'world');
}, common.platformTimeout(200));
process.once('exit', function() {
assert(watcherClosed, 'watcher Object was not closed');
});

View File

@@ -0,0 +1,111 @@
'use strict';
const common = require('../common');
const { setTimeout } = require('timers/promises');
if (common.isIBMi)
common.skip('IBMi does not support `fs.watch()`');
// fs-watch on folders have limited capability in AIX.
// The testcase makes use of folder watching, and causes
// hang. This behavior is documented. Skip this for AIX.
if (common.isAIX)
common.skip('folder watch capability is limited in AIX.');
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
const testDir = tmpdir.path;
tmpdir.refresh();
(async () => {
// Add a recursive symlink to the parent folder
const testDirectory = fs.mkdtempSync(testDir + path.sep);
// Do not use `testDirectory` as base. It will hang the tests.
const rootDirectory = path.join(testDirectory, 'test-1');
fs.mkdirSync(rootDirectory);
const filePath = path.join(rootDirectory, 'file.txt');
const symlinkFolder = path.join(rootDirectory, 'symlink-folder');
fs.symlinkSync(rootDirectory, symlinkFolder);
if (common.isMacOS) {
// On macOS delay watcher start to avoid leaking previous events.
// Refs: https://github.com/libuv/libuv/pull/4503
await setTimeout(common.platformTimeout(100));
}
const watcher = fs.watch(rootDirectory, { recursive: true });
let watcherClosed = false;
watcher.on('change', function(event, filename) {
assert.ok(event === 'rename', `Received ${event}`);
assert.ok(filename === path.basename(symlinkFolder) || filename === path.basename(filePath), `Received ${filename}`);
if (filename === path.basename(filePath)) {
watcher.close();
watcherClosed = true;
}
});
await setTimeout(common.platformTimeout(100));
fs.writeFileSync(filePath, 'world');
process.once('exit', function() {
assert(watcherClosed, 'watcher Object was not closed');
});
})().then(common.mustCall());
(async () => {
// This test checks how a symlink to outside the tracking folder can trigger change
// tmp/sub-directory/tracking-folder/symlink-folder -> tmp/sub-directory
const rootDirectory = fs.mkdtempSync(testDir + path.sep);
const subDirectory = path.join(rootDirectory, 'sub-directory');
fs.mkdirSync(subDirectory);
const trackingSubDirectory = path.join(subDirectory, 'tracking-folder');
fs.mkdirSync(trackingSubDirectory);
const symlinkFolder = path.join(trackingSubDirectory, 'symlink-folder');
fs.symlinkSync(subDirectory, symlinkFolder);
const forbiddenFile = path.join(subDirectory, 'forbidden.txt');
const acceptableFile = path.join(trackingSubDirectory, 'acceptable.txt');
if (common.isMacOS) {
// On macOS delay watcher start to avoid leaking previous events.
// Refs: https://github.com/libuv/libuv/pull/4503
await setTimeout(common.platformTimeout(100));
}
const watcher = fs.watch(trackingSubDirectory, { recursive: true });
let watcherClosed = false;
watcher.on('change', function(event, filename) {
// macOS will only change the following events:
// { event: 'rename', filename: 'symlink-folder' }
// { event: 'rename', filename: 'acceptable.txt' }
assert.ok(event === 'rename', `Received ${event}`);
assert.ok(filename === path.basename(symlinkFolder) || filename === path.basename(acceptableFile), `Received ${filename}`);
if (filename === path.basename(acceptableFile)) {
watcher.close();
watcherClosed = true;
}
});
await setTimeout(common.platformTimeout(100));
fs.writeFileSync(forbiddenFile, 'world');
await setTimeout(common.platformTimeout(100));
fs.writeFileSync(acceptableFile, 'acceptable');
process.once('exit', function() {
assert(watcherClosed, 'watcher Object was not closed');
});
})().then(common.mustCall());

View File

@@ -0,0 +1,35 @@
'use strict';
require('../common');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const fs = require('fs');
const stream = require('stream');
const tmpdir = require('../common/tmpdir');
const firstEncoding = 'base64';
const secondEncoding = 'latin1';
const examplePath = fixtures.path('x.txt');
const dummyPath = tmpdir.resolve('x.txt');
tmpdir.refresh();
const exampleReadStream = fs.createReadStream(examplePath, {
encoding: firstEncoding
});
const dummyWriteStream = fs.createWriteStream(dummyPath, {
encoding: firstEncoding
});
exampleReadStream.pipe(dummyWriteStream).on('finish', function() {
const assertWriteStream = new stream.Writable({
write: function(chunk, enc, next) {
const expected = Buffer.from('xyz\n');
assert(chunk.equals(expected));
}
});
assertWriteStream.setDefaultEncoding(secondEncoding);
fs.createReadStream(dummyPath, {
encoding: secondEncoding
}).pipe(assertWriteStream);
});