Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/files/test/browser/inmemoryFileService.test.ts
5223 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 { timeout } from '../../../../base/common/async.js';
8
import { streamToBuffer, VSBuffer } from '../../../../base/common/buffer.js';
9
import { Schemas } from '../../../../base/common/network.js';
10
import { joinPath } from '../../../../base/common/resources.js';
11
import { URI } from '../../../../base/common/uri.js';
12
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
13
import { FileChangeType, FileOperation, FileOperationEvent, FileSystemProviderCapabilities, IFileStat } from '../../common/files.js';
14
import { FileService } from '../../common/fileService.js';
15
import { InMemoryFileSystemProvider } from '../../common/inMemoryFilesystemProvider.js';
16
import { NullLogService } from '../../../log/common/log.js';
17
18
function getByName(root: IFileStat, name: string): IFileStat | undefined {
19
if (root.children === undefined) {
20
return undefined;
21
}
22
23
return root.children.find(child => child.name === name);
24
}
25
26
function createLargeBuffer(size: number, seed: number): VSBuffer {
27
const data = new Uint8Array(size);
28
for (let i = 0; i < data.length; i++) {
29
data[i] = (seed + i) % 256;
30
}
31
32
return VSBuffer.wrap(data);
33
}
34
35
type Fixture = {
36
root: URI;
37
indexHtml: URI;
38
siteCss: URI;
39
smallTxt: URI;
40
smallUmlautTxt: URI;
41
deepDir: URI;
42
otherDeepDir: URI;
43
};
44
45
suite('InMemory File Service', () => {
46
47
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
48
49
let service: FileService;
50
let provider: InMemoryFileSystemProvider;
51
let fixture: Fixture;
52
53
setup(async () => {
54
service = disposables.add(new FileService(new NullLogService()));
55
provider = disposables.add(new InMemoryFileSystemProvider());
56
disposables.add(service.registerProvider(Schemas.inMemory, provider));
57
58
fixture = await createFixture(service);
59
});
60
61
test('createFolder', async () => {
62
let event: FileOperationEvent | undefined;
63
disposables.add(service.onDidRunOperation(e => event = e));
64
65
const newFolderResource = joinPath(fixture.root, 'newFolder');
66
const newFolder = await service.createFolder(newFolderResource);
67
68
assert.strictEqual(newFolder.name, 'newFolder');
69
assert.strictEqual(await service.exists(newFolderResource), true);
70
71
assert.ok(event);
72
assert.strictEqual(event.resource.toString(), newFolderResource.toString());
73
assert.strictEqual(event.operation, FileOperation.CREATE);
74
assert.strictEqual(event.target?.resource.toString(), newFolderResource.toString());
75
assert.strictEqual(event.target?.isDirectory, true);
76
});
77
78
test('createFolder: creating multiple folders at once', async () => {
79
let event: FileOperationEvent | undefined;
80
disposables.add(service.onDidRunOperation(e => event = e));
81
82
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
83
const newFolderResource = joinPath(fixture.root, ...multiFolderPaths);
84
85
const newFolder = await service.createFolder(newFolderResource);
86
assert.strictEqual(newFolder.name, multiFolderPaths[multiFolderPaths.length - 1]);
87
assert.strictEqual(await service.exists(newFolderResource), true);
88
89
assert.ok(event);
90
assert.strictEqual(event.resource.toString(), newFolderResource.toString());
91
assert.strictEqual(event.operation, FileOperation.CREATE);
92
assert.strictEqual(event.target?.resource.toString(), newFolderResource.toString());
93
assert.strictEqual(event.target?.isDirectory, true);
94
});
95
96
test('exists', async () => {
97
let exists = await service.exists(fixture.root);
98
assert.strictEqual(exists, true);
99
100
exists = await service.exists(joinPath(fixture.root, 'does-not-exist'));
101
assert.strictEqual(exists, false);
102
});
103
104
test('resolve - file', async () => {
105
const resolved = await service.resolve(fixture.indexHtml);
106
107
assert.strictEqual(resolved.name, 'index.html');
108
assert.strictEqual(resolved.isFile, true);
109
assert.strictEqual(resolved.isDirectory, false);
110
});
111
112
test('resolve - directory', async () => {
113
const resolved = await service.resolve(fixture.root);
114
assert.strictEqual(resolved.isDirectory, true);
115
assert.ok(resolved.children);
116
117
const names = resolved.children.map(c => c.name).sort();
118
assert.deepStrictEqual(names, ['examples', 'index.html', 'other', 'site.css', 'deep', 'small.txt', 'small_umlaut.txt'].sort());
119
});
120
121
test('resolve - directory with resolveTo', async () => {
122
const resolved = await service.resolve(fixture.root, { resolveTo: [fixture.deepDir] });
123
124
const deep = getByName(resolved, 'deep');
125
assert.ok(deep);
126
assert.ok(deep.children);
127
assert.strictEqual(deep.children.length, 4);
128
});
129
130
test('readFile', async () => {
131
const content = await service.readFile(fixture.smallTxt);
132
assert.strictEqual(content.value.toString(), 'Small File');
133
});
134
135
test('readFile - from position (ASCII)', async () => {
136
const content = await service.readFile(fixture.smallTxt, { position: 6 });
137
assert.strictEqual(content.value.toString(), 'File');
138
});
139
140
test('readFile - from position (with umlaut)', async () => {
141
const pos = VSBuffer.fromString('Small File with Ü').byteLength;
142
const content = await service.readFile(fixture.smallUmlautTxt, { position: pos });
143
assert.strictEqual(content.value.toString(), 'mlaut');
144
});
145
146
test('readFileStream', async () => {
147
const content = await service.readFileStream(fixture.smallTxt);
148
assert.strictEqual((await streamToBuffer(content.value)).toString(), 'Small File');
149
});
150
151
test('writeFile', async () => {
152
await service.writeFile(fixture.smallTxt, VSBuffer.fromString('Updated'));
153
154
const content = await service.readFile(fixture.smallTxt);
155
assert.strictEqual(content.value.toString(), 'Updated');
156
});
157
158
test('provider open/write - append', async () => {
159
const resource = joinPath(fixture.root, 'append.txt');
160
await service.writeFile(resource, VSBuffer.fromString('Hello'));
161
162
const fd = await provider.open(resource, { create: true, unlock: false, append: true });
163
try {
164
const data = VSBuffer.fromString(' World').buffer;
165
await provider.write(fd, 0, data, 0, data.byteLength);
166
} finally {
167
await provider.close(fd);
168
}
169
170
const content = await service.readFile(resource);
171
assert.strictEqual(content.value.toString(), 'Hello World');
172
});
173
174
test('provider open/write - append (large)', async () => {
175
const resource = joinPath(fixture.root, 'append-large-open.txt');
176
const prefix = createLargeBuffer(256 * 1024, 1);
177
const suffix = createLargeBuffer(256 * 1024, 2);
178
179
await service.writeFile(resource, prefix);
180
181
const fd = await provider.open(resource, { create: true, unlock: false, append: true });
182
try {
183
await provider.write(fd, 123 /* ignored in append mode */, suffix.buffer, 0, suffix.byteLength);
184
} finally {
185
await provider.close(fd);
186
}
187
188
const content = await service.readFile(resource);
189
assert.strictEqual(content.value.byteLength, prefix.byteLength + suffix.byteLength);
190
191
assert.deepStrictEqual(content.value.slice(0, 64).buffer, prefix.slice(0, 64).buffer);
192
assert.deepStrictEqual(content.value.slice(prefix.byteLength, prefix.byteLength + 64).buffer, suffix.slice(0, 64).buffer);
193
assert.deepStrictEqual(content.value.slice(content.value.byteLength - 64, content.value.byteLength).buffer, suffix.slice(suffix.byteLength - 64, suffix.byteLength).buffer);
194
});
195
196
test('writeFile - append', async () => {
197
const resource = joinPath(fixture.root, 'append-via-writeFile.txt');
198
await service.writeFile(resource, VSBuffer.fromString('Hello'));
199
200
await service.writeFile(resource, VSBuffer.fromString(' World'), { append: true });
201
202
const content = await service.readFile(resource);
203
assert.strictEqual(content.value.toString(), 'Hello World');
204
});
205
206
test('writeFile - append (large)', async () => {
207
const resource = joinPath(fixture.root, 'append-large-writeFile.txt');
208
const prefix = createLargeBuffer(256 * 1024, 3);
209
const suffix = createLargeBuffer(256 * 1024, 4);
210
211
await service.writeFile(resource, prefix);
212
await service.writeFile(resource, suffix, { append: true });
213
214
const content = await service.readFile(resource);
215
assert.strictEqual(content.value.byteLength, prefix.byteLength + suffix.byteLength);
216
217
assert.deepStrictEqual(content.value.slice(0, 64).buffer, prefix.slice(0, 64).buffer);
218
assert.deepStrictEqual(content.value.slice(prefix.byteLength, prefix.byteLength + 64).buffer, suffix.slice(0, 64).buffer);
219
assert.deepStrictEqual(content.value.slice(content.value.byteLength - 64, content.value.byteLength).buffer, suffix.slice(suffix.byteLength - 64, suffix.byteLength).buffer);
220
});
221
222
test('rename', async () => {
223
const source = joinPath(fixture.root, 'site.css');
224
const target = joinPath(fixture.root, 'SITE.css');
225
226
await service.move(source, target, true);
227
228
assert.strictEqual(await service.exists(source), false);
229
assert.strictEqual(await service.exists(target), true);
230
});
231
232
test('copy', async () => {
233
const source = fixture.indexHtml;
234
const target = joinPath(fixture.root, 'index-copy.html');
235
236
await service.copy(source, target, true);
237
238
const copied = await service.readFile(target);
239
assert.strictEqual(copied.value.toString(), 'Index');
240
});
241
242
test('deleteFile', async () => {
243
const resource = joinPath(fixture.root, 'to-delete.txt');
244
await service.writeFile(resource, VSBuffer.fromString('delete me'));
245
assert.strictEqual(await service.exists(resource), true);
246
247
await service.del(resource);
248
assert.strictEqual(await service.exists(resource), false);
249
});
250
251
test('provider events bubble through file service', async () => {
252
let changeCount = 0;
253
const resource = joinPath(fixture.root, 'events.txt');
254
disposables.add(service.onDidFilesChange(e => {
255
if (e.contains(resource, FileChangeType.UPDATED) || e.contains(resource, FileChangeType.ADDED) || e.contains(resource, FileChangeType.DELETED)) {
256
changeCount++;
257
}
258
}));
259
260
await service.writeFile(resource, VSBuffer.fromString('1'));
261
await service.writeFile(resource, VSBuffer.fromString('2'));
262
await service.del(resource);
263
264
await timeout(20); // provider fires changes async
265
assert.ok(changeCount > 0);
266
});
267
268
test('setReadOnly toggles provider capabilities', async () => {
269
provider.setReadOnly(true);
270
assert.ok(provider.capabilities & FileSystemProviderCapabilities.Readonly);
271
272
let error: unknown;
273
try {
274
await service.writeFile(joinPath(fixture.root, 'readonly.txt'), VSBuffer.fromString('fail'));
275
} catch (e) {
276
error = e;
277
}
278
279
assert.ok(error);
280
281
provider.setReadOnly(false);
282
await service.writeFile(joinPath(fixture.root, 'readonly.txt'), VSBuffer.fromString('ok'));
283
});
284
});
285
286
async function createFixture(service: FileService): Promise<Fixture> {
287
const root = URI.from({ scheme: Schemas.inMemory, path: '/' });
288
289
await service.createFolder(joinPath(root, 'examples'));
290
await service.createFolder(joinPath(root, 'other'));
291
await service.writeFile(joinPath(root, 'index.html'), VSBuffer.fromString('Index'));
292
await service.writeFile(joinPath(root, 'site.css'), VSBuffer.fromString('body { }'));
293
294
await service.writeFile(joinPath(root, 'small.txt'), VSBuffer.fromString('Small File'));
295
await service.writeFile(joinPath(root, 'small_umlaut.txt'), VSBuffer.fromString('Small File with Ümlaut'));
296
297
await service.createFolder(joinPath(root, 'deep'));
298
await service.writeFile(joinPath(root, 'deep', 'conway.js'), VSBuffer.fromString('console.log("conway");'));
299
await service.writeFile(joinPath(root, 'deep', 'a.txt'), VSBuffer.fromString('A'));
300
await service.writeFile(joinPath(root, 'deep', 'b.txt'), VSBuffer.fromString('B'));
301
await service.writeFile(joinPath(root, 'deep', 'c.txt'), VSBuffer.fromString('C'));
302
303
await service.createFolder(joinPath(root, 'other', 'deep'));
304
await service.writeFile(joinPath(root, 'other', 'deep', '1.txt'), VSBuffer.fromString('1'));
305
await service.writeFile(joinPath(root, 'other', 'deep', '2.txt'), VSBuffer.fromString('2'));
306
await service.writeFile(joinPath(root, 'other', 'deep', '3.txt'), VSBuffer.fromString('3'));
307
await service.writeFile(joinPath(root, 'other', 'deep', '4.txt'), VSBuffer.fromString('4'));
308
309
return {
310
root,
311
indexHtml: joinPath(root, 'index.html'),
312
siteCss: joinPath(root, 'site.css'),
313
smallTxt: joinPath(root, 'small.txt'),
314
smallUmlautTxt: joinPath(root, 'small_umlaut.txt'),
315
deepDir: joinPath(root, 'deep'),
316
otherDeepDir: joinPath(root, 'other', 'deep')
317
};
318
}
319
320