mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-16 17:22:29 -05:00
2015 lines
70 KiB
TypeScript
2015 lines
70 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as assert from 'assert';
|
|
import { tmpdir } from 'os';
|
|
import { FileService } from 'vs/platform/files/common/fileService';
|
|
import { Schemas } from 'vs/base/common/network';
|
|
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
|
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
|
import { generateUuid } from 'vs/base/common/uuid';
|
|
import { join, basename, dirname, posix } from 'vs/base/common/path';
|
|
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
|
import { copy, rimraf, symlink, RimRafMode, rimrafSync } from 'vs/base/node/pfs';
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream } from 'fs';
|
|
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
|
import { NullLogService } from 'vs/platform/log/common/log';
|
|
import { isLinux, isWindows } from 'vs/base/common/platform';
|
|
import { DisposableStore } from 'vs/base/common/lifecycle';
|
|
import { isEqual } from 'vs/base/common/resources';
|
|
import { VSBuffer, VSBufferReadable, toVSBufferReadableStream, VSBufferReadableStream, bufferToReadable, bufferToStream } from 'vs/base/common/buffer';
|
|
|
|
function getByName(root: IFileStat, name: string): IFileStat | null {
|
|
if (root.children === undefined) {
|
|
return null;
|
|
}
|
|
|
|
for (const child of root.children) {
|
|
if (child.name === name) {
|
|
return child;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function toLineByLineReadable(content: string): VSBufferReadable {
|
|
let chunks = content.split('\n');
|
|
chunks = chunks.map((chunk, index) => {
|
|
if (index === 0) {
|
|
return chunk;
|
|
}
|
|
|
|
return '\n' + chunk;
|
|
});
|
|
|
|
return {
|
|
read(): VSBuffer | null {
|
|
const chunk = chunks.shift();
|
|
if (typeof chunk === 'string') {
|
|
return VSBuffer.fromString(chunk);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
};
|
|
}
|
|
|
|
export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
|
|
|
|
totalBytesRead: number = 0;
|
|
|
|
private invalidStatSize: boolean = false;
|
|
|
|
private _testCapabilities!: FileSystemProviderCapabilities;
|
|
get capabilities(): FileSystemProviderCapabilities {
|
|
if (!this._testCapabilities) {
|
|
this._testCapabilities =
|
|
FileSystemProviderCapabilities.FileReadWrite |
|
|
FileSystemProviderCapabilities.FileOpenReadWriteClose |
|
|
FileSystemProviderCapabilities.FileFolderCopy;
|
|
|
|
if (isLinux) {
|
|
this._testCapabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
|
|
}
|
|
}
|
|
|
|
return this._testCapabilities;
|
|
}
|
|
|
|
set capabilities(capabilities: FileSystemProviderCapabilities) {
|
|
this._testCapabilities = capabilities;
|
|
}
|
|
|
|
setInvalidStatSize(disabled: boolean): void {
|
|
this.invalidStatSize = disabled;
|
|
}
|
|
|
|
async stat(resource: URI): Promise<IStat> {
|
|
const res = await super.stat(resource);
|
|
|
|
if (this.invalidStatSize) {
|
|
res.size = String(res.size) as any; // for https://github.com/Microsoft/vscode/issues/72909
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
|
|
const bytesRead = await super.read(fd, pos, data, offset, length);
|
|
|
|
this.totalBytesRead += bytesRead;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
async readFile(resource: URI): Promise<Uint8Array> {
|
|
const res = await super.readFile(resource);
|
|
|
|
this.totalBytesRead += res.byteLength;
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
suite('Disk File Service', function () {
|
|
|
|
const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'diskfileservice');
|
|
const testSchema = 'test';
|
|
|
|
let service: FileService;
|
|
let fileProvider: TestDiskFileSystemProvider;
|
|
let testProvider: TestDiskFileSystemProvider;
|
|
let testDir: string;
|
|
|
|
const disposables = new DisposableStore();
|
|
|
|
// Given issues such as https://github.com/microsoft/vscode/issues/78602
|
|
// we see random test failures when accessing the native file system. To
|
|
// diagnose further, we retry node.js file access tests up to 3 times to
|
|
// rule out any random disk issue.
|
|
this.retries(3);
|
|
|
|
setup(async () => {
|
|
const logService = new NullLogService();
|
|
|
|
service = new FileService(logService);
|
|
disposables.add(service);
|
|
|
|
fileProvider = new TestDiskFileSystemProvider(logService);
|
|
disposables.add(service.registerProvider(Schemas.file, fileProvider));
|
|
disposables.add(fileProvider);
|
|
|
|
testProvider = new TestDiskFileSystemProvider(logService);
|
|
disposables.add(service.registerProvider(testSchema, testProvider));
|
|
disposables.add(testProvider);
|
|
|
|
const id = generateUuid();
|
|
testDir = join(parentDir, id);
|
|
const sourceDir = getPathFromAmdModule(require, './fixtures/service');
|
|
|
|
await copy(sourceDir, testDir);
|
|
});
|
|
|
|
teardown(async () => {
|
|
disposables.clear();
|
|
|
|
await rimraf(parentDir, RimRafMode.MOVE);
|
|
});
|
|
|
|
test('createFolder', async () => {
|
|
let event: FileOperationEvent | undefined;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const parent = await service.resolve(URI.file(testDir));
|
|
|
|
const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder'));
|
|
|
|
const newFolder = await service.createFolder(newFolderResource);
|
|
|
|
assert.equal(newFolder.name, 'newFolder');
|
|
assert.equal(existsSync(newFolder.resource.fsPath), true);
|
|
|
|
assert.ok(event);
|
|
assert.equal(event!.resource.fsPath, newFolderResource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.CREATE);
|
|
assert.equal(event!.target!.resource.fsPath, newFolderResource.fsPath);
|
|
assert.equal(event!.target!.isDirectory, true);
|
|
});
|
|
|
|
test('createFolder: creating multiple folders at once', async function () {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
|
|
const parent = await service.resolve(URI.file(testDir));
|
|
|
|
const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));
|
|
|
|
const newFolder = await service.createFolder(newFolderResource);
|
|
|
|
const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];
|
|
assert.equal(newFolder.name, lastFolderName);
|
|
assert.equal(existsSync(newFolder.resource.fsPath), true);
|
|
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, newFolderResource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.CREATE);
|
|
assert.equal(event!.target!.resource.fsPath, newFolderResource.fsPath);
|
|
assert.equal(event!.target!.isDirectory, true);
|
|
});
|
|
|
|
test('exists', async () => {
|
|
let exists = await service.exists(URI.file(testDir));
|
|
assert.equal(exists, true);
|
|
|
|
exists = await service.exists(URI.file(testDir + 'something'));
|
|
assert.equal(exists, false);
|
|
});
|
|
|
|
test('resolve', async () => {
|
|
const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });
|
|
assert.equal(resolved.children!.length, 8);
|
|
|
|
const deep = (getByName(resolved, 'deep')!);
|
|
assert.equal(deep.children!.length, 4);
|
|
});
|
|
|
|
test('resolve - directory', async () => {
|
|
const testsElements = ['examples', 'other', 'index.html', 'site.css'];
|
|
|
|
const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')));
|
|
|
|
assert.ok(result);
|
|
assert.ok(result.children);
|
|
assert.ok(result.children!.length > 0);
|
|
assert.ok(result!.isDirectory);
|
|
assert.equal(result.children!.length, testsElements.length);
|
|
|
|
assert.ok(result.children!.every(entry => {
|
|
return testsElements.some(name => {
|
|
return basename(entry.resource.fsPath) === name;
|
|
});
|
|
}));
|
|
|
|
result.children!.forEach(value => {
|
|
assert.ok(basename(value.resource.fsPath));
|
|
if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
|
|
assert.ok(value.isDirectory);
|
|
} else if (basename(value.resource.fsPath) === 'index.html') {
|
|
assert.ok(!value.isDirectory);
|
|
assert.ok(!value.children);
|
|
} else if (basename(value.resource.fsPath) === 'site.css') {
|
|
assert.ok(!value.isDirectory);
|
|
assert.ok(!value.children);
|
|
} else {
|
|
assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));
|
|
}
|
|
});
|
|
});
|
|
|
|
test('resolve - directory - with metadata', async () => {
|
|
const testsElements = ['examples', 'other', 'index.html', 'site.css'];
|
|
|
|
const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')), { resolveMetadata: true });
|
|
|
|
assert.ok(result);
|
|
assert.ok(result.children);
|
|
assert.ok(result.children!.length > 0);
|
|
assert.ok(result!.isDirectory);
|
|
assert.equal(result.children!.length, testsElements.length);
|
|
|
|
assert.ok(result.children!.every(entry => {
|
|
return testsElements.some(name => {
|
|
return basename(entry.resource.fsPath) === name;
|
|
});
|
|
}));
|
|
|
|
assert.ok(result.children!.every(entry => entry.etag.length > 0));
|
|
|
|
result.children!.forEach(value => {
|
|
assert.ok(basename(value.resource.fsPath));
|
|
if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
|
|
assert.ok(value.isDirectory);
|
|
} else if (basename(value.resource.fsPath) === 'index.html') {
|
|
assert.ok(!value.isDirectory);
|
|
assert.ok(!value.children);
|
|
} else if (basename(value.resource.fsPath) === 'site.css') {
|
|
assert.ok(!value.isDirectory);
|
|
assert.ok(!value.children);
|
|
} else {
|
|
assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));
|
|
}
|
|
});
|
|
});
|
|
|
|
test('resolve - directory - resolveTo single directory', async () => {
|
|
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
|
|
const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });
|
|
|
|
assert.ok(result);
|
|
assert.ok(result.children);
|
|
assert.ok(result.children!.length > 0);
|
|
assert.ok(result.isDirectory);
|
|
|
|
const children = result.children!;
|
|
assert.equal(children.length, 4);
|
|
|
|
const other = getByName(result, 'other');
|
|
assert.ok(other);
|
|
assert.ok(other!.children!.length > 0);
|
|
|
|
const deep = getByName(other!, 'deep');
|
|
assert.ok(deep);
|
|
assert.ok(deep!.children!.length > 0);
|
|
assert.equal(deep!.children!.length, 4);
|
|
});
|
|
|
|
test('resolve directory - resolveTo multiple directories', async () => {
|
|
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
|
|
const result = await service.resolve(URI.file(resolverFixturesPath), {
|
|
resolveTo: [
|
|
URI.file(join(resolverFixturesPath, 'other/deep')),
|
|
URI.file(join(resolverFixturesPath, 'examples'))
|
|
]
|
|
});
|
|
|
|
assert.ok(result);
|
|
assert.ok(result.children);
|
|
assert.ok(result.children!.length > 0);
|
|
assert.ok(result.isDirectory);
|
|
|
|
const children = result.children!;
|
|
assert.equal(children.length, 4);
|
|
|
|
const other = getByName(result, 'other');
|
|
assert.ok(other);
|
|
assert.ok(other!.children!.length > 0);
|
|
|
|
const deep = getByName(other!, 'deep');
|
|
assert.ok(deep);
|
|
assert.ok(deep!.children!.length > 0);
|
|
assert.equal(deep!.children!.length, 4);
|
|
|
|
const examples = getByName(result, 'examples');
|
|
assert.ok(examples);
|
|
assert.ok(examples!.children!.length > 0);
|
|
assert.equal(examples!.children!.length, 4);
|
|
});
|
|
|
|
test('resolve directory - resolveSingleChildFolders', async () => {
|
|
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver/other');
|
|
const result = await service.resolve(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true });
|
|
|
|
assert.ok(result);
|
|
assert.ok(result.children);
|
|
assert.ok(result.children!.length > 0);
|
|
assert.ok(result.isDirectory);
|
|
|
|
const children = result.children!;
|
|
assert.equal(children.length, 1);
|
|
|
|
let deep = getByName(result, 'deep');
|
|
assert.ok(deep);
|
|
assert.ok(deep!.children!.length > 0);
|
|
assert.equal(deep!.children!.length, 4);
|
|
});
|
|
|
|
test('resolves', async () => {
|
|
const res = await service.resolveAll([
|
|
{ resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } },
|
|
{ resource: URI.file(join(testDir, 'deep')) }
|
|
]);
|
|
|
|
const r1 = (res[0].stat!);
|
|
assert.equal(r1.children!.length, 8);
|
|
|
|
const deep = (getByName(r1, 'deep')!);
|
|
assert.equal(deep.children!.length, 4);
|
|
|
|
const r2 = (res[1].stat!);
|
|
assert.equal(r2.children!.length, 4);
|
|
assert.equal(r2.name, 'deep');
|
|
});
|
|
|
|
test('resolve - folder symbolic link', async () => {
|
|
if (isWindows) {
|
|
return; // not reliable on windows
|
|
}
|
|
|
|
const link = URI.file(join(testDir, 'deep-link'));
|
|
await symlink(join(testDir, 'deep'), link.fsPath);
|
|
|
|
const resolved = await service.resolve(link);
|
|
assert.equal(resolved.children!.length, 4);
|
|
assert.equal(resolved.isDirectory, true);
|
|
assert.equal(resolved.isSymbolicLink, true);
|
|
});
|
|
|
|
test('resolve - file symbolic link', async () => {
|
|
if (isWindows) {
|
|
return; // not reliable on windows
|
|
}
|
|
|
|
const link = URI.file(join(testDir, 'lorem.txt-linked'));
|
|
await symlink(join(testDir, 'lorem.txt'), link.fsPath);
|
|
|
|
const resolved = await service.resolve(link);
|
|
assert.equal(resolved.isDirectory, false);
|
|
assert.equal(resolved.isSymbolicLink, true);
|
|
});
|
|
|
|
test('resolve - invalid symbolic link does not break', async () => {
|
|
if (isWindows) {
|
|
return; // not reliable on windows
|
|
}
|
|
|
|
const link = URI.file(join(testDir, 'foo'));
|
|
await symlink(link.fsPath, join(testDir, 'bar'));
|
|
|
|
const resolved = await service.resolve(URI.file(testDir));
|
|
assert.equal(resolved.isDirectory, true);
|
|
assert.equal(resolved.children!.length, 8);
|
|
});
|
|
|
|
test('deleteFile', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const resource = URI.file(join(testDir, 'deep', 'conway.js'));
|
|
const source = await service.resolve(resource);
|
|
|
|
await service.del(source.resource);
|
|
|
|
assert.equal(existsSync(source.resource.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.DELETE);
|
|
});
|
|
|
|
test('deleteFolder (recursive)', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const resource = URI.file(join(testDir, 'deep'));
|
|
const source = await service.resolve(resource);
|
|
|
|
await service.del(source.resource, { recursive: true });
|
|
|
|
assert.equal(existsSync(source.resource.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.DELETE);
|
|
});
|
|
|
|
test('deleteFolder (non recursive)', async () => {
|
|
const resource = URI.file(join(testDir, 'deep'));
|
|
const source = await service.resolve(resource);
|
|
|
|
let error;
|
|
try {
|
|
await service.del(source.resource);
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
assert.ok(error);
|
|
});
|
|
|
|
test('move', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = URI.file(join(testDir, 'index.html'));
|
|
const sourceContents = readFileSync(source.fsPath);
|
|
|
|
const target = URI.file(join(dirname(source.fsPath), 'other.html'));
|
|
|
|
const renamed = await service.move(source, target);
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(existsSync(source.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.fsPath);
|
|
assert.equal(event!.operation, FileOperation.MOVE);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
|
|
const targetContents = readFileSync(target.fsPath);
|
|
|
|
assert.equal(sourceContents.byteLength, targetContents.byteLength);
|
|
assert.equal(sourceContents.toString(), targetContents.toString());
|
|
});
|
|
|
|
test('move - across providers (buffered => buffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await testMoveAcrossProviders();
|
|
});
|
|
|
|
test('move - across providers (unbuffered => unbuffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await testMoveAcrossProviders();
|
|
});
|
|
|
|
test('move - across providers (buffered => unbuffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await testMoveAcrossProviders();
|
|
});
|
|
|
|
test('move - across providers (unbuffered => buffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await testMoveAcrossProviders();
|
|
});
|
|
|
|
test('move - across providers - large (buffered => buffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await testMoveAcrossProviders('lorem.txt');
|
|
});
|
|
|
|
test('move - across providers - large (unbuffered => unbuffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await testMoveAcrossProviders('lorem.txt');
|
|
});
|
|
|
|
test('move - across providers - large (buffered => unbuffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await testMoveAcrossProviders('lorem.txt');
|
|
});
|
|
|
|
test('move - across providers - large (unbuffered => buffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await testMoveAcrossProviders('lorem.txt');
|
|
});
|
|
|
|
async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = URI.file(join(testDir, sourceFile));
|
|
const sourceContents = readFileSync(source.fsPath);
|
|
|
|
const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema });
|
|
|
|
const renamed = await service.move(source, target);
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(existsSync(source.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.fsPath);
|
|
assert.equal(event!.operation, FileOperation.COPY);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
|
|
const targetContents = readFileSync(target.fsPath);
|
|
|
|
assert.equal(sourceContents.byteLength, targetContents.byteLength);
|
|
assert.equal(sourceContents.toString(), targetContents.toString());
|
|
}
|
|
|
|
test('move - multi folder', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
|
|
const renameToPath = join(...multiFolderPaths, 'other.html');
|
|
|
|
const source = URI.file(join(testDir, 'index.html'));
|
|
|
|
const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath)));
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(existsSync(source.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.fsPath);
|
|
assert.equal(event!.operation, FileOperation.MOVE);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
});
|
|
|
|
test('move - directory', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = URI.file(join(testDir, 'deep'));
|
|
|
|
const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper')));
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(existsSync(source.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.fsPath);
|
|
assert.equal(event!.operation, FileOperation.MOVE);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
});
|
|
|
|
test('move - directory - across providers (buffered => buffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await testMoveFolderAcrossProviders();
|
|
});
|
|
|
|
test('move - directory - across providers (unbuffered => unbuffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await testMoveFolderAcrossProviders();
|
|
});
|
|
|
|
test('move - directory - across providers (buffered => unbuffered)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await testMoveFolderAcrossProviders();
|
|
});
|
|
|
|
test('move - directory - across providers (unbuffered => buffered)', async function () {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await testMoveFolderAcrossProviders();
|
|
});
|
|
|
|
async function testMoveFolderAcrossProviders(): Promise<void> {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = URI.file(join(testDir, 'deep'));
|
|
const sourceChildren = readdirSync(source.fsPath);
|
|
|
|
const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema });
|
|
|
|
const renamed = await service.move(source, target);
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(existsSync(source.fsPath), false);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.fsPath);
|
|
assert.equal(event!.operation, FileOperation.COPY);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
|
|
const targetChildren = readdirSync(target.fsPath);
|
|
assert.equal(sourceChildren.length, targetChildren.length);
|
|
for (let i = 0; i < sourceChildren.length; i++) {
|
|
assert.equal(sourceChildren[i], targetChildren[i]);
|
|
}
|
|
}
|
|
|
|
test('move - MIX CASE', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
assert.ok(source.size > 0);
|
|
|
|
const renamedResource = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
|
|
let renamed = await service.move(source.resource, renamedResource);
|
|
|
|
assert.equal(existsSync(renamedResource.fsPath), true);
|
|
assert.equal(basename(renamedResource.fsPath), 'INDEX.html');
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.MOVE);
|
|
assert.equal(event!.target!.resource.fsPath, renamedResource.fsPath);
|
|
|
|
renamed = await service.resolve(renamedResource, { resolveMetadata: true });
|
|
assert.equal(source.size, renamed.size);
|
|
});
|
|
|
|
test('move - same file', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
assert.ok(source.size > 0);
|
|
|
|
let renamed = await service.move(source.resource, URI.file(source.resource.fsPath));
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(basename(renamed.resource.fsPath), 'index.html');
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.MOVE);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
|
|
renamed = await service.resolve(renamed.resource, { resolveMetadata: true });
|
|
assert.equal(source.size, renamed.size);
|
|
});
|
|
|
|
test('move - same file #2', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
assert.ok(source.size > 0);
|
|
|
|
const targetParent = URI.file(testDir);
|
|
const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });
|
|
|
|
let renamed = await service.move(source.resource, target);
|
|
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.equal(basename(renamed.resource.fsPath), 'index.html');
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.MOVE);
|
|
assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
|
|
|
|
renamed = await service.resolve(renamed.resource, { resolveMetadata: true });
|
|
assert.equal(source.size, renamed.size);
|
|
});
|
|
|
|
test('move - source parent of target', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
const originalSize = source.size;
|
|
assert.ok(originalSize > 0);
|
|
|
|
let error;
|
|
try {
|
|
await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt')));
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.ok(!event!);
|
|
|
|
source = await service.resolve(source.resource, { resolveMetadata: true });
|
|
assert.equal(originalSize, source.size);
|
|
});
|
|
|
|
test('move - FILE_MOVE_CONFLICT', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
const originalSize = source.size;
|
|
assert.ok(originalSize > 0);
|
|
|
|
let error;
|
|
try {
|
|
await service.move(source.resource, URI.file(join(testDir, 'binary.txt')));
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
assert.equal(error.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT);
|
|
assert.ok(!event!);
|
|
|
|
source = await service.resolve(source.resource, { resolveMetadata: true });
|
|
assert.equal(originalSize, source.size);
|
|
});
|
|
|
|
test('move - overwrite folder with file', async () => {
|
|
let createEvent: FileOperationEvent;
|
|
let moveEvent: FileOperationEvent;
|
|
let deleteEvent: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => {
|
|
if (e.operation === FileOperation.CREATE) {
|
|
createEvent = e;
|
|
} else if (e.operation === FileOperation.DELETE) {
|
|
deleteEvent = e;
|
|
} else if (e.operation === FileOperation.MOVE) {
|
|
moveEvent = e;
|
|
}
|
|
}));
|
|
|
|
const parent = await service.resolve(URI.file(testDir));
|
|
const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
|
|
const f = await service.createFolder(folderResource);
|
|
const source = URI.file(join(testDir, 'deep', 'conway.js'));
|
|
|
|
const moved = await service.move(source, f.resource, true);
|
|
|
|
assert.equal(existsSync(moved.resource.fsPath), true);
|
|
assert.ok(statSync(moved.resource.fsPath).isFile);
|
|
assert.ok(createEvent!);
|
|
assert.ok(deleteEvent!);
|
|
assert.ok(moveEvent!);
|
|
assert.equal(moveEvent!.resource.fsPath, source.fsPath);
|
|
assert.equal(moveEvent!.target!.resource.fsPath, moved.resource.fsPath);
|
|
assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath);
|
|
});
|
|
|
|
test('copy', async () => {
|
|
await doTestCopy();
|
|
});
|
|
|
|
test('copy - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await doTestCopy();
|
|
});
|
|
|
|
test('copy - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
await doTestCopy('lorem.txt');
|
|
});
|
|
|
|
test('copy - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await doTestCopy();
|
|
});
|
|
|
|
test('copy - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
await doTestCopy('lorem.txt');
|
|
});
|
|
|
|
function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void {
|
|
provider.capabilities = capabilities;
|
|
if (isLinux) {
|
|
provider.capabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
|
|
}
|
|
}
|
|
|
|
async function doTestCopy(sourceName: string = 'index.html') {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = await service.resolve(URI.file(join(testDir, sourceName)));
|
|
const target = URI.file(join(testDir, 'other.html'));
|
|
|
|
const copied = await service.copy(source.resource, target);
|
|
|
|
assert.equal(existsSync(copied.resource.fsPath), true);
|
|
assert.equal(existsSync(source.resource.fsPath), true);
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.COPY);
|
|
assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath);
|
|
|
|
const sourceContents = readFileSync(source.resource.fsPath);
|
|
const targetContents = readFileSync(target.fsPath);
|
|
|
|
assert.equal(sourceContents.byteLength, targetContents.byteLength);
|
|
assert.equal(sourceContents.toString(), targetContents.toString());
|
|
}
|
|
|
|
test('copy - overwrite folder with file', async () => {
|
|
let createEvent: FileOperationEvent;
|
|
let copyEvent: FileOperationEvent;
|
|
let deleteEvent: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => {
|
|
if (e.operation === FileOperation.CREATE) {
|
|
createEvent = e;
|
|
} else if (e.operation === FileOperation.DELETE) {
|
|
deleteEvent = e;
|
|
} else if (e.operation === FileOperation.COPY) {
|
|
copyEvent = e;
|
|
}
|
|
}));
|
|
|
|
const parent = await service.resolve(URI.file(testDir));
|
|
const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
|
|
const f = await service.createFolder(folderResource);
|
|
const source = URI.file(join(testDir, 'deep', 'conway.js'));
|
|
|
|
const copied = await service.copy(source, f.resource, true);
|
|
|
|
assert.equal(existsSync(copied.resource.fsPath), true);
|
|
assert.ok(statSync(copied.resource.fsPath).isFile);
|
|
assert.ok(createEvent!);
|
|
assert.ok(deleteEvent!);
|
|
assert.ok(copyEvent!);
|
|
assert.equal(copyEvent!.resource.fsPath, source.fsPath);
|
|
assert.equal(copyEvent!.target!.resource.fsPath, copied.resource.fsPath);
|
|
assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath);
|
|
});
|
|
|
|
test('copy - MIX CASE same target - no overwrite', async () => {
|
|
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
const originalSize = source.size;
|
|
assert.ok(originalSize > 0);
|
|
|
|
const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
|
|
|
|
let error;
|
|
let copied: IFileStatWithMetadata;
|
|
try {
|
|
copied = await service.copy(source.resource, target);
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
if (isLinux) {
|
|
assert.ok(!error);
|
|
|
|
assert.equal(existsSync(copied!.resource.fsPath), true);
|
|
assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));
|
|
assert.equal(source.size, copied!.size);
|
|
} else {
|
|
assert.ok(error);
|
|
|
|
source = await service.resolve(source.resource, { resolveMetadata: true });
|
|
assert.equal(originalSize, source.size);
|
|
}
|
|
});
|
|
|
|
test('copy - MIX CASE same target - overwrite', async () => {
|
|
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
const originalSize = source.size;
|
|
assert.ok(originalSize > 0);
|
|
|
|
const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
|
|
|
|
let error;
|
|
let copied: IFileStatWithMetadata;
|
|
try {
|
|
copied = await service.copy(source.resource, target, true);
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
if (isLinux) {
|
|
assert.ok(!error);
|
|
|
|
assert.equal(existsSync(copied!.resource.fsPath), true);
|
|
assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));
|
|
assert.equal(source.size, copied!.size);
|
|
} else {
|
|
assert.ok(error);
|
|
|
|
source = await service.resolve(source.resource, { resolveMetadata: true });
|
|
assert.equal(originalSize, source.size);
|
|
}
|
|
});
|
|
|
|
test('copy - MIX CASE different taget - overwrite', async () => {
|
|
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
assert.ok(source.size > 0);
|
|
|
|
const renamed = await service.move(source.resource, URI.file(join(dirname(source.resource.fsPath), 'CONWAY.js')));
|
|
assert.equal(existsSync(renamed.resource.fsPath), true);
|
|
assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js'));
|
|
assert.equal(source.size, renamed.size);
|
|
|
|
const source_1 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true });
|
|
const target = URI.file(join(testDir, basename(source_1.resource.path)));
|
|
|
|
const res = await service.copy(source_1.resource, target, true);
|
|
assert.equal(existsSync(res.resource.fsPath), true);
|
|
assert.ok(readdirSync(testDir).some(f => f === 'conway.js'));
|
|
assert.equal(source_1.size, res.size);
|
|
});
|
|
|
|
test('copy - same file', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
assert.ok(source.size > 0);
|
|
|
|
let copied = await service.copy(source.resource, URI.file(source.resource.fsPath));
|
|
|
|
assert.equal(existsSync(copied.resource.fsPath), true);
|
|
assert.equal(basename(copied.resource.fsPath), 'index.html');
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.COPY);
|
|
assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath);
|
|
|
|
copied = await service.resolve(source.resource, { resolveMetadata: true });
|
|
assert.equal(source.size, copied.size);
|
|
});
|
|
|
|
test('copy - same file #2', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
|
|
assert.ok(source.size > 0);
|
|
|
|
const targetParent = URI.file(testDir);
|
|
const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });
|
|
|
|
let copied = await service.copy(source.resource, URI.file(target.fsPath));
|
|
|
|
assert.equal(existsSync(copied.resource.fsPath), true);
|
|
assert.equal(basename(copied.resource.fsPath), 'index.html');
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, source.resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.COPY);
|
|
assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath);
|
|
|
|
copied = await service.resolve(source.resource, { resolveMetadata: true });
|
|
assert.equal(source.size, copied.size);
|
|
});
|
|
|
|
test('readFile - small file - buffered', () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
return testReadFile(URI.file(join(testDir, 'small.txt')));
|
|
});
|
|
|
|
test('readFile - small file - buffered / readonly', () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);
|
|
|
|
return testReadFile(URI.file(join(testDir, 'small.txt')));
|
|
});
|
|
|
|
test('readFile - small file - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
return testReadFile(URI.file(join(testDir, 'small.txt')));
|
|
});
|
|
|
|
test('readFile - small file - unbuffered / readonly', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);
|
|
|
|
return testReadFile(URI.file(join(testDir, 'small.txt')));
|
|
});
|
|
|
|
test('readFile - large file - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
return testReadFile(URI.file(join(testDir, 'lorem.txt')));
|
|
});
|
|
|
|
test('readFile - large file - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
return testReadFile(URI.file(join(testDir, 'lorem.txt')));
|
|
});
|
|
|
|
async function testReadFile(resource: URI): Promise<void> {
|
|
const content = await service.readFile(resource);
|
|
|
|
assert.equal(content.value.toString(), readFileSync(resource.fsPath));
|
|
}
|
|
|
|
test('readFile - Files are intermingled #38331 - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
let resource1 = URI.file(join(testDir, 'lorem.txt'));
|
|
let resource2 = URI.file(join(testDir, 'some_utf16le.css'));
|
|
|
|
// load in sequence and keep data
|
|
const value1 = await service.readFile(resource1);
|
|
const value2 = await service.readFile(resource2);
|
|
|
|
// load in parallel in expect the same result
|
|
const result = await Promise.all([
|
|
service.readFile(resource1),
|
|
service.readFile(resource2)
|
|
]);
|
|
|
|
assert.equal(result[0].value.toString(), value1.value.toString());
|
|
assert.equal(result[1].value.toString(), value2.value.toString());
|
|
});
|
|
|
|
test('readFile - Files are intermingled #38331 - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
let resource1 = URI.file(join(testDir, 'lorem.txt'));
|
|
let resource2 = URI.file(join(testDir, 'some_utf16le.css'));
|
|
|
|
// load in sequence and keep data
|
|
const value1 = await service.readFile(resource1);
|
|
const value2 = await service.readFile(resource2);
|
|
|
|
// load in parallel in expect the same result
|
|
const result = await Promise.all([
|
|
service.readFile(resource1),
|
|
service.readFile(resource2)
|
|
]);
|
|
|
|
assert.equal(result[0].value.toString(), value1.value.toString());
|
|
assert.equal(result[1].value.toString(), value2.value.toString());
|
|
});
|
|
|
|
test('readFile - from position (ASCII) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const contents = await service.readFile(resource, { position: 6 });
|
|
|
|
assert.equal(contents.value.toString(), 'File');
|
|
});
|
|
|
|
test('readFile - from position (with umlaut) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'small_umlaut.txt'));
|
|
|
|
const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });
|
|
|
|
assert.equal(contents.value.toString(), 'mlaut');
|
|
});
|
|
|
|
test('readFile - from position (ASCII) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const contents = await service.readFile(resource, { position: 6 });
|
|
|
|
assert.equal(contents.value.toString(), 'File');
|
|
});
|
|
|
|
test('readFile - from position (with umlaut) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'small_umlaut.txt'));
|
|
|
|
const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });
|
|
|
|
assert.equal(contents.value.toString(), 'mlaut');
|
|
});
|
|
|
|
|
|
test('readFile - 3 bytes (ASCII) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const contents = await service.readFile(resource, { length: 3 });
|
|
|
|
assert.equal(contents.value.toString(), 'Sma');
|
|
});
|
|
|
|
test('readFile - 3 bytes (ASCII) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const contents = await service.readFile(resource, { length: 3 });
|
|
|
|
assert.equal(contents.value.toString(), 'Sma');
|
|
});
|
|
|
|
test('readFile - 20000 bytes (large) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const contents = await service.readFile(resource, { length: 20000 });
|
|
|
|
assert.equal(contents.value.byteLength, 20000);
|
|
});
|
|
|
|
test('readFile - 20000 bytes (large) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const contents = await service.readFile(resource, { length: 20000 });
|
|
|
|
assert.equal(contents.value.byteLength, 20000);
|
|
});
|
|
|
|
test('readFile - 80000 bytes (large) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const contents = await service.readFile(resource, { length: 80000 });
|
|
|
|
assert.equal(contents.value.byteLength, 80000);
|
|
});
|
|
|
|
test('readFile - 80000 bytes (large) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const contents = await service.readFile(resource, { length: 80000 });
|
|
|
|
assert.equal(contents.value.byteLength, 80000);
|
|
});
|
|
|
|
test('readFile - FILE_IS_DIRECTORY', async () => {
|
|
const resource = URI.file(join(testDir, 'deep'));
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource);
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY);
|
|
});
|
|
|
|
test('readFile - FILE_NOT_FOUND', async () => {
|
|
const resource = URI.file(join(testDir, '404.html'));
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource);
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_FOUND);
|
|
});
|
|
|
|
test('readFile - FILE_NOT_MODIFIED_SINCE - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
const contents = await service.readFile(resource);
|
|
fileProvider.totalBytesRead = 0;
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { etag: contents.etag });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);
|
|
assert.equal(fileProvider.totalBytesRead, 0);
|
|
});
|
|
|
|
test('readFile - FILE_NOT_MODIFIED_SINCE does not fire wrongly - https://github.com/Microsoft/vscode/issues/72909', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
fileProvider.setInvalidStatSize(true);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
await service.readFile(resource);
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { etag: undefined });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(!error);
|
|
});
|
|
|
|
test('readFile - FILE_NOT_MODIFIED_SINCE - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
const contents = await service.readFile(resource);
|
|
fileProvider.totalBytesRead = 0;
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { etag: contents.etag });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);
|
|
assert.equal(fileProvider.totalBytesRead, 0);
|
|
});
|
|
|
|
test('readFile - FILE_EXCEED_MEMORY_LIMIT - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { limits: { memory: 10 } });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
|
|
});
|
|
|
|
test('readFile - FILE_EXCEED_MEMORY_LIMIT - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { limits: { memory: 10 } });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
|
|
});
|
|
|
|
test('readFile - FILE_TOO_LARGE - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { limits: { size: 10 } });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);
|
|
});
|
|
|
|
test('readFile - FILE_TOO_LARGE - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'index.html'));
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.readFile(resource, { limits: { size: 10 } });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);
|
|
});
|
|
|
|
test('createFile', async () => {
|
|
assertCreateFile(contents => VSBuffer.fromString(contents));
|
|
});
|
|
|
|
test('createFile (readable)', async () => {
|
|
assertCreateFile(contents => bufferToReadable(VSBuffer.fromString(contents)));
|
|
});
|
|
|
|
test('createFile (stream)', async () => {
|
|
assertCreateFile(contents => bufferToStream(VSBuffer.fromString(contents)));
|
|
});
|
|
|
|
async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const contents = 'Hello World';
|
|
const resource = URI.file(join(testDir, 'test.txt'));
|
|
const fileStat = await service.createFile(resource, converter(contents));
|
|
assert.equal(fileStat.name, 'test.txt');
|
|
assert.equal(existsSync(fileStat.resource.fsPath), true);
|
|
assert.equal(readFileSync(fileStat.resource.fsPath), contents);
|
|
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.CREATE);
|
|
assert.equal(event!.target!.resource.fsPath, resource.fsPath);
|
|
}
|
|
|
|
test('createFile (does not overwrite by default)', async () => {
|
|
const contents = 'Hello World';
|
|
const resource = URI.file(join(testDir, 'test.txt'));
|
|
|
|
writeFileSync(resource.fsPath, ''); // create file
|
|
|
|
let error;
|
|
try {
|
|
await service.createFile(resource, VSBuffer.fromString(contents));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
});
|
|
|
|
test('createFile (allows to overwrite existing)', async () => {
|
|
let event: FileOperationEvent;
|
|
disposables.add(service.onAfterOperation(e => event = e));
|
|
|
|
const contents = 'Hello World';
|
|
const resource = URI.file(join(testDir, 'test.txt'));
|
|
|
|
writeFileSync(resource.fsPath, ''); // create file
|
|
|
|
const fileStat = await service.createFile(resource, VSBuffer.fromString(contents), { overwrite: true });
|
|
assert.equal(fileStat.name, 'test.txt');
|
|
assert.equal(existsSync(fileStat.resource.fsPath), true);
|
|
assert.equal(readFileSync(fileStat.resource.fsPath), contents);
|
|
|
|
assert.ok(event!);
|
|
assert.equal(event!.resource.fsPath, resource.fsPath);
|
|
assert.equal(event!.operation, FileOperation.CREATE);
|
|
assert.equal(event!.target!.resource.fsPath, resource.fsPath);
|
|
});
|
|
|
|
test('writeFile - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent));
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile (large file) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
const newContent = content.toString() + content.toString();
|
|
|
|
const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent));
|
|
assert.equal(fileStat.name, 'lorem.txt');
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile - buffered - readonly throws', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
|
|
let error: Error;
|
|
try {
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error!);
|
|
});
|
|
|
|
test('writeFile - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent));
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile (large file) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
const newContent = content.toString() + content.toString();
|
|
|
|
const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent));
|
|
assert.equal(fileStat.name, 'lorem.txt');
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile - unbuffered - readonly throws', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
|
|
let error: Error;
|
|
try {
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error!);
|
|
});
|
|
|
|
test('writeFile (large file) - multiple parallel writes queue up', async () => {
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
const newContent = content.toString() + content.toString();
|
|
|
|
await Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {
|
|
const fileStat = await service.writeFile(resource, VSBuffer.fromString(offset + newContent));
|
|
assert.equal(fileStat.name, 'lorem.txt');
|
|
}));
|
|
|
|
const fileContent = readFileSync(resource.fsPath).toString();
|
|
assert.ok(['0', '00', '000', '0000', '00000'].some(offset => fileContent === offset + newContent));
|
|
});
|
|
|
|
test('writeFile (readable) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
await service.writeFile(resource, toLineByLineReadable(newContent));
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile (large file - readable) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
const newContent = content.toString() + content.toString();
|
|
|
|
const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));
|
|
assert.equal(fileStat.name, 'lorem.txt');
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile (readable) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
await service.writeFile(resource, toLineByLineReadable(newContent));
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile (large file - readable) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
const newContent = content.toString() + content.toString();
|
|
|
|
const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));
|
|
assert.equal(fileStat.name, 'lorem.txt');
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile (stream) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const source = URI.file(join(testDir, 'small.txt'));
|
|
const target = URI.file(join(testDir, 'small-copy.txt'));
|
|
|
|
const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
|
|
assert.equal(fileStat.name, 'small-copy.txt');
|
|
|
|
assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
|
|
});
|
|
|
|
test('writeFile (large file - stream) - buffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
|
|
|
const source = URI.file(join(testDir, 'lorem.txt'));
|
|
const target = URI.file(join(testDir, 'lorem-copy.txt'));
|
|
|
|
const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
|
|
assert.equal(fileStat.name, 'lorem-copy.txt');
|
|
|
|
assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
|
|
});
|
|
|
|
test('writeFile (stream) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const source = URI.file(join(testDir, 'small.txt'));
|
|
const target = URI.file(join(testDir, 'small-copy.txt'));
|
|
|
|
const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
|
|
assert.equal(fileStat.name, 'small-copy.txt');
|
|
|
|
assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
|
|
});
|
|
|
|
test('writeFile (large file - stream) - unbuffered', async () => {
|
|
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
|
|
|
|
const source = URI.file(join(testDir, 'lorem.txt'));
|
|
const target = URI.file(join(testDir, 'lorem-copy.txt'));
|
|
|
|
const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
|
|
assert.equal(fileStat.name, 'lorem-copy.txt');
|
|
|
|
assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
|
|
});
|
|
|
|
test('writeFile (file is created including parents)', async () => {
|
|
const resource = URI.file(join(testDir, 'other', 'newfile.txt'));
|
|
|
|
const content = 'File is created including parent';
|
|
const fileStat = await service.writeFile(resource, VSBuffer.fromString(content));
|
|
assert.equal(fileStat.name, 'newfile.txt');
|
|
|
|
assert.equal(readFileSync(resource.fsPath), content);
|
|
});
|
|
|
|
test('writeFile (error when folder is encountered)', async () => {
|
|
const resource = URI.file(testDir);
|
|
|
|
let error: Error | undefined = undefined;
|
|
try {
|
|
await service.writeFile(resource, VSBuffer.fromString('File is created including parent'));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
});
|
|
|
|
test('writeFile (no error when providing up to date etag)', async () => {
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const stat = await service.resolve(resource);
|
|
|
|
const content = readFileSync(resource.fsPath);
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
|
|
|
|
assert.equal(readFileSync(resource.fsPath), newContent);
|
|
});
|
|
|
|
test('writeFile - error when writing to file that has been updated meanwhile', async () => {
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const stat = await service.resolve(resource);
|
|
|
|
const content = readFileSync(resource.fsPath).toString();
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = 'Updates to the small file';
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
|
|
|
|
const newContentLeadingToError = newContent + newContent;
|
|
|
|
const fakeMtime = 1000;
|
|
const fakeSize = 1000;
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToError), { etag: etag({ mtime: fakeMtime, size: fakeSize }), mtime: fakeMtime });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(error);
|
|
assert.ok(error instanceof FileOperationError);
|
|
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);
|
|
});
|
|
|
|
test('writeFile - no error when writing to file where size is the same', async () => {
|
|
const resource = URI.file(join(testDir, 'small.txt'));
|
|
|
|
const stat = await service.resolve(resource);
|
|
|
|
const content = readFileSync(resource.fsPath).toString();
|
|
assert.equal(content, 'Small File');
|
|
|
|
const newContent = content; // same content
|
|
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
|
|
|
|
const newContentLeadingToNoError = newContent; // writing the same content should be OK
|
|
|
|
const fakeMtime = 1000;
|
|
const actualSize = newContent.length;
|
|
|
|
let error: FileOperationError | undefined = undefined;
|
|
try {
|
|
await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToNoError), { etag: etag({ mtime: fakeMtime, size: actualSize }), mtime: fakeMtime });
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
assert.ok(!error);
|
|
});
|
|
|
|
const runWatchTests = isLinux;
|
|
|
|
(runWatchTests ? test : test.skip)('watch - file', done => {
|
|
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
|
|
writeFileSync(toWatch.fsPath, 'Init');
|
|
|
|
assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
|
|
|
|
setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes'), 50);
|
|
});
|
|
|
|
(runWatchTests && !isWindows /* symbolic links not reliable on windows */ ? test : test.skip)('watch - file symbolic link', async done => {
|
|
const toWatch = URI.file(join(testDir, 'lorem.txt-linked'));
|
|
await symlink(join(testDir, 'lorem.txt'), toWatch.fsPath);
|
|
|
|
assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
|
|
|
|
setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes'), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - file - multiple writes', done => {
|
|
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
|
|
writeFileSync(toWatch.fsPath, 'Init');
|
|
|
|
assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
|
|
|
|
setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes 1'), 0);
|
|
setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes 2'), 10);
|
|
setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes 3'), 20);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - file - delete file', done => {
|
|
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
|
|
writeFileSync(toWatch.fsPath, 'Init');
|
|
|
|
assertWatch(toWatch, [[FileChangeType.DELETED, toWatch]], done);
|
|
|
|
setTimeout(() => unlinkSync(toWatch.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - file - rename file', done => {
|
|
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
|
|
const toWatchRenamed = URI.file(join(testDir, 'index-watch1-renamed.html'));
|
|
writeFileSync(toWatch.fsPath, 'Init');
|
|
|
|
assertWatch(toWatch, [[FileChangeType.DELETED, toWatch]], done);
|
|
|
|
setTimeout(() => renameSync(toWatch.fsPath, toWatchRenamed.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - file - rename file (different case)', done => {
|
|
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
|
|
const toWatchRenamed = URI.file(join(testDir, 'INDEX-watch1.html'));
|
|
writeFileSync(toWatch.fsPath, 'Init');
|
|
|
|
if (isLinux) {
|
|
assertWatch(toWatch, [[FileChangeType.DELETED, toWatch]], done);
|
|
} else {
|
|
assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done); // case insensitive file system treat this as change
|
|
}
|
|
|
|
setTimeout(() => renameSync(toWatch.fsPath, toWatchRenamed.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - file (atomic save)', function (done) {
|
|
const toWatch = URI.file(join(testDir, 'index-watch2.html'));
|
|
writeFileSync(toWatch.fsPath, 'Init');
|
|
|
|
assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
|
|
|
|
setTimeout(() => {
|
|
// Simulate atomic save by deleting the file, creating it under different name
|
|
// and then replacing the previously deleted file with those contents
|
|
const renamed = `${toWatch.fsPath}.bak`;
|
|
unlinkSync(toWatch.fsPath);
|
|
writeFileSync(renamed, 'Changes');
|
|
renameSync(renamed, toWatch.fsPath);
|
|
}, 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - change file', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch3'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
|
writeFileSync(file.fsPath, 'Init');
|
|
|
|
assertWatch(watchDir, [[FileChangeType.UPDATED, file]], done);
|
|
|
|
setTimeout(() => writeFileSync(file.fsPath, 'Changes'), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - add file', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch4'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
|
|
|
assertWatch(watchDir, [[FileChangeType.ADDED, file]], done);
|
|
|
|
setTimeout(() => writeFileSync(file.fsPath, 'Changes'), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - delete file', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch5'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
|
writeFileSync(file.fsPath, 'Init');
|
|
|
|
assertWatch(watchDir, [[FileChangeType.DELETED, file]], done);
|
|
|
|
setTimeout(() => unlinkSync(file.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - add folder', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch6'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const folder = URI.file(join(watchDir.fsPath, 'folder'));
|
|
|
|
assertWatch(watchDir, [[FileChangeType.ADDED, folder]], done);
|
|
|
|
setTimeout(() => mkdirSync(folder.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - delete folder', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch7'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const folder = URI.file(join(watchDir.fsPath, 'folder'));
|
|
mkdirSync(folder.fsPath);
|
|
|
|
assertWatch(watchDir, [[FileChangeType.DELETED, folder]], done);
|
|
|
|
setTimeout(() => rimrafSync(folder.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests && !isWindows /* symbolic links not reliable on windows */ ? test : test.skip)('watch - folder (non recursive) - symbolic link - change file', async done => {
|
|
const watchDir = URI.file(join(testDir, 'deep-link'));
|
|
await symlink(join(testDir, 'deep'), watchDir.fsPath);
|
|
|
|
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
|
writeFileSync(file.fsPath, 'Init');
|
|
|
|
assertWatch(watchDir, [[FileChangeType.UPDATED, file]], done);
|
|
|
|
setTimeout(() => writeFileSync(file.fsPath, 'Changes'), 50);
|
|
});
|
|
|
|
(runWatchTests ? test : test.skip)('watch - folder (non recursive) - rename file', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch8'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
|
writeFileSync(file.fsPath, 'Init');
|
|
|
|
const fileRenamed = URI.file(join(watchDir.fsPath, 'index-renamed.html'));
|
|
|
|
assertWatch(watchDir, [[FileChangeType.DELETED, file], [FileChangeType.ADDED, fileRenamed]], done);
|
|
|
|
setTimeout(() => renameSync(file.fsPath, fileRenamed.fsPath), 50);
|
|
});
|
|
|
|
(runWatchTests && isLinux /* this test requires a case sensitive file system */ ? test : test.skip)('watch - folder (non recursive) - rename file (different case)', done => {
|
|
const watchDir = URI.file(join(testDir, 'watch8'));
|
|
mkdirSync(watchDir.fsPath);
|
|
|
|
const file = URI.file(join(watchDir.fsPath, 'index.html'));
|
|
writeFileSync(file.fsPath, 'Init');
|
|
|
|
const fileRenamed = URI.file(join(watchDir.fsPath, 'INDEX.html'));
|
|
|
|
assertWatch(watchDir, [[FileChangeType.DELETED, file], [FileChangeType.ADDED, fileRenamed]], done);
|
|
|
|
setTimeout(() => renameSync(file.fsPath, fileRenamed.fsPath), 50);
|
|
});
|
|
|
|
function assertWatch(toWatch: URI, expected: [FileChangeType, URI][], done: MochaDone): void {
|
|
const watcherDisposable = service.watch(toWatch);
|
|
|
|
function toString(type: FileChangeType): string {
|
|
switch (type) {
|
|
case FileChangeType.ADDED: return 'added';
|
|
case FileChangeType.DELETED: return 'deleted';
|
|
case FileChangeType.UPDATED: return 'updated';
|
|
}
|
|
}
|
|
|
|
function printEvents(event: FileChangesEvent): string {
|
|
return event.changes.map(change => `Change: type ${toString(change.type)} path ${change.resource.toString()}`).join('\n');
|
|
}
|
|
|
|
const listenerDisposable = service.onFileChanges(event => {
|
|
watcherDisposable.dispose();
|
|
listenerDisposable.dispose();
|
|
|
|
try {
|
|
assert.equal(event.changes.length, expected.length, `Expected ${expected.length} events, but got ${event.changes.length}. Details (${printEvents(event)})`);
|
|
|
|
if (expected.length === 1) {
|
|
assert.equal(event.changes[0].type, expected[0][0], `Expected ${toString(expected[0][0])} but got ${toString(event.changes[0].type)}. Details (${printEvents(event)})`);
|
|
assert.equal(event.changes[0].resource.fsPath, expected[0][1].fsPath);
|
|
} else {
|
|
for (const expect of expected) {
|
|
assert.equal(hasChange(event.changes, expect[0], expect[1]), true, `Unable to find ${toString(expect[0])} for ${expect[1].fsPath}. Details (${printEvents(event)})`);
|
|
}
|
|
}
|
|
|
|
done();
|
|
} catch (error) {
|
|
done(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function hasChange(changes: readonly IFileChange[], type: FileChangeType, resource: URI): boolean {
|
|
return changes.some(change => change.type === type && isEqual(change.resource, resource));
|
|
}
|
|
|
|
test('read - mixed positions', async () => {
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
// read multiple times from position 0
|
|
let buffer = VSBuffer.alloc(1024);
|
|
let fd = await fileProvider.open(resource, { create: false });
|
|
for (let i = 0; i < 3; i++) {
|
|
await fileProvider.read(fd, 0, buffer.buffer, 0, 26);
|
|
assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
|
|
}
|
|
await fileProvider.close(fd);
|
|
|
|
// read multiple times at various locations
|
|
buffer = VSBuffer.alloc(1024);
|
|
fd = await fileProvider.open(resource, { create: false });
|
|
|
|
let posInFile = 0;
|
|
|
|
await fileProvider.read(fd, posInFile, buffer.buffer, 0, 26);
|
|
assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
|
|
posInFile += 26;
|
|
|
|
await fileProvider.read(fd, posInFile, buffer.buffer, 0, 1);
|
|
assert.equal(buffer.slice(0, 1).toString(), ',');
|
|
posInFile += 1;
|
|
|
|
await fileProvider.read(fd, posInFile, buffer.buffer, 0, 12);
|
|
assert.equal(buffer.slice(0, 12).toString(), ' consectetur');
|
|
posInFile += 12;
|
|
|
|
await fileProvider.read(fd, 98 /* no longer in sequence of posInFile */, buffer.buffer, 0, 9);
|
|
assert.equal(buffer.slice(0, 9).toString(), 'fermentum');
|
|
|
|
await fileProvider.read(fd, 27, buffer.buffer, 0, 12);
|
|
assert.equal(buffer.slice(0, 12).toString(), ' consectetur');
|
|
|
|
await fileProvider.read(fd, 26, buffer.buffer, 0, 1);
|
|
assert.equal(buffer.slice(0, 1).toString(), ',');
|
|
|
|
await fileProvider.read(fd, 0, buffer.buffer, 0, 26);
|
|
assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
|
|
|
|
await fileProvider.read(fd, posInFile /* back in sequence */, buffer.buffer, 0, 11);
|
|
assert.equal(buffer.slice(0, 11).toString(), ' adipiscing');
|
|
|
|
await fileProvider.close(fd);
|
|
});
|
|
|
|
test('write - mixed positions', async () => {
|
|
const resource = URI.file(join(testDir, 'lorem.txt'));
|
|
|
|
const buffer = VSBuffer.alloc(1024);
|
|
const fdWrite = await fileProvider.open(resource, { create: true });
|
|
const fdRead = await fileProvider.open(resource, { create: false });
|
|
|
|
let posInFileWrite = 0;
|
|
let posInFileRead = 0;
|
|
|
|
const initialContents = VSBuffer.fromString('Lorem ipsum dolor sit amet');
|
|
await fileProvider.write(fdWrite, posInFileWrite, initialContents.buffer, 0, initialContents.byteLength);
|
|
posInFileWrite += initialContents.byteLength;
|
|
|
|
await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, 26);
|
|
assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
|
|
posInFileRead += 26;
|
|
|
|
const contents = VSBuffer.fromString('Hello World');
|
|
|
|
await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);
|
|
posInFileWrite += contents.byteLength;
|
|
|
|
await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, contents.byteLength);
|
|
assert.equal(buffer.slice(0, contents.byteLength).toString(), 'Hello World');
|
|
posInFileRead += contents.byteLength;
|
|
|
|
await fileProvider.write(fdWrite, 6, contents.buffer, 0, contents.byteLength);
|
|
|
|
await fileProvider.read(fdRead, 0, buffer.buffer, 0, 11);
|
|
assert.equal(buffer.slice(0, 11).toString(), 'Lorem Hello');
|
|
|
|
await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);
|
|
posInFileWrite += contents.byteLength;
|
|
|
|
await fileProvider.read(fdRead, posInFileWrite - contents.byteLength, buffer.buffer, 0, contents.byteLength);
|
|
assert.equal(buffer.slice(0, contents.byteLength).toString(), 'Hello World');
|
|
|
|
await fileProvider.close(fdWrite);
|
|
await fileProvider.close(fdRead);
|
|
});
|
|
});
|