Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/files/test/node/diskFileService.integrationTest.ts
5230 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 { createReadStream, existsSync, readdirSync, readFileSync, statSync, writeFileSync, promises } from 'fs';
8
import { tmpdir } from 'os';
9
import { timeout } from '../../../../base/common/async.js';
10
import { bufferToReadable, bufferToStream, streamToBuffer, streamToBufferReadableStream, VSBuffer, VSBufferReadable, VSBufferReadableStream } from '../../../../base/common/buffer.js';
11
import { DisposableStore } from '../../../../base/common/lifecycle.js';
12
import { FileAccess, Schemas } from '../../../../base/common/network.js';
13
import { basename, dirname, join, posix } from '../../../../base/common/path.js';
14
import { isLinux, isWindows } from '../../../../base/common/platform.js';
15
import { joinPath } from '../../../../base/common/resources.js';
16
import { URI } from '../../../../base/common/uri.js';
17
import { Promises } from '../../../../base/node/pfs.js';
18
import { flakySuite, getRandomTestPath } from '../../../../base/test/node/testUtils.js';
19
import { etag, IFileAtomicReadOptions, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FilePermission, FileSystemProviderCapabilities, hasFileAtomicReadCapability, hasOpenReadWriteCloseCapability, IFileStat, IFileStatWithMetadata, IReadFileOptions, IStat, NotModifiedSinceFileOperationError, TooLargeFileOperationError, IFileAtomicOptions } from '../../common/files.js';
20
import { FileService } from '../../common/fileService.js';
21
import { DiskFileSystemProvider } from '../../node/diskFileSystemProvider.js';
22
import { NullLogService } from '../../../log/common/log.js';
23
24
function getByName(root: IFileStat, name: string): IFileStat | undefined {
25
if (root.children === undefined) {
26
return undefined;
27
}
28
29
return root.children.find(child => child.name === name);
30
}
31
32
function toLineByLineReadable(content: string): VSBufferReadable {
33
let chunks = content.split('\n');
34
chunks = chunks.map((chunk, index) => {
35
if (index === 0) {
36
return chunk;
37
}
38
39
return '\n' + chunk;
40
});
41
42
return {
43
read(): VSBuffer | null {
44
const chunk = chunks.shift();
45
if (typeof chunk === 'string') {
46
return VSBuffer.fromString(chunk);
47
}
48
49
return null;
50
}
51
};
52
}
53
54
export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
55
56
totalBytesRead: number = 0;
57
58
private invalidStatSize: boolean = false;
59
private smallStatSize: boolean = false;
60
private readonly: boolean = false;
61
62
private _testCapabilities!: FileSystemProviderCapabilities;
63
override get capabilities(): FileSystemProviderCapabilities {
64
if (!this._testCapabilities) {
65
this._testCapabilities =
66
FileSystemProviderCapabilities.FileReadWrite |
67
FileSystemProviderCapabilities.FileOpenReadWriteClose |
68
FileSystemProviderCapabilities.FileReadStream |
69
FileSystemProviderCapabilities.Trash |
70
FileSystemProviderCapabilities.FileFolderCopy |
71
FileSystemProviderCapabilities.FileWriteUnlock |
72
FileSystemProviderCapabilities.FileAtomicRead |
73
FileSystemProviderCapabilities.FileAtomicWrite |
74
FileSystemProviderCapabilities.FileAtomicDelete |
75
FileSystemProviderCapabilities.FileClone |
76
FileSystemProviderCapabilities.FileAppend |
77
FileSystemProviderCapabilities.FileRealpath;
78
79
if (isLinux) {
80
this._testCapabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
81
}
82
}
83
84
return this._testCapabilities;
85
}
86
87
override set capabilities(capabilities: FileSystemProviderCapabilities) {
88
this._testCapabilities = capabilities;
89
}
90
91
setInvalidStatSize(enabled: boolean): void {
92
this.invalidStatSize = enabled;
93
}
94
95
setSmallStatSize(enabled: boolean): void {
96
this.smallStatSize = enabled;
97
}
98
99
setReadonly(readonly: boolean): void {
100
this.readonly = readonly;
101
}
102
103
override async stat(resource: URI): Promise<IStat> {
104
const res = await super.stat(resource);
105
106
if (this.invalidStatSize) {
107
// eslint-disable-next-line local/code-no-any-casts
108
(res as any).size = String(res.size) as any; // for https://github.com/microsoft/vscode/issues/72909
109
} else if (this.smallStatSize) {
110
// eslint-disable-next-line local/code-no-any-casts
111
(res as any).size = 1;
112
} else if (this.readonly) {
113
// eslint-disable-next-line local/code-no-any-casts
114
(res as any).permissions = FilePermission.Readonly;
115
}
116
117
return res;
118
}
119
120
override async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
121
const bytesRead = await super.read(fd, pos, data, offset, length);
122
123
this.totalBytesRead += bytesRead;
124
125
return bytesRead;
126
}
127
128
override async readFile(resource: URI, options?: IFileAtomicReadOptions): Promise<Uint8Array> {
129
const res = await super.readFile(resource, options);
130
131
this.totalBytesRead += res.byteLength;
132
133
return res;
134
}
135
}
136
137
DiskFileSystemProvider.configureFlushOnWrite(false); // speed up all unit tests by disabling flush on write
138
139
flakySuite('Disk File Service', function () {
140
141
const testSchema = 'test';
142
143
let service: FileService;
144
let fileProvider: TestDiskFileSystemProvider;
145
let testProvider: TestDiskFileSystemProvider;
146
147
let testDir: string;
148
149
const disposables = new DisposableStore();
150
151
setup(async () => {
152
const logService = new NullLogService();
153
154
service = disposables.add(new FileService(logService));
155
156
fileProvider = disposables.add(new TestDiskFileSystemProvider(logService));
157
disposables.add(service.registerProvider(Schemas.file, fileProvider));
158
159
testProvider = disposables.add(new TestDiskFileSystemProvider(logService));
160
disposables.add(service.registerProvider(testSchema, testProvider));
161
162
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'diskfileservice');
163
164
const sourceDir = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/service').fsPath;
165
166
await Promises.copy(sourceDir, testDir, { preserveSymlinks: false });
167
});
168
169
teardown(() => {
170
disposables.clear();
171
172
return Promises.rm(testDir);
173
});
174
175
test('createFolder', async () => {
176
let event: FileOperationEvent | undefined;
177
disposables.add(service.onDidRunOperation(e => event = e));
178
179
const parent = await service.resolve(URI.file(testDir));
180
181
const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder'));
182
183
const newFolder = await service.createFolder(newFolderResource);
184
185
assert.strictEqual(newFolder.name, 'newFolder');
186
assert.strictEqual(existsSync(newFolder.resource.fsPath), true);
187
188
assert.ok(event);
189
assert.strictEqual(event.resource.fsPath, newFolderResource.fsPath);
190
assert.strictEqual(event.operation, FileOperation.CREATE);
191
assert.strictEqual(event.target!.resource.fsPath, newFolderResource.fsPath);
192
assert.strictEqual(event.target!.isDirectory, true);
193
});
194
195
test('createFolder: creating multiple folders at once', async () => {
196
let event: FileOperationEvent;
197
disposables.add(service.onDidRunOperation(e => event = e));
198
199
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
200
const parent = await service.resolve(URI.file(testDir));
201
202
const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));
203
204
const newFolder = await service.createFolder(newFolderResource);
205
206
const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];
207
assert.strictEqual(newFolder.name, lastFolderName);
208
assert.strictEqual(existsSync(newFolder.resource.fsPath), true);
209
210
assert.ok(event!);
211
assert.strictEqual(event!.resource.fsPath, newFolderResource.fsPath);
212
assert.strictEqual(event!.operation, FileOperation.CREATE);
213
assert.strictEqual(event!.target!.resource.fsPath, newFolderResource.fsPath);
214
assert.strictEqual(event!.target!.isDirectory, true);
215
});
216
217
test('exists', async () => {
218
let exists = await service.exists(URI.file(testDir));
219
assert.strictEqual(exists, true);
220
221
exists = await service.exists(URI.file(testDir + 'something'));
222
assert.strictEqual(exists, false);
223
});
224
225
test('resolve - file', async () => {
226
const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/index.html');
227
const resolved = await service.resolve(resource);
228
229
assert.strictEqual(resolved.name, 'index.html');
230
assert.strictEqual(resolved.isFile, true);
231
assert.strictEqual(resolved.isDirectory, false);
232
assert.strictEqual(resolved.readonly, false);
233
assert.strictEqual(resolved.isSymbolicLink, false);
234
assert.strictEqual(resolved.resource.toString(), resource.toString());
235
assert.strictEqual(resolved.children, undefined);
236
assert.ok(resolved.mtime! > 0);
237
assert.ok(resolved.ctime! > 0);
238
assert.ok(resolved.size! > 0);
239
});
240
241
test('resolve - directory', async () => {
242
const testsElements = ['examples', 'other', 'index.html', 'site.css'];
243
244
const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver');
245
const result = await service.resolve(resource);
246
247
assert.ok(result);
248
assert.strictEqual(result.resource.toString(), resource.toString());
249
assert.strictEqual(result.name, 'resolver');
250
assert.ok(result.children);
251
assert.ok(result.children.length > 0);
252
assert.ok(result.isDirectory);
253
assert.strictEqual(result.readonly, false);
254
assert.ok(result.mtime! > 0);
255
assert.ok(result.ctime! > 0);
256
assert.strictEqual(result.children.length, testsElements.length);
257
258
assert.ok(result.children.every(entry => {
259
return testsElements.some(name => {
260
return basename(entry.resource.fsPath) === name;
261
});
262
}));
263
264
result.children.forEach(value => {
265
assert.ok(basename(value.resource.fsPath));
266
if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
267
assert.ok(value.isDirectory);
268
assert.strictEqual(value.mtime, undefined);
269
assert.strictEqual(value.ctime, undefined);
270
} else if (basename(value.resource.fsPath) === 'index.html') {
271
assert.ok(!value.isDirectory);
272
assert.ok(!value.children);
273
assert.strictEqual(value.mtime, undefined);
274
assert.strictEqual(value.ctime, undefined);
275
} else if (basename(value.resource.fsPath) === 'site.css') {
276
assert.ok(!value.isDirectory);
277
assert.ok(!value.children);
278
assert.strictEqual(value.mtime, undefined);
279
assert.strictEqual(value.ctime, undefined);
280
} else {
281
assert.fail('Unexpected value ' + basename(value.resource.fsPath));
282
}
283
});
284
});
285
286
test('resolve - directory - with metadata', async () => {
287
const testsElements = ['examples', 'other', 'index.html', 'site.css'];
288
289
const result = await service.resolve(FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver'), { resolveMetadata: true });
290
291
assert.ok(result);
292
assert.strictEqual(result.name, 'resolver');
293
assert.ok(result.children);
294
assert.ok(result.children.length > 0);
295
assert.ok(result.isDirectory);
296
assert.ok(result.mtime > 0);
297
assert.ok(result.ctime > 0);
298
assert.strictEqual(result.children.length, testsElements.length);
299
300
assert.ok(result.children.every(entry => {
301
return testsElements.some(name => {
302
return basename(entry.resource.fsPath) === name;
303
});
304
}));
305
306
assert.ok(result.children.every(entry => entry.etag.length > 0));
307
308
result.children.forEach(value => {
309
assert.ok(basename(value.resource.fsPath));
310
if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
311
assert.ok(value.isDirectory);
312
assert.ok(value.mtime > 0);
313
assert.ok(value.ctime > 0);
314
} else if (basename(value.resource.fsPath) === 'index.html') {
315
assert.ok(!value.isDirectory);
316
assert.ok(!value.children);
317
assert.ok(value.mtime > 0);
318
assert.ok(value.ctime > 0);
319
} else if (basename(value.resource.fsPath) === 'site.css') {
320
assert.ok(!value.isDirectory);
321
assert.ok(!value.children);
322
assert.ok(value.mtime > 0);
323
assert.ok(value.ctime > 0);
324
} else {
325
assert.fail('Unexpected value ' + basename(value.resource.fsPath));
326
}
327
});
328
});
329
330
test('resolve - directory with resolveTo', async () => {
331
const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });
332
assert.strictEqual(resolved.children!.length, 8);
333
334
const deep = (getByName(resolved, 'deep')!);
335
assert.strictEqual(deep.children!.length, 4);
336
});
337
338
test('resolve - directory - resolveTo single directory', async () => {
339
const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver').fsPath;
340
const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });
341
342
assert.ok(result);
343
assert.ok(result.children);
344
assert.ok(result.children.length > 0);
345
assert.ok(result.isDirectory);
346
347
const children = result.children;
348
assert.strictEqual(children.length, 4);
349
350
const other = getByName(result, 'other');
351
assert.ok(other);
352
assert.ok(other.children!.length > 0);
353
354
const deep = getByName(other, 'deep');
355
assert.ok(deep);
356
assert.ok(deep.children!.length > 0);
357
assert.strictEqual(deep.children!.length, 4);
358
});
359
360
test('resolve directory - resolveTo multiple directories', () => {
361
return testResolveDirectoryWithTarget(false);
362
});
363
364
test('resolve directory - resolveTo with a URI that has query parameter (https://github.com/microsoft/vscode/issues/128151)', () => {
365
return testResolveDirectoryWithTarget(true);
366
});
367
368
async function testResolveDirectoryWithTarget(withQueryParam: boolean): Promise<void> {
369
const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver').fsPath;
370
const result = await service.resolve(URI.file(resolverFixturesPath).with({ query: withQueryParam ? 'test' : undefined }), {
371
resolveTo: [
372
URI.file(join(resolverFixturesPath, 'other/deep')).with({ query: withQueryParam ? 'test' : undefined }),
373
URI.file(join(resolverFixturesPath, 'examples')).with({ query: withQueryParam ? 'test' : undefined })
374
]
375
});
376
377
assert.ok(result);
378
assert.ok(result.children);
379
assert.ok(result.children.length > 0);
380
assert.ok(result.isDirectory);
381
382
const children = result.children;
383
assert.strictEqual(children.length, 4);
384
385
const other = getByName(result, 'other');
386
assert.ok(other);
387
assert.ok(other.children!.length > 0);
388
389
const deep = getByName(other, 'deep');
390
assert.ok(deep);
391
assert.ok(deep.children!.length > 0);
392
assert.strictEqual(deep.children!.length, 4);
393
394
const examples = getByName(result, 'examples');
395
assert.ok(examples);
396
assert.ok(examples.children!.length > 0);
397
assert.strictEqual(examples.children!.length, 4);
398
}
399
400
test('resolve directory - resolveSingleChildFolders', async () => {
401
const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/other').fsPath;
402
const result = await service.resolve(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true });
403
404
assert.ok(result);
405
assert.ok(result.children);
406
assert.ok(result.children.length > 0);
407
assert.ok(result.isDirectory);
408
409
const children = result.children;
410
assert.strictEqual(children.length, 1);
411
412
const deep = getByName(result, 'deep');
413
assert.ok(deep);
414
assert.ok(deep.children!.length > 0);
415
assert.strictEqual(deep.children!.length, 4);
416
});
417
418
test('resolves', async () => {
419
const res = await service.resolveAll([
420
{ resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } },
421
{ resource: URI.file(join(testDir, 'deep')) }
422
]);
423
424
const r1 = (res[0].stat!);
425
assert.strictEqual(r1.children!.length, 8);
426
427
const deep = (getByName(r1, 'deep')!);
428
assert.strictEqual(deep.children!.length, 4);
429
430
const r2 = (res[1].stat!);
431
assert.strictEqual(r2.children!.length, 4);
432
assert.strictEqual(r2.name, 'deep');
433
});
434
435
test('resolve / realpath - folder symbolic link', async () => {
436
const link = URI.file(join(testDir, 'deep-link'));
437
await promises.symlink(join(testDir, 'deep'), link.fsPath, 'junction');
438
439
const resolved = await service.resolve(link);
440
assert.strictEqual(resolved.children!.length, 4);
441
assert.strictEqual(resolved.isDirectory, true);
442
assert.strictEqual(resolved.isSymbolicLink, true);
443
444
const realpath = await service.realpath(link);
445
assert.ok(realpath);
446
assert.strictEqual(basename(realpath.fsPath), 'deep');
447
});
448
449
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('resolve - file symbolic link', async () => {
450
const link = URI.file(join(testDir, 'lorem.txt-linked'));
451
await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);
452
453
const resolved = await service.resolve(link);
454
assert.strictEqual(resolved.isDirectory, false);
455
assert.strictEqual(resolved.isSymbolicLink, true);
456
});
457
458
test('resolve - symbolic link pointing to nonexistent file does not break', async () => {
459
await promises.symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction');
460
461
const resolved = await service.resolve(URI.file(testDir));
462
assert.strictEqual(resolved.isDirectory, true);
463
assert.strictEqual(resolved.children!.length, 9);
464
465
const resolvedLink = resolved.children?.find(child => child.name === 'bar' && child.isSymbolicLink);
466
assert.ok(resolvedLink);
467
468
assert.ok(!resolvedLink?.isDirectory);
469
assert.ok(!resolvedLink?.isFile);
470
});
471
472
test('stat - file', async () => {
473
const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/index.html');
474
const resolved = await service.stat(resource);
475
476
assert.strictEqual(resolved.name, 'index.html');
477
assert.strictEqual(resolved.isFile, true);
478
assert.strictEqual(resolved.isDirectory, false);
479
assert.strictEqual(resolved.readonly, false);
480
assert.strictEqual(resolved.isSymbolicLink, false);
481
assert.strictEqual(resolved.resource.toString(), resource.toString());
482
assert.ok(resolved.mtime > 0);
483
assert.ok(resolved.ctime > 0);
484
assert.ok(resolved.size > 0);
485
});
486
487
test('stat - directory', async () => {
488
const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver');
489
const result = await service.stat(resource);
490
491
assert.ok(result);
492
assert.strictEqual(result.resource.toString(), resource.toString());
493
assert.strictEqual(result.name, 'resolver');
494
assert.ok(result.isDirectory);
495
assert.strictEqual(result.readonly, false);
496
assert.ok(result.mtime > 0);
497
assert.ok(result.ctime > 0);
498
});
499
500
// The executable bit does not exist on Windows so use a condition not skip
501
if (!isWindows) {
502
test('stat - executable', async () => {
503
const nonExecutable = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/executable/non_executable');
504
let resolved = await service.stat(nonExecutable);
505
assert.strictEqual(resolved.isFile, true);
506
assert.strictEqual(resolved.executable, false);
507
508
const executable = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/executable/executable');
509
resolved = await service.stat(executable);
510
assert.strictEqual(resolved.isFile, true);
511
assert.strictEqual(resolved.executable, true);
512
});
513
}
514
515
test('deleteFile (non recursive)', async () => {
516
return testDeleteFile(false, false);
517
});
518
519
test('deleteFile (recursive)', async () => {
520
return testDeleteFile(false, true);
521
});
522
523
(isLinux /* trash is unreliable on Linux */ ? test.skip : test)('deleteFile (useTrash)', async () => {
524
return testDeleteFile(true, false);
525
});
526
527
async function testDeleteFile(useTrash: boolean, recursive: boolean): Promise<void> {
528
let event: FileOperationEvent;
529
disposables.add(service.onDidRunOperation(e => event = e));
530
531
const resource = URI.file(join(testDir, 'deep', 'conway.js'));
532
const source = await service.resolve(resource);
533
534
assert.strictEqual(await service.canDelete(source.resource, { useTrash, recursive }), true);
535
await service.del(source.resource, { useTrash, recursive });
536
537
assert.strictEqual(existsSync(source.resource.fsPath), false);
538
539
assert.ok(event!);
540
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
541
assert.strictEqual(event!.operation, FileOperation.DELETE);
542
543
let error: Error | undefined = undefined;
544
try {
545
await service.del(source.resource, { useTrash, recursive });
546
} catch (e) {
547
error = e;
548
}
549
550
assert.ok(error);
551
assert.strictEqual((<FileOperationError>error).fileOperationResult, FileOperationResult.FILE_NOT_FOUND);
552
}
553
554
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (exists)', async () => {
555
const target = URI.file(join(testDir, 'lorem.txt'));
556
const link = URI.file(join(testDir, 'lorem.txt-linked'));
557
await promises.symlink(target.fsPath, link.fsPath);
558
559
const source = await service.resolve(link);
560
561
let event: FileOperationEvent;
562
disposables.add(service.onDidRunOperation(e => event = e));
563
564
assert.strictEqual(await service.canDelete(source.resource), true);
565
await service.del(source.resource);
566
567
assert.strictEqual(existsSync(source.resource.fsPath), false);
568
569
assert.ok(event!);
570
assert.strictEqual(event!.resource.fsPath, link.fsPath);
571
assert.strictEqual(event!.operation, FileOperation.DELETE);
572
573
assert.strictEqual(existsSync(target.fsPath), true); // target the link pointed to is never deleted
574
});
575
576
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (pointing to nonexistent file)', async () => {
577
const target = URI.file(join(testDir, 'foo'));
578
const link = URI.file(join(testDir, 'bar'));
579
await promises.symlink(target.fsPath, link.fsPath);
580
581
let event: FileOperationEvent;
582
disposables.add(service.onDidRunOperation(e => event = e));
583
584
assert.strictEqual(await service.canDelete(link), true);
585
await service.del(link);
586
587
assert.strictEqual(existsSync(link.fsPath), false);
588
589
assert.ok(event!);
590
assert.strictEqual(event!.resource.fsPath, link.fsPath);
591
assert.strictEqual(event!.operation, FileOperation.DELETE);
592
});
593
594
test('deleteFolder (recursive)', async () => {
595
return testDeleteFolderRecursive(false, false);
596
});
597
598
test('deleteFolder (recursive, atomic)', async () => {
599
return testDeleteFolderRecursive(false, { postfix: '.vsctmp' });
600
});
601
602
(isLinux /* trash is unreliable on Linux */ ? test.skip : test)('deleteFolder (recursive, useTrash)', async () => {
603
return testDeleteFolderRecursive(true, false);
604
});
605
606
async function testDeleteFolderRecursive(useTrash: boolean, atomic: IFileAtomicOptions | false): Promise<void> {
607
let event: FileOperationEvent;
608
disposables.add(service.onDidRunOperation(e => event = e));
609
610
const resource = URI.file(join(testDir, 'deep'));
611
const source = await service.resolve(resource);
612
613
assert.strictEqual(await service.canDelete(source.resource, { recursive: true, useTrash, atomic }), true);
614
await service.del(source.resource, { recursive: true, useTrash, atomic });
615
616
assert.strictEqual(existsSync(source.resource.fsPath), false);
617
assert.ok(event!);
618
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
619
assert.strictEqual(event!.operation, FileOperation.DELETE);
620
}
621
622
test('deleteFolder (non recursive)', async () => {
623
const resource = URI.file(join(testDir, 'deep'));
624
const source = await service.resolve(resource);
625
626
assert.ok((await service.canDelete(source.resource)) instanceof Error);
627
628
let error;
629
try {
630
await service.del(source.resource);
631
} catch (e) {
632
error = e;
633
}
634
635
assert.ok(error);
636
});
637
638
test('deleteFolder empty folder (recursive)', () => {
639
return testDeleteEmptyFolder(true);
640
});
641
642
test('deleteFolder empty folder (non recursive)', () => {
643
return testDeleteEmptyFolder(false);
644
});
645
646
async function testDeleteEmptyFolder(recursive: boolean): Promise<void> {
647
const { resource } = await service.createFolder(URI.file(join(testDir, 'deep', 'empty')));
648
649
await service.del(resource, { recursive });
650
651
assert.strictEqual(await service.exists(resource), false);
652
}
653
654
test('move', async () => {
655
let event: FileOperationEvent;
656
disposables.add(service.onDidRunOperation(e => event = e));
657
658
const source = URI.file(join(testDir, 'index.html'));
659
const sourceContents = readFileSync(source.fsPath);
660
661
const target = URI.file(join(dirname(source.fsPath), 'other.html'));
662
663
assert.strictEqual(await service.canMove(source, target), true);
664
const renamed = await service.move(source, target);
665
666
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
667
assert.strictEqual(existsSync(source.fsPath), false);
668
assert.ok(event!);
669
assert.strictEqual(event!.resource.fsPath, source.fsPath);
670
assert.strictEqual(event!.operation, FileOperation.MOVE);
671
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
672
673
const targetContents = readFileSync(target.fsPath);
674
675
assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);
676
assert.strictEqual(sourceContents.toString(), targetContents.toString());
677
});
678
679
test('move - across providers (buffered => buffered)', async () => {
680
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
681
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
682
683
return testMoveAcrossProviders();
684
});
685
686
test('move - across providers (unbuffered => unbuffered)', async () => {
687
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
688
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
689
690
return testMoveAcrossProviders();
691
});
692
693
test('move - across providers (buffered => unbuffered)', async () => {
694
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
695
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
696
697
return testMoveAcrossProviders();
698
});
699
700
test('move - across providers (unbuffered => buffered)', async () => {
701
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
702
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
703
704
return testMoveAcrossProviders();
705
});
706
707
test('move - across providers - large (buffered => buffered)', async () => {
708
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
709
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
710
711
return testMoveAcrossProviders('lorem.txt');
712
});
713
714
test('move - across providers - large (unbuffered => unbuffered)', async () => {
715
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
716
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
717
718
return testMoveAcrossProviders('lorem.txt');
719
});
720
721
test('move - across providers - large (buffered => unbuffered)', async () => {
722
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
723
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
724
725
return testMoveAcrossProviders('lorem.txt');
726
});
727
728
test('move - across providers - large (unbuffered => buffered)', async () => {
729
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
730
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
731
732
return testMoveAcrossProviders('lorem.txt');
733
});
734
735
async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {
736
let event: FileOperationEvent;
737
disposables.add(service.onDidRunOperation(e => event = e));
738
739
const source = URI.file(join(testDir, sourceFile));
740
const sourceContents = readFileSync(source.fsPath);
741
742
const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema });
743
744
assert.strictEqual(await service.canMove(source, target), true);
745
const renamed = await service.move(source, target);
746
747
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
748
assert.strictEqual(existsSync(source.fsPath), false);
749
assert.ok(event!);
750
assert.strictEqual(event!.resource.fsPath, source.fsPath);
751
assert.strictEqual(event!.operation, FileOperation.COPY);
752
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
753
754
const targetContents = readFileSync(target.fsPath);
755
756
assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);
757
assert.strictEqual(sourceContents.toString(), targetContents.toString());
758
}
759
760
test('move - multi folder', async () => {
761
let event: FileOperationEvent;
762
disposables.add(service.onDidRunOperation(e => event = e));
763
764
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
765
const renameToPath = join(...multiFolderPaths, 'other.html');
766
767
const source = URI.file(join(testDir, 'index.html'));
768
769
assert.strictEqual(await service.canMove(source, URI.file(join(dirname(source.fsPath), renameToPath))), true);
770
const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath)));
771
772
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
773
assert.strictEqual(existsSync(source.fsPath), false);
774
assert.ok(event!);
775
assert.strictEqual(event!.resource.fsPath, source.fsPath);
776
assert.strictEqual(event!.operation, FileOperation.MOVE);
777
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
778
});
779
780
test('move - directory', async () => {
781
let event: FileOperationEvent;
782
disposables.add(service.onDidRunOperation(e => event = e));
783
784
const source = URI.file(join(testDir, 'deep'));
785
786
assert.strictEqual(await service.canMove(source, URI.file(join(dirname(source.fsPath), 'deeper'))), true);
787
const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper')));
788
789
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
790
assert.strictEqual(existsSync(source.fsPath), false);
791
assert.ok(event!);
792
assert.strictEqual(event!.resource.fsPath, source.fsPath);
793
assert.strictEqual(event!.operation, FileOperation.MOVE);
794
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
795
});
796
797
test('move - directory - across providers (buffered => buffered)', async () => {
798
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
799
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
800
801
return testMoveFolderAcrossProviders();
802
});
803
804
test('move - directory - across providers (unbuffered => unbuffered)', async () => {
805
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
806
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
807
808
return testMoveFolderAcrossProviders();
809
});
810
811
test('move - directory - across providers (buffered => unbuffered)', async () => {
812
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
813
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
814
815
return testMoveFolderAcrossProviders();
816
});
817
818
test('move - directory - across providers (unbuffered => buffered)', async () => {
819
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
820
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
821
822
return testMoveFolderAcrossProviders();
823
});
824
825
async function testMoveFolderAcrossProviders(): Promise<void> {
826
let event: FileOperationEvent;
827
disposables.add(service.onDidRunOperation(e => event = e));
828
829
const source = URI.file(join(testDir, 'deep'));
830
const sourceChildren = readdirSync(source.fsPath);
831
832
const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema });
833
834
assert.strictEqual(await service.canMove(source, target), true);
835
const renamed = await service.move(source, target);
836
837
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
838
assert.strictEqual(existsSync(source.fsPath), false);
839
assert.ok(event!);
840
assert.strictEqual(event!.resource.fsPath, source.fsPath);
841
assert.strictEqual(event!.operation, FileOperation.COPY);
842
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
843
844
const targetChildren = readdirSync(target.fsPath);
845
assert.strictEqual(sourceChildren.length, targetChildren.length);
846
for (let i = 0; i < sourceChildren.length; i++) {
847
assert.strictEqual(sourceChildren[i], targetChildren[i]);
848
}
849
}
850
851
test('move - MIX CASE', async () => {
852
let event: FileOperationEvent;
853
disposables.add(service.onDidRunOperation(e => event = e));
854
855
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
856
assert.ok(source.size > 0);
857
858
const renamedResource = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
859
assert.strictEqual(await service.canMove(source.resource, renamedResource), true);
860
let renamed = await service.move(source.resource, renamedResource);
861
862
assert.strictEqual(existsSync(renamedResource.fsPath), true);
863
assert.strictEqual(basename(renamedResource.fsPath), 'INDEX.html');
864
assert.ok(event!);
865
assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);
866
assert.strictEqual(event!.operation, FileOperation.MOVE);
867
assert.strictEqual(event!.target!.resource.fsPath, renamedResource.fsPath);
868
869
renamed = await service.resolve(renamedResource, { resolveMetadata: true });
870
assert.strictEqual(source.size, renamed.size);
871
});
872
873
test('move - same file', async () => {
874
let event: FileOperationEvent;
875
disposables.add(service.onDidRunOperation(e => event = e));
876
877
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
878
assert.ok(source.size > 0);
879
880
assert.strictEqual(await service.canMove(source.resource, URI.file(source.resource.fsPath)), true);
881
let renamed = await service.move(source.resource, URI.file(source.resource.fsPath));
882
883
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
884
assert.strictEqual(basename(renamed.resource.fsPath), 'index.html');
885
assert.ok(event!);
886
assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);
887
assert.strictEqual(event!.operation, FileOperation.MOVE);
888
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
889
890
renamed = await service.resolve(renamed.resource, { resolveMetadata: true });
891
assert.strictEqual(source.size, renamed.size);
892
});
893
894
test('move - same file #2', async () => {
895
let event: FileOperationEvent;
896
disposables.add(service.onDidRunOperation(e => event = e));
897
898
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
899
assert.ok(source.size > 0);
900
901
const targetParent = URI.file(testDir);
902
const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });
903
904
assert.strictEqual(await service.canMove(source.resource, target), true);
905
let renamed = await service.move(source.resource, target);
906
907
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
908
assert.strictEqual(basename(renamed.resource.fsPath), 'index.html');
909
assert.ok(event!);
910
assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);
911
assert.strictEqual(event!.operation, FileOperation.MOVE);
912
assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);
913
914
renamed = await service.resolve(renamed.resource, { resolveMetadata: true });
915
assert.strictEqual(source.size, renamed.size);
916
});
917
918
test('move - source parent of target', async () => {
919
let event: FileOperationEvent;
920
disposables.add(service.onDidRunOperation(e => event = e));
921
922
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
923
const originalSize = source.size;
924
assert.ok(originalSize > 0);
925
926
assert.ok((await service.canMove(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))) instanceof Error));
927
928
let error;
929
try {
930
await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt')));
931
} catch (e) {
932
error = e;
933
}
934
935
assert.ok(error);
936
assert.ok(!event!);
937
938
source = await service.resolve(source.resource, { resolveMetadata: true });
939
assert.strictEqual(originalSize, source.size);
940
});
941
942
test('move - FILE_MOVE_CONFLICT', async () => {
943
let event: FileOperationEvent;
944
disposables.add(service.onDidRunOperation(e => event = e));
945
946
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
947
const originalSize = source.size;
948
assert.ok(originalSize > 0);
949
950
assert.ok((await service.canMove(source.resource, URI.file(join(testDir, 'binary.txt'))) instanceof Error));
951
952
let error;
953
try {
954
await service.move(source.resource, URI.file(join(testDir, 'binary.txt')));
955
} catch (e) {
956
error = e;
957
}
958
959
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT);
960
assert.ok(!event!);
961
962
source = await service.resolve(source.resource, { resolveMetadata: true });
963
assert.strictEqual(originalSize, source.size);
964
});
965
966
test('move - overwrite folder with file', async () => {
967
let createEvent: FileOperationEvent;
968
let moveEvent: FileOperationEvent;
969
let deleteEvent: FileOperationEvent;
970
disposables.add(service.onDidRunOperation(e => {
971
if (e.operation === FileOperation.CREATE) {
972
createEvent = e;
973
} else if (e.operation === FileOperation.DELETE) {
974
deleteEvent = e;
975
} else if (e.operation === FileOperation.MOVE) {
976
moveEvent = e;
977
}
978
}));
979
980
const parent = await service.resolve(URI.file(testDir));
981
const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
982
const f = await service.createFolder(folderResource);
983
const source = URI.file(join(testDir, 'deep', 'conway.js'));
984
985
assert.strictEqual(await service.canMove(source, f.resource, true), true);
986
const moved = await service.move(source, f.resource, true);
987
988
assert.strictEqual(existsSync(moved.resource.fsPath), true);
989
assert.ok(statSync(moved.resource.fsPath).isFile);
990
assert.ok(createEvent!);
991
assert.ok(deleteEvent!);
992
assert.ok(moveEvent!);
993
assert.strictEqual(moveEvent!.resource.fsPath, source.fsPath);
994
assert.strictEqual(moveEvent!.target!.resource.fsPath, moved.resource.fsPath);
995
assert.strictEqual(deleteEvent!.resource.fsPath, folderResource.fsPath);
996
});
997
998
test('copy', async () => {
999
await doTestCopy();
1000
});
1001
1002
test('copy - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => {
1003
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1004
1005
await doTestCopy();
1006
});
1007
1008
test('copy - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => {
1009
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1010
1011
await doTestCopy('lorem.txt');
1012
});
1013
1014
test('copy - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
1015
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1016
1017
await doTestCopy();
1018
});
1019
1020
test('copy - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
1021
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1022
1023
await doTestCopy('lorem.txt');
1024
});
1025
1026
function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void {
1027
provider.capabilities = capabilities;
1028
if (isLinux) {
1029
provider.capabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
1030
}
1031
}
1032
1033
async function doTestCopy(sourceName: string = 'index.html') {
1034
let event: FileOperationEvent;
1035
disposables.add(service.onDidRunOperation(e => event = e));
1036
1037
const source = await service.resolve(URI.file(join(testDir, sourceName)));
1038
const target = URI.file(join(testDir, 'other.html'));
1039
1040
assert.strictEqual(await service.canCopy(source.resource, target), true);
1041
const copied = await service.copy(source.resource, target);
1042
1043
assert.strictEqual(existsSync(copied.resource.fsPath), true);
1044
assert.strictEqual(existsSync(source.resource.fsPath), true);
1045
assert.ok(event!);
1046
assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);
1047
assert.strictEqual(event!.operation, FileOperation.COPY);
1048
assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);
1049
1050
const sourceContents = readFileSync(source.resource.fsPath);
1051
const targetContents = readFileSync(target.fsPath);
1052
1053
assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);
1054
assert.strictEqual(sourceContents.toString(), targetContents.toString());
1055
}
1056
1057
test('copy - overwrite folder with file', async () => {
1058
let createEvent: FileOperationEvent;
1059
let copyEvent: FileOperationEvent;
1060
let deleteEvent: FileOperationEvent;
1061
disposables.add(service.onDidRunOperation(e => {
1062
if (e.operation === FileOperation.CREATE) {
1063
createEvent = e;
1064
} else if (e.operation === FileOperation.DELETE) {
1065
deleteEvent = e;
1066
} else if (e.operation === FileOperation.COPY) {
1067
copyEvent = e;
1068
}
1069
}));
1070
1071
const parent = await service.resolve(URI.file(testDir));
1072
const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
1073
const f = await service.createFolder(folderResource);
1074
const source = URI.file(join(testDir, 'deep', 'conway.js'));
1075
1076
assert.strictEqual(await service.canCopy(source, f.resource, true), true);
1077
const copied = await service.copy(source, f.resource, true);
1078
1079
assert.strictEqual(existsSync(copied.resource.fsPath), true);
1080
assert.ok(statSync(copied.resource.fsPath).isFile);
1081
assert.ok(createEvent!);
1082
assert.ok(deleteEvent!);
1083
assert.ok(copyEvent!);
1084
assert.strictEqual(copyEvent!.resource.fsPath, source.fsPath);
1085
assert.strictEqual(copyEvent!.target!.resource.fsPath, copied.resource.fsPath);
1086
assert.strictEqual(deleteEvent!.resource.fsPath, folderResource.fsPath);
1087
});
1088
1089
test('copy - MIX CASE same target - no overwrite', async () => {
1090
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
1091
const originalSize = source.size;
1092
assert.ok(originalSize > 0);
1093
1094
const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
1095
1096
const canCopy = await service.canCopy(source.resource, target);
1097
1098
let error;
1099
let copied: IFileStatWithMetadata;
1100
try {
1101
copied = await service.copy(source.resource, target);
1102
} catch (e) {
1103
error = e;
1104
}
1105
1106
if (isLinux) {
1107
assert.ok(!error);
1108
assert.strictEqual(canCopy, true);
1109
1110
assert.strictEqual(existsSync(copied!.resource.fsPath), true);
1111
assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));
1112
assert.strictEqual(source.size, copied!.size);
1113
} else {
1114
assert.ok(error);
1115
assert.ok(canCopy instanceof Error);
1116
1117
source = await service.resolve(source.resource, { resolveMetadata: true });
1118
assert.strictEqual(originalSize, source.size);
1119
}
1120
});
1121
1122
test('copy - MIX CASE same target - overwrite', async () => {
1123
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
1124
const originalSize = source.size;
1125
assert.ok(originalSize > 0);
1126
1127
const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
1128
1129
const canCopy = await service.canCopy(source.resource, target, true);
1130
1131
let error;
1132
let copied: IFileStatWithMetadata;
1133
try {
1134
copied = await service.copy(source.resource, target, true);
1135
} catch (e) {
1136
error = e;
1137
}
1138
1139
if (isLinux) {
1140
assert.ok(!error);
1141
assert.strictEqual(canCopy, true);
1142
1143
assert.strictEqual(existsSync(copied!.resource.fsPath), true);
1144
assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));
1145
assert.strictEqual(source.size, copied!.size);
1146
} else {
1147
assert.ok(error);
1148
assert.ok(canCopy instanceof Error);
1149
1150
source = await service.resolve(source.resource, { resolveMetadata: true });
1151
assert.strictEqual(originalSize, source.size);
1152
}
1153
});
1154
1155
test('copy - MIX CASE different target - overwrite', async () => {
1156
const source1 = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
1157
assert.ok(source1.size > 0);
1158
1159
const renamed = await service.move(source1.resource, URI.file(join(dirname(source1.resource.fsPath), 'CONWAY.js')));
1160
assert.strictEqual(existsSync(renamed.resource.fsPath), true);
1161
assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js'));
1162
assert.strictEqual(source1.size, renamed.size);
1163
1164
const source2 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true });
1165
const target = URI.file(join(testDir, basename(source2.resource.path)));
1166
1167
assert.strictEqual(await service.canCopy(source2.resource, target, true), true);
1168
const res = await service.copy(source2.resource, target, true);
1169
assert.strictEqual(existsSync(res.resource.fsPath), true);
1170
assert.ok(readdirSync(testDir).some(f => f === 'conway.js'));
1171
assert.strictEqual(source2.size, res.size);
1172
});
1173
1174
test('copy - same file', async () => {
1175
let event: FileOperationEvent;
1176
disposables.add(service.onDidRunOperation(e => event = e));
1177
1178
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
1179
assert.ok(source.size > 0);
1180
1181
assert.strictEqual(await service.canCopy(source.resource, URI.file(source.resource.fsPath)), true);
1182
let copied = await service.copy(source.resource, URI.file(source.resource.fsPath));
1183
1184
assert.strictEqual(existsSync(copied.resource.fsPath), true);
1185
assert.strictEqual(basename(copied.resource.fsPath), 'index.html');
1186
assert.ok(event!);
1187
assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);
1188
assert.strictEqual(event!.operation, FileOperation.COPY);
1189
assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);
1190
1191
copied = await service.resolve(source.resource, { resolveMetadata: true });
1192
assert.strictEqual(source.size, copied.size);
1193
});
1194
1195
test('copy - same file #2', async () => {
1196
let event: FileOperationEvent;
1197
disposables.add(service.onDidRunOperation(e => event = e));
1198
1199
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
1200
assert.ok(source.size > 0);
1201
1202
const targetParent = URI.file(testDir);
1203
const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });
1204
1205
assert.strictEqual(await service.canCopy(source.resource, URI.file(target.fsPath)), true);
1206
let copied = await service.copy(source.resource, URI.file(target.fsPath));
1207
1208
assert.strictEqual(existsSync(copied.resource.fsPath), true);
1209
assert.strictEqual(basename(copied.resource.fsPath), 'index.html');
1210
assert.ok(event!);
1211
assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);
1212
assert.strictEqual(event!.operation, FileOperation.COPY);
1213
assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);
1214
1215
copied = await service.resolve(source.resource, { resolveMetadata: true });
1216
assert.strictEqual(source.size, copied.size);
1217
});
1218
1219
test('cloneFile - basics', () => {
1220
return testCloneFile();
1221
});
1222
1223
test('cloneFile - via copy capability', () => {
1224
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileFolderCopy);
1225
1226
return testCloneFile();
1227
});
1228
1229
test('cloneFile - via pipe', () => {
1230
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1231
1232
return testCloneFile();
1233
});
1234
1235
async function testCloneFile(): Promise<void> {
1236
const source1 = URI.file(join(testDir, 'index.html'));
1237
const source1Size = (await service.resolve(source1, { resolveMetadata: true })).size;
1238
1239
const source2 = URI.file(join(testDir, 'lorem.txt'));
1240
const source2Size = (await service.resolve(source2, { resolveMetadata: true })).size;
1241
1242
const targetParent = URI.file(testDir);
1243
1244
// same path is a no-op
1245
await service.cloneFile(source1, source1);
1246
1247
// simple clone to existing parent folder path
1248
const target1 = targetParent.with({ path: posix.join(targetParent.path, `${posix.basename(source1.path)}-clone`) });
1249
1250
await service.cloneFile(source1, URI.file(target1.fsPath));
1251
1252
assert.strictEqual(existsSync(target1.fsPath), true);
1253
assert.strictEqual(basename(target1.fsPath), 'index.html-clone');
1254
1255
let target1Size = (await service.resolve(target1, { resolveMetadata: true })).size;
1256
1257
assert.strictEqual(source1Size, target1Size);
1258
1259
// clone to same path overwrites
1260
await service.cloneFile(source2, URI.file(target1.fsPath));
1261
1262
target1Size = (await service.resolve(target1, { resolveMetadata: true })).size;
1263
1264
assert.strictEqual(source2Size, target1Size);
1265
assert.notStrictEqual(source1Size, target1Size);
1266
1267
// clone creates missing folders ad-hoc
1268
const target2 = targetParent.with({ path: posix.join(targetParent.path, 'foo', 'bar', `${posix.basename(source1.path)}-clone`) });
1269
1270
await service.cloneFile(source1, URI.file(target2.fsPath));
1271
1272
assert.strictEqual(existsSync(target2.fsPath), true);
1273
assert.strictEqual(basename(target2.fsPath), 'index.html-clone');
1274
1275
const target2Size = (await service.resolve(target2, { resolveMetadata: true })).size;
1276
1277
assert.strictEqual(source1Size, target2Size);
1278
}
1279
1280
test('readFile - small file - default', () => {
1281
return testReadFile(URI.file(join(testDir, 'small.txt')));
1282
});
1283
1284
test('readFile - small file - buffered', () => {
1285
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1286
1287
return testReadFile(URI.file(join(testDir, 'small.txt')));
1288
});
1289
1290
test('readFile - small file - buffered / readonly', () => {
1291
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);
1292
1293
return testReadFile(URI.file(join(testDir, 'small.txt')));
1294
});
1295
1296
test('readFile - small file - unbuffered', async () => {
1297
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1298
1299
return testReadFile(URI.file(join(testDir, 'small.txt')));
1300
});
1301
1302
test('readFile - small file - unbuffered / readonly', async () => {
1303
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);
1304
1305
return testReadFile(URI.file(join(testDir, 'small.txt')));
1306
});
1307
1308
test('readFile - small file - streamed', async () => {
1309
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1310
1311
return testReadFile(URI.file(join(testDir, 'small.txt')));
1312
});
1313
1314
test('readFile - small file - streamed / readonly', async () => {
1315
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream | FileSystemProviderCapabilities.Readonly);
1316
1317
return testReadFile(URI.file(join(testDir, 'small.txt')));
1318
});
1319
1320
test('readFile - large file - default', async () => {
1321
return testReadFile(URI.file(join(testDir, 'lorem.txt')));
1322
});
1323
1324
test('readFile - large file - buffered', async () => {
1325
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1326
1327
return testReadFile(URI.file(join(testDir, 'lorem.txt')));
1328
});
1329
1330
test('readFile - large file - unbuffered', async () => {
1331
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1332
1333
return testReadFile(URI.file(join(testDir, 'lorem.txt')));
1334
});
1335
1336
test('readFile - large file - streamed', async () => {
1337
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1338
1339
return testReadFile(URI.file(join(testDir, 'lorem.txt')));
1340
});
1341
1342
test('readFile - atomic (emulated on service level)', async () => {
1343
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1344
1345
return testReadFile(URI.file(join(testDir, 'lorem.txt')), { atomic: true });
1346
});
1347
1348
test('readFile - atomic (natively supported)', async () => {
1349
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite & FileSystemProviderCapabilities.FileAtomicRead);
1350
1351
return testReadFile(URI.file(join(testDir, 'lorem.txt')), { atomic: true });
1352
});
1353
1354
async function testReadFile(resource: URI, options?: IReadFileOptions): Promise<void> {
1355
const content = await service.readFile(resource, options);
1356
1357
assert.strictEqual(content.value.toString(), readFileSync(resource.fsPath).toString());
1358
}
1359
1360
test('readFileStream - small file - default', () => {
1361
return testReadFileStream(URI.file(join(testDir, 'small.txt')));
1362
});
1363
1364
test('readFileStream - small file - buffered', () => {
1365
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1366
1367
return testReadFileStream(URI.file(join(testDir, 'small.txt')));
1368
});
1369
1370
test('readFileStream - small file - unbuffered', async () => {
1371
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1372
1373
return testReadFileStream(URI.file(join(testDir, 'small.txt')));
1374
});
1375
1376
test('readFileStream - small file - streamed', async () => {
1377
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1378
1379
return testReadFileStream(URI.file(join(testDir, 'small.txt')));
1380
});
1381
1382
async function testReadFileStream(resource: URI): Promise<void> {
1383
const content = await service.readFileStream(resource);
1384
1385
assert.strictEqual((await streamToBuffer(content.value)).toString(), readFileSync(resource.fsPath).toString());
1386
}
1387
1388
test('readFile - Files are intermingled #38331 - default', async () => {
1389
return testFilesNotIntermingled();
1390
});
1391
1392
test('readFile - Files are intermingled #38331 - buffered', async () => {
1393
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1394
1395
return testFilesNotIntermingled();
1396
});
1397
1398
test('readFile - Files are intermingled #38331 - unbuffered', async () => {
1399
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1400
1401
return testFilesNotIntermingled();
1402
});
1403
1404
test('readFile - Files are intermingled #38331 - streamed', async () => {
1405
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1406
1407
return testFilesNotIntermingled();
1408
});
1409
1410
async function testFilesNotIntermingled() {
1411
const resource1 = URI.file(join(testDir, 'lorem.txt'));
1412
const resource2 = URI.file(join(testDir, 'some_utf16le.css'));
1413
1414
// load in sequence and keep data
1415
const value1 = await service.readFile(resource1);
1416
const value2 = await service.readFile(resource2);
1417
1418
// load in parallel in expect the same result
1419
const result = await Promise.all([
1420
service.readFile(resource1),
1421
service.readFile(resource2)
1422
]);
1423
1424
assert.strictEqual(result[0].value.toString(), value1.value.toString());
1425
assert.strictEqual(result[1].value.toString(), value2.value.toString());
1426
}
1427
1428
test('readFile - from position (ASCII) - default', async () => {
1429
return testReadFileFromPositionAscii();
1430
});
1431
1432
test('readFile - from position (ASCII) - buffered', async () => {
1433
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1434
1435
return testReadFileFromPositionAscii();
1436
});
1437
1438
test('readFile - from position (ASCII) - unbuffered', async () => {
1439
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1440
1441
return testReadFileFromPositionAscii();
1442
});
1443
1444
test('readFile - from position (ASCII) - streamed', async () => {
1445
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1446
1447
return testReadFileFromPositionAscii();
1448
});
1449
1450
async function testReadFileFromPositionAscii() {
1451
const resource = URI.file(join(testDir, 'small.txt'));
1452
1453
const contents = await service.readFile(resource, { position: 6 });
1454
1455
assert.strictEqual(contents.value.toString(), 'File');
1456
}
1457
1458
test('readFile - from position (with umlaut) - default', async () => {
1459
return testReadFileFromPositionUmlaut();
1460
});
1461
1462
test('readFile - from position (with umlaut) - buffered', async () => {
1463
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1464
1465
return testReadFileFromPositionUmlaut();
1466
});
1467
1468
test('readFile - from position (with umlaut) - unbuffered', async () => {
1469
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1470
1471
return testReadFileFromPositionUmlaut();
1472
});
1473
1474
test('readFile - from position (with umlaut) - streamed', async () => {
1475
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1476
1477
return testReadFileFromPositionUmlaut();
1478
});
1479
1480
async function testReadFileFromPositionUmlaut() {
1481
const resource = URI.file(join(testDir, 'small_umlaut.txt'));
1482
1483
const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });
1484
1485
assert.strictEqual(contents.value.toString(), 'mlaut');
1486
}
1487
1488
test('readFile - 3 bytes (ASCII) - default', async () => {
1489
return testReadThreeBytesFromFile();
1490
});
1491
1492
test('readFile - 3 bytes (ASCII) - buffered', async () => {
1493
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1494
1495
return testReadThreeBytesFromFile();
1496
});
1497
1498
test('readFile - 3 bytes (ASCII) - unbuffered', async () => {
1499
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1500
1501
return testReadThreeBytesFromFile();
1502
});
1503
1504
test('readFile - 3 bytes (ASCII) - streamed', async () => {
1505
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1506
1507
return testReadThreeBytesFromFile();
1508
});
1509
1510
async function testReadThreeBytesFromFile() {
1511
const resource = URI.file(join(testDir, 'small.txt'));
1512
1513
const contents = await service.readFile(resource, { length: 3 });
1514
1515
assert.strictEqual(contents.value.toString(), 'Sma');
1516
}
1517
1518
test('readFile - 20000 bytes (large) - default', async () => {
1519
return readLargeFileWithLength(20000);
1520
});
1521
1522
test('readFile - 20000 bytes (large) - buffered', async () => {
1523
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1524
1525
return readLargeFileWithLength(20000);
1526
});
1527
1528
test('readFile - 20000 bytes (large) - unbuffered', async () => {
1529
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1530
1531
return readLargeFileWithLength(20000);
1532
});
1533
1534
test('readFile - 20000 bytes (large) - streamed', async () => {
1535
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1536
1537
return readLargeFileWithLength(20000);
1538
});
1539
1540
test('readFile - 80000 bytes (large) - default', async () => {
1541
return readLargeFileWithLength(80000);
1542
});
1543
1544
test('readFile - 80000 bytes (large) - buffered', async () => {
1545
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1546
1547
return readLargeFileWithLength(80000);
1548
});
1549
1550
test('readFile - 80000 bytes (large) - unbuffered', async () => {
1551
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1552
1553
return readLargeFileWithLength(80000);
1554
});
1555
1556
test('readFile - 80000 bytes (large) - streamed', async () => {
1557
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1558
1559
return readLargeFileWithLength(80000);
1560
});
1561
1562
async function readLargeFileWithLength(length: number) {
1563
const resource = URI.file(join(testDir, 'lorem.txt'));
1564
1565
const contents = await service.readFile(resource, { length });
1566
1567
assert.strictEqual(contents.value.byteLength, length);
1568
}
1569
1570
test('readFile - FILE_IS_DIRECTORY', async () => {
1571
const resource = URI.file(join(testDir, 'deep'));
1572
1573
let error: FileOperationError | undefined = undefined;
1574
try {
1575
await service.readFile(resource);
1576
} catch (err) {
1577
error = err;
1578
}
1579
1580
assert.ok(error);
1581
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY);
1582
});
1583
1584
(isWindows /* error code does not seem to be supported on windows */ ? test.skip : test)('readFile - FILE_NOT_DIRECTORY', async () => {
1585
const resource = URI.file(join(testDir, 'lorem.txt', 'file.txt'));
1586
1587
let error: FileOperationError | undefined = undefined;
1588
try {
1589
await service.readFile(resource);
1590
} catch (err) {
1591
error = err;
1592
}
1593
1594
assert.ok(error);
1595
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_DIRECTORY);
1596
});
1597
1598
test('readFile - FILE_NOT_FOUND', async () => {
1599
const resource = URI.file(join(testDir, '404.html'));
1600
1601
let error: FileOperationError | undefined = undefined;
1602
try {
1603
await service.readFile(resource);
1604
} catch (err) {
1605
error = err;
1606
}
1607
1608
assert.ok(error);
1609
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_FOUND);
1610
});
1611
1612
test('readFile - FILE_NOT_MODIFIED_SINCE - default', async () => {
1613
return testNotModifiedSince();
1614
});
1615
1616
test('readFile - FILE_NOT_MODIFIED_SINCE - buffered', async () => {
1617
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1618
1619
return testNotModifiedSince();
1620
});
1621
1622
test('readFile - FILE_NOT_MODIFIED_SINCE - unbuffered', async () => {
1623
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1624
1625
return testNotModifiedSince();
1626
});
1627
1628
test('readFile - FILE_NOT_MODIFIED_SINCE - streamed', async () => {
1629
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1630
1631
return testNotModifiedSince();
1632
});
1633
1634
async function testNotModifiedSince() {
1635
const resource = URI.file(join(testDir, 'index.html'));
1636
1637
const contents = await service.readFile(resource);
1638
fileProvider.totalBytesRead = 0;
1639
1640
let error: FileOperationError | undefined = undefined;
1641
try {
1642
await service.readFile(resource, { etag: contents.etag });
1643
} catch (err) {
1644
error = err;
1645
}
1646
1647
assert.ok(error);
1648
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);
1649
assert.ok(error instanceof NotModifiedSinceFileOperationError && error.stat);
1650
assert.strictEqual(fileProvider.totalBytesRead, 0);
1651
}
1652
1653
test('readFile - FILE_NOT_MODIFIED_SINCE does not fire wrongly - https://github.com/microsoft/vscode/issues/72909', async () => {
1654
fileProvider.setInvalidStatSize(true);
1655
1656
const resource = URI.file(join(testDir, 'index.html'));
1657
1658
await service.readFile(resource);
1659
1660
let error: FileOperationError | undefined = undefined;
1661
try {
1662
await service.readFile(resource, { etag: undefined });
1663
} catch (err) {
1664
error = err;
1665
}
1666
1667
assert.ok(!error);
1668
});
1669
1670
test('readFile - FILE_TOO_LARGE - default', async () => {
1671
return testFileTooLarge();
1672
});
1673
1674
test('readFile - FILE_TOO_LARGE - buffered', async () => {
1675
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1676
1677
return testFileTooLarge();
1678
});
1679
1680
test('readFile - FILE_TOO_LARGE - unbuffered', async () => {
1681
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1682
1683
return testFileTooLarge();
1684
});
1685
1686
test('readFile - FILE_TOO_LARGE - streamed', async () => {
1687
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);
1688
1689
return testFileTooLarge();
1690
});
1691
1692
async function testFileTooLarge() {
1693
await doTestFileTooLarge(false);
1694
1695
// Also test when the stat size is wrong
1696
fileProvider.setSmallStatSize(true);
1697
return doTestFileTooLarge(true);
1698
}
1699
1700
async function doTestFileTooLarge(statSizeWrong: boolean) {
1701
const resource = URI.file(join(testDir, 'index.html'));
1702
1703
let error: FileOperationError | undefined = undefined;
1704
try {
1705
await service.readFile(resource, { limits: { size: 10 } });
1706
} catch (err) {
1707
error = err;
1708
}
1709
1710
if (!statSizeWrong) {
1711
assert.ok(error instanceof TooLargeFileOperationError);
1712
assert.ok(typeof error.size === 'number');
1713
}
1714
assert.strictEqual(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);
1715
}
1716
1717
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('readFile - dangling symbolic link - https://github.com/microsoft/vscode/issues/116049', async () => {
1718
const link = URI.file(join(testDir, 'small.js-link'));
1719
await promises.symlink(join(testDir, 'small.js'), link.fsPath);
1720
1721
let error: FileOperationError | undefined = undefined;
1722
try {
1723
await service.readFile(link);
1724
} catch (err) {
1725
error = err;
1726
}
1727
1728
assert.ok(error);
1729
});
1730
1731
test('createFile', async () => {
1732
return assertCreateFile(contents => VSBuffer.fromString(contents));
1733
});
1734
1735
test('createFile (readable)', async () => {
1736
return assertCreateFile(contents => bufferToReadable(VSBuffer.fromString(contents)));
1737
});
1738
1739
test('createFile (stream)', async () => {
1740
return assertCreateFile(contents => bufferToStream(VSBuffer.fromString(contents)));
1741
});
1742
1743
async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {
1744
let event: FileOperationEvent;
1745
disposables.add(service.onDidRunOperation(e => event = e));
1746
1747
const contents = 'Hello World';
1748
const resource = URI.file(join(testDir, 'test.txt'));
1749
1750
assert.strictEqual(await service.canCreateFile(resource), true);
1751
const fileStat = await service.createFile(resource, converter(contents));
1752
assert.strictEqual(fileStat.name, 'test.txt');
1753
assert.strictEqual(existsSync(fileStat.resource.fsPath), true);
1754
assert.strictEqual(readFileSync(fileStat.resource.fsPath).toString(), contents);
1755
1756
assert.ok(event!);
1757
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
1758
assert.strictEqual(event!.operation, FileOperation.CREATE);
1759
assert.strictEqual(event!.target!.resource.fsPath, resource.fsPath);
1760
}
1761
1762
test('createFile (does not overwrite by default)', async () => {
1763
const contents = 'Hello World';
1764
const resource = URI.file(join(testDir, 'test.txt'));
1765
1766
writeFileSync(resource.fsPath, ''); // create file
1767
1768
assert.ok((await service.canCreateFile(resource)) instanceof Error);
1769
1770
let error;
1771
try {
1772
await service.createFile(resource, VSBuffer.fromString(contents));
1773
} catch (err) {
1774
error = err;
1775
}
1776
1777
assert.ok(error);
1778
});
1779
1780
test('createFile (allows to overwrite existing)', async () => {
1781
let event: FileOperationEvent;
1782
disposables.add(service.onDidRunOperation(e => event = e));
1783
1784
const contents = 'Hello World';
1785
const resource = URI.file(join(testDir, 'test.txt'));
1786
1787
writeFileSync(resource.fsPath, ''); // create file
1788
1789
assert.strictEqual(await service.canCreateFile(resource, { overwrite: true }), true);
1790
const fileStat = await service.createFile(resource, VSBuffer.fromString(contents), { overwrite: true });
1791
assert.strictEqual(fileStat.name, 'test.txt');
1792
assert.strictEqual(existsSync(fileStat.resource.fsPath), true);
1793
assert.strictEqual(readFileSync(fileStat.resource.fsPath).toString(), contents);
1794
1795
assert.ok(event!);
1796
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
1797
assert.strictEqual(event!.operation, FileOperation.CREATE);
1798
assert.strictEqual(event!.target!.resource.fsPath, resource.fsPath);
1799
});
1800
1801
test('writeFile - default', async () => {
1802
return testWriteFile(false);
1803
});
1804
1805
test('writeFile - flush on write', async () => {
1806
DiskFileSystemProvider.configureFlushOnWrite(true);
1807
try {
1808
return await testWriteFile(false);
1809
} finally {
1810
DiskFileSystemProvider.configureFlushOnWrite(false);
1811
}
1812
});
1813
1814
test('writeFile - buffered', async () => {
1815
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1816
1817
return testWriteFile(false);
1818
});
1819
1820
test('writeFile - unbuffered', async () => {
1821
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1822
1823
return testWriteFile(false);
1824
});
1825
1826
test('writeFile - default (atomic)', async () => {
1827
return testWriteFile(true);
1828
});
1829
1830
test('writeFile - flush on write (atomic)', async () => {
1831
DiskFileSystemProvider.configureFlushOnWrite(true);
1832
try {
1833
return await testWriteFile(true);
1834
} finally {
1835
DiskFileSystemProvider.configureFlushOnWrite(false);
1836
}
1837
});
1838
1839
test('writeFile - buffered (atomic)', async () => {
1840
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAtomicWrite);
1841
1842
let e;
1843
try {
1844
await testWriteFile(true);
1845
} catch (error) {
1846
e = error;
1847
}
1848
1849
assert.ok(e);
1850
});
1851
1852
test('writeFile - unbuffered (atomic)', async () => {
1853
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);
1854
1855
return testWriteFile(true);
1856
});
1857
1858
(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('writeFile - atomic writing does not break symlinks', async () => {
1859
const link = URI.file(join(testDir, 'lorem.txt-linked'));
1860
await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);
1861
1862
const content = 'Updates to the lorem file';
1863
await service.writeFile(link, VSBuffer.fromString(content), { atomic: { postfix: '.vsctmp' } });
1864
assert.strictEqual(readFileSync(link.fsPath).toString(), content);
1865
1866
const resolved = await service.resolve(link);
1867
assert.strictEqual(resolved.isSymbolicLink, true);
1868
});
1869
1870
async function testWriteFile(atomic: boolean) {
1871
let event: FileOperationEvent;
1872
disposables.add(service.onDidRunOperation(e => event = e));
1873
1874
const resource = URI.file(join(testDir, 'small.txt'));
1875
1876
const content = readFileSync(resource.fsPath).toString();
1877
assert.strictEqual(content, 'Small File');
1878
1879
const newContent = 'Updates to the small file';
1880
await service.writeFile(resource, VSBuffer.fromString(newContent), { atomic: atomic ? { postfix: '.vsctmp' } : false });
1881
1882
assert.ok(event!);
1883
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
1884
assert.strictEqual(event!.operation, FileOperation.WRITE);
1885
1886
assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);
1887
}
1888
1889
test('writeFile (large file) - default', async () => {
1890
return testWriteFileLarge(false);
1891
});
1892
1893
test('writeFile (large file) - buffered', async () => {
1894
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
1895
1896
return testWriteFileLarge(false);
1897
});
1898
1899
test('writeFile (large file) - unbuffered', async () => {
1900
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
1901
1902
return testWriteFileLarge(false);
1903
});
1904
1905
test('writeFile (large file) - default (atomic)', async () => {
1906
return testWriteFileLarge(true);
1907
});
1908
1909
test('writeFile (large file) - buffered (atomic)', async () => {
1910
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAtomicWrite);
1911
1912
let e;
1913
try {
1914
await testWriteFileLarge(true);
1915
} catch (error) {
1916
e = error;
1917
}
1918
1919
assert.ok(e);
1920
});
1921
1922
test('writeFile (large file) - unbuffered (atomic)', async () => {
1923
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);
1924
1925
return testWriteFileLarge(true);
1926
});
1927
1928
async function testWriteFileLarge(atomic: boolean) {
1929
const resource = URI.file(join(testDir, 'lorem.txt'));
1930
1931
const content = readFileSync(resource.fsPath);
1932
const newContent = content.toString() + content.toString();
1933
1934
const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent), { atomic: atomic ? { postfix: '.vsctmp' } : false });
1935
assert.strictEqual(fileStat.name, 'lorem.txt');
1936
1937
assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);
1938
}
1939
1940
test('writeFile (large file) - unbuffered (atomic) - concurrent writes with multiple services', async () => {
1941
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);
1942
1943
const resource = URI.file(join(testDir, 'lorem.txt'));
1944
1945
const content = readFileSync(resource.fsPath);
1946
const newContent = content.toString() + content.toString();
1947
1948
const promises: Promise<IFileStatWithMetadata>[] = [];
1949
let suffix = 0;
1950
for (let i = 0; i < 10; i++) {
1951
const service = disposables.add(new FileService(new NullLogService()));
1952
disposables.add(service.registerProvider(Schemas.file, fileProvider));
1953
1954
promises.push(service.writeFile(resource, VSBuffer.fromString(`${newContent}${++suffix}`), { atomic: { postfix: '.vsctmp' } }));
1955
await timeout(0);
1956
}
1957
1958
await Promise.allSettled(promises);
1959
1960
assert.strictEqual(readFileSync(resource.fsPath).toString(), `${newContent}${suffix}`);
1961
});
1962
1963
test('writeFile - buffered - readonly throws', async () => {
1964
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);
1965
1966
return testWriteFileReadonlyThrows();
1967
});
1968
1969
test('writeFile - unbuffered - readonly throws', async () => {
1970
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);
1971
1972
return testWriteFileReadonlyThrows();
1973
});
1974
1975
async function testWriteFileReadonlyThrows() {
1976
const resource = URI.file(join(testDir, 'small.txt'));
1977
1978
const content = readFileSync(resource.fsPath).toString();
1979
assert.strictEqual(content, 'Small File');
1980
1981
const newContent = 'Updates to the small file';
1982
1983
let error: Error;
1984
try {
1985
await service.writeFile(resource, VSBuffer.fromString(newContent));
1986
} catch (err) {
1987
error = err;
1988
}
1989
1990
assert.ok(error!);
1991
}
1992
1993
test('writeFile (large file) - multiple parallel writes queue up and atomic read support (via file service)', async () => {
1994
const resource = URI.file(join(testDir, 'lorem.txt'));
1995
1996
const content = readFileSync(resource.fsPath);
1997
const newContent = content.toString() + content.toString();
1998
1999
const writePromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {
2000
const fileStat = await service.writeFile(resource, VSBuffer.fromString(offset + newContent));
2001
assert.strictEqual(fileStat.name, 'lorem.txt');
2002
}));
2003
2004
const readPromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async () => {
2005
const fileContent = await service.readFile(resource, { atomic: true });
2006
assert.ok(fileContent.value.byteLength > 0); // `atomic: true` ensures we never read a truncated file
2007
}));
2008
2009
await Promise.all([writePromises, readPromises]);
2010
});
2011
2012
test('provider - write barrier prevents dirty writes', async () => {
2013
const resource = URI.file(join(testDir, 'lorem.txt'));
2014
2015
const content = readFileSync(resource.fsPath);
2016
const newContent = content.toString() + content.toString();
2017
2018
const provider = service.getProvider(resource.scheme);
2019
assert.ok(provider);
2020
assert.ok(hasOpenReadWriteCloseCapability(provider));
2021
2022
const writePromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {
2023
const content = offset + newContent;
2024
const contentBuffer = VSBuffer.fromString(content).buffer;
2025
2026
const fd = await provider.open(resource, { create: true, unlock: false });
2027
try {
2028
await provider.write(fd, 0, VSBuffer.fromString(content).buffer, 0, contentBuffer.byteLength);
2029
2030
// Here since `close` is not called, all other writes are
2031
// waiting on the barrier to release, so doing a readFile
2032
// should give us a consistent view of the file contents
2033
assert.strictEqual((await promises.readFile(resource.fsPath)).toString(), content);
2034
} finally {
2035
await provider.close(fd);
2036
}
2037
}));
2038
2039
await Promise.all([writePromises]);
2040
});
2041
2042
test('provider - write barrier is partitioned per resource', async () => {
2043
const resource1 = URI.file(join(testDir, 'lorem.txt'));
2044
const resource2 = URI.file(join(testDir, 'test.txt'));
2045
2046
const provider = service.getProvider(resource1.scheme);
2047
assert.ok(provider);
2048
assert.ok(hasOpenReadWriteCloseCapability(provider));
2049
2050
const fd1 = await provider.open(resource1, { create: true, unlock: false });
2051
const fd2 = await provider.open(resource2, { create: true, unlock: false });
2052
2053
const newContent = 'Hello World';
2054
2055
try {
2056
await provider.write(fd1, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength);
2057
assert.strictEqual((await promises.readFile(resource1.fsPath)).toString(), newContent);
2058
2059
await provider.write(fd2, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength);
2060
assert.strictEqual((await promises.readFile(resource2.fsPath)).toString(), newContent);
2061
} finally {
2062
await Promise.allSettled([
2063
await provider.close(fd1),
2064
await provider.close(fd2)
2065
]);
2066
}
2067
});
2068
2069
test('provider - write barrier not becoming stale', async () => {
2070
const newFolder = join(testDir, 'new-folder');
2071
const newResource = URI.file(join(newFolder, 'lorem.txt'));
2072
2073
const provider = service.getProvider(newResource.scheme);
2074
assert.ok(provider);
2075
assert.ok(hasOpenReadWriteCloseCapability(provider));
2076
2077
let error: Error | undefined = undefined;
2078
try {
2079
await provider.open(newResource, { create: true, unlock: false });
2080
} catch (e) {
2081
error = e;
2082
}
2083
2084
assert.ok(error); // expected because `new-folder` does not exist
2085
2086
await promises.mkdir(newFolder);
2087
2088
const content = readFileSync(URI.file(join(testDir, 'lorem.txt')).fsPath);
2089
const newContent = content.toString() + content.toString();
2090
const newContentBuffer = VSBuffer.fromString(newContent).buffer;
2091
2092
const fd = await provider.open(newResource, { create: true, unlock: false });
2093
try {
2094
await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);
2095
2096
assert.strictEqual((await promises.readFile(newResource.fsPath)).toString(), newContent);
2097
} finally {
2098
await provider.close(fd);
2099
}
2100
});
2101
2102
test('provider - atomic reads (write pending when read starts)', async () => {
2103
const resource = URI.file(join(testDir, 'lorem.txt'));
2104
2105
const content = readFileSync(resource.fsPath);
2106
const newContent = content.toString() + content.toString();
2107
const newContentBuffer = VSBuffer.fromString(newContent).buffer;
2108
2109
const provider = service.getProvider(resource.scheme);
2110
assert.ok(provider);
2111
assert.ok(hasOpenReadWriteCloseCapability(provider));
2112
assert.ok(hasFileAtomicReadCapability(provider));
2113
2114
let atomicReadPromise: Promise<Uint8Array> | undefined = undefined;
2115
const fd = await provider.open(resource, { create: true, unlock: false });
2116
try {
2117
2118
// Start reading while write is pending
2119
atomicReadPromise = provider.readFile(resource, { atomic: true });
2120
2121
// Simulate a slow write, giving the read
2122
// a chance to succeed if it were not atomic
2123
await timeout(20);
2124
2125
await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);
2126
} finally {
2127
await provider.close(fd);
2128
}
2129
2130
assert.ok(atomicReadPromise);
2131
2132
const atomicReadResult = await atomicReadPromise;
2133
assert.strictEqual(atomicReadResult.byteLength, newContentBuffer.byteLength);
2134
});
2135
2136
test('provider - atomic reads (read pending when write starts)', async () => {
2137
const resource = URI.file(join(testDir, 'lorem.txt'));
2138
2139
const content = readFileSync(resource.fsPath);
2140
const newContent = content.toString() + content.toString();
2141
const newContentBuffer = VSBuffer.fromString(newContent).buffer;
2142
2143
const provider = service.getProvider(resource.scheme);
2144
assert.ok(provider);
2145
assert.ok(hasOpenReadWriteCloseCapability(provider));
2146
assert.ok(hasFileAtomicReadCapability(provider));
2147
2148
let atomicReadPromise = provider.readFile(resource, { atomic: true });
2149
2150
const fdPromise = provider.open(resource, { create: true, unlock: false }).then(async fd => {
2151
try {
2152
return await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);
2153
} finally {
2154
await provider.close(fd);
2155
}
2156
});
2157
2158
let atomicReadResult = await atomicReadPromise;
2159
assert.strictEqual(atomicReadResult.byteLength, content.byteLength);
2160
2161
await fdPromise;
2162
2163
atomicReadPromise = provider.readFile(resource, { atomic: true });
2164
atomicReadResult = await atomicReadPromise;
2165
assert.strictEqual(atomicReadResult.byteLength, newContentBuffer.byteLength);
2166
});
2167
2168
test('writeFile (readable) - default', async () => {
2169
return testWriteFileReadable();
2170
});
2171
2172
test('writeFile (readable) - buffered', async () => {
2173
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
2174
2175
return testWriteFileReadable();
2176
});
2177
2178
test('writeFile (readable) - unbuffered', async () => {
2179
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2180
2181
return testWriteFileReadable();
2182
});
2183
2184
async function testWriteFileReadable() {
2185
const resource = URI.file(join(testDir, 'small.txt'));
2186
2187
const content = readFileSync(resource.fsPath).toString();
2188
assert.strictEqual(content, 'Small File');
2189
2190
const newContent = 'Updates to the small file';
2191
await service.writeFile(resource, toLineByLineReadable(newContent));
2192
2193
assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);
2194
}
2195
2196
test('writeFile (large file - readable) - default', async () => {
2197
return testWriteFileLargeReadable();
2198
});
2199
2200
test('writeFile (large file - readable) - buffered', async () => {
2201
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
2202
2203
return testWriteFileLargeReadable();
2204
});
2205
2206
test('writeFile (large file - readable) - unbuffered', async () => {
2207
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2208
2209
return testWriteFileLargeReadable();
2210
});
2211
2212
async function testWriteFileLargeReadable() {
2213
const resource = URI.file(join(testDir, 'lorem.txt'));
2214
2215
const content = readFileSync(resource.fsPath);
2216
const newContent = content.toString() + content.toString();
2217
2218
const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));
2219
assert.strictEqual(fileStat.name, 'lorem.txt');
2220
2221
assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);
2222
}
2223
2224
test('writeFile (stream) - default', async () => {
2225
return testWriteFileStream();
2226
});
2227
2228
test('writeFile (stream) - buffered', async () => {
2229
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
2230
2231
return testWriteFileStream();
2232
});
2233
2234
test('writeFile (stream) - unbuffered', async () => {
2235
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2236
2237
return testWriteFileStream();
2238
});
2239
2240
async function testWriteFileStream() {
2241
const source = URI.file(join(testDir, 'small.txt'));
2242
const target = URI.file(join(testDir, 'small-copy.txt'));
2243
2244
const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath)));
2245
assert.strictEqual(fileStat.name, 'small-copy.txt');
2246
2247
const targetContents = readFileSync(target.fsPath).toString();
2248
assert.strictEqual(readFileSync(source.fsPath).toString(), targetContents);
2249
}
2250
2251
test('writeFile (large file - stream) - default', async () => {
2252
return testWriteFileLargeStream();
2253
});
2254
2255
test('writeFile (large file - stream) - buffered', async () => {
2256
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
2257
2258
return testWriteFileLargeStream();
2259
});
2260
2261
test('writeFile (large file - stream) - unbuffered', async () => {
2262
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2263
2264
return testWriteFileLargeStream();
2265
});
2266
2267
async function testWriteFileLargeStream() {
2268
const source = URI.file(join(testDir, 'lorem.txt'));
2269
const target = URI.file(join(testDir, 'lorem-copy.txt'));
2270
2271
const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath)));
2272
assert.strictEqual(fileStat.name, 'lorem-copy.txt');
2273
2274
const targetContents = readFileSync(target.fsPath).toString();
2275
assert.strictEqual(readFileSync(source.fsPath).toString(), targetContents);
2276
}
2277
2278
test('writeFile (file is created including parents)', async () => {
2279
const resource = URI.file(join(testDir, 'other', 'newfile.txt'));
2280
2281
const content = 'File is created including parent';
2282
const fileStat = await service.writeFile(resource, VSBuffer.fromString(content));
2283
assert.strictEqual(fileStat.name, 'newfile.txt');
2284
2285
assert.strictEqual(readFileSync(resource.fsPath).toString(), content);
2286
});
2287
2288
test('writeFile - locked files and unlocking', async () => {
2289
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileWriteUnlock);
2290
2291
return testLockedFiles(false);
2292
});
2293
2294
test('writeFile (stream) - locked files and unlocking', async () => {
2295
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileWriteUnlock);
2296
2297
return testLockedFiles(false);
2298
});
2299
2300
test('writeFile - locked files and unlocking throws error when missing capability', async () => {
2301
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2302
2303
return testLockedFiles(true);
2304
});
2305
2306
test('writeFile (stream) - locked files and unlocking throws error when missing capability', async () => {
2307
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
2308
2309
return testLockedFiles(true);
2310
});
2311
2312
async function testLockedFiles(expectError: boolean) {
2313
const lockedFile = URI.file(join(testDir, 'my-locked-file'));
2314
2315
const content = await service.writeFile(lockedFile, VSBuffer.fromString('Locked File'));
2316
assert.strictEqual(content.locked, false);
2317
2318
const stats = await promises.stat(lockedFile.fsPath);
2319
await promises.chmod(lockedFile.fsPath, stats.mode & ~0o200);
2320
2321
let stat = await service.stat(lockedFile);
2322
assert.strictEqual(stat.locked, true);
2323
2324
let error;
2325
const newContent = 'Updates to locked file';
2326
try {
2327
await service.writeFile(lockedFile, VSBuffer.fromString(newContent));
2328
} catch (e) {
2329
error = e;
2330
}
2331
2332
assert.ok(error);
2333
error = undefined;
2334
2335
if (expectError) {
2336
try {
2337
await service.writeFile(lockedFile, VSBuffer.fromString(newContent), { unlock: true });
2338
} catch (e) {
2339
error = e;
2340
}
2341
2342
assert.ok(error);
2343
} else {
2344
await service.writeFile(lockedFile, VSBuffer.fromString(newContent), { unlock: true });
2345
assert.strictEqual(readFileSync(lockedFile.fsPath).toString(), newContent);
2346
2347
stat = await service.stat(lockedFile);
2348
assert.strictEqual(stat.locked, false);
2349
}
2350
}
2351
2352
test('writeFile (error when folder is encountered)', async () => {
2353
const resource = URI.file(testDir);
2354
2355
let error: Error | undefined = undefined;
2356
try {
2357
await service.writeFile(resource, VSBuffer.fromString('File is created including parent'));
2358
} catch (err) {
2359
error = err;
2360
}
2361
2362
assert.ok(error);
2363
});
2364
2365
test('writeFile (no error when providing up to date etag)', async () => {
2366
const resource = URI.file(join(testDir, 'small.txt'));
2367
2368
const stat = await service.resolve(resource);
2369
2370
const content = readFileSync(resource.fsPath).toString();
2371
assert.strictEqual(content, 'Small File');
2372
2373
const newContent = 'Updates to the small file';
2374
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
2375
2376
assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);
2377
});
2378
2379
test('writeFile - error when writing to file that has been updated meanwhile', async () => {
2380
const resource = URI.file(join(testDir, 'small.txt'));
2381
2382
const stat = await service.resolve(resource);
2383
2384
const content = readFileSync(resource.fsPath).toString();
2385
assert.strictEqual(content, 'Small File');
2386
2387
const newContent = 'Updates to the small file';
2388
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
2389
2390
const newContentLeadingToError = newContent + newContent;
2391
2392
const fakeMtime = 1000;
2393
const fakeSize = 1000;
2394
2395
let error: FileOperationError | undefined = undefined;
2396
try {
2397
await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToError), { etag: etag({ mtime: fakeMtime, size: fakeSize }), mtime: fakeMtime });
2398
} catch (err) {
2399
error = err;
2400
}
2401
2402
assert.ok(error);
2403
assert.ok(error instanceof FileOperationError);
2404
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);
2405
});
2406
2407
test('writeFile - no error when writing to file where size is the same', async () => {
2408
const resource = URI.file(join(testDir, 'small.txt'));
2409
2410
const stat = await service.resolve(resource);
2411
2412
const content = readFileSync(resource.fsPath).toString();
2413
assert.strictEqual(content, 'Small File');
2414
2415
const newContent = content; // same content
2416
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
2417
2418
const newContentLeadingToNoError = newContent; // writing the same content should be OK
2419
2420
const fakeMtime = 1000;
2421
const actualSize = newContent.length;
2422
2423
let error: FileOperationError | undefined = undefined;
2424
try {
2425
await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToNoError), { etag: etag({ mtime: fakeMtime, size: actualSize }), mtime: fakeMtime });
2426
} catch (err) {
2427
error = err;
2428
}
2429
2430
assert.ok(!error);
2431
});
2432
2433
test('writeFile - no error when writing to file where content is the same', async () => {
2434
const resource = URI.file(join(testDir, 'small.txt'));
2435
2436
await service.resolve(resource);
2437
2438
const content = readFileSync(resource.fsPath).toString();
2439
assert.strictEqual(content, 'Small File');
2440
2441
const newContent = content; // same content
2442
let error: FileOperationError | undefined = undefined;
2443
try {
2444
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */);
2445
} catch (err) {
2446
error = err;
2447
}
2448
2449
assert.ok(!error);
2450
});
2451
2452
test('writeFile - error when writing to file where content is the same length but different', async () => {
2453
const resource = URI.file(join(testDir, 'small.txt'));
2454
2455
await service.resolve(resource);
2456
2457
const content = readFileSync(resource.fsPath).toString();
2458
assert.strictEqual(content, 'Small File');
2459
2460
const newContent = content.split('').reverse().join(''); // reverse content
2461
let error: FileOperationError | undefined = undefined;
2462
try {
2463
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */);
2464
} catch (err) {
2465
error = err;
2466
}
2467
2468
assert.ok(error);
2469
assert.ok(error instanceof FileOperationError);
2470
assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);
2471
});
2472
2473
test('writeFile - no error when writing to same nonexistent folder multiple times different new files', async () => {
2474
const newFolder = URI.file(join(testDir, 'some', 'new', 'folder'));
2475
2476
const file1 = joinPath(newFolder, 'file-1');
2477
const file2 = joinPath(newFolder, 'file-2');
2478
const file3 = joinPath(newFolder, 'file-3');
2479
2480
// this essentially verifies that the mkdirp logic implemented
2481
// in the file service is able to receive multiple requests for
2482
// the same folder and will not throw errors if another racing
2483
// call succeeded first.
2484
const newContent = 'Updates to the small file';
2485
await Promise.all([
2486
service.writeFile(file1, VSBuffer.fromString(newContent)),
2487
service.writeFile(file2, VSBuffer.fromString(newContent)),
2488
service.writeFile(file3, VSBuffer.fromString(newContent))
2489
]);
2490
2491
assert.ok(service.exists(file1));
2492
assert.ok(service.exists(file2));
2493
assert.ok(service.exists(file3));
2494
});
2495
2496
test('writeFile - error when writing to folder that is a file', async () => {
2497
const existingFile = URI.file(join(testDir, 'my-file'));
2498
2499
await service.createFile(existingFile);
2500
2501
const newFile = joinPath(existingFile, 'file-1');
2502
2503
let error;
2504
const newContent = 'Updates to the small file';
2505
try {
2506
await service.writeFile(newFile, VSBuffer.fromString(newContent));
2507
} catch (e) {
2508
error = e;
2509
}
2510
2511
assert.ok(error);
2512
});
2513
2514
test('appendFile', async () => {
2515
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2516
2517
return testAppendFile();
2518
});
2519
2520
test('appendFile - buffered', async () => {
2521
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2522
2523
return testAppendFile();
2524
});
2525
2526
async function testAppendFile() {
2527
let event: FileOperationEvent;
2528
disposables.add(service.onDidRunOperation(e => event = e));
2529
2530
const resource = URI.file(join(testDir, 'small.txt'));
2531
2532
const content = readFileSync(resource.fsPath).toString();
2533
assert.strictEqual(content, 'Small File');
2534
2535
const appendContent = ' - Appended!';
2536
await service.writeFile(resource, VSBuffer.fromString(appendContent), { append: true });
2537
2538
assert.ok(event!);
2539
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
2540
assert.strictEqual(event!.operation, FileOperation.WRITE);
2541
2542
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended!');
2543
}
2544
2545
test('appendFile (readable)', async () => {
2546
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2547
2548
return testAppendFileReadable();
2549
});
2550
2551
test('appendFile (readable) - buffered', async () => {
2552
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2553
2554
return testAppendFileReadable();
2555
});
2556
2557
async function testAppendFileReadable() {
2558
const resource = URI.file(join(testDir, 'small.txt'));
2559
2560
const content = readFileSync(resource.fsPath).toString();
2561
assert.strictEqual(content, 'Small File');
2562
2563
const appendContent = ' - Appended via readable!';
2564
await service.writeFile(resource, bufferToReadable(VSBuffer.fromString(appendContent)), { append: true });
2565
2566
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via readable!');
2567
}
2568
2569
test('appendFile (stream)', async () => {
2570
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2571
2572
return testAppendFileStream();
2573
});
2574
2575
test('appendFile (stream) - buffered', async () => {
2576
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2577
2578
return testAppendFileStream();
2579
});
2580
2581
async function testAppendFileStream() {
2582
const resource = URI.file(join(testDir, 'small.txt'));
2583
2584
const content = readFileSync(resource.fsPath).toString();
2585
assert.strictEqual(content, 'Small File');
2586
2587
const appendContent = ' - Appended via stream!';
2588
await service.writeFile(resource, bufferToStream(VSBuffer.fromString(appendContent)), { append: true });
2589
2590
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via stream!');
2591
}
2592
2593
test('appendFile - creates file if not exists', async () => {
2594
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2595
2596
return testAppendFileCreatesFile();
2597
});
2598
2599
test('appendFile - creates file if not exists (buffered)', async () => {
2600
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2601
2602
return testAppendFileCreatesFile();
2603
});
2604
2605
async function testAppendFileCreatesFile() {
2606
const resource = URI.file(join(testDir, 'appendfile-new.txt'));
2607
2608
assert.strictEqual(existsSync(resource.fsPath), false);
2609
2610
const content = 'Initial content via append';
2611
await service.writeFile(resource, VSBuffer.fromString(content), { append: true });
2612
2613
assert.strictEqual(existsSync(resource.fsPath), true);
2614
assert.strictEqual(readFileSync(resource.fsPath).toString(), content);
2615
}
2616
2617
test('appendFile - multiple appends', async () => {
2618
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2619
2620
return testAppendFileMultiple();
2621
});
2622
2623
test('appendFile - multiple appends (buffered)', async () => {
2624
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2625
2626
return testAppendFileMultiple();
2627
});
2628
2629
async function testAppendFileMultiple() {
2630
const resource = URI.file(join(testDir, 'appendfile-multiple.txt'));
2631
2632
await service.writeFile(resource, VSBuffer.fromString('Line 1\n'), { append: true });
2633
await service.writeFile(resource, VSBuffer.fromString('Line 2\n'), { append: true });
2634
await service.writeFile(resource, VSBuffer.fromString('Line 3\n'), { append: true });
2635
2636
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Line 1\nLine 2\nLine 3\n');
2637
}
2638
2639
test('appendFile - throws when provider does not support append', async () => {
2640
// Remove FileAppend capability - should throw error
2641
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
2642
2643
const resource = URI.file(join(testDir, 'small.txt'));
2644
const appendContent = ' - Appended via fallback!';
2645
2646
let error: Error | undefined;
2647
try {
2648
await service.writeFile(resource, VSBuffer.fromString(appendContent), { append: true });
2649
} catch (e) {
2650
error = e as Error;
2651
}
2652
2653
assert.ok(error);
2654
assert.ok(error.message.includes('does not support append'));
2655
});
2656
2657
test('read - mixed positions', async () => {
2658
const resource = URI.file(join(testDir, 'lorem.txt'));
2659
2660
// read multiple times from position 0
2661
let buffer = VSBuffer.alloc(1024);
2662
let fd = await fileProvider.open(resource, { create: false });
2663
for (let i = 0; i < 3; i++) {
2664
await fileProvider.read(fd, 0, buffer.buffer, 0, 26);
2665
assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
2666
}
2667
await fileProvider.close(fd);
2668
2669
// read multiple times at various locations
2670
buffer = VSBuffer.alloc(1024);
2671
fd = await fileProvider.open(resource, { create: false });
2672
2673
let posInFile = 0;
2674
2675
await fileProvider.read(fd, posInFile, buffer.buffer, 0, 26);
2676
assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
2677
posInFile += 26;
2678
2679
await fileProvider.read(fd, posInFile, buffer.buffer, 0, 1);
2680
assert.strictEqual(buffer.slice(0, 1).toString(), ',');
2681
posInFile += 1;
2682
2683
await fileProvider.read(fd, posInFile, buffer.buffer, 0, 12);
2684
assert.strictEqual(buffer.slice(0, 12).toString(), ' consectetur');
2685
posInFile += 12;
2686
2687
await fileProvider.read(fd, 98 /* no longer in sequence of posInFile */, buffer.buffer, 0, 9);
2688
assert.strictEqual(buffer.slice(0, 9).toString(), 'fermentum');
2689
2690
await fileProvider.read(fd, 27, buffer.buffer, 0, 12);
2691
assert.strictEqual(buffer.slice(0, 12).toString(), ' consectetur');
2692
2693
await fileProvider.read(fd, 26, buffer.buffer, 0, 1);
2694
assert.strictEqual(buffer.slice(0, 1).toString(), ',');
2695
2696
await fileProvider.read(fd, 0, buffer.buffer, 0, 26);
2697
assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
2698
2699
await fileProvider.read(fd, posInFile /* back in sequence */, buffer.buffer, 0, 11);
2700
assert.strictEqual(buffer.slice(0, 11).toString(), ' adipiscing');
2701
2702
await fileProvider.close(fd);
2703
});
2704
2705
test('write - mixed positions', async () => {
2706
const resource = URI.file(join(testDir, 'lorem.txt'));
2707
2708
const buffer = VSBuffer.alloc(1024);
2709
const fdWrite = await fileProvider.open(resource, { create: true, unlock: false });
2710
const fdRead = await fileProvider.open(resource, { create: false });
2711
2712
let posInFileWrite = 0;
2713
let posInFileRead = 0;
2714
2715
const initialContents = VSBuffer.fromString('Lorem ipsum dolor sit amet');
2716
await fileProvider.write(fdWrite, posInFileWrite, initialContents.buffer, 0, initialContents.byteLength);
2717
posInFileWrite += initialContents.byteLength;
2718
2719
await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, 26);
2720
assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
2721
posInFileRead += 26;
2722
2723
const contents = VSBuffer.fromString('Hello World');
2724
2725
await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);
2726
posInFileWrite += contents.byteLength;
2727
2728
await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, contents.byteLength);
2729
assert.strictEqual(buffer.slice(0, contents.byteLength).toString(), 'Hello World');
2730
posInFileRead += contents.byteLength;
2731
2732
await fileProvider.write(fdWrite, 6, contents.buffer, 0, contents.byteLength);
2733
2734
await fileProvider.read(fdRead, 0, buffer.buffer, 0, 11);
2735
assert.strictEqual(buffer.slice(0, 11).toString(), 'Lorem Hello');
2736
2737
await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);
2738
posInFileWrite += contents.byteLength;
2739
2740
await fileProvider.read(fdRead, posInFileWrite - contents.byteLength, buffer.buffer, 0, contents.byteLength);
2741
assert.strictEqual(buffer.slice(0, contents.byteLength).toString(), 'Hello World');
2742
2743
await fileProvider.close(fdWrite);
2744
await fileProvider.close(fdRead);
2745
});
2746
2747
test('readonly - is handled properly for a single resource', async () => {
2748
fileProvider.setReadonly(true);
2749
2750
const resource = URI.file(join(testDir, 'index.html'));
2751
2752
const resolveResult = await service.resolve(resource);
2753
assert.strictEqual(resolveResult.readonly, true);
2754
2755
const readResult = await service.readFile(resource);
2756
assert.strictEqual(readResult.readonly, true);
2757
2758
let writeFileError: Error | undefined = undefined;
2759
try {
2760
await service.writeFile(resource, VSBuffer.fromString('Hello Test'));
2761
} catch (error) {
2762
writeFileError = error;
2763
}
2764
assert.ok(writeFileError);
2765
2766
let deleteFileError: Error | undefined = undefined;
2767
try {
2768
await service.del(resource);
2769
} catch (error) {
2770
deleteFileError = error;
2771
}
2772
assert.ok(deleteFileError);
2773
});
2774
});
2775
2776