Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/node/pfs/pfs.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import * as fs from 'fs';
8
import { tmpdir } from 'os';
9
import { timeout } from '../../../common/async.js';
10
import { VSBuffer } from '../../../common/buffer.js';
11
import { randomPath } from '../../../common/extpath.js';
12
import { FileAccess } from '../../../common/network.js';
13
import { basename, dirname, join, sep } from '../../../common/path.js';
14
import { isWindows } from '../../../common/platform.js';
15
import { configureFlushOnWrite, Promises, realcase, realpathSync, RimRafMode, SymlinkSupport, writeFileSync } from '../../../node/pfs.js';
16
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../common/utils.js';
17
import { flakySuite, getRandomTestPath } from '../testUtils.js';
18
19
configureFlushOnWrite(false); // speed up all unit tests by disabling flush on write
20
21
flakySuite('PFS', function () {
22
23
let testDir: string;
24
25
setup(() => {
26
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'pfs');
27
28
return fs.promises.mkdir(testDir, { recursive: true });
29
});
30
31
teardown(() => {
32
return Promises.rm(testDir);
33
});
34
35
test('writeFile', async () => {
36
const testFile = join(testDir, 'writefile.txt');
37
38
assert.ok(!(await Promises.exists(testFile)));
39
40
await Promises.writeFile(testFile, 'Hello World', (null!));
41
42
assert.strictEqual((await fs.promises.readFile(testFile)).toString(), 'Hello World');
43
});
44
45
test('writeFile - parallel write on different files works', async () => {
46
const testFile1 = join(testDir, 'writefile1.txt');
47
const testFile2 = join(testDir, 'writefile2.txt');
48
const testFile3 = join(testDir, 'writefile3.txt');
49
const testFile4 = join(testDir, 'writefile4.txt');
50
const testFile5 = join(testDir, 'writefile5.txt');
51
52
await Promise.all([
53
Promises.writeFile(testFile1, 'Hello World 1', (null!)),
54
Promises.writeFile(testFile2, 'Hello World 2', (null!)),
55
Promises.writeFile(testFile3, 'Hello World 3', (null!)),
56
Promises.writeFile(testFile4, 'Hello World 4', (null!)),
57
Promises.writeFile(testFile5, 'Hello World 5', (null!))
58
]);
59
assert.strictEqual(fs.readFileSync(testFile1).toString(), 'Hello World 1');
60
assert.strictEqual(fs.readFileSync(testFile2).toString(), 'Hello World 2');
61
assert.strictEqual(fs.readFileSync(testFile3).toString(), 'Hello World 3');
62
assert.strictEqual(fs.readFileSync(testFile4).toString(), 'Hello World 4');
63
assert.strictEqual(fs.readFileSync(testFile5).toString(), 'Hello World 5');
64
});
65
66
test('writeFile - parallel write on same files works and is sequentalized', async () => {
67
const testFile = join(testDir, 'writefile.txt');
68
69
await Promise.all([
70
Promises.writeFile(testFile, 'Hello World 1', undefined),
71
Promises.writeFile(testFile, 'Hello World 2', undefined),
72
timeout(10).then(() => Promises.writeFile(testFile, 'Hello World 3', undefined)),
73
Promises.writeFile(testFile, 'Hello World 4', undefined),
74
timeout(10).then(() => Promises.writeFile(testFile, 'Hello World 5', undefined))
75
]);
76
assert.strictEqual(fs.readFileSync(testFile).toString(), 'Hello World 5');
77
});
78
79
test('rimraf - simple - unlink', async () => {
80
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
81
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
82
83
await Promises.rm(testDir);
84
assert.ok(!fs.existsSync(testDir));
85
});
86
87
test('rimraf - simple - move', async () => {
88
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
89
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
90
91
await Promises.rm(testDir, RimRafMode.MOVE);
92
assert.ok(!fs.existsSync(testDir));
93
});
94
95
test('rimraf - simple - move (with moveToPath)', async () => {
96
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
97
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
98
99
await Promises.rm(testDir, RimRafMode.MOVE, join(dirname(testDir), `${basename(testDir)}.vsctmp`));
100
assert.ok(!fs.existsSync(testDir));
101
});
102
103
test('rimraf - path does not exist - move', async () => {
104
const nonExistingDir = join(testDir, 'unknown-move');
105
await Promises.rm(nonExistingDir, RimRafMode.MOVE);
106
});
107
108
test('rimraf - path does not exist - unlink', async () => {
109
const nonExistingDir = join(testDir, 'unknown-unlink');
110
await Promises.rm(nonExistingDir, RimRafMode.UNLINK);
111
});
112
113
test('rimraf - recursive folder structure - unlink', async () => {
114
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
115
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
116
fs.mkdirSync(join(testDir, 'somefolder'));
117
fs.writeFileSync(join(testDir, 'somefolder', 'somefile.txt'), 'Contents');
118
119
await Promises.rm(testDir);
120
assert.ok(!fs.existsSync(testDir));
121
});
122
123
test('rimraf - recursive folder structure - move', async () => {
124
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
125
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
126
fs.mkdirSync(join(testDir, 'somefolder'));
127
fs.writeFileSync(join(testDir, 'somefolder', 'somefile.txt'), 'Contents');
128
129
await Promises.rm(testDir, RimRafMode.MOVE);
130
assert.ok(!fs.existsSync(testDir));
131
});
132
133
test('rimraf - simple ends with dot - move', async () => {
134
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
135
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
136
137
await Promises.rm(testDir, RimRafMode.MOVE);
138
assert.ok(!fs.existsSync(testDir));
139
});
140
141
test('rimraf - simple ends with dot slash/backslash - move', async () => {
142
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
143
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
144
145
await Promises.rm(`${testDir}${sep}`, RimRafMode.MOVE);
146
assert.ok(!fs.existsSync(testDir));
147
});
148
149
test('copy, rename and delete', async () => {
150
const sourceDir = FileAccess.asFileUri('vs/base/test/node/pfs/fixtures').fsPath;
151
const parentDir = join(tmpdir(), 'vsctests', 'pfs');
152
const targetDir = randomPath(parentDir);
153
const targetDir2 = randomPath(parentDir);
154
155
await Promises.copy(sourceDir, targetDir, { preserveSymlinks: true });
156
157
assert.ok(fs.existsSync(targetDir));
158
assert.ok(fs.existsSync(join(targetDir, 'index.html')));
159
assert.ok(fs.existsSync(join(targetDir, 'site.css')));
160
assert.ok(fs.existsSync(join(targetDir, 'examples')));
161
assert.ok(fs.statSync(join(targetDir, 'examples')).isDirectory());
162
assert.ok(fs.existsSync(join(targetDir, 'examples', 'small.jxs')));
163
164
await Promises.rename(targetDir, targetDir2);
165
166
assert.ok(!fs.existsSync(targetDir));
167
assert.ok(fs.existsSync(targetDir2));
168
assert.ok(fs.existsSync(join(targetDir2, 'index.html')));
169
assert.ok(fs.existsSync(join(targetDir2, 'site.css')));
170
assert.ok(fs.existsSync(join(targetDir2, 'examples')));
171
assert.ok(fs.statSync(join(targetDir2, 'examples')).isDirectory());
172
assert.ok(fs.existsSync(join(targetDir2, 'examples', 'small.jxs')));
173
174
await Promises.rename(join(targetDir2, 'index.html'), join(targetDir2, 'index_moved.html'));
175
176
assert.ok(!fs.existsSync(join(targetDir2, 'index.html')));
177
assert.ok(fs.existsSync(join(targetDir2, 'index_moved.html')));
178
179
await Promises.rm(parentDir);
180
181
assert.ok(!fs.existsSync(parentDir));
182
});
183
184
test('rename without retry', async () => {
185
const sourceDir = FileAccess.asFileUri('vs/base/test/node/pfs/fixtures').fsPath;
186
const parentDir = join(tmpdir(), 'vsctests', 'pfs');
187
const targetDir = randomPath(parentDir);
188
const targetDir2 = randomPath(parentDir);
189
190
await Promises.copy(sourceDir, targetDir, { preserveSymlinks: true });
191
await Promises.rename(targetDir, targetDir2, false);
192
193
assert.ok(!fs.existsSync(targetDir));
194
assert.ok(fs.existsSync(targetDir2));
195
assert.ok(fs.existsSync(join(targetDir2, 'index.html')));
196
assert.ok(fs.existsSync(join(targetDir2, 'site.css')));
197
assert.ok(fs.existsSync(join(targetDir2, 'examples')));
198
assert.ok(fs.statSync(join(targetDir2, 'examples')).isDirectory());
199
assert.ok(fs.existsSync(join(targetDir2, 'examples', 'small.jxs')));
200
201
await Promises.rename(join(targetDir2, 'index.html'), join(targetDir2, 'index_moved.html'), false);
202
203
assert.ok(!fs.existsSync(join(targetDir2, 'index.html')));
204
assert.ok(fs.existsSync(join(targetDir2, 'index_moved.html')));
205
206
await Promises.rm(parentDir);
207
208
assert.ok(!fs.existsSync(parentDir));
209
});
210
211
test('copy handles symbolic links', async () => {
212
const symbolicLinkTarget = randomPath(testDir);
213
const symLink = randomPath(testDir);
214
const copyTarget = randomPath(testDir);
215
216
await fs.promises.mkdir(symbolicLinkTarget, { recursive: true });
217
218
fs.symlinkSync(symbolicLinkTarget, symLink, 'junction');
219
220
// Copy preserves symlinks if configured as such
221
//
222
// Windows: this test does not work because creating symlinks
223
// requires priviledged permissions (admin).
224
if (!isWindows) {
225
await Promises.copy(symLink, copyTarget, { preserveSymlinks: true });
226
227
assert.ok(fs.existsSync(copyTarget));
228
229
const { symbolicLink } = await SymlinkSupport.stat(copyTarget);
230
assert.ok(symbolicLink);
231
assert.ok(!symbolicLink.dangling);
232
233
const target = await fs.promises.readlink(copyTarget);
234
assert.strictEqual(target, symbolicLinkTarget);
235
236
// Copy does not preserve symlinks if configured as such
237
238
await Promises.rm(copyTarget);
239
await Promises.copy(symLink, copyTarget, { preserveSymlinks: false });
240
241
assert.ok(fs.existsSync(copyTarget));
242
243
const { symbolicLink: symbolicLink2 } = await SymlinkSupport.stat(copyTarget);
244
assert.ok(!symbolicLink2);
245
}
246
247
// Copy does not fail over dangling symlinks
248
249
await Promises.rm(copyTarget);
250
await Promises.rm(symbolicLinkTarget);
251
252
await Promises.copy(symLink, copyTarget, { preserveSymlinks: true }); // this should not throw
253
254
if (!isWindows) {
255
const { symbolicLink } = await SymlinkSupport.stat(copyTarget);
256
assert.ok(symbolicLink?.dangling);
257
} else {
258
assert.ok(!fs.existsSync(copyTarget));
259
}
260
});
261
262
test('copy handles symbolic links when the reference is inside source', async () => {
263
264
// Source Folder
265
const sourceFolder = join(randomPath(testDir), 'copy-test'); // copy-test
266
const sourceLinkTestFolder = join(sourceFolder, 'link-test'); // copy-test/link-test
267
const sourceLinkMD5JSFolder = join(sourceLinkTestFolder, 'md5'); // copy-test/link-test/md5
268
const sourceLinkMD5JSFile = join(sourceLinkMD5JSFolder, 'md5.js'); // copy-test/link-test/md5/md5.js
269
await fs.promises.mkdir(sourceLinkMD5JSFolder, { recursive: true });
270
await Promises.writeFile(sourceLinkMD5JSFile, 'Hello from MD5');
271
272
const sourceLinkMD5JSFolderLinked = join(sourceLinkTestFolder, 'md5-linked'); // copy-test/link-test/md5-linked
273
fs.symlinkSync(sourceLinkMD5JSFolder, sourceLinkMD5JSFolderLinked, 'junction');
274
275
// Target Folder
276
const targetLinkTestFolder = join(sourceFolder, 'link-test copy'); // copy-test/link-test copy
277
const targetLinkMD5JSFolder = join(targetLinkTestFolder, 'md5'); // copy-test/link-test copy/md5
278
const targetLinkMD5JSFile = join(targetLinkMD5JSFolder, 'md5.js'); // copy-test/link-test copy/md5/md5.js
279
const targetLinkMD5JSFolderLinked = join(targetLinkTestFolder, 'md5-linked'); // copy-test/link-test copy/md5-linked
280
281
// Copy with `preserveSymlinks: true` and verify result
282
//
283
// Windows: this test does not work because creating symlinks
284
// requires priviledged permissions (admin).
285
if (!isWindows) {
286
await Promises.copy(sourceLinkTestFolder, targetLinkTestFolder, { preserveSymlinks: true });
287
288
assert.ok(fs.existsSync(targetLinkTestFolder));
289
assert.ok(fs.existsSync(targetLinkMD5JSFolder));
290
assert.ok(fs.existsSync(targetLinkMD5JSFile));
291
assert.ok(fs.existsSync(targetLinkMD5JSFolderLinked));
292
assert.ok(fs.lstatSync(targetLinkMD5JSFolderLinked).isSymbolicLink());
293
294
const linkTarget = await fs.promises.readlink(targetLinkMD5JSFolderLinked);
295
assert.strictEqual(linkTarget, targetLinkMD5JSFolder);
296
297
await Promises.rm(targetLinkTestFolder);
298
}
299
300
// Copy with `preserveSymlinks: false` and verify result
301
await Promises.copy(sourceLinkTestFolder, targetLinkTestFolder, { preserveSymlinks: false });
302
303
assert.ok(fs.existsSync(targetLinkTestFolder));
304
assert.ok(fs.existsSync(targetLinkMD5JSFolder));
305
assert.ok(fs.existsSync(targetLinkMD5JSFile));
306
assert.ok(fs.existsSync(targetLinkMD5JSFolderLinked));
307
assert.ok(fs.lstatSync(targetLinkMD5JSFolderLinked).isDirectory());
308
});
309
310
test('readDirsInDir', async () => {
311
fs.mkdirSync(join(testDir, 'somefolder1'));
312
fs.mkdirSync(join(testDir, 'somefolder2'));
313
fs.mkdirSync(join(testDir, 'somefolder3'));
314
fs.writeFileSync(join(testDir, 'somefile.txt'), 'Contents');
315
fs.writeFileSync(join(testDir, 'someOtherFile.txt'), 'Contents');
316
317
const result = await Promises.readDirsInDir(testDir);
318
assert.strictEqual(result.length, 3);
319
assert.ok(result.indexOf('somefolder1') !== -1);
320
assert.ok(result.indexOf('somefolder2') !== -1);
321
assert.ok(result.indexOf('somefolder3') !== -1);
322
});
323
324
test('stat link', async () => {
325
const directory = randomPath(testDir);
326
const symbolicLink = randomPath(testDir);
327
328
await fs.promises.mkdir(directory, { recursive: true });
329
330
fs.symlinkSync(directory, symbolicLink, 'junction');
331
332
let statAndIsLink = await SymlinkSupport.stat(directory);
333
assert.ok(!statAndIsLink?.symbolicLink);
334
335
statAndIsLink = await SymlinkSupport.stat(symbolicLink);
336
assert.ok(statAndIsLink?.symbolicLink);
337
assert.ok(!statAndIsLink?.symbolicLink?.dangling);
338
});
339
340
test('stat link (non existing target)', async () => {
341
const directory = randomPath(testDir);
342
const symbolicLink = randomPath(testDir);
343
344
await fs.promises.mkdir(directory, { recursive: true });
345
346
fs.symlinkSync(directory, symbolicLink, 'junction');
347
348
await Promises.rm(directory);
349
350
const statAndIsLink = await SymlinkSupport.stat(symbolicLink);
351
assert.ok(statAndIsLink?.symbolicLink);
352
assert.ok(statAndIsLink?.symbolicLink?.dangling);
353
});
354
355
test('readdir', async () => {
356
const parent = randomPath(join(testDir, 'pfs'));
357
const newDir = join(parent, 'öäü');
358
359
await fs.promises.mkdir(newDir, { recursive: true });
360
361
assert.ok(fs.existsSync(newDir));
362
363
const children = await Promises.readdir(parent);
364
assert.strictEqual(children.some(n => n === 'öäü'), true); // Mac always converts to NFD, so
365
});
366
367
test('readdir (with file types)', async () => {
368
const newDir = join(testDir, 'öäü');
369
await fs.promises.mkdir(newDir, { recursive: true });
370
371
await Promises.writeFile(join(testDir, 'somefile.txt'), 'contents');
372
373
assert.ok(fs.existsSync(newDir));
374
375
const children = await Promises.readdir(testDir, { withFileTypes: true });
376
377
assert.strictEqual(children.some(n => n.name === 'öäü'), true); // Mac always converts to NFD, so
378
assert.strictEqual(children.some(n => n.isDirectory()), true);
379
380
assert.strictEqual(children.some(n => n.name === 'somefile.txt'), true);
381
assert.strictEqual(children.some(n => n.isFile()), true);
382
});
383
384
test('writeFile (string)', async () => {
385
const smallData = 'Hello World';
386
const bigData = (new Array(100 * 1024)).join('Large String\n');
387
388
return testWriteFile(smallData, smallData, bigData, bigData);
389
});
390
391
test('writeFile (string) - flush on write', async () => {
392
configureFlushOnWrite(true);
393
try {
394
const smallData = 'Hello World';
395
const bigData = (new Array(100 * 1024)).join('Large String\n');
396
397
return await testWriteFile(smallData, smallData, bigData, bigData);
398
} finally {
399
configureFlushOnWrite(false);
400
}
401
});
402
403
test('writeFile (Buffer)', async () => {
404
const smallData = 'Hello World';
405
const bigData = (new Array(100 * 1024)).join('Large String\n');
406
407
return testWriteFile(Buffer.from(smallData), smallData, Buffer.from(bigData), bigData);
408
});
409
410
test('writeFile (UInt8Array)', async () => {
411
const smallData = 'Hello World';
412
const bigData = (new Array(100 * 1024)).join('Large String\n');
413
414
return testWriteFile(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData);
415
});
416
417
async function testWriteFile(
418
smallData: string | Buffer | Uint8Array,
419
smallDataValue: string,
420
bigData: string | Buffer | Uint8Array,
421
bigDataValue: string
422
): Promise<void> {
423
const testFile = join(testDir, 'flushed.txt');
424
425
assert.ok(fs.existsSync(testDir));
426
427
await Promises.writeFile(testFile, smallData);
428
assert.strictEqual(fs.readFileSync(testFile).toString(), smallDataValue);
429
430
await Promises.writeFile(testFile, bigData);
431
assert.strictEqual(fs.readFileSync(testFile).toString(), bigDataValue);
432
}
433
434
test('writeFile (string, error handling)', async () => {
435
const testFile = join(testDir, 'flushed.txt');
436
437
fs.mkdirSync(testFile); // this will trigger an error later because testFile is now a directory!
438
439
let expectedError: Error | undefined;
440
try {
441
await Promises.writeFile(testFile, 'Hello World');
442
} catch (error) {
443
expectedError = error;
444
}
445
446
assert.ok(expectedError);
447
});
448
449
test('writeFileSync', async () => {
450
const testFile = join(testDir, 'flushed.txt');
451
452
writeFileSync(testFile, 'Hello World');
453
assert.strictEqual(fs.readFileSync(testFile).toString(), 'Hello World');
454
455
const largeString = (new Array(100 * 1024)).join('Large String\n');
456
457
writeFileSync(testFile, largeString);
458
assert.strictEqual(fs.readFileSync(testFile).toString(), largeString);
459
});
460
461
test('realcase', async () => {
462
463
// assume case insensitive file system
464
if (process.platform === 'win32' || process.platform === 'darwin') {
465
const upper = testDir.toUpperCase();
466
const real = await realcase(upper);
467
468
if (real) { // can be null in case of permission errors
469
assert.notStrictEqual(real, upper);
470
assert.strictEqual(real.toUpperCase(), upper);
471
assert.strictEqual(real, testDir);
472
}
473
}
474
475
// linux, unix, etc. -> assume case sensitive file system
476
else {
477
let real = await realcase(testDir);
478
assert.strictEqual(real, testDir);
479
480
real = await realcase(testDir.toUpperCase());
481
assert.strictEqual(real, testDir.toUpperCase());
482
}
483
});
484
485
test('realpath', async () => {
486
const realpathVal = await Promises.realpath(testDir);
487
assert.ok(realpathVal);
488
});
489
490
test('realpathSync', () => {
491
const realpath = realpathSync(testDir);
492
assert.ok(realpath);
493
});
494
495
ensureNoDisposablesAreLeakedInTestSuite();
496
});
497
498