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