@zenfs/core 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/fetch.js +5 -5
- package/dist/config.js +1 -0
- package/license.md +1 -1
- package/package.json +5 -3
- package/readme.md +3 -8
- package/scripts/make-index.js +3 -3
- package/scripts/test.js +31 -0
- package/tests/assignment.ts +20 -0
- package/tests/common.ts +21 -0
- package/tests/data/49chars.txt +1 -0
- package/tests/data/a.js +46 -0
- package/tests/data/a1.js +46 -0
- package/tests/data/elipses.txt +1 -0
- package/tests/data/empty.txt +0 -0
- package/tests/data/exit.js +22 -0
- package/tests/data/x.txt +1 -0
- package/tests/devices.test.ts +29 -0
- package/tests/fs/appendFile.test.ts +33 -0
- package/tests/fs/chmod.test.ts +48 -0
- package/tests/fs/dir.test.ts +156 -0
- package/tests/fs/directory.test.ts +129 -0
- package/tests/fs/errors.test.ts +53 -0
- package/tests/fs/exists.test.ts +22 -0
- package/tests/fs/links.test.ts +46 -0
- package/tests/fs/open.test.ts +39 -0
- package/tests/fs/permissions.test.ts +52 -0
- package/tests/fs/read.test.ts +67 -0
- package/tests/fs/readFile.test.ts +73 -0
- package/tests/fs/readdir.test.ts +87 -0
- package/tests/fs/rename.test.ts +107 -0
- package/tests/fs/stat.test.ts +48 -0
- package/tests/fs/streams.test.ts +177 -0
- package/tests/fs/times.test.ts +84 -0
- package/tests/fs/truncate.test.ts +94 -0
- package/tests/fs/watch.test.ts +124 -0
- package/tests/fs/write.test.ts +58 -0
- package/tests/fs/writeFile.test.ts +69 -0
- package/tests/handle.test.ts +60 -0
- package/tests/mutex.test.ts +62 -0
- package/tests/path.test.ts +34 -0
- package/tests/port/channel.test.ts +39 -0
- package/tests/port/config.test.ts +31 -0
- package/tests/port/config.worker.ts +5 -0
- package/tests/port/remote.test.ts +33 -0
- package/tests/port/remote.worker.ts +5 -0
- package/tests/port/timeout.test.ts +48 -0
- package/tests/readme.md +5 -0
- package/tests/setup/common.ts +28 -0
- package/tests/setup/cow+fetch.ts +43 -0
- package/tests/setup/memory.ts +3 -0
- package/tests/tsconfig.json +14 -0
- package/src/backends/backend.ts +0 -160
- package/src/backends/fetch.ts +0 -179
- package/src/backends/file_index.ts +0 -210
- package/src/backends/memory.ts +0 -50
- package/src/backends/overlay.ts +0 -568
- package/src/backends/port/fs.ts +0 -335
- package/src/backends/port/readme.md +0 -54
- package/src/backends/port/rpc.ts +0 -167
- package/src/backends/readme.md +0 -3
- package/src/backends/store/fs.ts +0 -715
- package/src/backends/store/readme.md +0 -9
- package/src/backends/store/simple.ts +0 -146
- package/src/backends/store/store.ts +0 -173
- package/src/config.ts +0 -152
- package/src/credentials.ts +0 -31
- package/src/devices.ts +0 -471
- package/src/emulation/async.ts +0 -834
- package/src/emulation/constants.ts +0 -182
- package/src/emulation/dir.ts +0 -138
- package/src/emulation/index.ts +0 -8
- package/src/emulation/path.ts +0 -440
- package/src/emulation/promises.ts +0 -1098
- package/src/emulation/shared.ts +0 -135
- package/src/emulation/streams.ts +0 -34
- package/src/emulation/sync.ts +0 -845
- package/src/emulation/watchers.ts +0 -193
- package/src/error.ts +0 -307
- package/src/file.ts +0 -661
- package/src/filesystem.ts +0 -174
- package/src/index.ts +0 -25
- package/src/inode.ts +0 -132
- package/src/mixins/async.ts +0 -208
- package/src/mixins/index.ts +0 -5
- package/src/mixins/mutexed.ts +0 -257
- package/src/mixins/readonly.ts +0 -96
- package/src/mixins/shared.ts +0 -25
- package/src/mixins/sync.ts +0 -58
- package/src/polyfills.ts +0 -21
- package/src/stats.ts +0 -363
- package/src/utils.ts +0 -288
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { fs } from '../common.js';
|
|
4
|
+
|
|
5
|
+
const testDir = 'test-dir';
|
|
6
|
+
const testFiles = ['file1.txt', 'file2.txt', 'file3.txt'];
|
|
7
|
+
const testDirectories = ['subdir1', 'subdir2'];
|
|
8
|
+
|
|
9
|
+
await fs.promises.mkdir(testDir);
|
|
10
|
+
for (const file of testFiles) {
|
|
11
|
+
await fs.promises.writeFile(`${testDir}/${file}`, 'Sample content');
|
|
12
|
+
}
|
|
13
|
+
for (const dir of testDirectories) {
|
|
14
|
+
await fs.promises.mkdir(`${testDir}/${dir}`);
|
|
15
|
+
for (const file of ['file4.txt', 'file5.txt']) {
|
|
16
|
+
await fs.promises.writeFile(`${testDir}/${dir}/${file}`, 'Sample content');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
suite('readdir and readdirSync', () => {
|
|
21
|
+
test('readdir returns files and directories', async () => {
|
|
22
|
+
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true });
|
|
23
|
+
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name);
|
|
24
|
+
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
|
|
25
|
+
|
|
26
|
+
assert(testFiles.every(file => files.includes(file)));
|
|
27
|
+
assert(testDirectories.every(dir => dirs.includes(dir)));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('readdirSync returns files and directories', () => {
|
|
31
|
+
const dirents = fs.readdirSync(testDir, { withFileTypes: true });
|
|
32
|
+
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name);
|
|
33
|
+
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
|
|
34
|
+
|
|
35
|
+
assert(testFiles.every(file => files.includes(file)));
|
|
36
|
+
assert(testDirectories.every(dir => dirs.includes(dir)));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('readdir returns Dirent objects', async () => {
|
|
40
|
+
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true });
|
|
41
|
+
assert(dirents[0] instanceof fs.Dirent);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('readdirSync returns Dirent objects', () => {
|
|
45
|
+
const dirents = fs.readdirSync(testDir, { withFileTypes: true });
|
|
46
|
+
assert(dirents[0] instanceof fs.Dirent);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('readdir works without withFileTypes option', async () => {
|
|
50
|
+
const files = await fs.promises.readdir(testDir);
|
|
51
|
+
assert(testFiles.every(entry => files.includes(entry)));
|
|
52
|
+
assert(testDirectories.every(entry => files.includes(entry)));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('readdirSync works without withFileTypes option', () => {
|
|
56
|
+
const files = fs.readdirSync(testDir);
|
|
57
|
+
assert(testFiles.every(entry => files.includes(entry)));
|
|
58
|
+
assert(testDirectories.every(entry => files.includes(entry)));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('readdir returns files recursively', async () => {
|
|
62
|
+
const entries = await fs.promises.readdir(testDir, { recursive: true });
|
|
63
|
+
assert(entries.includes('file1.txt'));
|
|
64
|
+
assert(entries.includes('subdir1/file4.txt'));
|
|
65
|
+
assert(entries.includes('subdir2/file5.txt'));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('readdir returns Dirent recursively', async () => {
|
|
69
|
+
const entries = await fs.promises.readdir(testDir, { recursive: true, withFileTypes: true });
|
|
70
|
+
assert.equal(entries[0].path, 'file1.txt');
|
|
71
|
+
assert.equal(entries[4].path, 'subdir1/file4.txt');
|
|
72
|
+
assert.equal(entries[entries.length - 1].path, 'subdir2/file5.txt');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// New test for readdirSync with recursive: true
|
|
76
|
+
test('readdirSync returns files recursively', () => {
|
|
77
|
+
const entries = fs.readdirSync(testDir, { recursive: true });
|
|
78
|
+
assert(entries.includes('file1.txt'));
|
|
79
|
+
assert(entries.includes('subdir1/file4.txt'));
|
|
80
|
+
assert(entries.includes('subdir2/file5.txt'));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('Cyrillic file names', () => {
|
|
84
|
+
fs.writeFileSync('/мой-файл.txt', 'HELLO!', 'utf-8');
|
|
85
|
+
assert(fs.readdirSync('/').includes('мой-файл.txt'));
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { ErrnoError } from '../../src/error.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
suite('Rename', () => {
|
|
7
|
+
/**
|
|
8
|
+
* Creates the following directory structure within `dir`:
|
|
9
|
+
* - _rename_me
|
|
10
|
+
* - lol.txt
|
|
11
|
+
* - file.dat
|
|
12
|
+
*/
|
|
13
|
+
async function populate(dir: string) {
|
|
14
|
+
await fs.promises.mkdir(dir + '/_rename_me');
|
|
15
|
+
await fs.promises.writeFile(dir + '/file.dat', 'filedata');
|
|
16
|
+
await fs.promises.writeFile(dir + '/_rename_me/lol.txt', 'lololol');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check that the directory structure created in populate_directory remains.
|
|
21
|
+
*/
|
|
22
|
+
async function check_directory(dir: string) {
|
|
23
|
+
const contents = await fs.promises.readdir(dir);
|
|
24
|
+
assert(contents.length === 2);
|
|
25
|
+
|
|
26
|
+
const subContents = await fs.promises.readdir(dir + '/_rename_me');
|
|
27
|
+
assert(subContents.length === 1);
|
|
28
|
+
|
|
29
|
+
assert(await fs.promises.exists(dir + '/file.dat'));
|
|
30
|
+
assert(await fs.promises.exists(dir + '/_rename_me/lol.txt'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test('rename directory', async () => {
|
|
34
|
+
const oldDir = '/rename_test';
|
|
35
|
+
const newDir = '/rename_test2';
|
|
36
|
+
|
|
37
|
+
await fs.promises.mkdir(oldDir);
|
|
38
|
+
|
|
39
|
+
await populate(oldDir);
|
|
40
|
+
|
|
41
|
+
await fs.promises.rename(oldDir, oldDir);
|
|
42
|
+
|
|
43
|
+
await check_directory(oldDir);
|
|
44
|
+
|
|
45
|
+
await fs.promises.rename(oldDir, newDir);
|
|
46
|
+
|
|
47
|
+
await check_directory(newDir);
|
|
48
|
+
|
|
49
|
+
assert(!(await fs.promises.exists(oldDir)));
|
|
50
|
+
|
|
51
|
+
await fs.promises.mkdir(oldDir);
|
|
52
|
+
await populate(oldDir);
|
|
53
|
+
await fs.promises.rename(oldDir, newDir + '/newDir');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('rename file', async () => {
|
|
57
|
+
const dir = '/rename_file_test';
|
|
58
|
+
const one = dir + '/fun.js';
|
|
59
|
+
const two = dir + '/fun2.js';
|
|
60
|
+
|
|
61
|
+
await fs.promises.mkdir(dir);
|
|
62
|
+
await fs.promises.writeFile(one, 'while(1) alert("Hey! Listen!");');
|
|
63
|
+
await fs.promises.rename(one, one);
|
|
64
|
+
await fs.promises.rename(one, two);
|
|
65
|
+
|
|
66
|
+
await fs.promises.writeFile(one, 'hey');
|
|
67
|
+
await fs.promises.rename(one, two);
|
|
68
|
+
|
|
69
|
+
assert((await fs.promises.readFile(two, 'utf8')) === 'hey');
|
|
70
|
+
assert(!(await fs.promises.exists(one)));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('File to Directory and Directory to File Rename', async () => {
|
|
74
|
+
const dir = '/rename_filedir_test';
|
|
75
|
+
const file = '/rename_filedir_test.txt';
|
|
76
|
+
|
|
77
|
+
await fs.promises.mkdir(dir);
|
|
78
|
+
await fs.promises.writeFile(file, 'file contents go here');
|
|
79
|
+
|
|
80
|
+
await fs.promises.rename(file, dir).catch((error: ErrnoError) => {
|
|
81
|
+
assert(error instanceof ErrnoError);
|
|
82
|
+
assert(error.code === 'EISDIR' || error.code === 'EPERM');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// JV: Removing test for now. I noticed that you can do that in Node v0.12 on Mac,
|
|
86
|
+
// but it might be FS independent.
|
|
87
|
+
/*fs.rename(dir, file, function (e) {
|
|
88
|
+
if (e == null) {
|
|
89
|
+
throw new Error("Failed invariant: Cannot rename a directory over a file.");
|
|
90
|
+
} else {
|
|
91
|
+
assert(e.code === 'ENOTDIR');
|
|
92
|
+
}
|
|
93
|
+
});*/
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('rename directory inside itself', async () => {
|
|
97
|
+
const renDir1 = '/renamedir_1';
|
|
98
|
+
const renDir2 = '/renamedir_1/lol';
|
|
99
|
+
|
|
100
|
+
await fs.promises.mkdir(renDir1);
|
|
101
|
+
|
|
102
|
+
await fs.promises.rename(renDir1, renDir2).catch((error: ErrnoError) => {
|
|
103
|
+
assert(error instanceof ErrnoError);
|
|
104
|
+
assert(error.code === 'EBUSY');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { Stats } from '../../src/stats.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
suite('Stats', () => {
|
|
7
|
+
const existing_file = 'x.txt';
|
|
8
|
+
|
|
9
|
+
test('stat empty path', () => {
|
|
10
|
+
assert.rejects(fs.promises.stat(''));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('stat directory', async () => {
|
|
14
|
+
const stats = await fs.promises.stat('/');
|
|
15
|
+
assert(stats instanceof Stats);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('lstat directory', async () => {
|
|
19
|
+
const stats = await fs.promises.lstat('/');
|
|
20
|
+
assert(stats instanceof Stats);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('FileHandle.stat', async () => {
|
|
24
|
+
const handle = await fs.promises.open(existing_file, 'r');
|
|
25
|
+
const stats = await handle.stat();
|
|
26
|
+
assert(stats instanceof Stats);
|
|
27
|
+
await handle.close();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('fstatSync file', () => {
|
|
31
|
+
const fd = fs.openSync(existing_file, 'r');
|
|
32
|
+
const stats = fs.fstatSync(fd);
|
|
33
|
+
assert(stats instanceof Stats);
|
|
34
|
+
fs.close(fd);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('stat file', async () => {
|
|
38
|
+
const stats = await fs.promises.stat(existing_file);
|
|
39
|
+
assert(!stats.isDirectory());
|
|
40
|
+
assert(stats.isFile());
|
|
41
|
+
assert(!stats.isSocket());
|
|
42
|
+
assert(!stats.isBlockDevice());
|
|
43
|
+
assert(!stats.isCharacterDevice());
|
|
44
|
+
assert(!stats.isFIFO());
|
|
45
|
+
assert(!stats.isSymbolicLink());
|
|
46
|
+
assert(stats instanceof Stats);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { fs } from '../common.js';
|
|
4
|
+
|
|
5
|
+
// Top-level initialization
|
|
6
|
+
const testFilePath = 'test-file.txt';
|
|
7
|
+
const testData = 'Hello, World!';
|
|
8
|
+
await fs.promises.writeFile(testFilePath, testData);
|
|
9
|
+
|
|
10
|
+
const testFilePathWrite = 'test-file-write.txt';
|
|
11
|
+
await fs.promises.writeFile(testFilePathWrite, ''); // Ensure the file exists
|
|
12
|
+
|
|
13
|
+
suite('ReadStream', () => {
|
|
14
|
+
test('ReadStream reads data correctly', (_, done) => {
|
|
15
|
+
const readStream = fs.createReadStream(testFilePath);
|
|
16
|
+
let data = '';
|
|
17
|
+
readStream.on('data', chunk => {
|
|
18
|
+
data += chunk;
|
|
19
|
+
});
|
|
20
|
+
readStream.on('end', () => {
|
|
21
|
+
assert(data == testData);
|
|
22
|
+
done();
|
|
23
|
+
});
|
|
24
|
+
readStream.on('error', err => {
|
|
25
|
+
done(err);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('ReadStream close method works', (_, done) => {
|
|
30
|
+
const readStream = fs.createReadStream(testFilePath);
|
|
31
|
+
let closed = false;
|
|
32
|
+
readStream.on('close', () => {
|
|
33
|
+
closed = true;
|
|
34
|
+
});
|
|
35
|
+
readStream.close(err => {
|
|
36
|
+
assert(err === undefined);
|
|
37
|
+
assert(closed);
|
|
38
|
+
done();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('ReadStream declared properties', () => {
|
|
43
|
+
const readStream = new fs.ReadStream();
|
|
44
|
+
assert(readStream.bytesRead === undefined);
|
|
45
|
+
assert(readStream.path === undefined);
|
|
46
|
+
assert(readStream.pending === undefined);
|
|
47
|
+
|
|
48
|
+
// Assign values
|
|
49
|
+
readStream.bytesRead = 10;
|
|
50
|
+
readStream.path = testFilePath;
|
|
51
|
+
readStream.pending = false;
|
|
52
|
+
|
|
53
|
+
assert(readStream.bytesRead === 10);
|
|
54
|
+
assert(readStream.path === testFilePath);
|
|
55
|
+
assert(!readStream.pending);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('ReadStream close method can be called multiple times', (_, done) => {
|
|
59
|
+
const readStream = new fs.ReadStream();
|
|
60
|
+
readStream.close(err => {
|
|
61
|
+
assert(err === undefined);
|
|
62
|
+
// Call close again
|
|
63
|
+
readStream.close(err2 => {
|
|
64
|
+
assert(err2 === undefined);
|
|
65
|
+
done();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
suite('WriteStream', () => {
|
|
72
|
+
test.skip('WriteStream writes data correctly', (_, done) => {
|
|
73
|
+
const writeStream = fs.createWriteStream(testFilePathWrite);
|
|
74
|
+
writeStream.write(testData, 'utf8', err => {
|
|
75
|
+
if (err) {
|
|
76
|
+
done(err);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
writeStream.end();
|
|
80
|
+
});
|
|
81
|
+
writeStream.on('finish', () => {
|
|
82
|
+
assert(fs.readFileSync(testFilePathWrite, 'utf8') == testData);
|
|
83
|
+
done();
|
|
84
|
+
});
|
|
85
|
+
writeStream.on('error', err => {
|
|
86
|
+
done(err);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('WriteStream close method works', (_, done) => {
|
|
91
|
+
const writeStream = fs.createWriteStream(testFilePathWrite);
|
|
92
|
+
let closed = false;
|
|
93
|
+
writeStream.on('close', () => {
|
|
94
|
+
closed = true;
|
|
95
|
+
});
|
|
96
|
+
writeStream.close(err => {
|
|
97
|
+
assert(err === undefined);
|
|
98
|
+
assert(closed);
|
|
99
|
+
done();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('WriteStream declared properties', () => {
|
|
104
|
+
const writeStream = new fs.WriteStream();
|
|
105
|
+
assert(writeStream.bytesWritten === undefined);
|
|
106
|
+
assert(writeStream.path === undefined);
|
|
107
|
+
assert(writeStream.pending === undefined);
|
|
108
|
+
|
|
109
|
+
// Assign values
|
|
110
|
+
writeStream.bytesWritten = 20;
|
|
111
|
+
writeStream.path = testFilePathWrite;
|
|
112
|
+
writeStream.pending = true;
|
|
113
|
+
|
|
114
|
+
assert(writeStream.bytesWritten === 20);
|
|
115
|
+
assert(writeStream.path === testFilePathWrite);
|
|
116
|
+
assert(writeStream.pending);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('WriteStream close method can be called multiple times', (_, done) => {
|
|
120
|
+
const writeStream = new fs.WriteStream();
|
|
121
|
+
writeStream.close(err => {
|
|
122
|
+
assert(err === undefined);
|
|
123
|
+
// Call close again
|
|
124
|
+
writeStream.close(err2 => {
|
|
125
|
+
assert(err2 === undefined);
|
|
126
|
+
done();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
suite('FileHandle', () => {
|
|
133
|
+
test.skip('FileHandle.createReadStream reads data correctly', async () => {
|
|
134
|
+
const fileHandle = await fs.promises.open(testFilePath, 'r');
|
|
135
|
+
const readStream = fileHandle.createReadStream();
|
|
136
|
+
let data = '';
|
|
137
|
+
await new Promise<void>((resolve, reject) => {
|
|
138
|
+
readStream.on('data', chunk => {
|
|
139
|
+
data += chunk;
|
|
140
|
+
});
|
|
141
|
+
readStream.on('end', () => {
|
|
142
|
+
assert(data == testData);
|
|
143
|
+
resolve();
|
|
144
|
+
});
|
|
145
|
+
readStream.on('error', reject);
|
|
146
|
+
});
|
|
147
|
+
await fileHandle.close();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test.skip('FileHandle.createWriteStream writes data correctly', async () => {
|
|
151
|
+
const fileHandle = await fs.promises.open(testFilePathWrite, 'w');
|
|
152
|
+
const writeStream = fileHandle.createWriteStream();
|
|
153
|
+
await new Promise<void>((resolve, reject) => {
|
|
154
|
+
writeStream.write(testData, 'utf8', err => {
|
|
155
|
+
if (err) return reject(err);
|
|
156
|
+
writeStream.end();
|
|
157
|
+
});
|
|
158
|
+
writeStream.on('finish', resolve);
|
|
159
|
+
writeStream.on('error', reject);
|
|
160
|
+
});
|
|
161
|
+
const data = await fs.promises.readFile(testFilePathWrite, 'utf8');
|
|
162
|
+
assert(data == testData);
|
|
163
|
+
await fileHandle.close();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('FileHandle.createReadStream after close should throw', async () => {
|
|
167
|
+
const fileHandle = await fs.promises.open(testFilePath, 'r');
|
|
168
|
+
await fileHandle.close();
|
|
169
|
+
assert.throws(() => fileHandle.createReadStream());
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test.skip('FileHandle.createWriteStream after close should throw', async () => {
|
|
173
|
+
const fileHandle = await fs.promises.open(testFilePathWrite, 'w');
|
|
174
|
+
await fileHandle.close();
|
|
175
|
+
assert.throws(() => fileHandle.createWriteStream());
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { wait } from 'utilium';
|
|
4
|
+
import { ErrnoError } from '../../src/error.js';
|
|
5
|
+
import { _toUnixTimestamp } from '../../src/utils.js';
|
|
6
|
+
import { fs } from '../common.js';
|
|
7
|
+
|
|
8
|
+
suite('times', () => {
|
|
9
|
+
const path = 'x.txt';
|
|
10
|
+
|
|
11
|
+
function expect_assert(resource: string | number, atime: Date | number, mtime: Date | number) {
|
|
12
|
+
const stats = typeof resource == 'string' ? fs.statSync(resource) : fs.fstatSync(resource);
|
|
13
|
+
// check up to single-second precision since sub-second precision is OS and fs dependent
|
|
14
|
+
assert(_toUnixTimestamp(atime) == _toUnixTimestamp(stats.atime));
|
|
15
|
+
assert(_toUnixTimestamp(mtime) == _toUnixTimestamp(stats.mtime));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function runTest(atime: Date | number, mtime: Date | number): Promise<void> {
|
|
19
|
+
await fs.promises.utimes(path, atime, mtime);
|
|
20
|
+
expect_assert(path, atime, mtime);
|
|
21
|
+
|
|
22
|
+
await fs.promises.utimes('foobarbaz', atime, mtime).catch((error: ErrnoError) => {
|
|
23
|
+
assert(error instanceof ErrnoError);
|
|
24
|
+
assert(error.code === 'ENOENT');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// don't close this fd
|
|
28
|
+
const handle = await fs.promises.open(path, 'r');
|
|
29
|
+
|
|
30
|
+
await handle.utimes(atime, mtime);
|
|
31
|
+
expect_assert(handle.fd, atime, mtime);
|
|
32
|
+
|
|
33
|
+
fs.utimesSync(path, atime, mtime);
|
|
34
|
+
expect_assert(path, atime, mtime);
|
|
35
|
+
|
|
36
|
+
// some systems don't have futimes
|
|
37
|
+
// if there's an error, it be ENOSYS
|
|
38
|
+
try {
|
|
39
|
+
fs.futimesSync(handle.fd, atime, mtime);
|
|
40
|
+
expect_assert(handle.fd, atime, mtime);
|
|
41
|
+
} catch (error: any) {
|
|
42
|
+
assert(error instanceof ErrnoError);
|
|
43
|
+
assert(error.code === 'ENOSYS');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
fs.utimesSync('foobarbaz', atime, mtime);
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
assert(error instanceof ErrnoError);
|
|
50
|
+
assert(error.code === 'ENOENT');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
fs.futimesSync(-1, atime, mtime);
|
|
55
|
+
} catch (error: any) {
|
|
56
|
+
assert(error instanceof ErrnoError);
|
|
57
|
+
assert(error.code == 'EBADF');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test('utimes works', async () => {
|
|
62
|
+
await runTest(new Date('1982/09/10 13:37:00'), new Date('1982/09/10 13:37:00'));
|
|
63
|
+
await runTest(new Date(), new Date());
|
|
64
|
+
await runTest(123456.789, 123456.789);
|
|
65
|
+
const stats = fs.statSync(path);
|
|
66
|
+
await runTest(stats.atime, stats.mtime);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('read changes atime', async () => {
|
|
70
|
+
const before = fs.statSync(path).atimeMs;
|
|
71
|
+
fs.readFileSync(path);
|
|
72
|
+
await wait(100);
|
|
73
|
+
const after = fs.statSync(path).atimeMs;
|
|
74
|
+
assert(before < after);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('write changes mtime', async () => {
|
|
78
|
+
const before = fs.statSync(path).mtimeMs;
|
|
79
|
+
fs.writeFileSync(path, 'cool');
|
|
80
|
+
await wait(100);
|
|
81
|
+
const after = fs.statSync(path).mtimeMs;
|
|
82
|
+
assert(before < after);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import type { FileHandle } from '../../src/emulation/promises.js';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
const path: string = 'truncate-file.txt',
|
|
7
|
+
size = 1024 * 16,
|
|
8
|
+
data = new Uint8Array(size).fill('x'.charCodeAt(0));
|
|
9
|
+
|
|
10
|
+
suite('Truncate, sync', () => {
|
|
11
|
+
test('initial write', () => {
|
|
12
|
+
fs.writeFileSync(path, data);
|
|
13
|
+
assert(fs.statSync(path).size === size);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('truncate to 1024', () => {
|
|
17
|
+
fs.truncateSync(path, 1024);
|
|
18
|
+
assert(fs.statSync(path).size === 1024);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('truncate to 0', () => {
|
|
22
|
+
fs.truncateSync(path);
|
|
23
|
+
assert(fs.statSync(path).size === 0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('write', () => {
|
|
27
|
+
fs.writeFileSync(path, data);
|
|
28
|
+
assert(fs.statSync(path).size === size);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let fd: number;
|
|
32
|
+
test('open r+', () => {
|
|
33
|
+
fd = fs.openSync(path, 'r+');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('ftruncate to 1024', () => {
|
|
37
|
+
fs.ftruncateSync(fd, 1024);
|
|
38
|
+
assert(fs.fstatSync(fd).size === 1024);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('ftruncate to 0', () => {
|
|
42
|
+
fs.ftruncateSync(fd);
|
|
43
|
+
assert(fs.fstatSync(fd).size === 0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('close fd', () => {
|
|
47
|
+
fs.closeSync(fd);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
suite('Truncate, async', () => {
|
|
51
|
+
const statSize = async (path: string) => (await fs.promises.stat(path)).size;
|
|
52
|
+
|
|
53
|
+
test('initial write', async () => {
|
|
54
|
+
await fs.promises.writeFile(path, data);
|
|
55
|
+
|
|
56
|
+
assert((await statSize(path)) === 1024 * 16);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('truncate to 1024', async () => {
|
|
60
|
+
await fs.promises.truncate(path, 1024);
|
|
61
|
+
assert((await statSize(path)) === 1024);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('truncate to 0', async () => {
|
|
65
|
+
await fs.promises.truncate(path);
|
|
66
|
+
assert((await statSize(path)) === 0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('write', async () => {
|
|
70
|
+
await fs.promises.writeFile(path, data);
|
|
71
|
+
assert((await statSize(path)) === size);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let handle: FileHandle;
|
|
75
|
+
test('open w', async () => {
|
|
76
|
+
handle = await fs.promises.open(path, 'w');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('handle.truncate to 1024', async () => {
|
|
80
|
+
await handle.truncate(1024);
|
|
81
|
+
await handle.sync();
|
|
82
|
+
assert((await statSize(path)) === 1024);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('handle.truncate to 0', async () => {
|
|
86
|
+
await handle.truncate();
|
|
87
|
+
await handle.sync();
|
|
88
|
+
assert((await statSize(path)) === 0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('close handle', async () => {
|
|
92
|
+
await handle.close();
|
|
93
|
+
});
|
|
94
|
+
});
|